diff --git a/doc/rtd/content/01_welcome/sub/deps.txt b/doc/rtd/content/01_welcome/sub/deps.txt index be1755d09..0b99e24c2 100644 --- a/doc/rtd/content/01_welcome/sub/deps.txt +++ b/doc/rtd/content/01_welcome/sub/deps.txt @@ -1,7 +1,7 @@ -dill,0.3.6 +dill,0.3.9 +multiprocess,0.70.17 numpy,1.24.2 -torch,2.0.0 -matplotlib,3.7.1 +PySide6,6.8.1 +matplotlib,3.10.0 scipy,1.10.1 -multiprocess,0.70.14 pandas,2.1.3 diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer0_elementary/04_data.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer0_elementary/04_data.rst index d47f7f458..fbc9da0d2 100644 --- a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer0_elementary/04_data.rst +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer0_elementary/04_data.rst @@ -2,7 +2,7 @@ Data Management =============== Data management in a framework is extremely important, which mostly refers to the organization, storage, and retrieval of data within the framework. -In MLPro, our team also provides such functionalities as saving data, loading data, storing data, creating a buffer, and plotting data. +MLPro also provides such functionalities as saving data, loading data, storing data, creating a buffer, and plotting data. This involves defining a data model that describes the structure and relationships between data elements, implementing mechanisms for storing and retrieving data, and managing data consistency and integrity. A well-designed data management system is essential for the efficient and effective processing of data within the framework. @@ -14,7 +14,7 @@ The related data management classes can be accessed as follows: from mlpro.bf.data import * -In general, there are two main functionalities of data management in MLPro: +In general, there are three main functionalities of data management in MLPro: 1) **Data Storing** The second possibility is to store a bunch of data in MLPro's **DataStoring** class with three different layers, as follows: @@ -53,6 +53,11 @@ In general, there are two main functionalities of data management in MLPro: - Clearing the buffer. For better understanding : :ref:`Howto BF-004: Buffers ` + + + 3) **Configuration data** + A further class named **ConfigFile** enables persistent storage of program configuration data in a local JSON file. + **Cross Reference** + :ref:`Howto BF-003: Store and plot data ` diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/04_control.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/04_control.rst new file mode 100644 index 000000000..ea8fe8abe --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/04_control.rst @@ -0,0 +1,22 @@ +.. _target_bf_control: +Closed-loop Control +=================== + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + control/* + + +**Cross Reference** + +- :ref:`Howtos BF-Control ` +- :ref:`API Reference BF-Control ` +- :ref:`API Reference BF-Control Pool Objects ` +- :ref:`BF-Systems - Basics of State-based Systems ` diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/10_control_scenarios.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/10_control_scenarios.rst new file mode 100644 index 000000000..3d742fa5d --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/10_control_scenarios.rst @@ -0,0 +1,19 @@ +.. _target_bf_control_scenarios: +Control scenarios +================= + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + control_scenarios/* + + +**Cross Reference** + +... diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/20_pool_objects.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/20_pool_objects.rst new file mode 100644 index 000000000..1185a04be --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/20_pool_objects.rst @@ -0,0 +1,19 @@ +.. _target_bf_control_pool_objects: +Pool objects +============ + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + pool_objects/* + + +**Cross Reference** + +... diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/01_basic.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/01_basic.rst new file mode 100644 index 000000000..6fad88e6d --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/01_basic.rst @@ -0,0 +1,16 @@ +.. _target_bf_control_scenario_basic: +Basic control system +-------------------- + +Further descriptions coming soon... + + + +.. image:: + images/01_control_system.drawio.png + :scale: 50% + + +**Cross Reference** + +... diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/02_basic_integrator.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/02_basic_integrator.rst new file mode 100644 index 000000000..cc2938ea4 --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/02_basic_integrator.rst @@ -0,0 +1,17 @@ +.. _target_bf_control_scenario_basic_int: +Basic control system with additional integrator +----------------------------------------------- + +Further descriptions coming soon... + + + +.. image:: + images/02_control_system_with_integrator.drawio.png + :scale: 50% + + + +**Cross Reference** + +... diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/03_cascade.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/03_cascade.rst new file mode 100644 index 000000000..56b31f47d --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/03_cascade.rst @@ -0,0 +1,17 @@ +.. _target_bf_control_scenario_cascade: +Cascade control system +---------------------- + +Further descriptions coming soon... + + + +.. image:: + images/03_cascade_control_system.drawio.png + :scale: 50% + + + +**Cross Reference** + +... diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/01_control_system.drawio b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/01_control_system.drawio new file mode 100644 index 000000000..345b7613b --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/01_control_system.drawio @@ -0,0 +1 @@ +1VhNc5swEP01HN3hw2B8tJ20PbQznfGhzVEGgdUIRIUcm/z6rkAy4sOOEzud9ARarbRi39PTCstbZYcvHBXb7yzG1HLt+GB5d5bruuF8Cg9pqRqL43pBY0k5iZWtNazJM1ZGW1l3JMZlx1EwRgUpusaI5TmORMeGOGf7rlvCaDdqgVI8MKwjRIfWnyQWW2V1gnnb8RWTdKtCh+6s6ciQdlZfUm5RzPaGybu3vBVnTDRv2WGFqcyezksz7vOJ3uPCOM7FJQOy5zXKJpunZ/srzhbBn+X+J5nIAXKaJ0R36otXLBecyZFlVQqcqdWLSqeEs10eYzmrbXnL/ZYIvC5QJHv3wAKwbUVGoeXAK0UbTJcoekzrYStGGYeunOXgv0wglsLclZMhStIcGhQn8E3LJ8wFASwWyixYoQep5cgQCaFUz2u5XpIkbhSBvYTPeMS9iAxWSoRkYygDDpOo8ipD44NhUkn9glmGBa/ARfVOFb5Vt7lv2eJrCmwNoni2MiLF0PQ4cwsivCgcX4HpbABppCENqEzrhsNbWicYcYI2kMk+xjiGXaCajIstS1mO6H1rXXZZ0Pp8YxKkGpjfWIhKwYt2gnWZARnn1S+z8SAn++Tr5t1BTd60KtUyOONML2OYQRZ8IOKX8W7EhFYbUjZ0xJMkKdmOR/gMFFqxEE+xOLcNGz+Z9UsoN7E/BeHMbwZxTJEgT129GiOVmu4HI/AZx7mCsMtfJ+jxslm8GtVSc8E5qgy3QjqUp+P4vX3i9OXqBX837PjDS7OC3mi9HJYkJRZWfy8dc/n27aUFYqCYFPPrtLInZDHCYTIUMugJohBvkjEpNPZGKEfqM8e5jdZ5bheUcCh2zpjYhe+ldcEpraOQ9A8ld33ZOsqfoXgPphqekz8D8vPy93bpci+Urumrpcu9Trfef4efrIlqUtkmrwwSBX92spSrAZqUNdILcHDC4lADofs1Gdc3qK7+P8UYq4/+rWT4l5dHGC4QQ03/l7WRbV1VG+lqp61wHswCZ7TaeU099WZ1mV6oLs7r1OU/EJf5xzh84oW8HLe4guUzkV9ShygBGKE9IorKkkTarNzO6sOl+L+I67XFbU98+neuE7XtzUpFe6A1wKriGK7Pgm9y63WR0zfiCFIM9eXwTpyROG5IguHQqaubBkNVkMPk/tLy7668OQ2QPkfuwfFw/D+j1meZv0DGN/Is9LwOeFoLrqTEdNaddd6d4P02/nSkqsgKNBkeOxyJsWOHUlKU+OWiAJVF8ycsIQcpGmfPfH9YRWAn9vFsrIqYBzMPBedIOKDrresJ59R11agnwtuUE9Bs/841LGh/cnr3fwE= \ No newline at end of file diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/01_control_system.drawio.png b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/01_control_system.drawio.png new file mode 100644 index 000000000..59edb54dd Binary files /dev/null and b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/01_control_system.drawio.png differ diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/02_control_system_with_integrator.drawio b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/02_control_system_with_integrator.drawio new file mode 100644 index 000000000..8889310db --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/02_control_system_with_integrator.drawio @@ -0,0 +1 @@ +7Vlbc+I2GP01PJLxBRvzGMits2lnZ9h2t4/Clo0a2fLKAkx+fT/ZEr4D2ZA0O9snrE/SJ0vn6OhYjOxFnN9zlK5/ZwGmI8sI8pF9M7Isy5tN4EdG9mXEtGy3jEScBCpWBZbkGaugoaIbEuCs0VAwRgVJm0GfJQn2RSOGOGe7ZrOQ0eaoKYpwJ7D0Ee1Gv5JArFXUdGdVxQMm0VoN7VnTsiJGurGaSbZGAdvVQvbtyF5wxkT5FOcLTOXq6XUp+90N1B5ejONEnNPh7g//y+RLsEqeUO5E4n4ae/FYZdkiulETXrBEcCY7ZvtM4BgedgSmAokSgSOOBONqQmKvV4mzTRJgOZAxsue7NRF4mSJf1u6AGBBbi5hCyYRHilaYzpH/FBXdFoxCRvsmYQm0n4cwvqKBJZMhSqIEChSHMM35FnNBAJ5rFRYs1Z3U68ghQkKpzjuy7DAMLd+HeAZTe8KtERm8KRGSoJ4csLuuepFgaJzXQmqd7zGLseB7aKJqJwryfbO4qwjkGiq2rnHH1kGkSBsdMle4woOCth/m+HmJ4vFq+2w84Pja/T7ffSXjaQdmX8PsUrmsK8DUjYoFRpygFaxkG2McwMZQRcbFmkUsQfS2is6bLKjaPDIJUgHMP1iIvYIXbQRrMgNWnO+/yf5Xji7+rdIVhZu8UdqrUo0z5uQ8hh3IMoR2xjbcx0fWVKsR4hFWXR8e/1p+4ve7b1vX+pORT3buTXU7uXzncGdsXJmWU/bhmCJBtk0p6iOHyvaZwR6tUrluk4em2+JX+e6qV106dCLdkIVhhkWHhoe3/3FmDgoQxa+UmZYGBAh7YVcDoMb1PbwK+1SkRitP9tQKbl5GJuyWTnhdnTCtHp3w3kom3CGZoLDoH0op2jt+SDnMc5SjBvlx5fhxsbC6YtHbbvJisbBeJxVvv8Ot4R0OpDLqvKqRyP2+kcaoAGicFUhfQwPTS/MCCF2vybhUZuXXUow+a/G+kuF10NU2sSkZZ0A7GYC2x6q8ItuH8DfmlWHOGg7nyvG8E1pVlD5jTgA5zAesD86JqOkglA4yCM9VZlnQiS9tl47ZoJMKaL1MAcEtebbV2BTa7V7YPR32mk5RTvk/dU/O+b4ew8dw11G9q6lvEf6Fpr6f2ca7MvvYmX2S2S/8EPgJjvbZx7B+wbW86KlwhcgdkTMphsgAGKFb+BRlGfF1WDU7ejqfi/9JXF+rRy05al8WDHzMXexDzehoDbAqPQzXZsGj3HpN5PRVjg9LLA+wzmVOTIKgJAmGk7w4pUsMi2GKeTrzkXNz6U/+Y+TumLPDXaN6v1H9Oq9/I089224eUeZFjqjJtJl11kzwdht/0uPp4xSNu8dO/30hDEHSDJ+25ChLy1vdkORSNI46bqfr4bEZOHja5+Fn7tRG7jESduh6aTdvegP3MzU3772RmT/m0Wqw/jZ85/s/hrLWmbwbhlCs/i0od3L1p4t9+y8= \ No newline at end of file diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/02_control_system_with_integrator.drawio.png b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/02_control_system_with_integrator.drawio.png new file mode 100644 index 000000000..c887d20ae Binary files /dev/null and b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/02_control_system_with_integrator.drawio.png differ diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/03_cascade_control_system.drawio b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/03_cascade_control_system.drawio new file mode 100644 index 000000000..b8edfa6ca --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/03_cascade_control_system.drawio @@ -0,0 +1 @@ +7Vvbdto6EP0aHunyDWM/BsilPelqu3Ky0uRNsWXQiY2ILQLk649kS/gi2RgCIbR5ijWSJVl7a2bP0HbMYbS8jMFs8h37MOwYmr/smKOOYRiOa9E/zLLKLLph2pllHCOf23LDDXqF3Khx6xz5MCkNJBiHBM3KRg9Pp9AjJRuIY7woDwtwWF51BsZQMtx4IJStd8gnE27VbTfvuIJoPOFLO0Y/64iAGMy/JJkAHy8KJvO8Yw5jjEn2FC2HMGSnJ84le++ipne9sRhOSZsXnlxPvwJXy4tvD672fbqyg+HPrpHN8gLCOf/gIUg84MPsSEmM2RTJKiEw4p9BVuJsYjyf+pBNr3XMwWKCCLyZAY/1LigdqG1CopC2dPoYgkcYDoD3NE5fG+IQx7Rriqd0/CCga3HwDTYZCNF4ShshDOjHDV5gTBAF5YybCZ6Jl/h22BIBCkMxb8cwgyAwPI/aE/oZT7CyIqY7RYTR0mELyqfJD5gtDZcFEz/dS4gjSOIVHcJ7LQ70qtxcFGhj29w4KVDGEgwBnKvj9dQ5nPSBI6pGN3q9AVH38eVVu4LRmf08WNyhriHD+2NOYFwCN8T0ME8OWmjXQ7sHLHWjDKauKdDsqdA0rT2gqbyrugTmV+rz/gAwHQ/Wg+mDZJLuXd8PsrZbQVZxT21DAawhPPver6kM7DADNEzR1d8GaOW0fQCdQD5t2mN7DnwMVHjloOsOe1NEMtbJuAcQ42G2mz1AZFY8qaG6fCqInIM50nqE/A7biR0y+j/GJZzs5zkL7un5dZP0AM8Yns5smR6U6KdPY/b3hofZvw9xvffhIO9JkK+9bA52hhukOlN9UaFPxSRv4phM8BhPQXieWwdlYPMx18yRZ+f9HyRkxdEAc4LLYNNDj1e/+ftp4541vvREc7Qsdo5WvFWE2GK9S0R+52/S1r14jz7nk7CGmKNFrFiTqo4kCZ7HHmwAgmcQBMRjSDZ7UXbkbSinfeGXOoYhIOilrPdVdOJT/cSIfkI+BAdBQjdW5dt6xd0p6B6DTxIvpv4Zy6NyXKnlArEvSZdIKDBEjPBCkCTIE2Y+rDFyt8V/I6674ih0QdkBmVVRnvGPv7R/rEW+W/A3lFUzvke1Z7lm16+MntBjHj1m5qAlRRYh38+IAmlEAo/pfAzHdKn0W3uDTm+kYMI2t71NlGjivBQ61hk+33KnmESr73ffMc0SpsJFvJEpVr88q1ue4HD+wFKokGgGunJEigGpi0hhiGYJ3CwiQDLL6ikBWjJ/0qgRerLqgLrfg32V6nDtvgnsJm5KLD64/nAq8sOR5YfznurDrlMfqeCswv0CYpTeiyNpEDlmCE2Sy5D7ol5p0iQFijV7HLVi0RsVy+5hyGgpQ9qGq7Wb0izLPZQSoXEZrAojuJOvdW26rVWLWJWiYuUFw2gcTx+yLezqB+8vXfd1pn27H2Gth0e/EHT/2ZQvG9IV+MOzJ9c4YvKkROid8+W/DnFdP2a+rITclSCvzZcL4UpG7gOnzK1Lo2kQ3D79bXJ2xbjTdOX2lya9iQ2mygGk/I5lPnyq1Ha3vmdvvvTvqlL7O935ky6TbZcEC4kqngtrHkCitnUV5r5dhVob9rWapGpDSaVetMoydK9Jt/KwnPYcF3XgkwpqzWFrE+F3j2uKsm5T/PsgcW37EoyCDJ/BTRHc+i0U7aGC2/Xz64N+9nB7e3Gu3aIf/y5/3S0UOYy6BNMigbFqEpiPI4S3q9O8RQgfsG7TpIs3+pnD/MywdQHGqARN02wuwPSdxvHlAsx7xk1ZGxZ+zzi9CFnjdTdLwt2Za+6ZuZwxXe2LZlhWiTXi16c3Kj7Htss+3KpMUaP4DsRLpVvf4nf9gns2T4mwh65TKM9VwdamOvr767mmXberU3xKuZbVSfOIhQolzLJsb/lz2kkFqqPUJxX6qummHfze02b+b+qzoJH/1wTz/H8= \ No newline at end of file diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/03_cascade_control_system.drawio.png b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/03_cascade_control_system.drawio.png new file mode 100644 index 000000000..86856ac24 Binary files /dev/null and b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/control_scenarios/images/03_cascade_control_system.drawio.png differ diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/10_operators.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/10_operators.rst new file mode 100644 index 000000000..22eccafdb --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/10_operators.rst @@ -0,0 +1,19 @@ +.. _target_bf_control_pool_operators: +Operators +========= + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + operators/* + + +**Cross Reference** + +... diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/20_controllers.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/20_controllers.rst new file mode 100644 index 000000000..3db4782d1 --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/20_controllers.rst @@ -0,0 +1,19 @@ +.. _target_bf_control_pool_controllers: +Controllers +=========== + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + controllers/* + + +**Cross Reference** + +... diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/30_controlled_systems.rst b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/30_controlled_systems.rst new file mode 100644 index 000000000..183ed9c9c --- /dev/null +++ b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/30_controlled_systems.rst @@ -0,0 +1,19 @@ +.. _target_bf_control_pool_systems: +Controlled systems +================== + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + controlled_systems/* + + +**Cross Reference** + +... diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/controlled_systems/.gitkeep b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/controlled_systems/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/controlled_systems/images/.gitkeep b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/controlled_systems/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/controllers/.gitkeep b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/controllers/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/controllers/images/.gitkeep b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/controllers/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/operators/.gitkeep b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/operators/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/operators/images/.gitkeep b/doc/rtd/content/02_basic_functions/mlpro_bf/sub/layer3_application_support/control/pool_objects/operators/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/05_oa_control.rst b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/05_oa_control.rst new file mode 100644 index 000000000..439e6fb62 --- /dev/null +++ b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/05_oa_control.rst @@ -0,0 +1,22 @@ +.. _target_oa_control: +Online Adaptive Closed-loop Control +=================================== + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + oa_control/* + + +**Cross Reference** + +- :ref:`Howtos OA-Control ` +- :ref:`API Reference OA-Control ` +- :ref:`API Reference OA-Control Pool Objects ` +- :ref:`BF-Control - Basics of Closed-loop Control ` diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/.gitkeep b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/10_pool_objects.rst b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/10_pool_objects.rst new file mode 100644 index 000000000..ece5898fc --- /dev/null +++ b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/10_pool_objects.rst @@ -0,0 +1,19 @@ +.. _target_oa_control_pool_objects: +Pool objects +============ + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + pool_objects/* + + +**Cross Reference** + +... diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/10_controllers.rst b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/10_controllers.rst new file mode 100644 index 000000000..bdb6d6eee --- /dev/null +++ b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/10_controllers.rst @@ -0,0 +1,19 @@ +.. _target_oa_control_pool_controllers: +Online-adaptive controllers +=========================== + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + controllers/* + + +**Cross Reference** + +... diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/.gitkeep b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/10_rl_policies.rst b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/10_rl_policies.rst new file mode 100644 index 000000000..dbe03ae78 --- /dev/null +++ b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/10_rl_policies.rst @@ -0,0 +1,19 @@ +.. _target_oa_control_pool_controllers_rl_policies: +RL Policies +=========== + +Further descriptions coming soon... + + +**Learn more** + +.. toctree:: + :maxdepth: 2 + :glob: + + rl_policies/* + + +**Cross Reference** + +... diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/images/.gitkeep b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/rl_policies/.gitkeep b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/rl_policies/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/rl_policies/images/.gitkeep b/doc/rtd/content/03_machine_learning/mlpro_oa/sub/oa_control/pool_objects/controllers/rl_policies/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix1/sub/mlpro_bf/layer3_application_support/04_control.rst b/doc/rtd/content/99_appendices/appendix1/sub/mlpro_bf/layer3_application_support/04_control.rst new file mode 100644 index 000000000..116455120 --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix1/sub/mlpro_bf/layer3_application_support/04_control.rst @@ -0,0 +1,11 @@ +.. _target_howto_bf_control: +Closed-loop Control +=================== + +.. toctree:: + :maxdepth: 1 + :glob: + + control/* + + diff --git a/doc/rtd/content/99_appendices/appendix1/sub/mlpro_bf/layer3_application_support/control/.gitkeep b/doc/rtd/content/99_appendices/appendix1/sub/mlpro_bf/layer3_application_support/control/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix1/sub/mlpro_bf/layer3_application_support/control/images/.gitkeep b/doc/rtd/content/99_appendices/appendix1/sub/mlpro_bf/layer3_application_support/control/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix1/sub/mlpro_oa/03_control.rst b/doc/rtd/content/99_appendices/appendix1/sub/mlpro_oa/03_control.rst new file mode 100644 index 000000000..36e13baaf --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix1/sub/mlpro_oa/03_control.rst @@ -0,0 +1,9 @@ +.. _target_howto_oa_control: +Online Adaptive Control +======================= + +.. toctree:: + :maxdepth: 1 + :glob: + + control/* diff --git a/doc/rtd/content/99_appendices/appendix1/sub/mlpro_oa/control/.gitkeep b/doc/rtd/content/99_appendices/appendix1/sub/mlpro_oa/control/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix1/sub/mlpro_oa/control/images/.gitkeep b/doc/rtd/content/99_appendices/appendix1/sub/mlpro_oa/control/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/02_data_management.rst b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/02_data_management.rst index 1dcc71781..ddb41176b 100644 --- a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/02_data_management.rst +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/02_data_management.rst @@ -5,7 +5,19 @@ BF-DATA - Data Management .. image:: images/MLPro-BF-Data_class_diagram.drawio.png :scale: 50% -.. automodule:: mlpro.bf.data +.. automodule:: mlpro.bf.data.buffers + :members: + :undoc-members: + :private-members: + :show-inheritance: + +.. automodule:: mlpro.bf.data.datastoring + :members: + :undoc-members: + :private-members: + :show-inheritance: + +.. automodule:: mlpro.bf.data.cfg_file :members: :undoc-members: :private-members: diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/03_plot.rst b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/03_plot.rst index 60c2592a4..142a3bc2e 100644 --- a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/03_plot.rst +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/03_plot.rst @@ -5,8 +5,32 @@ BF-PLOT - Plotting and Visualization .. image:: images/MLPro-BF-Plot_class_diagram.drawio.png :scale: 50% -.. automodule:: mlpro.bf.plot +.. automodule:: mlpro.bf.plot.basics :members: :undoc-members: :private-members: - :show-inheritance: \ No newline at end of file + :show-inheritance: + +.. automodule:: mlpro.bf.plot.backends.basics + :members: + :undoc-members: + :private-members: + :show-inheritance: + +.. automodule:: mlpro.bf.plot.backends.qtagg + :members: + :undoc-members: + :private-members: + :show-inheritance: + +.. automodule:: mlpro.bf.plot.backends.tkagg + :members: + :undoc-members: + :private-members: + :show-inheritance: + +.. automodule:: mlpro.bf.plot.dataplotting + :members: + :undoc-members: + :private-members: + :show-inheritance: \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Data_class_diagram.drawio b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Data_class_diagram.drawio index 4c4657952..02a874874 100644 --- a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Data_class_diagram.drawio +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Data_class_diagram.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Data_class_diagram.drawio.png b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Data_class_diagram.drawio.png index 1c84e593c..459ec4080 100644 Binary files a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Data_class_diagram.drawio.png and b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Data_class_diagram.drawio.png differ diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Plot_class_diagram.drawio b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Plot_class_diagram.drawio index ec397c2a0..80302b6e1 100644 --- a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Plot_class_diagram.drawio +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Plot_class_diagram.drawio @@ -1 +1 @@ -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 \ No newline at end of file +7V1rd6I6F/41XTPnXcsuLl4/Wm17OrU9nel9vrBQojJFYACrzq9/EwgIJmisEDuaObPmKCCBPE929i07J2pnMr/0dHd84xjAOlEkY36idk8U+KdRhf9DRxbREVlWG9GRkWca+NjywL35B+CDEj46NQ3gZy4MHMcKTDd7cODYNhgEmWO65zmz7GVDx8q26uojQBy4H+gWefTZNIIxfjFVrS9P/AvM0Thuul7Dr9zXB28jz5nauEHbsUF0ZqLH98Ev6Y91w5mlDqnnJ2rHc5wg+jSZd4CFOjbusuerxbPVe6tffvvu/9Yfz64fbp8q0c0utvlJ8nYesIMP33r+cD5+erx2X6uN5x+dl1f15e6touJ7v+vWFPelbwzhX8Mfov8NdR+/fLCIOxsYsO/xV8cLxs7IsXXrfHn0LOxPgNqV4LflNT3HceFBGR78BYJggYmkTwMHHhoHEwufHTp20HEsx4PfDTDUpxZ87zNgG21EFniwbzmDt+jQhWnFPwNzM3jBraLPr+jzaQ1/685Tp7oL/IWxb3E/+c7UG+B+eH18+y7/tKumdz25tK7fQf+XWlEx+3VvBII1HR+TCnVmisMYukvgTEDgLeAFHrD0wHzPEl3H42WUXLcEHn7A2NN54Fy0Oz+ezltv131lfDY0z5XpK6ZOmgZ3lhMEet8CBP7+zJxYug0wTjEVVPhdt8yRDT8PYGcCCN7ZO/ACE47TNj4RIAKcDcamZfT0hTNFXeQHcAzG387Gjmf+gbfVY1DhaS/ATFHqmSvu0S8xjh7w4TV3MY5ycqin+wG+ZuBYlu76Zj98YHTJBOJk2mfwVZ1JfKMMeZNBHz2K57wlEga98BCSLybqiaIaOmgOB8mVqTP1QRP0h+gM7r2L7C8vwj/w/MjTDRMs6Z8+tx1XUd+D+Vpy4bM1CeOP5b9ax99nS2GqxteM03JUrdfyGYnb+wElvm6PEJPiBuHdMg3KVbJBuU5pUKlnm9MtyDNbD8AZQs0nxkHyrkxDo+01ft64je5z9zd4nvUGg/bLlDI0Otpd778Hrd15uHo6h6dO1DaaSeCEF36Bl0gXuuWTIwfCEaRGiQWGQe4Y8V19YNqjXnhNt7o88gP3BjrkwN8OrZCfY9MwgB3yN9CjgRsx2HVMOwi7q3YG/8LX6SCZWIMP1IHf5eV3+Bdd7kHu2ZDAuhmyDMDxMwNoDGXE8glihCTl8HKtkNnMy0UW7U00jNm0i1ikSmlZyQP//qF92233/rvNIcCDNxX474p/be/4N/Pwf2r3rrra09X5831CAMuEvRQTIOztrqDAjhRo7p0CrTwKdM8v2o+9h5AE8ARSmO6hVgvx8U874VHtVhBgVwLIMkcGPPbk6bzrqq1vZ1fOqP5w8zCfVJRGPgMe2lc9rXf+dN5LpADqsVgISAL+XeGv7h3+3Dmg89/txdWldtd++DdBH3Zagv6XiWt5TsWFgkHzsWQ4aEIsTfXS6NDgSAfp8o/WuFL9vtYb/ff8s159s4MK6TDR3k1/CvH7A1ZUQYH0Lrq/tO+ZXyWVf21lLGO401O/gH032FWOsF/Pr6Rz4+qX9PTfvfnj7K7yTaO5wjDqAXC1gTMNPVuZyV4AvpONzxHw6kx9uem/Pj98t6evnVqzWTMuK3nDHN597BjLUW6YAwH2jmC3OIJ99+y0/fn09ffireEa2ovUUW9zR7czs7WhOZp6YhIvEG+VpwePrr2T5nsE+CDshI5y0j5sI40DyDzddD9HyqMOfv9pV35/ux4vJjfqop87qE3bDMxQMzfEqC4QcJ5OuR/juj7/dvbUdp46tcXtt7NKW6IBviK8L/BXAfQOQFd5+t6o4XRSN4vk9nHgmg6vU5JACgKZp4eNCjLpUgmltwEC3bQ0C7yDY5HYfABn9aGtBtsLyzYhRzWBr2WGCSapnKBs8oW8GfwJhDFMR8JoP4TJRxWZYIRKMkKloG/pfWDdOT7UKRx0fy+6doUVm4AvC9WapLCh2twdVKpjlDInhwqYpn1FL5zrJc1mTEj/HMlQL40HcU4mD3F++ToGw647vlUrrYkn+5OFZFOIMAJB1nP6FaIsVU6U+ijqCuFBLZAAlFyq0sQ7lQCUnFKCAKFIYPGmC4GwcxLNvqd7leBDOC8g9CMeYNtNbWOrTe3ehmnYHcQJ9FiqHKbqYm+NtOMHRuJF81LyJCU8xsy0DWemBWZgLWfFdAQ5avxohgAX9bfWYlSUamtSW5kjTE/V4WOl7f83v3scda8f2rO7nzkTZKwf6Ui9JCZI4bbaGfh6tcFPEFIjyvU1EyOKLnp6ACKJmHxN5xQdixgojQANlZ9qvG51CE01Dv1axLAPJwMB+g6gNyRG0IsY9VTQq/mjPgI9HPHYr5lRAsSA3xH7OPq3N+zX2MJp9yYx7kVeyc7Yx+rb3mZ7MvKs+z4EiQJ/KAEy/m4x7RfHhKbMcdrvVloP86sGuGwML1pOTf05PaPMAJFnNLJ7v9JswVWBIOKZRSwmkTkygZqOopITAtT1HQ/E+MctfRWjfkesq4xYFyH/6VhTUsU9MEC9LtAufJkYR9v+5ockWfq0UVck+bvT+j6YnX2naHpDxxsAbTgS4O4KboujCUcFlwxYe2DoAX8cObEFwLsB3JI52mlUgEnHjCYQLhLh6r6tMYrilUShNMWgB6LKikBt/OAuo1PHFxPlsa601WA1BVq7E5K67ISiHS4JqQpCHhkhZYk1cSfO7yickWtFpC0YeXSMrHFkJPWZScfZ1DX0ACS5I//7n6u9zXTvCCDnnCYhSy2OeWTUlbgUpTwFf6K0pcRAVgaUkbSzy4cjIiufyjDMy1MK0OGoDCX9AhmGqoKhR85Q1iBkETMovYjFeobagqFHztAWR4ZSXW+kjueBCez2VH4wdsVBcoZpkGo3qq544PjzVvgUlaOPjkqFGimsUlxACt9hI85lxCt1joFRKsxkGmwGZlXAXATMzX1HRMmyiRmYbQFzATCrMsfYKD3NgWIEaQb8oaYdOMAcyh2qKsfI6L0ZGNWXxx81Z3ppmFPp7U2ZV8jqlwSmaP8Ml+yCrWrTJ3uDYNhO0nts0GrWx/roIvs11U/VJq2flAL0WGpHkeme0HRK1LfwBWO5UP89dSLahBIC4bk8JPnx9irxMTl9Os4IjG7dzyARX4UarES3QZ5xuerOyVv0wAigXU/ayb281bvDfuhTjkXvs/IgxI9ZjmzqmeEw2zOb3rROe9G170d7FyhlbGpr+DlRS96o/zW0SsP4R+rTP/kPgN9241tQ4YL/1ydIKtp93426gzyU91qZy67Qy7meM4I2lc9020IPhc3DUWIa4Uo3zg/wA+gGkiKBA/+ZoooDSZ9FsG9H0OzQzUNqS/5tNyqKGIeZI5mH14PAM/vTIFyfCuWjIkVlEdHUXhxKt2CGODntW+YAyeZloxdRczFiffQc5sS1wARtMWNsLZWKBPSD8NGaWNvNKaGUtHSCnNqZR1m5oBhsQmAuTHsMPDPs7k0gjXX07qa/ChOchD2QD0uxIyHzChrRt4WiSXsEbbsxgtkPtVao/4SdFffnRfrZP88AILtU+sgrZ3mVev2bPdEp592LEqysD0s3mVKlkJisp9kY9uw9NJjQbWaejo5l97YijNV6XULHic2rcN+s7Ia1svfVMPyTvEDaElhrWGw2hvan08cpOSmlvgtcEO40JSHdZQUqyg51KdjyCkjF+PVWzicFrFgLXWV3wwu9xpEtIStb47JxYzqMWUWuxYkC2BSrKHKMUgq5GgW42hrccIN3TrQIDrfWOm21WvXkv0amYVmRTiVJleRaswH/rUsru+VFm/fhOy6ZQTRSq542q005/i9eNc7YSrT13+ZWWiuFuJzh0AeZn0Aq6YvUFdiNkds5q5uq4WGxHADRDZfDYbvd0egjhExjvIqkOhoiQ8+ZfN5BIh3lIFGVU7VZrUuq3ED/VksZI+sb2XaILFJ9vG7AFEZq0qVJsFjsgrksxLiqCTQHYEDdBbPfrFVr6IaUXS4T/SFnh8zy9Iv47GaPIi23aIXbf/UGmPTRQOaQEKPhiMp1rpcYu+xtSaNXAdU66Q9Mpl0IEbeFiBsOFbqIM+r9eq3+WUWckHE5w4FMPREyLk9k/DUyjkw0ETKOXcYxbGb+GWWc0OPyhgOZkCNkXJ7I+IQy7tKwBu+O+zRv/nqeKFfvsjymVV2/8xwXPj16xIGl+6EbJqwsB2WPbeiegbfDQUlY6NzUHqCehagGi1PSS31uIyz85PdWVJIWfkT+j0kfGEZyO5QzfkpwaimD5By3dIpuu+tZthNSOOuqziTQEhTO8lLJFfUkibbKcckVWY1mVoooDYoUUSiskqU1ziFWXg3vXyYX/15NXu9/e3bw0B5OXx4pvOrCUXmHQYZnvvaHp++6ZzpT/7Tn6Ma9/g7IdC8xvUYAqkVNrxfhn/zpNTlfFldldSXzSq2TZM1ZhaUc0BT7S+lcv13+qbS0X8r4rvI6azkPlFGDt4p+eL071zqvJ3Fh7y+dxcAyB1+IAfO350eStFsrXnaZZul5kGvUOFaBSIWWjHOkoT2/W0J77pq+Ywhwt9m7Zs/gkv7+LLjaDQnvCcpT0G2BMnMdzio/lKlPTA7hY7Z/igE1DtFx2HWK+sDkyE12nXI1A3bkSVxSG+mv9wHUB5H6Si9aUui/rhYsXPRMq/NEgUtq47KxiJmSVPCt/bEzQ50VL7csttCLq7kQiiBqIHeTlYIRGZojnF3/tQHba/xTcJfFmWVfoJ391oeU/MLjreLSwRHdvvj6BLYbnju89TdlyTFZYlQw5dKUkPzNIfwDXEhVmqEQm4t7UybJUFKIYTg+tcEh1votDcsaR5WRiiUZJU9hCVyBJTuWDUZNsTT1n5SvGSy1CbTmBKDsu8CzbjhbGqDk4Jw477CzIyRdaAe40yCqYZNoSQJfZnyZXW4FLD2mPjI5kfo6rh7gQ3hdHT4WBnfoeBO0VCT6ZrimNpmGFrzAmxnvGuN4LqISEPWRyXSNuyhGCREDth8uFsWxzDiy2R+eIiP/NGXcn8LDOUHLkDnoV7ofxizDG97oATpumX0RuvxAOKjeyCZpJ5TZFJ5pFEAjasmzOFC6wqPc7aVFjHKXFKBNcch1McyySLkSTmeOUDalNVPZXxehpA4OSqJjR3u6On/WlO6qRyuKdyjdwwtxsFZxWS9fCg9eFrHtFB11cmrFqKs5qKsC9V1Rr+0ddTLhD6N+m4P6rUB9V9SbHFGnbjZIy0F5aveuuiH09zHwAuVdUGYvPV2alkvWm3o3w0ISqR2EDwzk8uCsckwvoj8zWWdNn4cWbATnZGmlouOn7fCkwJcRX0pCLWd8Kd5nx9fmCcCHuONzeXi2OAaG6M9M6lYIz4XA80MGkswxOCRVZp7ybdCSz5Thz0v3bWRedii5YcjXIsbnB/GscowNUfEkteAQTzE+P+jA4KgfUfFUSDwD4GqeHtUgE5BuDSnz9k5lQUqGbw0Q6KalWeAdWALVj6CqyhwVo9fHt+/yT7tqeteTS+v6HfR/qZSgbbwPdoJouImHgJQVUpWjbvRuK7V2Z2AvjP74h6XcDO+vbylzKbxcDM+PYFnnqBdRhycZCEBeI02fBg6UvmAQiFH6MWRZ/b6lIUsxSNE2Gzi6LMbrR1Ctsu7JWxqqpFsQ5bsIVHdCVd2390glp9RkU7sIUMMcCETZEa1x1JGA/vr93/uRc//n+6w9kXov+lObts+iB0YmNFI9YGhO/1eCrAUPCmSZkW1y1JioyNKXAGih+2HgTMPEMCGGP5IfwbrkqjwnvlgRXAaurCuwClgTTH9kygaD8aJg9MoaGSgvdm1rvDAzE8Ftx1/g80jJgtcSWqXEFXGrcgn7+UbtLTi2RwvMlN8g1zeku7bLRTHXWMTN1gpeYB43nG/PxO8rldVyrrsZt1xOszn+UNxmsta+6GZXfXYlsunAN+kuceZkNGPkOHhTuLZLGqaxGRNNnaGYSJszYdWBQ0T5Q1txF0SEhsJqzxaw9o7KA1IzntqCCXtgAvMS+LKYQEbeTV+zYO+kHBwbGSFVlttbHWZkYa8koWwlUxpJYqM5vaOPMQLxW8IuGjsjVE/2fHl0ZRHa8pqeE9rCyBT+BYJggdfFoXBU1uimbKwd7khG2UwouwsW2enr1phF299E11F3u8adGu1gE12HE7E37gXkAUsPzPf0RSVsx01O4Gjx45k+eEO7/a4iJ9Y+Flz+fO/1WVux+Nh28WOLYaOpv2fxI3Vs0Gp43rZvzhNj6MuJepH8PeylUFiI5nBxrWj5lEVbqU/MsMnNEfl3iwGVtVhrAd7dGC6aI/ewzYy0ilMakBxjptQnprjph+Zo6oHYReZACyOcwrWeaU/nkZURXZLYGBfxV0GIXQnBM9RKWQ+VC/5zWLnJz8CvtgXwhQHPXPyyEOTJSTkX+a7uzUxbjPuS4edZMlMhE8Rz4cd+FIF/uWq6zLPMJiU4nwO/gD0a9ht8iAVxgGd5Tkr6YywDUMnjwAwsIFQ+HqJf4VnEUyX9LyTsNGVPAF888Dx9MyrFjieAF7oeF9x57rWj5ht5S9yFkscHeJ7b76ikjbeKu8Cbo3bHvE1PIeDnW3j+FtpdVMo5vDo5F+bqHhE5yuKDGnvWufAh38/rr1X7lj6+TkwEtS0oUBAFeJY8U8nFqSQFKApgigEpaSBIUBgJeNZFU8m1rCQJaNqgYEHJLOBZTU0l61aukkCoBZ9BZ6zyrMkWJw3lWIrxLVmcgkUvZvg8H1bzaCOd6TLd3cfA/tIIz1q0Ti0gtaW63heaEJ7FHSr4Lvj+Eb6zZo0XseFPPLY28Z3BCyzoLuj+kXQ3hSfd13u+E7qzOL8F3w+V76VmddRYE/8L4ftah398R0Hzo6Q5xWYtj/Z1yv5z5dF+fahjC7O1E/NlL8PA1ZZvjx9slaZH5F4pjZqUpVHlGZjrva1bGZiCnEdATtYEwELISTqBqeRksAYFNw+fmw3m3baK4Ob6WOVWppsg56cgZ6l2FnOFjkLImZs977PaWYKTn4KT/AJ5jRbHuEYtf3WXDp/XDJgzv4ZhLfrj4UJZ8Dd5ru6q5fs9Mfxs9oYgQIEE4Lm8q5Yf58EEYNLpBf4F4s9zaVctP66N8WfTmwUBiiNAi+cir3h9CamhRvgL3PejBbb2XownNuL5Fo6D3egtXvDvwy+v6AuEDH/tztMnuwv8Lc+M3FhwDpH8JfU51Rz8tmwNfYkbI0FnLVJH3TQGK2HpInXx2sviqtThn945UTXhuPxYnFQSryuuxwv+43tET4V/tqQU7FR9kboMj7vchpr1lYakRj19v40/qOE1MUtKR4+wJPh2Fcdqe6L3Gjrm8DhnstmpLqLcIDm3TnAVzMNt2SNXWyv0ievk5dEH8mv9L3Ym0Np+pddz/B3oI1RH/GvqGDlviiKPh1bkUVarK2Ssyirb9C7XGxtl+t9e5pEMfa7WeQwHzmEXeFwWIWZUKRNR8/fUd5TJOCKB6bEXeNweVo4VHmv5wTZRDaoQb8EH4OfpLsrPUhF5siKBkAvfWcteFpEwWGNMfBHJBYKc0VmFkZ1FBG9JchKQfn1YuKBrDgJhZh6jmSnXGOko19bw8a8zMyl2Bm48ln2PNlLla2ehzwiFMaBQ0oNQxhytCGwx820P9iTFSRA1KxAtEFGe1eYortKF6/gC0CIB5VlFTiFDyXDSX90VVWDHHALmaNjLZKLqXAzGggFlNg4KQZTM7CGwOiJf6/ZgsRZcK8KtWiOgOcxYtLK3sDPzJLY2gPvw1hYB3CO0rFd36WOP36ryGln+1xnWyuZQbThEjjdUq3zqqCzFZXjMSsH2YHGMtdZJc0rEWj+qq38AaY7WV52x/JCoLifCqgVQm2cEta6wUVtkDAhqF0BtZhdLIdxeX07IdfxYYmseGETUnoUH4qUzaRdbWBV5j0kCrqYHAZi4wfKRoozqSLGXpfihZ7oZ5FwiCTVjW8LWGQlbRMJAPX+1tygydOREVFgLsRVCxO2KttDE5yr7UozAWw1E0ioRamo76s9QSqUkWea4IM12pFE5VqGqr9+YRiTjHTMRG+WVnIJfPccJ0k5TT3fHN44B0BX/Bw== \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Plot_class_diagram.drawio.png b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Plot_class_diagram.drawio.png index 52145259b..3df644053 100644 Binary files a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Plot_class_diagram.drawio.png and b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer0_elementary/images/MLPro-BF-Plot_class_diagram.drawio.png differ diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer2_mathematics/images/MLPro-BF-Math_class_diagram.drawio b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer2_mathematics/images/MLPro-BF-Math_class_diagram.drawio index 9a885062a..02fa85ade 100644 --- a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer2_mathematics/images/MLPro-BF-Math_class_diagram.drawio +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer2_mathematics/images/MLPro-BF-Math_class_diagram.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer2_mathematics/images/MLPro-BF-Math_class_diagram.drawio.png b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer2_mathematics/images/MLPro-BF-Math_class_diagram.drawio.png index eae45c9bc..619faafcb 100644 Binary files a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer2_mathematics/images/MLPro-BF-Math_class_diagram.drawio.png and b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer2_mathematics/images/MLPro-BF-Math_class_diagram.drawio.png differ diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/05_control.rst b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/05_control.rst new file mode 100644 index 000000000..8f8901218 --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/05_control.rst @@ -0,0 +1,12 @@ +.. _target_api_bf_control: +BF-CONTROL - Closed-loop Control +================================ + +.. image:: images/MLPro-BF-Control_class_diagram.drawio.png + :scale: 50% + +.. automodule:: mlpro.bf.control.basics + :members: + :undoc-members: + :private-members: + :show-inheritance: \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/.$MLPro-BF-Streams_class_diagram.drawio.bkp b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/.$MLPro-BF-Streams_class_diagram.drawio.bkp deleted file mode 100644 index 6c8905ffb..000000000 --- a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/.$MLPro-BF-Streams_class_diagram.drawio.bkp +++ /dev/null @@ -1 +0,0 @@ -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 \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio new file mode 100644 index 000000000..22f1cc6cb --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio.png b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio.png new file mode 100644 index 000000000..d416774c9 Binary files /dev/null and b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_bf/layer3_application_support/images/MLPro-BF-Control_class_diagram.drawio.png differ diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/03_control.rst b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/03_control.rst new file mode 100644 index 000000000..b8ed57bc5 --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/03_control.rst @@ -0,0 +1,12 @@ +.. _target_api_oa_control: +OA Control +========== + +.. image:: control/images/MLPro-OA-Control.drawio.png + :scale: 50% + +.. automodule:: mlpro.oa.control.basics + :members: + :undoc-members: + :private-members: + :show-inheritance: \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/control/images/MLPro-OA-Control_class_diagram.drawio b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/control/images/MLPro-OA-Control_class_diagram.drawio new file mode 100644 index 000000000..72daef064 --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/control/images/MLPro-OA-Control_class_diagram.drawio @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/control/images/MLPro-OA-Control_class_diagram.drawio.png b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/control/images/MLPro-OA-Control_class_diagram.drawio.png new file mode 100644 index 000000000..6e37052ce Binary files /dev/null and b/doc/rtd/content/99_appendices/appendix2/sub/core/mlpro_oa/control/images/MLPro-OA-Control_class_diagram.drawio.png differ diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/03_bf_control.rst b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/03_bf_control.rst new file mode 100644 index 000000000..82d1284ac --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/03_bf_control.rst @@ -0,0 +1,9 @@ +.. _target_pool_bf_control: +BF-CONTROL - Closed-loop Control +================================ + +.. toctree:: + :maxdepth: 2 + :glob: + + bf_control/* \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/bf_control/01_controllers.rst b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/bf_control/01_controllers.rst new file mode 100644 index 000000000..24137c6fd --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/bf_control/01_controllers.rst @@ -0,0 +1,9 @@ +.. _target_pool_bf_control_controllers: +BF-CONTROL - Controllers +======================== + +.. toctree:: + :maxdepth: 2 + :glob: + + controllers/* \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/bf_control/controllers/.gitkeep b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/bf_control/controllers/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/bf_control/controllers/images/.gitkeep b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_bf/bf_control/controllers/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/30_control.rst b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/30_control.rst new file mode 100644 index 000000000..73c22358c --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/30_control.rst @@ -0,0 +1,9 @@ +.. _target_api_pool_oa_control: +OA Control +========== + +.. toctree:: + :maxdepth: 2 + :glob: + + control/* \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/01_controllers.rst b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/01_controllers.rst new file mode 100644 index 000000000..fcee10115 --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/01_controllers.rst @@ -0,0 +1,9 @@ +.. _target_pool_oa_control_controllers: +Controllers +=========== + +.. toctree:: + :maxdepth: 2 + :glob: + + controllers/* \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/.gitkeep b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/01_rl_policies.rst b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/01_rl_policies.rst new file mode 100644 index 000000000..6a0eba28c --- /dev/null +++ b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/01_rl_policies.rst @@ -0,0 +1,9 @@ +.. _target_pool_oa_control_controllers_rl_policies: +RL Policies +=========== + +.. toctree:: + :maxdepth: 2 + :glob: + + rl_policies/* \ No newline at end of file diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/images/.gitkeep b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/rl_policies/.gitkeep b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/rl_policies/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/rl_policies/images/.gitkeep b/doc/rtd/content/99_appendices/appendix2/sub/pool/mlpro_oa/control/controllers/rl_policies/images/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/doc/rtd/requirements.txt b/doc/rtd/requirements.txt index 8bc01fc69..5b055e054 100644 --- a/doc/rtd/requirements.txt +++ b/doc/rtd/requirements.txt @@ -1,10 +1,11 @@ -dill>=0.3.6 +dill>=0.3.9 numpy>=1.24.2 torch>=2.0.0 -matplotlib>=3.7.1 +PySide6>=6.7.1 +matplotlib>=3.10.0 transformations>=2022.9.26 scipy>=1.10.1 -multiprocess>=0.70.14 +multiprocess>=0.70.17 pyglet>=1.5.27 sphinxcontrib-napoleon sphinx-copybutton diff --git a/requirements.txt b/requirements.txt index 629f3d6ff..cb4ef1f14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,11 @@ -dill>=0.3.6 +dill>=0.3.9 +multiprocess>=0.70.17 numpy>=1.24.2 torch>=2.0.0 -matplotlib>=3.7.1 +PySide6>=6.8.1 +matplotlib>=3.10.0 scipy>=1.10.1 -multiprocess>=0.70.14 pandas>=2.1.3 river -mlpro_int_river +mlpro-int-river diff --git a/setup.cfg b/setup.cfg index e8519b3d4..5e7960d91 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,10 +26,10 @@ where = src [options.extras_require] full = - dill>=0.3.6 + dill>=0.3.9 + multiprocess>=0.70.17 numpy>=1.24.2 - torch>=2.0.0 - matplotlib>=3.7.1 + PySide6>=6.8.1 + matplotlib>=3.10.0 scipy>=1.10.1 - multiprocess>=0.70.14 pandas>=2.1.3 diff --git a/src/conda/conda_build_config.yaml b/src/conda/conda_build_config.yaml index 32b2d29bc..a9cbee3f8 100644 --- a/src/conda/conda_build_config.yaml +++ b/src/conda/conda_build_config.yaml @@ -1,4 +1,4 @@ python: - - 3.8 - - 3.9 - - 3.10 \ No newline at end of file + - 3.10 + - 3.11 + - 3.12 \ No newline at end of file diff --git a/src/mlpro/bf/control/__init__.py b/src/mlpro/bf/control/__init__.py new file mode 100644 index 000000000..eace87d6b --- /dev/null +++ b/src/mlpro/bf/control/__init__.py @@ -0,0 +1 @@ +from mlpro.bf.control.basics import * \ No newline at end of file diff --git a/src/mlpro/bf/control/basics.py b/src/mlpro/bf/control/basics.py new file mode 100644 index 000000000..f15d7ade3 --- /dev/null +++ b/src/mlpro/bf/control/basics.py @@ -0,0 +1,1171 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control +## -- Module : basics.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-08-31 0.0.0 DA Creation +## -- 2024-09-04 0.1.0 DA Updates on class design +## -- 2024-09-07 0.2.0 DA Classes CTRLError, Controller: design updates +## -- 2024-09-11 0.3.0 DA - class CTRLError renamed ControlError +## -- - new class ControlPanel +## -- 2024-09-27 0.4.0 DA Class ControlPanel: new parent EventManager +## -- 2024-10-04 0.5.0 DA Updates on class Controller +## -- 2024-10-06 0.6.0 DA New classes ControlTask, Operator +## -- 2024-10-07 0.7.0 DA - new method ControlShared.get_tstamp() +## -- - refactoring of class Controller +## -- 2024-10-08 0.8.0 DA Classes ControlPanel, ControlShared: refactoring +## -- 2024-10-10 0.9.0 DA - class Controller: bugfix in method compute_output() +## -- - class ControlWorkflow: method run() redefined +## -- 2024-10-13 0.10.0 DA Refactoring: changed parent of class Action to Instance +## -- 2024-10-24 0.11.0 DA Class ControlledSystem: redefinition of method init_plot(), +## -- update_plot(), remove_plot() +## -- 2024-11-08 0.12.0 DA Little refactoring of class Operator +## -- 2024-11-09 0.13.0 DA Various changes and improvements +## -- 2024-11-10 0.14.0 DA - class ControlWorkflow: master plot disabled +## -- - new helper functions get_ctrl_data(), replace_ctrl_data() +## -- 2024-11-11 0.15.0 DA Implementation of custom method ControlWorkflow._on_event() +## -- 2024-11-14 0.16.0 DA Introduction of time management +## -- 2024-11-15 0.17.0 DA Various corrections +## -- 2024-11-26 1.0.0 DA Classes Controller, ControlledSystem: initial idle loop to +## -- determine the initial system state +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 1.0.0 (2024-11-26) + +This module provides basic classes around the topic closed-loop control. + +""" + +from typing import Iterable, Tuple, List +from datetime import datetime, timedelta + +from matplotlib.figure import Figure + +from mlpro.bf.plot import PlotSettings +from mlpro.bf.various import Log, TStampType, Timer +from mlpro.bf.mt import Range, Task, Workflow, Shared +from mlpro.bf.ops import Mode +from mlpro.bf.events import Event, EventManager +from mlpro.bf.exceptions import * +from mlpro.bf.math import Element, Function, MSpace +from mlpro.bf.streams import InstDict, InstTypeNew, Instance, StreamTask, StreamWorkflow, StreamShared, StreamScenario +from mlpro.bf.systems import Action, System + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlData (Instance): + """ + Root class for all types of control data. + + Parameters + ---------- + p_id : int, + Instance id. + p_value_space : MSpace + Metric space of the values. + p_values : Iterable + Values. + p_tstamp : TStampType = None + Optional time stamp. + **p_kwargs + Optional further keyword arguments. + """ + + C_TYPE = 'Control Data' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_id : int, + p_value_space : MSpace, + p_values : Iterable = None, + p_tstamp: TStampType = None, + **p_kwargs ): + + feature_element = Element( p_set = p_value_space ) + feature_element.set_values( p_values = p_values ) + + super().__init__( p_feature_data = feature_element, + p_label_data = None, + p_tstamp = p_tstamp, + **p_kwargs ) + + self.set_id( p_id = p_id ) + + +## ------------------------------------------------------------------------------------------------- + def _get_value_space(self): + return self.get_feature_data().get_related_set() + + +## ------------------------------------------------------------------------------------------------- + def _get_values(self): + return self.get_feature_data().get_values() + + +## ------------------------------------------------------------------------------------------------- + def _set_values(self, p_values): + self.get_feature_data().set_values( p_values = p_values) + + +## ------------------------------------------------------------------------------------------------- + def copy(self): + duplicate = self.__class__( p_id = self.get_id(), + p_value_space = self.value_space, + p_values = self.values, + p_tstamp=self.get_tstamp(), + p_kwargs=self._get_kwargs() ) + return duplicate + + +## ------------------------------------------------------------------------------------------------- + value_space = property( fget=_get_value_space ) + values = property( fget=_get_values, fset=_set_values ) + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +def get_ctrl_data( p_inst: InstDict, p_type: type, p_remove: bool = False) -> ControlData: + """ + Gets and optionally removes a control data instance of a particular type from the p_inst dictionary. + + Parameters + ---------- + p_inst: InstDict + Dictionary of instances. + p_type: type + Type of instance to be found. + p_remove: bool = False + If true, the found instance is removed. + """ + + ctrl_data_found : ControlData = None + + for (inst_type, inst) in p_inst.values(): + if isinstance( inst, p_type): + ctrl_data_found = inst + break + + if ( p_remove ) and ( ctrl_data_found is not None ): + del p_inst[ctrl_data_found.id] + + return ctrl_data_found + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +def replace_ctrl_data( p_inst: InstDict, p_ctrl_data: ControlData ): + """ + Adds the specified control data instance to the p_inst dictionary and removes an already existing + control data instance of the same type before that. + + Parameters + ---------- + p_inst: InstDict + Dictionary of instances. + p_ctrl_data : ControlData + Control data instance to be added. + + Returns + ------- + bool + True, if control data of the same type were actually removed. False otherwise. + """ + + result = ( get_ctrl_data( p_inst = p_inst, p_type = type(p_ctrl_data), p_remove = True ) != None ) + p_inst[p_ctrl_data.id] = (InstTypeNew, p_ctrl_data) + + return result + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class SetPoint (ControlData): + """ + Setpoint. + """ + + C_NAME = 'Setpoint' + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlError (ControlData): + """ + Control error. + """ + + C_NAME = 'Control Error' + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlVariable (ControlData): + """ + Output of a controller/input of a controlled system. + """ + + C_NAME = 'Control Variable' + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlledVariable (ControlData): + """ + Output of a controlled system. + """ + + C_NAME = 'Controlled Variable' + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlTask (StreamTask): + """ + Base class for all control tasks. + """ + + C_TYPE = 'Control Task' + +## ------------------------------------------------------------------------------------------------- + def reset(self, p_seed = None, **p_kwargs): + self._reset( p_seed = p_seed, **p_kwargs ) + + +## ------------------------------------------------------------------------------------------------- + def _reset(self, p_seed = None, **p_kwargs): + pass + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class Operator (ControlTask): + """ + Base class for all operators. + + Parameters + ---------- + p_range_max : int + Maximum range of asynchonicity. See class Range. Default is Range.C_RANGE_PROCESS. + p_visualize : bool + Boolean switch for visualisation. Default = False. + p_logging + Log level (see constants of class Log). Default: Log.C_LOG_ALL + """ + + C_TYPE = 'Operator' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_range_max=Range.C_RANGE_NONE, + p_visualize = False, + p_logging=Log.C_LOG_ALL ): + + super().__init__( p_name = self.C_NAME, + p_range_max = p_range_max, + p_duplicate_data = False, + p_visualize = p_visualize, + p_logging = p_logging ) + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class Controller (ControlTask): + """ + Template class for closed-loop controllers. + + Parameters + ---------- + p_input_space : MSpace + Input (or error) space of the controller. + p_output_space : MSpace + Output (or action) space of the controller. + p_id = None + Unique id of the controller + ... + """ + + C_TYPE = 'Controller' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_input_space : MSpace, + p_output_space : MSpace, + p_id = None, + p_name: str = None, + p_range_max = Range.C_RANGE_NONE, + p_visualize: bool = False, + p_logging=Log.C_LOG_ALL, + **p_kwargs ): + + self._input_space : MSpace = p_input_space + self._output_space : MSpace = p_output_space + self._last_update : TStampType = None + self._current_ctrl_var : ControlVariable = None + self._computation_time : timedelta = None + + super().__init__( p_name = p_name, + p_range_max = p_range_max, + p_duplicate_data = False, + p_visualize = p_visualize, + p_logging = p_logging, + **p_kwargs ) + + if p_id is not None: + self.id = p_id + + +## ------------------------------------------------------------------------------------------------- + def set_parameter(self, **p_param): + """ + Custom method to set/change the parameters of a specific controller implementation. + + Parameters + ---------- + **p_param + Parameters of the controller. + """ + + pass + + +## ------------------------------------------------------------------------------------------------- + def _run(self, p_inst: InstDict): + + # 0 Intro + so : ControlShared = self.get_so() + + + # 1 Get control error instance + ctrl_error = get_ctrl_data( p_inst = p_inst, p_type = ControlError, p_remove = True ) + if ctrl_error is None: + self.log(Log.C_LOG_TYPE_W, 'Control error instance is missing!') + return + + + # 2 Remove existing control variable from inst dictionary + get_ctrl_data( p_inst = p_inst, p_type = ControlVariable, p_remove = True ) + + + # 3 Compute control output + try: + compute = ( so.timer.get_time() - self._last_update ) >= ( so.latency - self._computation_time ) + except: + compute = True + + if compute or ( self._current_ctrl_var is None ): + self.log(Log.C_LOG_TYPE_I, 'Computation started') + tstamp_before = so.timer.get_time() + self._current_ctrl_var = self.compute_output( p_ctrl_error = ctrl_error ) + tstamp_after = so.timer.get_time() + tdelta = tstamp_after - tstamp_before + + # Determine the smallest computation time > 0 + if ( self._computation_time is None ) or ( tdelta < self._computation_time ): + self._computation_time = tdelta + + self._last_update = tstamp_after + self.log(Log.C_LOG_TYPE_I, 'Computation finished') + else: + self._current_ctrl_var = self._current_ctrl_var.copy() + self._current_ctrl_var.id = so.get_next_inst_id() + self._current_ctrl_var.tstamp = so.get_tstamp() + self.log(Log.C_LOG_TYPE_I, 'Computation skipped. Last action duplicated.') + + + # 4 Complete and store new control variable + p_inst[self._current_ctrl_var.id] = (InstTypeNew, self._current_ctrl_var) + + +## ------------------------------------------------------------------------------------------------- + def compute_output( self, p_ctrl_error: ControlError ) -> ControlVariable: + """ + Computes a control variable based on an incoming control error. It creates a new control + variable and call the custom method _compute_output() to fill it with values. + + Parameters + ---------- + p_ctrl_error : ControlError + Control error. + + Returns + ------- + ControlVariable + ControlVariable. + """ + + # 1 Create new control variable + so = self.get_so() + ctrl_var = ControlVariable( p_id = so.get_next_inst_id(), + p_value_space = self._output_space ) + + # 2 Call custom method to fill the new action element + tstamp_before = datetime.now() + self._compute_output( p_ctrl_error = p_ctrl_error, p_ctrl_var = ctrl_var ) + tstamp_after = datetime.now() + tdelta = tstamp_after - tstamp_before + so.timer.add_time( p_delta = tdelta ) + + + # 3 Complete and return the new control variable + ctrl_var.tstamp = so.get_tstamp() + return ctrl_var + + +## ------------------------------------------------------------------------------------------------- + def _compute_output( self, p_ctrl_error : ControlError, p_ctrl_var : ControlVariable ): + """ + Custom method to compute a control output based on an incoming control error. The result needs + to be stored in the control variable element handed over. + + Parameters + ---------- + p_ctrl_error : CTRLError + Control error. + p_ctrl_var : ControlVariable + Control variable to be filled with resulting value(s). + """ + + raise NotImplementedError + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControllerFct (Controller): + """ + Wrapper class for controllers based on a mathematical function mapping an error to an action. + + Parameters + ---------- + p_fct : Function + Function object mapping a control error to an action + + See class Controller for further parameters. + """ + + C_TYPE = 'Controller Fct' + C_NAME = '' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_fct: Function, + p_name: str = None, + p_range_max = Range.C_RANGE_NONE, + p_duplicate_data: bool = False, + p_visualize: bool = False, + p_logging = Log.C_LOG_ALL, + **p_kwargs ): + + super().__init__( p_name = p_name, + p_range_max = p_range_max, + p_duplicate_data = p_duplicate_data, + p_visualize = p_visualize, + p_logging = p_logging, + **p_kwargs ) + + self._fct : Function = p_fct + + +## ------------------------------------------------------------------------------------------------- + def set_parameter(self, **p_param): + + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def _compute_outsput(self, p_ctrl_error: ControlError, p_ctrl_var: ControlVariable): + + raise NotImplementedError + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlledSystem (ControlTask): + """ + Wrapper class for state-based systems. + """ + + C_TYPE = 'Controlled System' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_system : System, + p_name: str = None, + p_range_max = Range.C_RANGE_NONE, + p_visualize: bool = False, + p_logging = Log.C_LOG_ALL ): + + super().__init__( p_name = p_name, + p_range_max = p_range_max, + p_duplicate_data = False, + p_visualize = p_visualize, + p_logging = p_logging ) + + self.system : System = p_system + self._last_update : TStampType = None + self._current_action : Action = None + + +## ------------------------------------------------------------------------------------------------- + def _run(self, p_inst: InstDict ): + + # 0 Intro + so : ControlShared = self.get_so() + + + # 1 Remove an already existing controlled variable + get_ctrl_data( p_inst = p_inst, p_type = ControlledVariable, p_remove = True ) + + + # 2 Get and remove control variable + ctrl_var = get_ctrl_data( p_inst = p_inst, p_type = ControlVariable, p_remove = True ) + + if ctrl_var is not None: + # 3 Process control variable + + # 3.1 Update the current action instance for the wrapped system after the latency time period + if ( self._last_update is None ) or ( ( so.timer.get_time() - self._last_update ) >= so.latency ): + self._current_action = Action( p_agent_id = 0, + p_action_space = ctrl_var.get_feature_data().get_related_set(), + p_values = ctrl_var.values, + p_tstamp = ctrl_var.tstamp ) + self._last_update = so.timer.get_time() + self.log(Log.C_LOG_TYPE_I, 'Action updated') + + + # 3.2 Let the wrapped system process the action + if self.system.process_action( p_action = self._current_action, p_t_step = so.latency_min ): + so.timer.add_time( p_delta = so.latency_min ) + + else: + self.log(Log.C_LOG_TYPE_E, 'Processing of control variable failed!') + + else: + self.log(Log.C_LOG_TYPE_W, 'Control variable missing!') + + + # 4 Determine the current system state + state = self.system.get_state() + ctrlled_var = ControlledVariable( p_id = so.get_next_inst_id(), + p_value_space = self.system.get_state_space(), + p_values = state.values, + p_tstamp = so.get_tstamp() ) + p_inst[ctrlled_var.id] = ( InstTypeNew, ctrlled_var) + self.log(Log.C_LOG_TYPE_S, 'Controlled variable created') + + +## ------------------------------------------------------------------------------------------------- + def init_plot(self, p_figure = None, p_plot_settings = None): + super().init_plot(p_figure = p_figure, p_plot_settings = p_plot_settings) + self.system.init_plot(p_figure = p_figure, p_plot_settings = p_plot_settings) + + +## ------------------------------------------------------------------------------------------------- + def update_plot(self, p_inst : InstDict = None, **p_kwargs): + super().update_plot(p_inst = p_inst, **p_kwargs) + self.system.update_plot( **p_kwargs) + + +## ------------------------------------------------------------------------------------------------- + def remove_plot(self, p_refresh : bool = True): + super().remove_plot(p_refresh = p_refresh) + self.system.remove_plot(p_refresh = p_refresh) + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlPanel (EventManager): + """ + Enables external control of a closed-loop control. + """ + + C_TYPE = 'Control Panel' + C_NAME = '????' + + C_EVENT_ID_SETPOINT_CHG = 'SETPOINT_CHG' + + +## ------------------------------------------------------------------------------------------------- + def start(self): + """ + (Re-)starts a closed-loop control. + """ + + self.log(Log.C_LOG_TYPE_S, 'Control process started') + self._start() + + +## ------------------------------------------------------------------------------------------------- + def _start(self): + """ + Custom method to (re-)start a closed-loop control. + """ + + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def stop(self): + """ + Ends a closed-loop control. + """ + + self.log(Log.C_LOG_TYPE_S, 'Control process stopped') + self._stop() + + +## ------------------------------------------------------------------------------------------------- + def _stop(self): + """ + Custom method to end a closed-loop control. + """ + + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def set_setpoint( self, p_values: Iterable ): + """ + Changes the setpoint values of a closed-loop control. + + Parameters + ---------- + p_values : Iterable + New setpoint values. + """ + + self.log(Log.C_LOG_TYPE_S, 'Setpoint values changed to', p_values) + self._set_setpoint( p_values = p_values ) + self._raise_event( p_event_id = self.C_EVENT_ID_SETPOINT_CHG, + p_event_object = Event( p_raising_object = self ) ) + + +## ------------------------------------------------------------------------------------------------- + def _set_setpoint( self, p_values: Iterable ): + """ + Custom method to change setpoint values. + + Parameters + ---------- + p_values : Iterable + New setpoint values. + """ + + raise NotImplementedError + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlShared (StreamShared, ControlPanel, Log): + """ + ... + """ + + C_TID_ADMIN = 'wf' + +## ------------------------------------------------------------------------------------------------- + def __init__(self, p_range: int = Range.C_RANGE_PROCESS): + + StreamShared.__init__(self, p_range=p_range) + Log.__init__(self, p_logging = Log.C_LOG_NOTHING) + + self._next_inst_id = 0 + self._superior_so : ControlShared = None + self._top_so : ControlShared = self + self._ctrlled_var_space : MSpace = None + self._ctrl_var_space : MSpace = None + self._timer : Timer = None + self._latency : timedelta = None + self._latency_min : timedelta = None + + +## ------------------------------------------------------------------------------------------------- + def init( self, + p_ctrlled_var_space: MSpace, + p_ctrl_var_space: MSpace, + p_mode: int, + p_latency: timedelta ): + """ + Initializes the shared object with contextual information. + + Parameters + ---------- + p_ctrlled_var_space : MSpace + Controlled variable space. + p_ctrl_var_space : MSpace + Control variable space. + p_mode : int + Operation mode (0 = Simulation, 1 = Real operation) + p_latency : timedelta + controlled system + """ + + self._ctrlled_var_space = p_ctrlled_var_space + self._ctrl_var_space = p_ctrl_var_space + + mode_timer = 1 - p_mode + self._timer = Timer( p_mode = mode_timer ) + + if ( self.latency is None ) or ( p_latency < self.latency ): + self.latency = p_latency + + +## ------------------------------------------------------------------------------------------------- + def reset(self, p_inst: InstDict): + setpoint = get_ctrl_data( p_inst = p_inst, p_type = SetPoint, p_remove = False) + if setpoint != None: + replace_ctrl_data( p_inst = self._instances[self.C_TID_ADMIN], p_ctrl_data = setpoint ) + + +## ------------------------------------------------------------------------------------------------- + def get_superior_so(self) -> Shared: + return self._superior_so + + +## ------------------------------------------------------------------------------------------------- + def set_superior_so(self, p_so : Shared ): + """ + Sets the superior shared object. This is relevant for cascade control systems, where the + top level shared object is responsible for system-wide unique instance ids etc. + + Parameters + ---------- + p_so : Shared + Superior shared object. + """ + + if ( p_so.latency_min is None ) or ( self.latency_min is None ) or ( self.latency_min < p_so.latency_min ): + p_so.latency_min = self.latency_min + + self._superior_so = p_so + self._top_so = p_so.top_so + + +## ------------------------------------------------------------------------------------------------- + def get_top_so(self) -> Shared: + return self._top_so + + +## ------------------------------------------------------------------------------------------------- + def get_latency(self) -> timedelta: + return self._latency + + +## ------------------------------------------------------------------------------------------------- + def set_latency(self, p_latency : timedelta): + self._latency = p_latency + if ( self.latency_min is None ) or ( p_latency < self.latency_min ): + self.latency_min = p_latency + + +## ------------------------------------------------------------------------------------------------- + def get_latency_min(self) -> timedelta: + if self == self._top_so: + return self._latency_min + else: + return self._top_so.latency_min + + +## ------------------------------------------------------------------------------------------------- + def set_latency_min(self, p_latency : timedelta): + if self == self.top_so: + self._latency_min = p_latency + else: + self.top_so.latency_min = p_latency + + +## ------------------------------------------------------------------------------------------------- + def get_next_inst_id(self) -> int: + """ + Returns the next instance id. + + Returns + ------- + int + Next instance id. + """ + + if self.top_so == self: + self.lock( p_tid = self.C_TID_ADMIN ) + next_id = self._next_inst_id + self._next_inst_id += 1 + self.unlock() + return next_id + + else: + return self.top_so.get_next_inst_id() + + +## ------------------------------------------------------------------------------------------------- + def get_tstamp(self) -> TStampType: + """ + Returns the current process time stamp. + """ + + if self.top_so == self: + return self._timer.get_time() + + else: + return self.top_so.get_tstamp() + + +## ------------------------------------------------------------------------------------------------- + def get_timer(self) -> Timer: + if self.top_so == self: + return self._timer + else: + return self.top_so.timer + + +## ------------------------------------------------------------------------------------------------- + def _start(self): + + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def _stop(self): + + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def _set_setpoint(self, p_values: Iterable): + + # 0 Intro + setpoint : SetPoint = None + self.lock( p_tid = self.C_TID_ADMIN ) + + + # 1 Locate the instance dictionary for the first control task + try: + inst_admin = self._instances[self.C_TID_ADMIN] + except: + inst_admin = {} + self._instances[self.C_TID_ADMIN] = inst_admin + + + # 2 Replace setpoint instance + get_ctrl_data( p_inst = inst_admin, p_type = SetPoint, p_remove = True) + + setpoint = SetPoint( p_id = self.get_next_inst_id(), + p_value_space = self._ctrlled_var_space, + p_values = p_values, + p_tstamp = self.get_tstamp() ) + + inst_admin[setpoint.id] = (InstTypeNew, setpoint) + + + # 3 Outro + self.unlock() + + +## ------------------------------------------------------------------------------------------------- + superior_so = property( fget = get_superior_so, fset = set_superior_so ) + top_so = property( fget = get_top_so ) + latency = property( fget = get_latency, fset = set_latency ) + latency_min = property( fget = get_latency_min, fset = set_latency_min ) + timer = property( fget = get_timer ) + + + + +ControlPanelEntry = Tuple[ControlPanel, Workflow] + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlWorkflow (StreamWorkflow, Mode): + """ + Container class for all tasks of a control cycle. + """ + + C_TYPE = 'Control Workflow' + C_NAME = '' + + C_PLOT_ACTIVE = False # Currently no master plot window here... + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_mode, + p_name: str = None, + p_range_max = Range.C_RANGE_NONE, + p_class_shared = ControlShared, + p_visualize : bool = False, + p_logging = Log.C_LOG_ALL, + **p_kwargs ): + + StreamWorkflow.__init__( self, + p_name = p_name, + p_range_max = p_range_max, + p_class_shared = p_class_shared, + p_visualize = p_visualize, + p_logging = p_logging, + **p_kwargs ) + + Mode.__init__( self, + p_mode = p_mode, + p_logging = p_logging ) + + self.get_so().switch_logging( p_logging = p_logging ) + + self._workflows = [] + + +## ------------------------------------------------------------------------------------------------- + def get_control_panels(self) -> List[ControlPanelEntry]: + result = [ (self.get_so(), self) ] + + for workflow in self._workflows: + result.extend( workflow.get_control_panels() ) + + return result + + +## ------------------------------------------------------------------------------------------------- + def set_mode(self, p_mode): + + if p_mode == self._mode: return + + Mode.set_mode( self, p_mode = p_mode ) + + for task in self.tasks: + try: + task.set_mode( p_mode = p_mode ) + except: + pass + + +## ------------------------------------------------------------------------------------------------- + def assign_so(self, p_so): + """ + External assignment of shared objects is disabled for control workflows. + """ + + self.get_so().superior_so = p_so + + +## ------------------------------------------------------------------------------------------------- + def add_task(self, p_task: Task, p_pred_tasks: list = None): + + if not isinstance( p_task, Workflow ): + p_task.set_name( 'Workflow ' + self.get_name() + ', ' + p_task.get_name() ) + + if isinstance( p_task, ControlWorkflow ): + self._workflows.append(p_task) + + StreamWorkflow.add_task( self, p_task = p_task, p_pred_tasks = p_pred_tasks) + + try: + p_task.set_mode( p_mode = self._mode ) + except: + pass + + if isinstance( p_task, ControlledSystem ): + self.get_so().init( p_ctrlled_var_space = p_task.system.get_state_space(), + p_ctrl_var_space = p_task.system.get_action_space(), + p_mode = self._mode, + p_latency = p_task.system.get_latency() ) + + +## ------------------------------------------------------------------------------------------------- + def run( self, + p_range : int = None, + p_wait: bool = False, + p_inst : InstDict = None ): + + # 1 Transfer the setpoint instances from the predecessor tasks of a superior workflow + try: + inst_dict = p_inst.copy() + except: + superior_setpoint : SetPoint = None + inst_dict = None + + superior_so = self.get_so().superior_so + + if superior_so is not None: + superior_so.lock( p_tid = self.get_tid() ) + + for pred_task in self.get_predecessors(): + superior_setpoint = get_ctrl_data( p_inst = superior_so._instances[pred_task.get_tid()], + p_type = SetPoint, + p_remove = True ) + + superior_so.unlock() + + if superior_setpoint is not None: + self.get_so().set_setpoint( p_values = superior_setpoint.values ) + + + # 2 Execute all tasks + StreamWorkflow.run( self, p_range = p_range, p_wait = p_wait, p_inst = inst_dict) + + +## ------------------------------------------------------------------------------------------------- + def _on_finished(self): + + # 1 Add/replace the outcomes of the final task to the instance dict of the initial task + so = self.get_so() + so.lock( p_tid = ControlShared.C_TID_ADMIN ) + setpoint = get_ctrl_data( p_inst = so._instances[ControlShared.C_TID_ADMIN], p_type = SetPoint, p_remove = False ) + + del so._instances[ControlShared.C_TID_ADMIN] + new_setpoint = setpoint.copy() + new_setpoint.id = so.get_next_inst_id() + new_setpoint.tstamp = so.get_tstamp() + so._instances[ControlShared.C_TID_ADMIN] = { new_setpoint.id : (InstTypeNew, new_setpoint) } + + for task in self._final_tasks: + so._instances[ControlShared.C_TID_ADMIN].update(so._instances[task.id]) + + so.unlock() + + # 2 Add the outcomes of the final task to the instance dict of a superior shared object + superior_so = so.superior_so + + if superior_so is not None: + superior_so.lock( p_tid = self.get_tid() ) + + superior_so._instances[self.get_tid()] = {} + + for task in self._final_tasks: + superior_so._instances[self.get_tid()].update( so._instances[task.id] ) + + superior_so.unlock() + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ControlSystem (StreamScenario): + """ + Template class for custom control systems. Please implement + """ + + C_TYPE = 'Control System' + +## ------------------------------------------------------------------------------------------------- + def setup(self, **p_kwargs): + self._control_workflow = self._setup( p_mode = self.get_mode(), + p_visualize = self.get_visualization(), + p_logging = self.get_log_level(), + **p_kwargs ) + + + ## ------------------------------------------------------------------------------------------------- + def _setup(self, p_mode, p_visualize: bool, p_logging, **p_kwargs) -> ControlWorkflow: + """ + Custom method to set up a control workflow. Create a new object of type ControlWorkflow and + add all control tasks of your scenario. + + Parameters + ---------- + p_mode + Operation mode. See Mode.C_VALID_MODES for valid values. Default = Mode.C_MODE_SIM. + p_visualize : bool + Boolean switch for visualisation. + p_logging + Log level (see constants of class Log). Default: Log.C_LOG_ALL. + p_kwargs : dict + Custom keyword parameters. + + Returns + ------- + ControlCycle + Object of type ControlWorkflow. + """ + + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def _set_mode(self, p_mode): + self._control_workflow.set_mode( p_mode = p_mode) + + +## ------------------------------------------------------------------------------------------------- + def _reset(self, p_seed): + self._control_workflow.reset( p_seed = p_seed) + + +## ------------------------------------------------------------------------------------------------- + def get_control_panels(self) -> List[ControlPanelEntry]: + """ + Returns + ------- + panel : List[ControlPanelEntry] + Object that enables the external control of a closed-loop control process. + """ + + return self._control_workflow.get_control_panels() + + +## ------------------------------------------------------------------------------------------------- + def _run_cycle(self): + + error = False + + try: + self._control_workflow.run() + except KeyError as Argument: + error = True + if Argument.args[0] == ControlShared.C_TID_ADMIN: + self.log(Log.C_LOG_TYPE_E, 'Setpoint missing') + else: + self.log(Log.C_LOG_TYPE_E, 'Control instance missing for task', Argument.args[0]) + + return False, error, False, False + + +## ------------------------------------------------------------------------------------------------- + def init_plot(self, p_figure: Figure = None, p_plot_settings: PlotSettings = None): + self._control_workflow.init_plot( p_figure = p_figure, + p_plot_settings = p_plot_settings ) + \ No newline at end of file diff --git a/src/mlpro/bf/control/controllers/__init__.py b/src/mlpro/bf/control/controllers/__init__.py new file mode 100644 index 000000000..77fc305cd --- /dev/null +++ b/src/mlpro/bf/control/controllers/__init__.py @@ -0,0 +1 @@ +from mlpro.bf.control.controllers.hunter import Hunter \ No newline at end of file diff --git a/src/mlpro/bf/control/controllers/hunter.py b/src/mlpro/bf/control/controllers/hunter.py new file mode 100644 index 000000000..3eb185343 --- /dev/null +++ b/src/mlpro/bf/control/controllers/hunter.py @@ -0,0 +1,37 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control.controllers +## -- Module : hunter.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-10-10 0.1.0 DA Initial implementation +## -- 2024-12-03 0.1.1 DA Bugfix +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.1.1 (2024-12-03) + +This module provides a simple demo system that just cumulates a percentage part of the incoming +action to the inner state. +""" + + +import numpy as np + +from mlpro.bf.control import Controller, ControlError, ControlVariable + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class Hunter (Controller): + """ + ... + """ + + C_NAME = 'Hunter' + +## ------------------------------------------------------------------------------------------------- + def _compute_output(self, p_ctrl_error : ControlError, p_ctrl_var : ControlVariable ): + p_ctrl_var.values = np.array(p_ctrl_error.values) diff --git a/src/mlpro/bf/control/controlsystems/__init__.py b/src/mlpro/bf/control/controlsystems/__init__.py new file mode 100644 index 000000000..915ff285e --- /dev/null +++ b/src/mlpro/bf/control/controlsystems/__init__.py @@ -0,0 +1,2 @@ +from mlpro.bf.control.controlsystems.cascade import ControllerList, ControlledSystemList, CascadeControlSystem +from mlpro.bf.control.controlsystems.basic import BasicControlSystem \ No newline at end of file diff --git a/src/mlpro/bf/control/controlsystems/basic.py b/src/mlpro/bf/control/controlsystems/basic.py new file mode 100644 index 000000000..cbc5ff6ea --- /dev/null +++ b/src/mlpro/bf/control/controlsystems/basic.py @@ -0,0 +1,97 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control.control_scenarios +## -- Module : basic.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-10-04 0.1.0 DA Initial implementation +## -- 2024-10-09 0.2.0 DA Refactoring +## -- 2024-11-09 0.3.0 DA Refactoring +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.3.0 (2024-11-09) + +This module provides a simplified container class for a basic synchronous control system containing + +- a controller +- a controlled system +- an optional integrator for the control variable + +""" + +from typing import Union + +from mlpro.bf.various import Log +from mlpro.bf.systems import System +from mlpro.bf.control import Controller, ControlledSystem +from mlpro.bf.control.controlsystems import CascadeControlSystem +from mlpro.bf.control.operators import Integrator + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class BasicControlSystem (CascadeControlSystem): + """ + Simplified container class for a basic synchronous control system containing + + - a controller + - a controlled system + - an optional integrator for the control variable + + Parameters + ---------- + p_mode + Operation mode. See Mode.C_VALID_MODES for valid values. Default = Mode.C_MODE_SIM. + p_controller : Controller + Controller to be used in the control workflow + p_controlled_system : ControlledSystem + Controlled system to be used in the control workflow + p_name : str = '' + Name of the control system + p_cycle_limit : int + Maximum number of cycles. Default = 0 (no limit). + p_ctrl_var_integration : bool = False + If True, an optional intrator is added to control workflow + p_visualize : bool + Boolean switch for visualisation. Default = False. + p_logging + Log level (see constants of class Log). Default: Log.C_LOG_ALL. + p_kwargs : dict + Custom keyword parameters handed over to custom method setup(). + """ + + C_TYPE = 'Basic Control System' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_mode, + p_controller : Controller, + p_controlled_system : Union[System, ControlledSystem], + p_ctrl_var_integration : bool = False, + p_name : str = '', + p_cycle_limit = 0, + p_visualize : bool = False, + p_logging = Log.C_LOG_ALL, + **p_kwargs ): + + if p_ctrl_var_integration: + controllers= [ [ p_controller, + Integrator( p_range_max = p_controller.get_range(), + p_visualize = p_visualize, + p_logging = p_logging ) ] ] + else: + controllers = [ p_controller ] + + + super().__init__( p_mode = p_mode, + p_controllers = controllers, + p_controlled_systems = [ p_controlled_system ], + p_name = p_name, + p_cycle_limit = p_cycle_limit, + p_visualize = p_visualize, + p_logging = p_logging, + **p_kwargs ) \ No newline at end of file diff --git a/src/mlpro/bf/control/controlsystems/cascade.py b/src/mlpro/bf/control/controlsystems/cascade.py new file mode 100644 index 000000000..eb13917c9 --- /dev/null +++ b/src/mlpro/bf/control/controlsystems/cascade.py @@ -0,0 +1,164 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control.controlsystems +## -- Module : cascade.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-11-08 0.1.0 DA Initial implementation +## -- 2024-11-09 1.0.0 DA Extended ControlledSystemList by type System +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 1.0.0 (2024-11-089) + +This module provides a container class for cascade control systems. + +""" + +from typing import List, Union + +from mlpro.bf.various import Log +from mlpro.bf.exceptions import * +from mlpro.bf.systems import System +from mlpro.bf.control import * +from mlpro.bf.control.operators import Comparator, Converter + + + +ControllerList = List[ Union[ Controller, List[ControlTask] ] ] +ControlledSystemList = List[ Union[ System, ControlledSystem, List[ControlTask] ] ] + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class CascadeControlSystem (ControlSystem): + """ + Cascade control system. + + Parameters + ---------- + p_mode + Operation mode. See Mode.C_VALID_MODES for valid values. Default = Mode.C_MODE_SIM. + p_controllers : ControllerList + List of controllers to be cascaded in order to outer to inner controller. + p_controlled_systems : ControlledSystemLists + List of controlled systems to be cascaded in order to outer to inner controlled system. + p_name : str = '' + Name of the control system + p_cycle_limit : int + Maximum number of cycles. Default = 0 (no limit). + p_visualize : bool + Boolean switch for visualisation. Default = False. + p_logging + Log level (see constants of class Log). Default: Log.C_LOG_ALL. + p_kwargs : dict + Custom keyword parameters handed over to custom method setup(). + """ + + C_TYPE = 'Cascade Control System' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_mode, + p_controllers : ControllerList, + p_controlled_systems : ControlledSystemList, + p_name : str = '', + p_cycle_limit = 0, + p_visualize : bool = False, + p_logging = Log.C_LOG_ALL, + **p_kwargs ): + + if ( len(p_controllers) == 0) or ( len(p_controllers) != len(p_controlled_systems) ): + raise ParamError( 'Please provide an equal number of controllers and related controlled systems') + + self._controllers = p_controllers.copy() + self._controlled_systems = p_controlled_systems.copy() + self.set_name( p_name = p_name ) + + super().__init__( p_mode = p_mode, + p_cycle_limit = p_cycle_limit, + p_visualize = p_visualize, + p_logging = p_logging, + **p_kwargs ) + + +## ------------------------------------------------------------------------------------------------- + def _add_tasks_to_workflow(self, p_workflow : ControlWorkflow, p_tasks, p_pred_tasks = [] ) -> ControlTask: + + if isinstance( p_tasks, list): + tasks = p_tasks + else: + tasks = [ p_tasks ] + + pred_tasks = p_pred_tasks + + for task in tasks: + if isinstance( task, System ): + # Native systems need to be wrapped + task = ControlledSystem( p_system = task, + p_name = task.get_name(), + p_range_max = task.get_range(), + p_visualize = self.get_visualization(), + p_logging = self.get_log_level() ) + + p_workflow.add_task( p_task = task, p_pred_tasks = pred_tasks ) + pred_tasks = [ task ] + + return task + + +## ------------------------------------------------------------------------------------------------- + def _setup(self, p_mode, p_visualize: bool, p_logging, **p_kwargs) -> ControlWorkflow: + + # 0 Intro + workflow_prev : ControlWorkflow = None + + + # 1 Create cascaded workflows from config data + workflow_list = list(zip(self._controllers, self._controlled_systems)) + workflow_list.reverse() + num_workflows = len( workflow_list ) + + for i, (t_ctrl, t_ctrl_sys) in enumerate(workflow_list): + + workflow_id = num_workflows - i - 1 + + workflow = ControlWorkflow( p_mode = p_mode, + p_name = str(workflow_id), + p_visualize = p_visualize, + p_logging = p_logging ) + + t_comp = Comparator( p_visualize = p_visualize, p_logging = p_logging ) + workflow.add_task( p_task = t_comp ) + + last_task = self._add_tasks_to_workflow( p_workflow = workflow, p_tasks = t_ctrl, p_pred_tasks = [ t_comp ] ) + + if workflow_prev is not None: + t_conv = Converter( p_src_type = ControlVariable, + p_dst_type = SetPoint, + p_visualize = p_visualize, + p_logging = p_logging ) + workflow.add_task( p_task = t_conv, p_pred_tasks = [ last_task ] ) + + workflow.add_task( p_task = workflow_prev, p_pred_tasks = [ t_conv ] ) + + t_conv = Converter( p_src_type = ControlledVariable, + p_dst_type = ControlVariable, + p_visualize = p_visualize, + p_logging = p_logging ) + workflow.add_task( p_task = t_conv, p_pred_tasks = [ workflow_prev ] ) + + self._add_tasks_to_workflow( p_workflow = workflow, p_tasks = t_ctrl_sys, p_pred_tasks = [ t_conv ] ) + + else: + self._add_tasks_to_workflow( p_workflow = workflow, p_tasks = t_ctrl_sys, p_pred_tasks = [ last_task ] ) + + workflow_prev = workflow + + + # 2 Return the outer control workflow + return workflow + diff --git a/src/mlpro/bf/control/operators/__init__.py b/src/mlpro/bf/control/operators/__init__.py new file mode 100644 index 000000000..492115ae9 --- /dev/null +++ b/src/mlpro/bf/control/operators/__init__.py @@ -0,0 +1,3 @@ +from mlpro.bf.control.operators.comparator import Comparator +from mlpro.bf.control.operators.integrator import Integrator +from mlpro.bf.control.operators.converter import Converter \ No newline at end of file diff --git a/src/mlpro/bf/control/operators/comparator.py b/src/mlpro/bf/control/operators/comparator.py new file mode 100644 index 000000000..870059477 --- /dev/null +++ b/src/mlpro/bf/control/operators/comparator.py @@ -0,0 +1,89 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control.operators +## -- Module : comparator.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-10-06 0.1.0 DA Creation and initial implementation +## -- 2024-10-08 0.2.0 DA Validation and various changes +## -- 2024-10-09 0.3.0 DA Refactoring +## -- 2024-10-13 0.4.0 DA Refactoring +## -- 2024-11-10 0.5.0 DA Refactoring +## -- 2024-11-26 0.6.0 DA Method Comparator._run(): creation of ControlError only if +## -- both SetPoint and ControlledVariable are detected +## -- 2024-12-03 0.6.1 DA Bugfix in method Comparator.get_control_error() +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.6.1 (2024-12-03) + +This module provides an implementation of a comparator that determins the control error based on +setpoint and controlled variable (system state). + +""" + +import numpy as np + +from mlpro.bf.various import Log +from mlpro.bf.math import Element +from mlpro.bf.streams import InstDict, InstTypeNew +from mlpro.bf.control import SetPoint, ControlledVariable, ControlError, Operator, get_ctrl_data + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class Comparator (Operator): + """ + The comparator computes the control error based on the current setpoint and controlled variable. + It consumes (not to say: removes) the current controlled variable and replaces it by a control error. + """ + + C_NAME = 'Comparator' + +## ------------------------------------------------------------------------------------------------- + def _run(self, p_inst: InstDict): + + # 1 Get setpoint + setpoint : SetPoint = get_ctrl_data( p_inst = p_inst, p_type = SetPoint, p_remove = True ) + if setpoint is None: + self.log(Log.C_LOG_TYPE_E, 'Setpoint missing!') + return + + + # 2 Get and remove current controlled variable + ctrlled_var : ControlledVariable = get_ctrl_data( p_inst = p_inst, p_type = ControlledVariable, p_remove = True ) + if ctrlled_var is None: + self.log(Log.C_LOG_TYPE_W, 'Controlled variable missing!') + return + + + # 3 Compute control error + control_error = self.get_control_error( p_setpoint = setpoint, p_ctrlled_var = ctrlled_var ) + p_inst[control_error.id] = (InstTypeNew, control_error) + + +## ------------------------------------------------------------------------------------------------- + def get_control_error(self, p_setpoint: SetPoint, p_ctrlled_var: ControlledVariable ) -> ControlError: + """ + Returns a new control error as the difference betweeen a specified setpoint and controlled + variable. + + Parameters + ---------- + p_setpoint : SetPoint + Setpoint object. + p_ctrlled_var : ControlledVariable + Controlled variable object. + + Returns + ------- + ControlError + New control error object. + """ + + return ControlError( p_id = self.get_so().get_next_inst_id(), + p_value_space = p_ctrlled_var.value_space, + p_values = np.subtract( np.array(p_setpoint.values), np.array(p_ctrlled_var.values) ), + p_tstamp = self.get_so().get_tstamp() ) diff --git a/src/mlpro/bf/control/operators/converter.py b/src/mlpro/bf/control/operators/converter.py new file mode 100644 index 000000000..0ea39968d --- /dev/null +++ b/src/mlpro/bf/control/operators/converter.py @@ -0,0 +1,83 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control.operators +## -- Module : integrator.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-11-05 0.1.0 AP Creation and initial implementation +## -- 2024-11-10 0.2.0 DA Turned off visualization +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.2.0 (2024-11-10) + +This module provides an implementation of a converter that convertsdetermins the next control variable by +buffering and cumulating it. + +""" + +import numpy as np + +from mlpro.bf.math.basics import Log +from mlpro.bf.mt import Log, Task +from mlpro.bf.streams import InstDict, InstTypeNew +from mlpro.bf.control import ControlData, Operator, get_ctrl_data + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class Converter (Operator): + """ + The converter converts the source type into the destination type. It consumes (not to say: removes) + the current source instance variable and replaces it by destination instance variable. + + Parameters + ---------- + p_src_type : type + p_dst_type : type + ... + """ + + C_NAME = 'Converter' + C_PLOT_ACTIVE = False + +## ------------------------------------------------------------------------------------------------- + def __init__(self, + p_src_type: type, + p_dst_type: type, + p_range_max=Task.C_RANGE_THREAD, + p_visualize=False, + p_logging=Log.C_LOG_ALL, + **p_kwargs): + + super().__init__(p_range_max, p_visualize, p_logging, **p_kwargs) + + self._src_type = p_src_type + self._dst_type = p_dst_type + self._duplicate_data = True + + +## ------------------------------------------------------------------------------------------------- + def _run(self, p_inst: InstDict): + + # 1 Get source instance + src_instance : ControlData = get_ctrl_data( p_inst = p_inst, p_type = self._src_type, p_remove = True ) + + if src_instance is None: + + self.log(Log.C_LOG_TYPE_E, f'{self._src_type} missing!') + return + + + # 2 Create destination instance + dst_instance =self._dst_type( p_id = self.get_so().get_next_inst_id(), + p_value_space = src_instance.value_space, + p_values = src_instance.values, + p_tstamp = src_instance.get_tstamp() ) + + + # 3 Store destination instance + p_inst[dst_instance.id] = (InstTypeNew, dst_instance) \ No newline at end of file diff --git a/src/mlpro/bf/control/operators/integrator.py b/src/mlpro/bf/control/operators/integrator.py new file mode 100644 index 000000000..e6085d40a --- /dev/null +++ b/src/mlpro/bf/control/operators/integrator.py @@ -0,0 +1,95 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control.operators +## -- Module : integrator.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-10-07 0.1.0 DA Creation and initial implementation +## -- 2024-10-09 0.2.0 DA Refactoring +## -- 2024-10-13 0.3.0 DA Refactoring +## -- 2024-11-09 0.3.1 DA Class Integrator: correction of C_NAME +## -- 2024-11-10 0.4.0 DA Refactoring +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.4.0 (2024-11-010) + +This module provides an implementation of an integrator that determins the next control variable by +buffering and cumulating it. + +""" + +import numpy as np + +from mlpro.bf.math.basics import Log +from mlpro.bf.mt import Log, Task +from mlpro.bf.streams import InstDict, InstTypeNew +from mlpro.bf.control import ControlVariable, Operator, get_ctrl_data + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class Integrator (Operator): + """ + Operator that determins the next control action by buffering and cumulating it. The origin action + provided by a controller is replaced. + """ + + C_NAME = 'Integrator' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_range_max=Task.C_RANGE_THREAD, + p_visualize: bool = False, + p_logging=Log.C_LOG_ALL ): + + self._ctrl_var : ControlVariable = None + + super().__init__( p_range_max = p_range_max, + p_visualize = p_visualize, + p_logging = p_logging ) + + +## ------------------------------------------------------------------------------------------------- + def _run(self, p_inst: InstDict): + + ctrl_var = get_ctrl_data( p_inst = p_inst, p_type = ControlVariable, p_remove = True ) + + if ctrl_var is None: + self.log(Log.C_LOG_TYPE_E, 'Control variable not found') + return + + ctrl_var_int = self.integrate( p_ctrl_var = ctrl_var ) + + p_inst[ctrl_var_int.id] = (InstTypeNew, ctrl_var_int ) + + +## ------------------------------------------------------------------------------------------------- + def integrate(self, p_ctrl_var : ControlVariable) -> ControlVariable: + """ + Numerically integrates the incoming control variable. + + Parameters + ---------- + p_ctrl_var : ControlVariable + Control variable to be cumulated. + + Returns + ------- + ControlVariable + Integrated control variable. + """ + + if self._ctrl_var is None: + self._ctrl_var = p_ctrl_var.copy() + else: + self._ctrl_var.values = np.add( np.array(self._ctrl_var.values), np.array(p_ctrl_var.values) ) + + ctrl_var_int = self._ctrl_var.copy() + ctrl_var_int.id = self.get_so().get_next_inst_id() + ctrl_var_int.tstamp = self.get_so().get_tstamp() + + return ctrl_var_int \ No newline at end of file diff --git a/src/mlpro/bf/data/__init__.py b/src/mlpro/bf/data/__init__.py index cce80ebf0..a808266e5 100644 --- a/src/mlpro/bf/data/__init__.py +++ b/src/mlpro/bf/data/__init__.py @@ -1,2 +1,3 @@ from mlpro.bf.data.datastoring import * -from mlpro.bf.data.buffers import * \ No newline at end of file +from mlpro.bf.data.buffers import * +from mlpro.bf.data.cfg_file import ConfigFile \ No newline at end of file diff --git a/src/mlpro/bf/data/cfg_file.py b/src/mlpro/bf/data/cfg_file.py new file mode 100644 index 000000000..feebdf148 --- /dev/null +++ b/src/mlpro/bf/data/cfg_file.py @@ -0,0 +1,104 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.data +## -- Module : cfg_file.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-12-11 0.1.0 DA Creation +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.1.0 (2024-12-11) + +This module provides classes to deal with persistent configuration data. + +""" + +from pathlib import Path +import json + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class ConfigFile: + """ + Stores configuration data in a local JSON file. + + Parameters + ---------- + p_fname : str + Name of the local JSON file. + """ + +## ------------------------------------------------------------------------------------------------- + def __init__(self, p_fname: str): + self._fname = p_fname + + +## ------------------------------------------------------------------------------------------------- + def get(self, p_key): + """ + Returns the values stored for the specified key. If no values were found an exception is + raised. + + Parameters + ---------- + p_key + Key. + + Returns + ------- + values + Values stored for the specified key. + """ + + with open(self._fname, "r") as file: + config = json.load(file) + + return config[p_key] + + +## ------------------------------------------------------------------------------------------------- + def set(self, p_key, p_values) -> bool: + """ + Stores the values of the specified key. + + Parameters + ---------- + p_key + Key. + p_values + Values to be stored. + + Returns + ------- + bool + True, if storing was successfull. False otherwise. + """ + + try: + file_path = Path(self._fname) + file_path.parent.mkdir(parents=True, exist_ok=True) + + try: + with file_path.open("r+") as file: + try: + config = json.load(file) + except: + config = {} + + config[p_key] = p_values + file.seek(0) + json.dump(config, file, indent=4) + file.truncate() + except: + with file_path.open("w") as file: + config = { p_key : p_values } + json.dump(config, file, indent=4) + + return True + + except: + return False \ No newline at end of file diff --git a/src/mlpro/bf/events.py b/src/mlpro/bf/events.py index d9a70d131..5b6d9ecc5 100644 --- a/src/mlpro/bf/events.py +++ b/src/mlpro/bf/events.py @@ -1,5 +1,5 @@ ## ------------------------------------------------------------------------------------------------- -## -- Project : MLPro - A Synoptic Framework for Standardized Machine Learning Tasks +## -- Project : MLPro - The integrative middleware framework for standardized machine learning ## -- Package : mlpro.bf ## -- Module : events ## ------------------------------------------------------------------------------------------------- diff --git a/src/mlpro/bf/exceptions.py b/src/mlpro/bf/exceptions.py index c0c4e106a..4ee5bd022 100644 --- a/src/mlpro/bf/exceptions.py +++ b/src/mlpro/bf/exceptions.py @@ -1,5 +1,5 @@ ## ------------------------------------------------------------------------------------------------- -## -- Project : MLPro - A Synoptic Framework for Standardized Machine Learning Tasks +## -- Project : MLPro - The integrative middleware framework for standardized machine learning ## -- Package : mlpro.bf ## -- Module : exceptions ## ------------------------------------------------------------------------------------------------- diff --git a/src/mlpro/bf/math/geometry/crosshair.py b/src/mlpro/bf/math/geometry/crosshair.py index 5be718be7..6a5283714 100644 --- a/src/mlpro/bf/math/geometry/crosshair.py +++ b/src/mlpro/bf/math/geometry/crosshair.py @@ -6,21 +6,25 @@ ## -- History : ## -- yyyy-mm-dd Ver. Auth. Description ## -- 2024-10-31 1.0.0 DA Creation +## -- 2024-12-11 1.0.1 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.0.0 (2024-10-31) +Ver. 1.0.1 (2024-12-11) This module provides the class Crosshair that provides crosshair functionality. """ -from matplotlib.figure import Figure -from mpl_toolkits.mplot3d.art3d import Line3D +try: + from matplotlib.figure import Figure + from mpl_toolkits.mplot3d.art3d import Line3D +except: + class Figure : pass + class Line3D : pass from mlpro.bf.plot import PlotSettings from mlpro.bf.streams import * -from mlpro.bf.various import Id from mlpro.bf.math.geometry import Point from mlpro.bf.math.properties import * diff --git a/src/mlpro/bf/math/geometry/hypercuboid.py b/src/mlpro/bf/math/geometry/hypercuboid.py index b778fcaa9..1eba48a36 100644 --- a/src/mlpro/bf/math/geometry/hypercuboid.py +++ b/src/mlpro/bf/math/geometry/hypercuboid.py @@ -12,10 +12,11 @@ ## -- 2024-06-30 1.2.0 DA Refactoring of method Hypercuboid.set() ## -- 2024-07-13 1.3.0 DA Refactoring ## -- 2024-08-20 1.4.0 DA New method Hypercuboid.check_collision() +## -- 2024-12-11 1.4.1 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.4.0 (2024-08-20) +Ver. 1.4.1 (2024-12-11) This module provides a property class for the geometric shape 'hypercuboid'. @@ -23,8 +24,14 @@ """ import numpy as np -from matplotlib.patches import Rectangle -from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3D + +try: + from matplotlib.patches import Rectangle + from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3D +except: + class Rectangle : pass + class Poly3DCollection : pass + class Line3D : pass from mlpro.bf.plot import * from mlpro.bf.math.properties import * diff --git a/src/mlpro/bf/math/geometry/point.py b/src/mlpro/bf/math/geometry/point.py index 7092e8050..e67b7823b 100644 --- a/src/mlpro/bf/math/geometry/point.py +++ b/src/mlpro/bf/math/geometry/point.py @@ -20,17 +20,22 @@ ## -- 2024-06-03 1.8.0 DA Class Point: new attributes color, marker ## -- 2024-06-05 1.8.1 DA Bugfix in Point._remove_plot_2d() ## -- 2024-06-26 1.9.0 DA Refactoring +## -- 2024-12-11 1.9.1 DA Pseudo class Figure if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.9.0 (2024-06-26) +Ver. 1.9.1 (2024-12-11) This module provides a property class for the geometric shape 'point'. """ -from matplotlib.figure import Figure +try: + from matplotlib.figure import Figure +except: + class Figure: pass + from mlpro.bf.plot import PlotSettings from mlpro.bf.math.properties import * from mlpro.bf.math.normalizers import Normalizer diff --git a/src/mlpro/bf/math/normalizers/ztrans.py b/src/mlpro/bf/math/normalizers/ztrans.py index 50612dd84..4fc33a60b 100644 --- a/src/mlpro/bf/math/normalizers/ztrans.py +++ b/src/mlpro/bf/math/normalizers/ztrans.py @@ -25,10 +25,11 @@ ## -- 2024-05-23 1.2.0 DA Refactoring (not yet finished) ## -- 2024-05-24 1.2.1 LSB Bug fix for Parameter update using only p_data_del in Z-transform ## -- 2024-05-27 1.2.2 LSB Scientific Reference added +## -- 2024-12-09 1.3.0 DA Method NormalizerZTrans.update_parameters(): review/optimization ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.2.2 (2024-05-27) +Ver. 1.3.0 (2024-12-09) This module provides a class for Z transformation. """ @@ -85,9 +86,10 @@ def update_parameters(self, # 1 Update on dataset if p_dataset is not None: - self._std = np.std(p_dataset, axis=0, dtype=np.float64) - self._mean = np.mean(p_dataset, axis=0, dtype=np.float64) self._n = len(p_dataset) + self._mean = np.mean(p_dataset, axis=0, dtype=np.float64) + self._std = np.std(p_dataset, axis=0, dtype=np.float64) + self._s = np.square(self._std) * self._n if self._param_new is None: self._param_new = np.zeros([2, self._std.shape[-1]]) @@ -102,34 +104,41 @@ def update_parameters(self, data_new = p_data_new if self._n == 0: - self._n = 1 + self._n = 1 self._mean = data_new.copy() - self._std = np.zeros(shape=data_new.shape) + self._s = np.zeros(shape=data_new.shape) + self._std = np.zeros(shape=data_new.shape) else: + self._n += 1 old_mean = self._mean.copy() - self._mean = (old_mean * self._n + data_new) / (self._n + 1) + self._mean = old_mean + ( data_new - old_mean ) / self._n + self._s = self._s + (data_new - self._mean) * (data_new - old_mean) + self._std = np.sqrt( self._s / self._n ) - self._std = np.sqrt((np.square(self._std) * self._n - + (data_new - self._mean) * (data_new - old_mean)) / (self._n+1)) - self._n += 1 - if self._param_new is None: self._param_new = np.zeros([2, data_new.shape[-1]]) # 3 Update on obsolete data - if ( p_data_del is not None ) and ( self._n > 0 ): + if p_data_del is not None: + try: data_del = np.array(p_data_del.get_values()) except: data_del = p_data_del - - old_mean = self._mean.copy() - self._mean = (old_mean * self._n - data_del) / (self._n-1) - - self._std = np.sqrt((np.square(self._std)*self._n - (data_del - old_mean)*(data_del - self._mean)) / (self._n-1)) - self._n -= 1 + if self._n > 0: + self._n -= 1 + old_mean = self._mean.copy() + self._mean = old_mean - ( data_del - old_mean ) / self._n + self._s = self._s - (data_del - self._mean) * (data_del - old_mean) + self._std = np.sqrt( self._s / self._n ) + + else: + self._n = 0 + self._mean = np.zeros(shape=data_del.shape) + self._s = np.zeros(shape=data_del.shape) + self._std = np.zeros(shape=data_del.shape) # 4 Update of parameters diff --git a/src/mlpro/bf/math/properties.py b/src/mlpro/bf/math/properties.py index f3a60d54a..89f9072e9 100644 --- a/src/mlpro/bf/math/properties.py +++ b/src/mlpro/bf/math/properties.py @@ -38,10 +38,11 @@ ## -- p_upd_derivatives ## -- 2026-07-08 1.6.0 DA Introduction of kwargs ## -- 2024-07-27 1.7.0 DA Class Property: introduction of self._value_bak +## -- 2024-12-11 1.7.1 DA Pseudo class Figure if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.7.0 (2024-07-27) +Ver. 1.7.1 (2024-12-11) This module provides a systematics for enriched managed properties. MLPro's enriched properties store any data like class attributes and they can be used like class attributes. They extend the @@ -60,7 +61,11 @@ from collections.abc import Iterable import numpy as np -from matplotlib.figure import Figure + +try: + from matplotlib.figure import Figure +except: + class Figure: pass from mlpro.bf.various import KWArgs from mlpro.bf.plot import Plottable, PlotSettings diff --git a/src/mlpro/bf/ml/basics.py b/src/mlpro/bf/ml/basics.py index 96a86ce35..d207951b4 100644 --- a/src/mlpro/bf/ml/basics.py +++ b/src/mlpro/bf/ml/basics.py @@ -491,7 +491,6 @@ def _add_objective(self, **p_kwargs): raise NotImplementedError - ## ------------------------------------------------------------------------------------------------- def get_accuracy(self) -> float: """ diff --git a/src/mlpro/bf/mt.py b/src/mlpro/bf/mt.py index beed75a0d..26c278cfe 100644 --- a/src/mlpro/bf/mt.py +++ b/src/mlpro/bf/mt.py @@ -1,5 +1,5 @@ ## ------------------------------------------------------------------------------------------------- -## -- Project : MLPro - A Synoptic Framework for Standardized Machine Learning Tasks +## -- Project : MLPro - The integrative middleware framework for standardized machine learning ## -- Package : mlpro.bf ## -- Module : mt ## ------------------------------------------------------------------------------------------------- @@ -36,14 +36,18 @@ ## -- 2024-05-31 1.9.2 DA Class Task: new exception rule for MacOs in meth. init_plot() ## -- 2024-06-17 2.0.0 DA Class Workflow: new method get_tasks() ## -- 2024-06-18 2.1.0 DA Class Task: new parent class KWArgs +## -- 2024-10-07 2.2.0 DA Classes Task, Workflow: new method reset() ## -- 2024-11-10 2.2.0 DA Refactoring of class Workflow regarding plotting ## -- 2024-11-11 2.3.0 DA Class Task: ## -- - new method _on_finished() ## -- - redefinition of method _raise_event() +## -- 2024-12-10 2.3.1 DA - Method Task.init_plot(): refactoring +## -- - Method Workflow.init_plot(): Bugfix and optimization +## -- 2024-12-11 2.4.0 DA New method Workflow.remove_plot() ## ------------------------------------------------------------------------------------------------- """ -Ver. 2.3.0 (2024-11-11) +Ver. 2.4.0 (2024-12-11) This module provides classes for multitasking with optional interprocess communication (IPC) based on shared objects. Multitasking in MLPro combines multrithreading and multiprocessing and simplifies @@ -59,9 +63,13 @@ import threading as mt import multiprocess as mp -import matplotlib -from matplotlib.figure import Figure from multiprocess.managers import BaseManager + +try: + from matplotlib.figure import Figure +except: + class Figure : pass + from mlpro.bf.exceptions import * from mlpro.bf.various import * from mlpro.bf.events import EventManager, Event @@ -70,7 +78,6 @@ - ## ------------------------------------------------------------------------------------------------- ## ------------------------------------------------------------------------------------------------- class Range: @@ -561,6 +568,11 @@ def get_tid(self): """ return self.get_id() + + +## ------------------------------------------------------------------------------------------------- + def reset(self, **p_kwargs): + pass ## ------------------------------------------------------------------------------------------------- @@ -752,7 +764,8 @@ def run_on_event(self, p_event_id, p_event_object:Event): ## ------------------------------------------------------------------------------------------------- def init_plot( self, p_figure: Figure = None, - p_plot_settings : PlotSettings = None ): + p_plot_settings : PlotSettings = None, + p_window_title: str = None ): try: if ( not self.C_PLOT_ACTIVE ) or ( not self._visualize ): return except: @@ -764,23 +777,21 @@ def init_plot( self, pass self.log(Log.C_LOG_TYPE_S, 'Init plot') - Plottable.init_plot( self, - p_figure=p_figure, - p_plot_settings=p_plot_settings ) - if self._plot_own_figure: - title = 'MLPro: ' + self.C_TYPE + ' ' + self.get_name() + ' (' + self._plot_settings.view + ')' - backend = matplotlib.get_backend() + try: + view = p_plot_settings.view + except: + view = self.C_PLOT_DEFAULT_VIEW - if backend == 'TkAgg': - self._figure.canvas.manager.window.title(title) - elif backend == 'macosx': - self.log(Log.C_LOG_TYPE_W, 'Window titles can not be set under MacOs ("darwin")') - else: - try: - self._figure.canvas.setWindowTitle(title) - except AttributeError: - self._figure.canvas.set_window_title(title) + if p_window_title is not None: + title = p_window_title + else: + title = 'MLPro: ' + self.C_TYPE + ' ' + self.get_name() + ' (' + view + ')' + + Plottable.init_plot( self, + p_figure=p_figure, + p_plot_settings=p_plot_settings, + p_window_title=title ) ## ------------------------------------------------------------------------------------------------- @@ -924,6 +935,12 @@ def get_tasks(self) -> list: return self._tasks +## ------------------------------------------------------------------------------------------------- + def reset(self, **p_kwargs): + for task in self._tasks: + task.reset(**p_kwargs) + + ## ------------------------------------------------------------------------------------------------- def _get_plot_host_task(self, p_task : Task) -> Task: plot_host = None @@ -942,7 +959,8 @@ def _get_plot_host_task(self, p_task : Task) -> Task: ## ------------------------------------------------------------------------------------------------- def init_plot( self, p_figure:Figure=None, - p_plot_settings : PlotSettings = None ): + p_plot_settings : PlotSettings = None, + p_window_title: str = None ): """ Initializes the plot of a workflow. The method creates a host figure for all tasks if no external host figure is parameterized. The sub-plots of the tasks are autmatically arranged @@ -956,64 +974,68 @@ def init_plot( self, Optional MatPlotLib host figure, where the plot shall be embedded. The default is None. p_plot_settings : PlotSettings Optional plot settings. If None, the default view is plotted (see attribute C_PLOT_DEFAULT_VIEW). + p_window_title : str + Optional window title. """ # 1 Init plot output on workflow level (if activated) Task.init_plot( self, - p_figure=p_figure, - p_plot_settings=p_plot_settings ) + p_figure = p_figure, + p_plot_settings = p_plot_settings, + p_window_title = p_window_title ) # 2 Init plot output on task level for task in self._tasks: - task_plot_settings = None - if not task.C_PLOT_STANDALONE: - plot_host = self._get_plot_host_task(p_task=task) - else: - plot_host = self + task_figure = None + task_plot_settings = p_plot_settings - ps = plot_host.get_plot_settings() - if ps is None: ps = p_plot_settings + if task.get_visualization(): - if task.C_PLOT_STANDALONE: - # Task plots in a separate figure (=window) - task_figure = None - - if ps is not None: - task_plot_settings = ps.copy() + if not task.C_PLOT_STANDALONE: + plot_host = self._get_plot_host_task(p_task=task) else: - task_plot_settings = PlotSettings( p_view = self.C_PLOT_DEFAULT_VIEW ) + plot_host = self - task_plot_settings.axes = None - task_plot_settings.pos_x = 1 - task_plot_settings.pos_y = 1 - task_plot_settings.id = 1 + ps = plot_host.get_plot_settings() + if ps is None: ps = p_plot_settings - else: - # Task plots embedded in the predecessor/workflow figure/subplot - task_figure = plot_host._figure - task_plot_settings = ps + if task.C_PLOT_STANDALONE: + # Task plots in a separate figure (=window) + if ps is not None: + task_plot_settings = ps.copy() + else: + task_plot_settings = PlotSettings( p_view = self.C_PLOT_DEFAULT_VIEW ) + + task_plot_settings.axes = None + task_plot_settings.pos_x = 1 + task_plot_settings.pos_y = 1 + task_plot_settings.id = 1 + + else: + # Task plots embedded in the predecessor/workflow figure/subplot + task_figure = plot_host._figure + task_plot_settings = ps task.init_plot( p_figure=task_figure, p_plot_settings=task_plot_settings ) - # 3 Force workflow window to stay in foreground - self.force_fg() - - - # 4 Force task windows to stay in foreground - for task in self._tasks: - if task.get_visualization() and task.C_PLOT_STANDALONE: task.force_fg() - - - # 5 Initial refresh of workflow window + # 3 Initial refresh of workflow window if self.get_visualization() and self._plot_own_figure: self._figure.canvas.draw() self._figure.canvas.flush_events() +## ------------------------------------------------------------------------------------------------- + def remove_plot(self, p_refresh = True): + for task in self._tasks: + task.remove_plot( p_refresh=p_refresh ) + + return super().remove_plot(p_refresh) + + ## ------------------------------------------------------------------------------------------------- def run(self, p_range:int=None, p_wait:bool=False, **p_kwargs): """ diff --git a/src/mlpro/bf/ops.py b/src/mlpro/bf/ops.py index ad33289b6..7bd449e6d 100644 --- a/src/mlpro/bf/ops.py +++ b/src/mlpro/bf/ops.py @@ -1,5 +1,5 @@ ## ------------------------------------------------------------------------------------------------- -## -- Project : MLPro - A Synoptic Framework for Standardized Machine Learning Tasks +## -- Project : MLPro - The integrative middleware framework for standardized machine learning ## -- Package : mlpro.bf ## -- Module : ops ## ------------------------------------------------------------------------------------------------- @@ -15,17 +15,18 @@ ## -- 2022-11-21 1.2.2 DA Eliminated all uses of super() ## -- 2023-03-25 1.2.3 DA Class ScenarioBase: new parent class Persistent ## -- 2024-11-09 1.3.0 DA Class ScenarioBase: new parent class KWArgs +## -- 2024-12-29 1.4.0 DA Method ScenarioBase.run(): logging of duration/speed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.3.0 (2024-11-09) +Ver. 1.4.0 (2024-12-29) This module provides classes for operation. """ import sys -from datetime import timedelta +from datetime import timedelta, datetime from mlpro.bf.various import Log, Persistent, Timer, KWArgs from mlpro.bf.plot import Plottable from mlpro.bf.events import * @@ -314,6 +315,7 @@ def run_cycle(self): # 3 Update visualization + if self._visualize: self.update_plot() @@ -405,11 +407,18 @@ def run(self, if self._timer is None: self._init_timer() self.log(self.C_LOG_TYPE_S, 'Process time', self._timer.get_time(), ': Start of processing') + # 2 Late initialization of visualization with default parameters - if self._visualize: + if self.get_visualization(): self.init_plot() - # 3 Main loop + + # 3 Time measurement, if logging is active and a cycle limit is set + if ( self.get_log_level() in [ Log.C_LOG_ALL, Log.C_LOG_WE ] ) and ( self._cycle_limit > 0 ): + tp_before = datetime.now() + + + # 4 Main loop while True: success, error, timeout, limit, adapted_cycle, end_of_data = self.run_cycle() adapted = adapted or adapted_cycle @@ -418,6 +427,17 @@ def run(self, if p_term_on_timeout and timeout: break if limit or end_of_data: break - # 4 Outro + + # 5 Outro self.log(self.C_LOG_TYPE_S, 'Process time', self._timer.get_time(), ': End of processing') + + if ( self.get_log_level() in [ Log.C_LOG_ALL, Log.C_LOG_WE ] ) and ( self._cycle_limit > 0 ): + tp_after = datetime.now() + tp_delta = tp_after - tp_before + duration_musec = ( tp_delta.seconds * 1000000 + tp_delta.microseconds ) + if duration_musec == 0: duration_musec = 1 + duration_sec = duration_musec / 1000000 + + self.log(Log.C_LOG_TYPE_W, str(self._cycle_id + 1),'cycles in', round(duration_sec,2), 's (' + str(round( (self._cycle_id + 1) / duration_sec,1)), 'cycles/s)', p_type_col='S') + return success, error, timeout, limit, adapted, end_of_data, self._cycle_id \ No newline at end of file diff --git a/src/mlpro/bf/physics/basics.py b/src/mlpro/bf/physics/basics.py index 8d921baf2..17d23d340 100644 --- a/src/mlpro/bf/physics/basics.py +++ b/src/mlpro/bf/physics/basics.py @@ -7,17 +7,22 @@ ## -- yyyy-mm-dd Ver. Auth. Description ## -- 2023-02-04 0.0.0 SY Creation ## -- 2023-02-04 1.0.0 SY First release +## -- 2024-12-11 1.0.1 DA Optional import of matplotlib ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.0.0 (2023-02-04) +Ver. 1.0.1 (2024-12-11) This module provides models and templates for physics. """ from mlpro.bf.various import ScientificObject, PersonalisedStamp, Log -import matplotlib.pyplot as plt + +try: + import matplotlib.pyplot as plt +except: + pass diff --git a/src/mlpro/bf/plot/__init__.py b/src/mlpro/bf/plot/__init__.py new file mode 100644 index 000000000..8932e41a1 --- /dev/null +++ b/src/mlpro/bf/plot/__init__.py @@ -0,0 +1,2 @@ +from mlpro.bf.plot.basics import PlotSettings, Plottable +from mlpro.bf.plot.dataplotting import DataPlotting \ No newline at end of file diff --git a/src/mlpro/bf/plot/backends/__init__.py b/src/mlpro/bf/plot/backends/__init__.py new file mode 100644 index 000000000..656f482f9 --- /dev/null +++ b/src/mlpro/bf/plot/backends/__init__.py @@ -0,0 +1,3 @@ +from mlpro.bf.plot.backends.basics import WindowState, WSMINIMIZED, WSMAXIMIZED, WSNORMAL, WindowGeometry, PlotBackend +from mlpro.bf.plot.backends.tkagg import PlotBackendTkAgg +from mlpro.bf.plot.backends.qtagg import PlotBackendqtagg \ No newline at end of file diff --git a/src/mlpro/bf/plot/backends/basics.py b/src/mlpro/bf/plot/backends/basics.py new file mode 100644 index 000000000..c5e9e8cea --- /dev/null +++ b/src/mlpro/bf/plot/backends/basics.py @@ -0,0 +1,218 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.plot.backends +## -- Module : basics.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-12-10 0.1.0 DA Initial implementation +## -- 2024-12-13 0.2.0 DA Removed methods get_title(), set_title() +## -- 2024-12-16 0.3.0 DA Introduction of platform-dependant methods +## -- 2024-12-30 0.4.0 DA Class PlotBackend: new set of methods atexit*() +## -- 2025-01-03 0.5.0 DA New class WindowGeometry +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.5.0 (2025-01-03) + +This module provides the template class PlotBackend for special support of common Matplotlib backends. + +""" + + +import platform +import atexit +from typing import TypedDict, Union + +try: + from matplotlib.figure import Figure +except: + class Figure : pass + + + +WindowState = int +WSMINIMIZED = 0 +WSNORMAL = 1 +WSMAXIMIZED = 2 + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class WindowGeometry (TypedDict, total=True): + xpos : Union[int, float] + ypos : Union[int, float] + width : Union[int, float] + height : Union[int, float] + state : WindowState + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class PlotBackend: + + C_NAME = '????' + +## ------------------------------------------------------------------------------------------------- + def __init__(self): + self._os = platform.system() # 'Linux', 'Windows', 'Darwin' + + self.figure_force_foreground = getattr(self, '_figure_force_foreground_' + self._os, self._figure_force_foreground_default ) + self.figure_get_title = getattr(self, '_figure_get_title_' + self._os, self._figure_get_title_default ) + self.figure_set_title = getattr(self, '_figure_set_title_' + self._os, self._figure_set_title_default ) + self.figure_get_geometry = getattr(self, '_figure_get_geometry_' + self._os, self._figure_get_geometry_default ) + self.figure_set_geometry = getattr(self, '_figure_set_geometry_' + self._os, self._figure_set_geometry_default ) + self.figure_atexit = getattr(self, '_figure_atexit_' + self._os, self._figure_atexit_default ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_force_foreground_Linux(self, p_figure : Figure): + self._figure_force_foreground_default( p_figure=p_figure) + + +## ------------------------------------------------------------------------------------------------- + def _figure_force_foreground_Windows(self, p_figure : Figure): + self._figure_force_foreground_default( p_figure=p_figure) + + +## ------------------------------------------------------------------------------------------------- + def _figure_force_foreground_Darwin(self, p_figure : Figure): + self._figure_force_foreground_default( p_figure=p_figure) + + +## ------------------------------------------------------------------------------------------------- + def _figure_force_foreground_default(self, p_figure : Figure): + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def figure_force_foreground(self, p_figure : Figure): + pass + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_title_Linux(self, p_figure : Figure) -> str: + return self._figure_get_title_default(p_figure = p_figure) + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_title_Windows(self, p_figure : Figure) -> str: + return self._figure_get_title_default(p_figure = p_figure) + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_title_Darwin(self, p_figure : Figure) -> str: + return self._figure_get_title_default(p_figure = p_figure) + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_title_default(self, p_figure : Figure) -> str: + return p_figure.canvas.manager.get_window_title() + + +## ------------------------------------------------------------------------------------------------- + def figure_get_title(self, p_figure : Figure) -> str: + pass + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_title_Linux(self, p_figure : Figure, p_title : str): + self._figure_set_title_default( p_figure = p_figure, p_title = p_title ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_title_Windows(self, p_figure : Figure, p_title : str): + self._figure_set_title_default( p_figure = p_figure, p_title = p_title ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_title_Darwin(self, p_figure : Figure, p_title : str): + self._figure_set_title_default( p_figure = p_figure, p_title = p_title ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_title_default(self, p_figure : Figure, p_title : str): + p_figure.canvas.manager.set_window_title( p_title ) + + +## ------------------------------------------------------------------------------------------------- + def figure_set_title(self, p_figure : Figure, p_title : str): + pass + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_geometry_Linux(self, p_figure : Figure) -> WindowGeometry: + return self._figure_get_geometry_default( p_figure = p_figure ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_geometry_Windows(self, p_figure : Figure) -> WindowGeometry: + return self._figure_get_geometry_default( p_figure = p_figure ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_geometry_Darwin(self, p_figure : Figure) -> WindowGeometry: + return self._figure_get_geometry_default( p_figure = p_figure ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_geometry_default(self, p_figure : Figure) -> WindowGeometry: + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def figure_get_geometry(self, p_figure : Figure) -> WindowGeometry: + pass + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_geometry_Linux( self, p_figure : Figure, p_geometry : WindowGeometry ): + self._figure_set_geometry_default( p_figure = p_figure, p_geometry = p_geometry ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_geometry_Windows( self, p_figure : Figure, p_geometry : WindowGeometry ): + self._figure_set_geometry_default( p_figure = p_figure, p_geometry = p_geometry ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_geometry_Darwin( self, p_figure : Figure, p_geometry : WindowGeometry ): + self._figure_set_geometry_default( p_figure = p_figure, p_geometry = p_geometry ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_geometry_default( self, p_figure : Figure, p_geometry : WindowGeometry ): + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def figure_set_geometry( self, p_figure : Figure, p_geometry : WindowGeometry ): + pass + +## ------------------------------------------------------------------------------------------------- + def _figure_atexit_Linux(self, p_figure : Figure, p_fct): + self._figure_atexit_default( p_figure = p_figure, p_fct=p_fct) + + +## ------------------------------------------------------------------------------------------------- + def _figure_atexit_Windows(self, p_figure : Figure, p_fct): + self._figure_atexit_default( p_figure = p_figure, p_fct=p_fct) + + +## ------------------------------------------------------------------------------------------------- + def _figure_atexit_Darwin(self, p_figure : Figure, p_fct): + self._figure_atexit_default( p_figure = p_figure, p_fct=p_fct) + + +## ------------------------------------------------------------------------------------------------- + def _figure_atexit_default(self, p_figure : Figure, p_fct): + atexit.register(p_fct) + + +## ------------------------------------------------------------------------------------------------- + def figure_atexit(self, p_figure : Figure, p_fct): + pass \ No newline at end of file diff --git a/src/mlpro/bf/plot/backends/qtagg.py b/src/mlpro/bf/plot/backends/qtagg.py new file mode 100644 index 000000000..c810102f1 --- /dev/null +++ b/src/mlpro/bf/plot/backends/qtagg.py @@ -0,0 +1,90 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.plot.backends +## -- Module : qtagg.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-12-30 0.1.0 DA Initial implementation +## -- 2025-01-03 0.2.0 DA Refactoring +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.2.0 (2025-01-03) + +This module provides an integration for Matplotlib backend 'qtagg'. + +""" + + +import re + +try: + from matplotlib.figure import Figure + from matplotlib.backends.qt_compat import QtCore +except: + class Figure : pass + +from mlpro.bf.plot.backends import PlotBackend, WindowGeometry, WindowState, WSNORMAL, WSMINIMIZED, WSMAXIMIZED + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class PlotBackendqtagg (PlotBackend): + """ + Integrates the Matplotlib backend 'qtagg' into MLPro. + """ + + C_NAME = 'qtagg' + +## ------------------------------------------------------------------------------------------------- + def _figure_force_foreground_default(self, p_figure : Figure): + window = p_figure.canvas.manager.window + window.setWindowFlags(window.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) + window.show() + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_geometry_default(self, p_figure : Figure) -> WindowGeometry: + + # 1 Get size and position + window = p_figure.canvas.manager.window + pos = window.pos() + size = window.size() + + # 2 Get window state + state_qt = window.windowState() + if state_qt & QtCore.Qt.WindowMinimized: + state = WSMINIMIZED + elif state_qt & QtCore.Qt.WindowMaximized: + state = WSMAXIMIZED + else: + state = WSNORMAL + + # 3 Return dictionary compatible with type WindowGeometry + return { 'xpos' : pos.x(), + 'ypos' : pos.y(), + 'width' : size.width(), + 'height' : size.height(), + 'state' : state } + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_geometry_default(self, p_figure : Figure, p_geometry : WindowGeometry): + + # 1 Set size and position + window = p_figure.canvas.manager.window + window.move( p_geometry['xpos'], p_geometry['ypos'] ) + window.resize( p_geometry['width'], p_geometry['height'] ) + + # 2 Set window state + state = p_geometry['state'] + if state == WSMINIMIZED: + state_qt = QtCore.Qt.WindowMinimized + elif state == WSMAXIMIZED: + state_qt = QtCore.Qt.WindowMaximized + else: + state_qt = QtCore.Qt.WindowNoState + + window.setWindowState(state_qt) diff --git a/src/mlpro/bf/plot/backends/tkagg.py b/src/mlpro/bf/plot/backends/tkagg.py new file mode 100644 index 000000000..79cb68dad --- /dev/null +++ b/src/mlpro/bf/plot/backends/tkagg.py @@ -0,0 +1,183 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.plot.backends +## -- Module : tkagg.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-12-10 0.1.0 DA Initial implementation +## -- 2024-12-12 0.1.1 DA Stabilization of method PlotBackendTkAgg.force_foreground() +## -- 2024-12-13 0.2.0 DA - bugfix in PlotBackendTkAgg._set_geometry() +## -- - removed methods get_title(), set_title() +## -- 2024-12-16 0.3.0 DA Refactoring and validation for Linux +## -- 2025-01-03 0.4.0 DA Refactoring +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.4.0 (2025-01-03) + +This module provides an integration for Matplotlib backend 'TkAgg'. + +""" + + +import re + +try: + from matplotlib.figure import Figure +except: + class Figure : pass + +from mlpro.bf.plot.backends import PlotBackend, WindowGeometry, WindowState, WSNORMAL, WSMINIMIZED, WSMAXIMIZED + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class PlotBackendTkAgg (PlotBackend): + """ + Integrates the Matplotlib backend 'TkAgg' into MLPro. + """ + + C_NAME = 'TkAgg' + +## ------------------------------------------------------------------------------------------------- + def _figure_force_foreground_default(self, p_figure): + window = p_figure.canvas.manager.window + window.after( 2000, lambda: window.attributes('-topmost', True) ) + # p_figure.canvas.manager.window.attributes('-topmost', True) + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_geometry_Windows(self, p_figure) -> WindowGeometry: + + # 1 Get size and position + window = p_figure.canvas.manager.window + geometry = window.geometry().split('+') + xpos = int(geometry[1]) + ypos = int(geometry[2]) + + size = p_figure.get_size_inches() + width = size[0] + height = size[1] + + # 2 Get window state + state_tk = window.wm_state() + if state_tk == 'iconic': + state = WSMINIMIZED + elif state_tk == 'zoomed': + state = WSMAXIMIZED + else: + state = WSNORMAL + + # 3 Return dictionary compatible with type WindowGeometry + return { 'xpos' : xpos, + 'ypos' : ypos, + 'width' : width, + 'height' : height, + 'state' : state } + + +## ------------------------------------------------------------------------------------------------- + def _figure_get_geometry_default(self, p_figure) -> WindowGeometry: + + # 1 Get size and position + window = p_figure.canvas.manager.window + geometry = window.geometry() + numbers = list(map(int, re.findall(r'\d+', geometry))) + + # 2 Get window state + state_tk = window.wm_state() + if state_tk == 'iconic': + state = WSMINIMIZED + elif state_tk == 'zoomed': + state = WSMAXIMIZED + else: + state = WSNORMAL + + # 3 Return dictionary compatible with type WindowGeometry + return { 'xpos' : numbers[2], + 'ypos' : numbers[3], + 'width' : numbers[0], + 'height' : numbers[1], + 'state' : state } + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_pos_Windows_rec(self, p_window, p_pos: str, p_attempts: int = 10, p_wait: int = 100): + if p_attempts <= 0: return + + p_window.update_idletasks() + p_window.geometry(p_pos) + p_window.update_idletasks() + + match = re.search(r'\+\d+\+\d+', p_window.geometry()) + if match: + pos_new = match.group() + + if p_pos == pos_new: + return + + p_window.after( p_wait, lambda: self._figure_set_pos_Windows_rec( p_window = p_window, + p_pos = p_pos, + p_attempts = p_attempts - 1, + p_wait = p_wait ) ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_geometry_Windows(self, p_figure : Figure, p_geometry : WindowGeometry): + + # 1 Set size and position + window=p_figure.canvas.manager.window + p_figure.set_size_inches( w = p_geometry['width'], h = p_geometry['height']) + self._figure_set_pos_Windows_rec( p_window=window, p_pos=f'+{p_geometry["xpos"]}+{p_geometry["ypos"]}') + + # 2 Set window state + state = p_geometry['state'] + if state == WSMINIMIZED: + state_tk = 'iconic' + elif state == WSMAXIMIZED: + state_tk = 'zoomed' + else: + state_tk = 'normal' + + window.wm_state(state_tk) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_geometry_default_rec(self, p_window, p_geometry: str, p_attempts: int = 10, p_wait: int = 100): + if p_attempts <= 0: return + + p_window.update_idletasks() + p_window.geometry(p_geometry) + p_window.update_idletasks() + + geo_new = p_window.geometry() + + if p_geometry == geo_new: + return + + p_window.after( p_wait, lambda: self._figure_set_geometry_default_rec( p_window = p_window, + p_geometry = p_geometry, + p_attempts = p_attempts - 1, + p_wait = p_wait ) ) + + +## ------------------------------------------------------------------------------------------------- + def _figure_set_geometry_default(self, p_figure : Figure, p_geometry : WindowGeometry): + + # 1 Set size and position + window=p_figure.canvas.manager.window + geometry_tk = f'{p_geometry["width"]}x{p_geometry["height"]}+{p_geometry["xpos"]}+{p_geometry["ypos"]}' + self._figure_set_geometry_default_rec( p_window = window, p_geometry=geometry_tk ) + + # 2 Set window state + state = p_geometry['state'] + if state == WSMINIMIZED: + state_tk = 'iconic' + elif state == WSMAXIMIZED: + state_tk = 'zoomed' + else: + state_tk = 'normal' + + window.wm_state(state_tk) \ No newline at end of file diff --git a/src/mlpro/bf/plot.py b/src/mlpro/bf/plot/basics.py similarity index 66% rename from src/mlpro/bf/plot.py rename to src/mlpro/bf/plot/basics.py index bce8aa1b0..f50df0cfd 100644 --- a/src/mlpro/bf/plot.py +++ b/src/mlpro/bf/plot/basics.py @@ -1,7 +1,7 @@ ## ------------------------------------------------------------------------------------------------- -## -- Project : MLPro - A Synoptic Framework for Standardized Machine Learning Tasks -## -- Package : mlpro.bf -## -- Module : plot.py +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.plot +## -- Module : basics.py ## ------------------------------------------------------------------------------------------------- ## -- History : ## -- yyyy-mm-dd Ver. Auth. Description @@ -58,38 +58,35 @@ ## -- - Class Plottable: extensions on init_plot(), update_plot() ## -- - Refactoring: removed par p_force from Plottable.refresh() ## -- 2024-11-10 2.18.0 DA Bugfix in method Plottable.force_fg() +## -- 2024-12-10 3.0.0 DA - Created new module basics.py for classes PlotSettings, Plottable +## -- - Class Plottable: memory function for plot window geometries +## -- 2024-12-12 3.1.0 DA Method Plottable._init_figure(): optimization +## -- 2024-12-29 3.2.0 DA Import of all plot packages moved to class Plottable ## ------------------------------------------------------------------------------------------------- """ -Ver. 2.18.0 (2024-11-10) +Ver. 3.2.0 (2024-12-29) This module provides various classes related to data plotting. """ - from operator import mod -import numpy as np -from sys import platform - -try: - from tkinter import * - import matplotlib - # Due to bug in TKinter for macos, disabled for macos https://bugs.python.org/issue46573 - #if platform != 'darwin': - matplotlib.use('TkAgg') -except: - print('Please install tkinter for a better plot experience') - import matplotlib - -from matplotlib.figure import Figure -from matplotlib.axes import Axes -import matplotlib.pyplot as plt +import sys import os -import statistics +from pathlib import Path + from mlpro.bf.exceptions import ImplementationError, ParamError -from mlpro.bf.various import Persistent -from mlpro.bf.data import DataStoring +from mlpro.bf.plot.backends import * +from mlpro.bf.data import ConfigFile + +# Pseudo definitions until the final plot classes are imported +class Figure: pass +class Axes: pass + + +g_plot_packages_imported = None +g_event_loop_started = False @@ -259,7 +256,6 @@ def copy(self): p_id = self.id, p_view_autoselect = self.view_autoselect, p_kwargs = self.kwargs ) - @@ -308,16 +304,78 @@ class Plottable: C_PLOT_VALID_VIEWS : list = [] C_PLOT_DEFAULT_VIEW : str = PlotSettings.C_VIEW_ND C_PLOT_DETAIL_LEVEL : int = 0 + C_PLOT_CONFIG_PATH : str = 'mlpro-plot-settings' ## ------------------------------------------------------------------------------------------------- def __init__(self, p_visualize:bool=False): - self._visualize = self.C_PLOT_ACTIVE and p_visualize + + # 1 Initialize attributes needed (even in 'dark' mode) + self._visualize = p_visualize and self.C_PLOT_ACTIVE self._plot_settings : PlotSettings = None - self.plot_detail_level = self.C_PLOT_DETAIL_LEVEL - self._plot_initialized : bool = False - self._plot_first_time : bool = True - self._plot_own_figure : bool = False - self._plot_color = None + + # 1.1 Import plot packages + if self._visualize: + self._visualize = self._import_plot_packages() + + # 1.2 Do nothing if visualization is turned off + if not self._visualize: return + + + # 2 Initialize further attributes + self.plot_detail_level = self.C_PLOT_DETAIL_LEVEL + self._plot_initialized : bool = False + self._plot_first_time : bool = True + self._plot_own_figure : bool = False + self._plot_color = None + self._figure : Figure = None + self._plot_window = None + + + # 3 Initialize MLPro's backend object + backend = matplotlib.get_backend() + + try: + self._plot_backend : PlotBackend = globals()[ 'PlotBackend' + backend ]() + except KeyError: + raise NotImplementedError( 'Matplotlib backend "' + backend + '" is not yet supported by MLPro. Please contact the team on GitHub.') + + + # 4 Initialize related configuration file + self._cfg_file = ConfigFile( p_fname = str(Path.home()) + os.sep + self.C_PLOT_CONFIG_PATH + os.sep + Path(sys.argv[0]).stem + '.json' ) + self._cfg_key = None + + +## ------------------------------------------------------------------------------------------------- + def _import_plot_packages(self) -> bool: + + global g_plot_packages_imported + + if g_plot_packages_imported is not None: return g_plot_packages_imported + + try: + global matplotlib, tkinter, Figure, Axes, plt + import matplotlib + + try: + from matplotlib.backends.qt_compat import QtCore + qtcore_test = QtCore.__version__ + matplotlib.use('qtagg') + except: + import tkinter + matplotlib.use('TkAgg') + print('Plot backend "Tk" activated. Nevertheless, we recommend installing package PySide6 for better plot performance.') + + from matplotlib.figure import Figure + from matplotlib.axes import Axes + import matplotlib.pyplot as plt + + g_plot_packages_imported = True + + except: + print('Plotting disabled. To enable it, please install matplotlib and Qt or Tk.') + g_plot_packages_imported = False + + return g_plot_packages_imported ## ------------------------------------------------------------------------------------------------- @@ -356,8 +414,9 @@ def set_plot_settings(self, p_plot_settings : PlotSettings ): ## ------------------------------------------------------------------------------------------------- def init_plot( self, - p_figure:Figure = None, - p_plot_settings : PlotSettings = None ) -> bool: + p_figure: Figure = None, + p_plot_settings: PlotSettings = None, + p_window_title: str = None ) -> bool: """ Initializes the plot functionalities of the class. @@ -371,10 +430,7 @@ def init_plot( self, """ # 1 Plot functionality turned on? Initialization already called? - try: - if ( not self.C_PLOT_ACTIVE ) or ( not self._visualize ): return - except: - return + if not self.get_visualization(): return try: if ( p_plot_settings.detail_level > 0 ) and ( p_plot_settings.detail_level < self.plot_detail_level ): return @@ -401,8 +457,11 @@ def init_plot( self, # 2.3 Setup the Matplotlib host figure if no one is provided as parameter if p_figure is None: - self._figure : Figure = self._init_figure() + self._init_figure( p_window_title=p_window_title ) self._plot_own_figure = True + elif self._figure is not None: + if p_window_title is not None: + self._plot_backend.figure_set_title( p_figure = self._figure, p_title = p_window_title ) else: self._figure : Figure = p_figure @@ -482,50 +541,90 @@ def assign_plot_detail_level(self, p_detail_level:int): ## ------------------------------------------------------------------------------------------------- - def _init_figure(self) -> Figure: + def _init_figure(self, p_window_title: str = None): """ - Custom method to initialize a suitable standalone Matplotlib figure. - - Returns - ------- - figure : Matplotlib.figure.Figure - Matplotlib figure object to host the subplot(s) + Method to initialize a suitable standalone Matplotlib figure. """ - fig = plt.figure() - plt.show(block=False) - self._force_fg(p_fig=fig) - return fig + # 1 Start Matplotlib event-loop once + global g_event_loop_started + + if not g_event_loop_started: + plt.show(block=False) + g_event_loop_started = True + + + # 2 Create a new window with an embedded figure + self._figure = plt.figure() + self._plot_window = self._figure.canvas.manager.window + + + # 3 Set optional window title + if p_window_title is not None: + self._plot_backend.figure_set_title( p_figure = self._figure, p_title = p_window_title ) + + + # 4 Create unique configuration key from actual window title + if self._cfg_key is None: + self._cfg_key = self._plot_backend.figure_get_title( p_figure = self._figure ) + + + # 5 Make window visible + # while not self._plot_window.winfo_viewable(): # Tk + # while not self._plot_window.isVisible(): # Qt + plt.pause(0.1) + + + # 6 Recover size and position of the window + self._recover_window_geometry() + + + # 7 Bring window on top + self.force_fg() + + + # 8 Force storing the window geometry at program termination + self._plot_backend.figure_atexit( p_figure = self._figure, p_fct = self._store_window_geometry ) ## ------------------------------------------------------------------------------------------------- - def force_fg(self): - """ - Internal use. - """ + def _store_window_geometry(self): + if self._plot_window is None: return - # 1 Plot functionality turned on? try: - if ( not self.C_PLOT_ACTIVE ) or ( not self._visualize ): return + geometry = self._plot_backend.figure_get_geometry( p_figure = self._figure ) + self._cfg_file.set( p_key = self._cfg_key, p_values = geometry ) + except: + pass + + +## ------------------------------------------------------------------------------------------------- + def _recover_window_geometry(self): + + # 1 Get window geometry from local file + try: + geometry = self._cfg_file.get( p_key = self._cfg_key ) except: return - - # 2 Call internal custom method - self._force_fg(p_fig = self._figure) + + # 2 Set geometry + self._plot_backend.figure_set_geometry( p_figure = self._figure, + p_geometry = geometry ) ## ------------------------------------------------------------------------------------------------- - def _force_fg(self, p_fig : Figure): + def force_fg(self): """ Internal use. """ - if not self._plot_settings.force_fg: return - - backend = matplotlib.get_backend() + # 1 Plot functionality turned on? + if not self.get_visualization(): return - if backend == 'TkAgg': - p_fig.canvas.manager.window.attributes('-topmost', True) + if ( not self._plot_settings.force_fg ) or ( self._figure is None ): return + + # 2 Call internal custom method + self._plot_backend.figure_force_foreground( p_figure = self._figure ) ## ------------------------------------------------------------------------------------------------- @@ -535,10 +634,7 @@ def refresh_plot(self): """ # 1 Plot functionality turned on? - try: - if ( not self.C_PLOT_ACTIVE ) or ( not self._visualize ): return - except: - return + if not self.get_visualization(): return # 2 Update the plot step counter @@ -635,10 +731,7 @@ def update_plot(self, **p_kwargs): """ # 0 Plot functionality turned on? - try: - if ( not self.C_PLOT_ACTIVE ) or ( not self._visualize ): return - except: - return + if not self.get_visualization(): return # 1 Plot already initialized? @@ -722,19 +815,19 @@ def remove_plot(self, p_refresh:bool = True): """ # 1 Plot functionality turned on? - try: - if not self._plot_initialized: return - except: - return + if not self.get_visualization(): return + + # 2 Store window geometry in the local config file + self._store_window_geometry() - # 2 Call _remove_plot method of current view + # 3 Call _remove_plot method of current view view = self._plot_settings.view self._plot_methods[view][2]() - # 3 Optionally refresh + # 4 Optionally refresh if p_refresh: self.refresh_plot() - # 4 Clear internal plot parameters + # 5 Clear internal plot parameters self._plot_settings.unregister( p_plot_obj = self ) self._plot_first_time = True @@ -769,303 +862,3 @@ def _remove_plot_nd(self): ## ------------------------------------------------------------------------------------------------- color = property( fget = get_plot_color, fset = set_plot_color ) plot_detail_level = property( fget = get_plot_detail_level, fset = assign_plot_detail_level ) - - - - - -## ------------------------------------------------------------------------------------------------- -## ------------------------------------------------------------------------------------------------- -class DataPlotting(Persistent): - """ - This class provides a functionality to plot the stored values of variables. - - Parameters - ---------- - p_data : DataStoring - Data object with stored variables values. - p_type : str, optional - Type of plot. The default is C_PLOT_TYPE_EP. - p_window : int, optional - Moving average parameter. The default is 100. - p_showing : Bool, optional - Showing graphs after they are generated. The default is True. - p_printing : dict, optional - Additional important parameters for plotting. - [0] = Bool : Whether the stored values is plotted. - [1] = Float : Min. value on graph. - [2] = Float : Max. value on graph. Set to -1, if you want to set min/max value according to the stored values. - Example = {"p_variable_1" : [True,0,-1], - "p_variable_2" : [True,-0.5,10]}. - The default is None. - p_figsize : int, optional - Frame size. The default is (7,7). - p_color : str, optional - Line colors. The default is "darkblue". - p_window_type : str, optional - Plotting type for moving average. The default is 'same'. Options: 'same', 'full', 'valid' - - Attributes - ---------- - C_PLOT_TYPE_CY : str - one of the plotting types, which plot the graph with multiple lines according to the number of frames. - C_PLOT_TYPE_EP : str - one of the plotting types, which plot the graph everything in one line regardless the number of frames. - C_PLOT_TYPE_EP_M : str - one of the plotting types, which plot only the mean value of each variable for each frame. - - """ - - C_PLOT_TYPE_CY = 'Cyclic' - C_PLOT_TYPE_EP = 'Episodic' - C_PLOT_TYPE_EP_M = 'Episodic Mean' - C_PLOT_TYPE_EP_S = 'Episodic Sum' - -## ------------------------------------------------------------------------------------------------- - def __init__(self, p_data: DataStoring, p_type=C_PLOT_TYPE_EP, p_window=100, - p_showing=True, p_printing=None, p_figsize=(7, 7), p_color="darkblue", - p_window_type='same'): - self.data = p_data - self.type = p_type - self.window = p_window - self.showing = p_showing - self.plots = [[], []] - self.printing = p_printing - self.figsize = p_figsize - self.color = p_color - self.window_type = p_window_type - - -## ------------------------------------------------------------------------------------------------- - def get_plots(self): - """ - A function to plot data. - """ - - if self.type == 'Cyclic': - self.plots_type_cy() - elif self.type == 'Episodic': - self.plots_type_ep() - elif self.type == 'Episodic Mean': - self.plots_type_ep_mean() - elif self.type == 'Episodic Sum': - self.plots_type_ep_sum() - - -## ------------------------------------------------------------------------------------------------- - def plots_type_cy(self): - """ - A function to plot data per cycle. - """ - - for name in self.data.names: - maxval = 0 - minval = 0 - try: - if self.printing[name][0]: - fig = plt.figure(figsize=self.figsize) - lines = [] - label = [] - plt.title(name) - plt.grid(True, which="both", axis="both") - for fr in range(len(self.data.memory_dict[name])): - fr_id = self.data.frame_id[name][fr] - lines += plt.plot(self.moving_mean(self.data.get_values(name, fr_id), self.window), - color=self.color, alpha=(fr + 1.0) / (len(self.data.memory_dict[name]) + 1)) - if self.printing[name][2] == -1: - maxval = max(max(self.data.get_values(name, fr_id)), maxval) - minval = min(min(self.data.get_values(name, fr_id)), minval) - else: - maxval = self.printing[name][2] - minval = self.printing[name][1] - label.append("%s" % fr_id) - plt.ylim(minval, maxval) - plt.xlabel("cycles") - plt.legend(label, bbox_to_anchor=(1, 0.5), loc="center left") - self.plots[0].append(name) - self.plots[1].append(fig) - if self.showing: - plt.show() - else: - plt.close(fig) - except: - pass - - -## ------------------------------------------------------------------------------------------------- - def plots_type_ep(self): - """ - A function to plot data per frame by extending the cyclic plots in one plot. - """ - - for name in self.data.names: - maxval = 0 - minval = 0 - try: - if self.printing[name][0]: - fig = plt.figure(figsize=self.figsize) - lines = [] - data = [] - plt.title(name) - plt.grid(True, which="both", axis="both") - for fr in range(len(self.data.memory_dict[name])): - fr_id = self.data.frame_id[name][fr] - data.extend(self.data.get_values(name, fr_id)) - if self.printing[name][2] == -1: - maxval = max(max(self.data.get_values(name, fr_id)), maxval) - minval = min(min(self.data.get_values(name, fr_id)), minval) - else: - maxval = self.printing[name][2] - minval = self.printing[name][1] - lines += plt.plot(self.moving_mean(data[:], self.window), color=self.color) - plt.ylim(minval, maxval) - plt.xlabel("continuous cycles") - self.plots[0].append(name) - self.plots[1].append(fig) - if self.showing: - plt.show() - else: - plt.close(fig) - except: - pass - - -## ------------------------------------------------------------------------------------------------- - def plots_type_ep_mean(self): - """ - A function to plot data per frame according to its mean value. - """ - - for name in self.data.names: - maxval = 0 - minval = 0 - try: - if self.printing[name][0]: - fig = plt.figure(figsize=self.figsize) - lines = [] - data = [] - plt.title(name) - plt.grid(True, which="both", axis="both") - for fr in range(len(self.data.memory_dict[name])): - fr_id = self.data.frame_id[name][fr] - data.extend([statistics.mean(self.data.get_values(name, fr_id))]) - if self.printing[name][2] == -1: - maxval = max(max(data[:]), maxval) - minval = min(min(data[:]), minval) - else: - maxval = self.printing[name][2] - minval = self.printing[name][1] - lines += plt.plot(self.moving_mean(data[:], self.window), color=self.color) - plt.ylim(minval, maxval) - plt.xlabel("episodes") - self.plots[0].append(name) - self.plots[1].append(fig) - if self.showing: - plt.show() - else: - plt.close(fig) - except: - pass - - -## ------------------------------------------------------------------------------------------------- - def plots_type_ep_sum(self): - """ - A function to plot data per frame according to its sum value. - """ - - for name in self.data.names: - maxval = 0 - minval = 0 - try: - if self.printing[name][0]: - fig = plt.figure(figsize=self.figsize) - lines = [] - data = [] - plt.title(name) - plt.grid(True, which="both", axis="both") - for fr in range(len(self.data.memory_dict[name])): - fr_id = self.data.frame_id[name][fr] - data.extend([sum(self.data.get_values(name, fr_id))]) - if self.printing[name][2] == -1: - maxval = max(max(data[:]), maxval) - minval = min(min(data[:]), minval) - else: - maxval = self.printing[name][2] - minval = self.printing[name][1] - lines += plt.plot(self.moving_mean(data[:], self.window), color=self.color) - plt.ylim(minval, maxval) - plt.xlabel("episodes") - self.plots[0].append(name) - self.plots[1].append(fig) - if self.showing: - plt.show() - else: - plt.close(fig) - except: - pass - - -## ------------------------------------------------------------------------------------------------- - def moving_mean(self, p_inputs, p_window): - """ - This method creates a series of averages of different subsets of the full data set. - - Parameters - ---------- - p_inputs : list of floats - input dataset. - p_window : int - moving average parameter. - - Returns - ------- - outputs : list of floats - transformed data set. - - """ - - inputs = np.array(p_inputs) - outputs = np.zeros_like(inputs) - if len(inputs.shape) == 1: - outputs = np.convolve(inputs, np.ones((p_window,)) / p_window, mode=self.window_type) - else: - for col in range(inputs.shape[1]): - outputs[:, col] = np.convolve(inputs[:, col], np.ones((p_window,)) / p_window, mode=self.window_type) - return outputs - - -## ------------------------------------------------------------------------------------------------- - def save_plots(self, p_path, p_format, p_dpi_mul=1): - """ - This method is used to save generated plots. - - Parameters - ---------- - p_path : str - Path where file will be saved. - p_format : str - Format of the saved file. - Options: 'eps', 'jpg', 'png', 'pdf', 'svg'. - p_dpi_mul : int, optional - Saving plots parameter. The default is 1. - - Returns - ------- - bool - True, if plots where saved successfully. False otherwise.. - - """ - - num_plots = len(self.plots[0]) - if num_plots == 0: return False - - try: - if not os.path.exists(p_path): - os.makedirs(p_path) - for idx in range(num_plots): - self.plots[1][idx].savefig(p_path + os.sep + self.plots[0][idx] + "." + p_format, dpi=500 * p_dpi_mul, - bbox_inches='tight') - return True - except: - return False \ No newline at end of file diff --git a/src/mlpro/bf/plot/dataplotting.py b/src/mlpro/bf/plot/dataplotting.py new file mode 100644 index 000000000..5ebeb5b8a --- /dev/null +++ b/src/mlpro/bf/plot/dataplotting.py @@ -0,0 +1,383 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.plot +## -- Module : dataplotting.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2021-10-06 1.0.0 DA Creation and transfer of classes DataPlotting, Plottable from +## -- mlpro.bf.various +## -- 2021-10-25 1.0.1 SY Improve get_plots() functionality, enable episodic plots +## -- 2021-12-10 1.0.2 SY Add errors and exceptions, if p_printing is None. +## -- Clean code assurance. +## -- 2022-10-24 2.0.0 DA New class PlotSettings and extensions on class Plottable +## -- 2022-10-28 2.0.1 DA Corrections of class documentations +## -- 2022-10-29 2.0.2 DA Refactoring of class Plottable +## -- 2022-10-31 2.1.0 DA Class Plottable: fixes and improvements +## -- 2022-11-07 2.2.0 DA Class Plottable: new method get_visualization() +## -- 2022-11-09 2.2.1 DA Classes Plottable, PlotSettings: correction +## -- 2022-11-17 2.3.0 DA Classes Plottable, PlotSettings: extensions, corrections +## -- 2022-11-18 2.3.1 DA Classes Plottable, PlotSettings: improvements try/except +## -- 2022-12-20 2.4.0 DA New method Plottable.set_plot_settings() +## -- 2022-12-28 2.5.0 DA - Corrections in method Plottable.init_plot() +## -- - Reduction to one active plot view per task +## -- 2022-12-29 2.6.0 DA Refactoring of plot settings +## -- 2023-01-01 2.7.0 DA Class Plottable: introduction of update step rate +## -- 2023-01-04 2.8.0 DA Class PlotSettings: new parameters p_horizon, p_force_fg +## -- 2023-02-02 2.8.1 MRD Disable Tkinter backend for macos https://bugs.python.org/issue46573 +## -- 2023-02-17 2.8.2 SY Add p_window_type in DataPlotting +## -- 2023-02-23 2.9.0 DA Class PlotSettings: new parameter p_view_autoselect +## -- 2023-04-10 2.9.1 MRD Turn on Tkinter backend for macos +## -- 2023-05-01 2.9.2 DA Turn off Tkinter backend for macos due to workflow problems +## -- 2023-12-28 2.10.0 DA Method Plottable._init_plot_3d(): init 3D view perspective +## -- 2024-02-23 2.11.0 DA Class Plottable: new methods +## -- - _remove_plot_2d(), _remove_plot_3d(), _remove_plot_nd() +## -- - __del__() +## -- 2024-02-24 2.11.1 DA Class Plottable: +## -- - new methods remove_plot(), _remove_plot() +## -- - new methods refresh_plot(), _refresh_plot() +## -- 2024-05-21 2.12.0 DA Class PlotSettings: +## -- - parameter horizon replaced by plot_horizon with new default +## -- value 500 +## -- - new parameter data_horizon with default value 1000 +## -- 2024-05-22 2.13.0 DA New method PlotSettings.copy() +## -- 2024-06-04 2.13.1 DA/SK Turned on TKAgg for Mac +## -- 2024-06-07 2.13.2 SY Introducing new data plotting type of Episodic Sum +## -- 2024-06-24 2.14.0 DA New auto-managed attribute Plottable._plot_first_time : bool +## -- 2024-06-25 2.15.0 DA Class Plottable: +## -- - removed method set_plot_detail_level() +## -- - added methods assign_plot_detail_level(), +## -- get_plot_detail_level() and related property plot_detail_level +## -- - added new constant attribute C_PLOT_DETAIL_LEVEL +## -- 2024-06-26 2.16.0 DA - Refactoring, corrections, adjustments +## -- - New property Plottable.color +## -- - Class PlotSettings: removed parameter p_plot_depth +## -- 2024-07-08 2.16.1 SY Add MinVal for undefined range in DataPlotting +## -- 2024-10-30 2.17.0 DA - Class PlotSettings: new methods register(), unregister(), +## -- is_last_registered() +## -- - Class Plottable: extensions on init_plot(), update_plot() +## -- - Refactoring: removed par p_force from Plottable.refresh() +## -- 2024-11-10 2.18.0 DA Bugfix in method Plottable.force_fg() +## -- 2024-12-10 3.0.0 DA Created new module dataplotting.py for class DataPlotting +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 3.0.0 (2024-12-10) + +This module provides various classes related to data plotting. + +""" + + +from operator import mod +import numpy as np +from sys import platform + +try: + import matplotlib.pyplot as plt +except: + pass + +import os +import statistics +from mlpro.bf.various import Persistent +from mlpro.bf.data import DataStoring + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class DataPlotting(Persistent): + """ + This class provides a functionality to plot the stored values of variables. + + Parameters + ---------- + p_data : DataStoring + Data object with stored variables values. + p_type : str, optional + Type of plot. The default is C_PLOT_TYPE_EP. + p_window : int, optional + Moving average parameter. The default is 100. + p_showing : Bool, optional + Showing graphs after they are generated. The default is True. + p_printing : dict, optional + Additional important parameters for plotting. + [0] = Bool : Whether the stored values is plotted. + [1] = Float : Min. value on graph. + [2] = Float : Max. value on graph. Set to -1, if you want to set min/max value according to the stored values. + Example = {"p_variable_1" : [True,0,-1], + "p_variable_2" : [True,-0.5,10]}. + The default is None. + p_figsize : int, optional + Frame size. The default is (7,7). + p_color : str, optional + Line colors. The default is "darkblue". + p_window_type : str, optional + Plotting type for moving average. The default is 'same'. Options: 'same', 'full', 'valid' + + Attributes + ---------- + C_PLOT_TYPE_CY : str + one of the plotting types, which plot the graph with multiple lines according to the number of frames. + C_PLOT_TYPE_EP : str + one of the plotting types, which plot the graph everything in one line regardless the number of frames. + C_PLOT_TYPE_EP_M : str + one of the plotting types, which plot only the mean value of each variable for each frame. + + """ + + C_PLOT_TYPE_CY = 'Cyclic' + C_PLOT_TYPE_EP = 'Episodic' + C_PLOT_TYPE_EP_M = 'Episodic Mean' + C_PLOT_TYPE_EP_S = 'Episodic Sum' + +## ------------------------------------------------------------------------------------------------- + def __init__(self, p_data: DataStoring, p_type=C_PLOT_TYPE_EP, p_window=100, + p_showing=True, p_printing=None, p_figsize=(7, 7), p_color="darkblue", + p_window_type='same'): + self.data = p_data + self.type = p_type + self.window = p_window + self.showing = p_showing + self.plots = [[], []] + self.printing = p_printing + self.figsize = p_figsize + self.color = p_color + self.window_type = p_window_type + + +## ------------------------------------------------------------------------------------------------- + def get_plots(self): + """ + A function to plot data. + """ + + if self.type == 'Cyclic': + self.plots_type_cy() + elif self.type == 'Episodic': + self.plots_type_ep() + elif self.type == 'Episodic Mean': + self.plots_type_ep_mean() + elif self.type == 'Episodic Sum': + self.plots_type_ep_sum() + + +## ------------------------------------------------------------------------------------------------- + def plots_type_cy(self): + """ + A function to plot data per cycle. + """ + + for name in self.data.names: + maxval = 0 + minval = 0 + try: + if self.printing[name][0]: + fig = plt.figure(figsize=self.figsize) + lines = [] + label = [] + plt.title(name) + plt.grid(True, which="both", axis="both") + for fr in range(len(self.data.memory_dict[name])): + fr_id = self.data.frame_id[name][fr] + lines += plt.plot(self.moving_mean(self.data.get_values(name, fr_id), self.window), + color=self.color, alpha=(fr + 1.0) / (len(self.data.memory_dict[name]) + 1)) + if self.printing[name][2] == -1: + maxval = max(max(self.data.get_values(name, fr_id)), maxval) + minval = min(min(self.data.get_values(name, fr_id)), minval) + else: + maxval = self.printing[name][2] + minval = self.printing[name][1] + label.append("%s" % fr_id) + plt.ylim(minval, maxval) + plt.xlabel("cycles") + plt.legend(label, bbox_to_anchor=(1, 0.5), loc="center left") + self.plots[0].append(name) + self.plots[1].append(fig) + if self.showing: + plt.show() + else: + plt.close(fig) + except: + pass + + +## ------------------------------------------------------------------------------------------------- + def plots_type_ep(self): + """ + A function to plot data per frame by extending the cyclic plots in one plot. + """ + + for name in self.data.names: + maxval = 0 + minval = 0 + try: + if self.printing[name][0]: + fig = plt.figure(figsize=self.figsize) + lines = [] + data = [] + plt.title(name) + plt.grid(True, which="both", axis="both") + for fr in range(len(self.data.memory_dict[name])): + fr_id = self.data.frame_id[name][fr] + data.extend(self.data.get_values(name, fr_id)) + if self.printing[name][2] == -1: + maxval = max(max(self.data.get_values(name, fr_id)), maxval) + minval = min(min(self.data.get_values(name, fr_id)), minval) + else: + maxval = self.printing[name][2] + minval = self.printing[name][1] + lines += plt.plot(self.moving_mean(data[:], self.window), color=self.color) + plt.ylim(minval, maxval) + plt.xlabel("continuous cycles") + self.plots[0].append(name) + self.plots[1].append(fig) + if self.showing: + plt.show() + else: + plt.close(fig) + except: + pass + + +## ------------------------------------------------------------------------------------------------- + def plots_type_ep_mean(self): + """ + A function to plot data per frame according to its mean value. + """ + + for name in self.data.names: + maxval = 0 + minval = 0 + try: + if self.printing[name][0]: + fig = plt.figure(figsize=self.figsize) + lines = [] + data = [] + plt.title(name) + plt.grid(True, which="both", axis="both") + for fr in range(len(self.data.memory_dict[name])): + fr_id = self.data.frame_id[name][fr] + data.extend([statistics.mean(self.data.get_values(name, fr_id))]) + if self.printing[name][2] == -1: + maxval = max(max(data[:]), maxval) + minval = min(min(data[:]), minval) + else: + maxval = self.printing[name][2] + minval = self.printing[name][1] + lines += plt.plot(self.moving_mean(data[:], self.window), color=self.color) + plt.ylim(minval, maxval) + plt.xlabel("episodes") + self.plots[0].append(name) + self.plots[1].append(fig) + if self.showing: + plt.show() + else: + plt.close(fig) + except: + pass + + +## ------------------------------------------------------------------------------------------------- + def plots_type_ep_sum(self): + """ + A function to plot data per frame according to its sum value. + """ + + for name in self.data.names: + maxval = 0 + minval = 0 + try: + if self.printing[name][0]: + fig = plt.figure(figsize=self.figsize) + lines = [] + data = [] + plt.title(name) + plt.grid(True, which="both", axis="both") + for fr in range(len(self.data.memory_dict[name])): + fr_id = self.data.frame_id[name][fr] + data.extend([sum(self.data.get_values(name, fr_id))]) + if self.printing[name][2] == -1: + maxval = max(max(data[:]), maxval) + minval = min(min(data[:]), minval) + else: + maxval = self.printing[name][2] + minval = self.printing[name][1] + lines += plt.plot(self.moving_mean(data[:], self.window), color=self.color) + plt.ylim(minval, maxval) + plt.xlabel("episodes") + self.plots[0].append(name) + self.plots[1].append(fig) + if self.showing: + plt.show() + else: + plt.close(fig) + except: + pass + + +## ------------------------------------------------------------------------------------------------- + def moving_mean(self, p_inputs, p_window): + """ + This method creates a series of averages of different subsets of the full data set. + + Parameters + ---------- + p_inputs : list of floats + input dataset. + p_window : int + moving average parameter. + + Returns + ------- + outputs : list of floats + transformed data set. + + """ + + inputs = np.array(p_inputs) + outputs = np.zeros_like(inputs) + if len(inputs.shape) == 1: + outputs = np.convolve(inputs, np.ones((p_window,)) / p_window, mode=self.window_type) + else: + for col in range(inputs.shape[1]): + outputs[:, col] = np.convolve(inputs[:, col], np.ones((p_window,)) / p_window, mode=self.window_type) + return outputs + + +## ------------------------------------------------------------------------------------------------- + def save_plots(self, p_path, p_format, p_dpi_mul=1): + """ + This method is used to save generated plots. + + Parameters + ---------- + p_path : str + Path where file will be saved. + p_format : str + Format of the saved file. + Options: 'eps', 'jpg', 'png', 'pdf', 'svg'. + p_dpi_mul : int, optional + Saving plots parameter. The default is 1. + + Returns + ------- + bool + True, if plots where saved successfully. False otherwise.. + + """ + + num_plots = len(self.plots[0]) + if num_plots == 0: return False + + try: + if not os.path.exists(p_path): + os.makedirs(p_path) + for idx in range(num_plots): + self.plots[1][idx].savefig(p_path + os.sep + self.plots[0][idx] + "." + p_format, dpi=500 * p_dpi_mul, + bbox_inches='tight') + return True + except: + return False \ No newline at end of file diff --git a/src/mlpro/bf/streams/basics.py b/src/mlpro/bf/streams/basics.py index 9e46c1943..1239cac05 100644 --- a/src/mlpro/bf/streams/basics.py +++ b/src/mlpro/bf/streams/basics.py @@ -69,23 +69,28 @@ ## -- 2024-07-19 2.0.3 DA Class StreamTask: excluded non-numeric feature data from default ## -- visualization 2D,3D,ND ## -- 2024-09-11 2.1.0 DA Class Instance: new parent KWArgs +## -- 2024-10-01 2.1.1 DA Method StreamScenario.__init__(): simplification ## -- 2024-10-29 2.2.0 DA Changed definiton of InstType, InstTypeNew, InstTypeDel ## -- 2024-10-30 2.3.0 DA Refactoring of StreamTask.update_plot() ## -- 2024-11-10 2.4.0 DA Refactoring of StreamWorkflow.init_plot() +## -- 2024-12-11 2.4.1 DA Pseudo class Figure if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 2.4.0 (2024-11-10) +Ver. 2.4.1 (2024-12-11) This module provides classes for standardized data stream processing. """ -import datetime -from matplotlib.figure import Figure import random from typing import Dict, Tuple +try: + from matplotlib.figure import Figure +except: + class Figure : pass + from mlpro.bf.math.basics import * from mlpro.bf.various import Id, TStamp, KWArgs from mlpro.bf.ops import Mode, ScenarioBase @@ -395,7 +400,9 @@ def _omit_instance(self, p_inst:Instance) -> bool: Parameters ---------- - p_inst : Instance + p_inst : Instancep_set = {} +p_set[0] = tuple([1,Instance(12)]) +print(p_set) An input instance to be filtered. Returns @@ -1227,14 +1234,10 @@ def _update_plot_2d( self, # 5 Update of ax limits if ax_limits_changed: - try: + if self._plot_2d_xmin != self._plot_2d_xmax: p_settings.axes.set_xlim( self._plot_2d_xmin, self._plot_2d_xmax ) - except: - pass - try: + if self._plot_2d_ymin != self._plot_2d_ymax: p_settings.axes.set_ylim( self._plot_2d_ymin, self._plot_2d_ymax ) - except: - pass ## ------------------------------------------------------------------------------------------------- @@ -1373,9 +1376,12 @@ def _update_plot_3d( self, # 5 Update of ax limits if ax_limits_changed: - p_settings.axes.set_xlim( self._plot_3d_xmin, self._plot_3d_xmax ) - p_settings.axes.set_ylim( self._plot_3d_ymin, self._plot_3d_ymax ) - p_settings.axes.set_zlim( self._plot_3d_zmin, self._plot_3d_zmax ) + if self._plot_3d_xmin != self._plot_3d_xmax: + p_settings.axes.set_xlim( self._plot_3d_xmin, self._plot_3d_xmax ) + if self._plot_3d_ymin != self._plot_3d_ymax: + p_settings.axes.set_ylim( self._plot_3d_ymin, self._plot_3d_ymax ) + if self._plot_3d_zmin != self._plot_3d_zmax: + p_settings.axes.set_zlim( self._plot_3d_zmin, self._plot_3d_zmax ) ## ------------------------------------------------------------------------------------------------- @@ -1661,10 +1667,6 @@ def __init__( self, p_logging=Log.C_LOG_ALL, **p_kwargs ): - self._stream : Stream = None - self._iterator : Stream = None - self._workflow : StreamWorkflow = None - ScenarioBase.__init__( self, p_mode, p_cycle_limit=p_cycle_limit, @@ -1795,6 +1797,11 @@ def update_plot(self, **p_kwargs): pass +## ------------------------------------------------------------------------------------------------- + def remove_plot(self, p_refresh = True): + self._workflow.remove_plot(p_refresh=p_refresh) + + ## ------------------------------------------------------------------------------------------------- def get_stream(self) -> Stream: return self._stream diff --git a/src/mlpro/bf/streams/tasks/windows/ringbuffer.py b/src/mlpro/bf/streams/tasks/windows/ringbuffer.py index d944e8523..f3b2b5a74 100644 --- a/src/mlpro/bf/streams/tasks/windows/ringbuffer.py +++ b/src/mlpro/bf/streams/tasks/windows/ringbuffer.py @@ -26,19 +26,27 @@ ## -- 2024-05-22 1.2.0 DA Refactoring, splitting, and renaming to RingBuffer ## -- 2024-05-23 1.2.1 DA Bugfixes on plotting ## -- 2024-10-31 1.2.2 DA Bugfix in RingBuffer.get_boundaries() +## -- 2024-12-11 1.2.3 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.2.2 (2024-10-31) +Ver. 1.2.3 (2024-12-11) This module provides pool of window objects further used in the context of online adaptivity. """ -from matplotlib.axes import Axes -from mpl_toolkits.mplot3d.art3d import Poly3DCollection -from matplotlib.patches import Rectangle import numpy as np + +try: + from matplotlib.axes import Axes + from mpl_toolkits.mplot3d.art3d import Poly3DCollection + from matplotlib.patches import Rectangle +except: + class Axes : pass + class Poly3DCollection : pass + class Rectangle : pass + from mlpro.bf.streams.basics import * from mlpro.bf.events import * from mlpro.bf.streams.tasks.windows.basics import Window diff --git a/src/mlpro/bf/systems/basics.py b/src/mlpro/bf/systems/basics.py index 74b9987bb..3441bf6cc 100644 --- a/src/mlpro/bf/systems/basics.py +++ b/src/mlpro/bf/systems/basics.py @@ -51,23 +51,24 @@ ## -- 2024-09-09 2.3.0 DA Class Action: parent TSTamp replaced by Instance ## -- 2024-09-11 2.4.0 DA - code review and documentation ## -- - new method State.get_kwargs() +## -- 2024-10-06 2.5.0 DA New property attribute State.value +## -- 2024-12-11 2.5.0 DA New method DemoScenario.init_plot() ## ------------------------------------------------------------------------------------------------- """ -Ver. 2.4.0 (2024-09-11) +Ver. 2.5.0 (2024-12-11) This module provides models and templates for state based systems. """ from time import sleep -from typing import List import numpy as np from mlpro.bf.mt import Range from mlpro.bf.streams.basics import Instance -from mlpro.bf.various import TStamp, ScientificObject, Persistent +from mlpro.bf.various import ScientificObject, Persistent from mlpro.bf.data import * from mlpro.bf.plot import Plottable, PlotSettings from matplotlib.figure import Figure @@ -223,6 +224,9 @@ def copy(self): return copied_state +## ------------------------------------------------------------------------------------------------- + values = property( fget=Element.get_values, fset=Element.set_values) + @@ -2402,10 +2406,14 @@ def _get_next_action(self): action.append(random.randint(*dim.get_boundaries())) return Action(p_action_space=action_space, p_values=action) + + +## ------------------------------------------------------------------------------------------------- + def init_plot(self, p_figure = None, p_plot_settings = None, p_window_title = None): + self._system.init_plot( p_figure = p_figure, p_plot_settings= p_plot_settings, p_window_title = p_window_title ) ## ------------------------------------------------------------------------------------------------- def update_plot(self, **p_kwargs): - - super().update_plot(**p_kwargs) + # super().update_plot(**p_kwargs) self._system.update_plot(**p_kwargs) \ No newline at end of file diff --git a/src/mlpro/bf/systems/pool/__init__.py b/src/mlpro/bf/systems/pool/__init__.py index 7a25d6882..0143b1f3d 100644 --- a/src/mlpro/bf/systems/pool/__init__.py +++ b/src/mlpro/bf/systems/pool/__init__.py @@ -1 +1,2 @@ -from mlpro.bf.systems.pool.doublependulum import DoublePendulumSystemS4, DoublePendulumSystemS7 \ No newline at end of file +from mlpro.bf.systems.pool.doublependulum import DoublePendulumSystemS4, DoublePendulumSystemS7 +from mlpro.bf.systems.pool.fox import Fox \ No newline at end of file diff --git a/src/mlpro/bf/systems/pool/doublependulum.py b/src/mlpro/bf/systems/pool/doublependulum.py index b7e2b10ea..cc467dadd 100644 --- a/src/mlpro/bf/systems/pool/doublependulum.py +++ b/src/mlpro/bf/systems/pool/doublependulum.py @@ -1,17 +1,18 @@ ## ------------------------------------------------------------------------------------------------- -## -- Project : MLPro - A Synoptic Framework for Standardized Machine Learning Tasks +## -- Project : MLPro - The integrative middleware framework for standardized machine learning ## -- Package : mlpro.bf.systems.pool ## -- Module : doublependulum.py ## ------------------------------------------------------------------------------------------------- ## -- History : ## -- yyyy-mm-dd Ver. Auth. Description -## -- 2023-03-05 0.0.0 LSB Creation -## -- 2023-03-05 1.0.0 LSB Release -## -- 2023-03-08 1.0.1 LSB Refactoring for visualization +## -- 2023-03-05 0.0.0 LSB Creation +## -- 2023-03-05 1.0.0 LSB Release +## -- 2023-03-08 1.0.1 LSB Refactoring for visualization +## -- 2024-12-11 1.0.2 DA Refactoring ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.0.1 (2023-03-08) +Ver. 1.0.2 (2024-12-11) The Double Pendulum System is an implementation of a classic control problem of Double Pendulum system. The dynamics of the system are based on the `Double Pendulum `_ implementation by @@ -21,15 +22,14 @@ """ import random -from mlpro.bf.systems import * -from mlpro.bf.various import * import numpy as np from numpy import sin, cos from matplotlib.patches import Arc, RegularPolygon import scipy.integrate as integrate from collections import deque - +from mlpro.bf.various import * +from mlpro.bf.systems import * @@ -498,15 +498,22 @@ def _init_plot_2d(self, p_figure: Figure, p_settings: PlotSettings): theta2=250, color='crimson') endX = (0.5 * self._l1 / 2) * np.cos(np.radians(0)) endY = (0.5 * self._l1 / 2) * np.sin(np.radians(0)) - self._cw_arrow = RegularPolygon((endX, endY), 3, 0.5 * self._l1 / 9, np.radians(180), - color='crimson') + self._cw_arrow = RegularPolygon( xy = (endX, endY), + numVertices = 3, + radius = 0.5 * self._l1 / 9, + orientation = np.radians(180), + color='crimson' ) self._ccw_arc = Arc([0, 0], 0.5 * self._l1, 0.5 * self._l1, angle=70, theta1=0, theta2=320, color='crimson') endX = (0.5 * self._l1 / 2) * np.cos(np.radians(70 + 320)) endY = (0.5 * self._l1 / 2) * np.sin(np.radians(70 + 320)) - self._ccw_arrow = RegularPolygon((endX, endY), 3, 0.5 * self._l1 / 9, np.radians(70 + 320), - color='crimson') + + self._ccw_arrow = RegularPolygon( xy = (endX, endY), + numVertices = 3, + radius = 0.5 * self._l1 / 9, + orientation = np.radians(70 + 320), + color='crimson' ) p_settings.axes[0].add_patch(self._cw_arc) p_settings.axes[0].add_patch(self._cw_arrow) diff --git a/src/mlpro/bf/systems/pool/fox.py b/src/mlpro/bf/systems/pool/fox.py new file mode 100644 index 000000000..fe4b43dcc --- /dev/null +++ b/src/mlpro/bf/systems/pool/fox.py @@ -0,0 +1,108 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.systems.pool +## -- Module : fox.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-10-10 0.1.0 DA Initial implementation +## -- 2024-10-13 0.2.0 DA Random state jump +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.2.0 (2024-10-13) + +This module provides a simple demo system that just cumulates a percentage part of the incoming +action to the inner state. +""" + + +import random +from datetime import timedelta + +import numpy as np + +from mlpro.bf.various import Log +from mlpro.bf.ops import Mode +from mlpro.bf.mt import Task +from mlpro.bf.math import Dimension, MSpace, ESpace +from mlpro.bf.systems import State, Action, System + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class Fox (System): + """ + Dummy system mirroring the input with a delay. Just made for validation and basic demonstration. + """ + + C_NAME = 'Fox' + C_BOUNDARIES = [-10,10] + C_PLOT_ACTIVE = False + + C_LATENCY = timedelta( seconds = 1 ) + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_id=None, + p_name = C_NAME, + p_num_dim: int = 1, + p_delay: float = 0.8, + p_thr_jump: float = 0.1, + p_latency : timedelta = None, + p_range_max = Task.C_RANGE_NONE, + p_visualize = False, + p_logging=Log.C_LOG_ALL ): + + super().__init__( p_id = p_id, + p_name = p_name, + p_range_max = p_range_max, + p_mode = Mode.C_MODE_SIM, + p_latency = p_latency, + p_visualize = False, + p_logging = p_logging ) + + self._action_factor: float = min(1, max(0, 1 - p_delay)) + self._thr_jump: float = p_thr_jump + self._state_space, self._action_space = self._setup_spaces(p_num_dim=p_num_dim) + + +## ------------------------------------------------------------------------------------------------- + def _setup_spaces(self, p_num_dim: int): + + state_action_space : MSpace = ESpace() + + for i in range(p_num_dim): + state_action_space.add_dim( p_dim = Dimension( p_name_short = 'var ' + str(i), + p_base_set = Dimension.C_BASE_SET_R, + p_boundaries = self.C_BOUNDARIES ) ) + + return state_action_space, state_action_space + + +## ------------------------------------------------------------------------------------------------- + def _jump_away(self, p_state: State): + p_state.set_initial( p_initial=True ) + p_state.values = [random.uniform( self.C_BOUNDARIES[0], self.C_BOUNDARIES[1] ) for _ in range(self.get_state_space().get_num_dim())] + + +## ------------------------------------------------------------------------------------------------- + def _reset(self, p_seed=None): + random.seed( p_seed ) + new_state = State( p_state_space = self.get_state_space(), p_initial = True ) + self._jump_away( p_state = new_state ) + self._set_state( p_state = new_state ) + + +## ------------------------------------------------------------------------------------------------- + def _simulate_reaction(self, p_state: State, p_action: Action, p_step = None): + + agent_id = p_action.get_agent_ids()[0] + new_state = State( p_state_space = self.get_state_space()) + new_state.values = np.array(p_state.values) + np.array(p_action.get_elem(p_id=agent_id).get_values()) * self._action_factor + + if np.linalg.norm( new_state.values ) <= self._thr_jump: self._jump_away( p_state=new_state) + + return new_state \ No newline at end of file diff --git a/src/mlpro/bf/various.py b/src/mlpro/bf/various.py index 3860671b2..0a2aa053b 100644 --- a/src/mlpro/bf/various.py +++ b/src/mlpro/bf/various.py @@ -1,5 +1,5 @@ ## ------------------------------------------------------------------------------------------------- -## -- Project : MLPro - A Synoptic Framework for Standardized Machine Learning Tasks +## -- Project : MLPro - The integrative middleware framework for standardized machine learning ## -- Package : mlpro.bf ## -- Module : various ## ------------------------------------------------------------------------------------------------- @@ -52,10 +52,13 @@ ## -- 2024-05-21 2.3.0 DA Class TStamp: introduction of alias TStampType ## -- 2024-06-18 2.4.0 DA New class KWArgs ## -- 2024-12-02 2.5.0 DA New property KWargs.kwargs +## -- 2024-12-06 2.6.0 DA Class Log: tuning by about 10% +## -- 2024-12-29 2.7.0 DA - Method Log.log(): new parameter p_type_col +## -- - Class Log: code optimization ## ------------------------------------------------------------------------------------------------- """ -Ver. 2.5.0 (2024-12-02) +Ver. 2.7.0 (2024-12-29) This module provides various classes with elementry functionalities for reuse in higher level classes. For example: logging, persistence, timer... @@ -170,9 +173,14 @@ class Log: ## ------------------------------------------------------------------------------------------------- def __init__(self, p_logging=C_LOG_ALL): - if p_logging not in self.C_LOG_LEVELS: - raise ParamError('Wrong log level. See class Log for valid log levels') - self._level = p_logging + + self._log_color_map = { + self.C_LOG_TYPE_W: self.C_COL_WARNING, + self.C_LOG_TYPE_E: self.C_COL_ERROR, + self.C_LOG_TYPE_S: self.C_COL_SUCCESS, + } + + self._switch_logging( p_logging=p_logging ) if self.C_INST_MSG: self.log(self.C_LOG_TYPE_I, 'Instantiated') @@ -190,7 +198,7 @@ def set_name(self, p_name:str): ## ------------------------------------------------------------------------------------------------- - def switch_logging(self, p_logging): + def _switch_logging(self, p_logging): """ Sets new log level. @@ -201,50 +209,67 @@ def switch_logging(self, p_logging): """ - if p_logging not in self.C_LOG_LEVELS: raise ParamError('Wrong log level. See class Log for valid log levels') + if p_logging not in self.C_LOG_LEVELS: + raise ParamError('Wrong log level. See class Log for valid log levels') + self._level = p_logging + if self._level: + self.log = self._log + else: + self.log = lambda *args, **kwargs: None + + + ## ------------------------------------------------------------------------------------------------- + def switch_logging(self, p_logging): + self._switch_logging(p_logging=p_logging) ## ------------------------------------------------------------------------------------------------- def get_log_level(self): return self._level + - - ## ------------------------------------------------------------------------------------------------- - def log(self, p_type, *p_args): +## ------------------------------------------------------------------------------------------------- + def log(self, p_type, *p_args, p_type_col = None): """ - Writes log line to standard output in format: - yyyy-mm-dd hh:mm:ss.mmmmmm [p_type C_TYPE C_NAME]: [p_args] - - Parameters: - p_type type of log entry - p_args log informations + Writes a log line to the standard output in the format: + yyyy-mm-dd hh:mm:ss.mmmmmm [p_type] [self.C_TYPE] [self.C_NAME]: [p_args] - Returns: - Nothing + Parameters + ---------- + p_type + Type of log entry + p_args + Optional log information + p_type_col = None + Optional differing log type used to color the log line. """ + + # self.log is assigned dynamically to the method self._log. See constructor for more details... + pass - if not self._level: return - if self._level == self.C_LOG_WE: - if (p_type == self.C_LOG_TYPE_I) or (p_type == self.C_LOG_TYPE_S): return - elif self._level == self.C_LOG_E: - if (p_type == self.C_LOG_TYPE_I) or (p_type == self.C_LOG_TYPE_S) or (p_type == self.C_LOG_TYPE_W): return +## ------------------------------------------------------------------------------------------------- + def _log(self, p_type, *p_args, p_type_col = None): - now = datetime.now() + level = self._level - if p_type == self.C_LOG_TYPE_W: - col = self.C_COL_WARNING - elif p_type == self.C_LOG_TYPE_E: - col = self.C_COL_ERROR - elif p_type == self.C_LOG_TYPE_S: - col = self.C_COL_SUCCESS + if level == self.C_LOG_WE: + if p_type in (self.C_LOG_TYPE_I, self.C_LOG_TYPE_S): return + elif level == self.C_LOG_E: + if p_type in (self.C_LOG_TYPE_I, self.C_LOG_TYPE_S, self.C_LOG_TYPE_W): return + + now = datetime.now().isoformat( sep = ' ' ) + + if p_type_col is not None: + type_col = p_type_col else: - col = self.C_COL_RESET + type_col = p_type + + col = self._log_color_map.get(type_col, self.C_COL_RESET) - print(col + '%04d-%02d-%02d %02d:%02d:%02d.%06d ' % ( - now.year, now.month, now.day, now.hour, now.minute, now.second, now.microsecond), - p_type + ' ' + self.C_TYPE + ' "' + self.C_NAME + '":', *p_args, self.C_COL_RESET) + arg_str = ' '.join(map(str, p_args)) + print( col + now + ' ' + p_type + ' ' + self.C_TYPE + ' "' + self.C_NAME + '": ' + arg_str + self.C_COL_RESET ) diff --git a/src/mlpro/oa/control/__init__.py b/src/mlpro/oa/control/__init__.py new file mode 100644 index 000000000..8b4175433 --- /dev/null +++ b/src/mlpro/oa/control/__init__.py @@ -0,0 +1,2 @@ +from mlpro.oa.control.basics import * +from mlpro.oa.control.controlsystems.basic import OAControlSystemBasic \ No newline at end of file diff --git a/src/mlpro/oa/control/basics.py b/src/mlpro/oa/control/basics.py new file mode 100644 index 000000000..a5cb3ae82 --- /dev/null +++ b/src/mlpro/oa/control/basics.py @@ -0,0 +1,242 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.oa.control +## -- Module : basics.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-09-12 0.0.0 DA Creation +## -- 2024-09-16 0.1.0 DA Initial implementation of class OAController +## -- 2024-09-19 0.2.0 DA Completion of classes and their parents +## -- 2024-09-27 0.3.0 DA New method OAController hdl_setpoint_changed +## -- 2024-10-04 0.3.1 DA Bugfix in OAController.__init__() +## -- 2024-10-09 0.4.0 DA Refactoring +## -- 2024-12-05 0.5.0 DA Refactoring and code cleanup +## -- 2024-12-06 0.5.1 DA Bugfixes in methods OAController.__init__(), ._run() +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.5.1 (2024-12-06) + +This module provides basic classes around the topic online-adaptive closed-loop control. + +""" + + +from mlpro.bf.various import Log +from mlpro.bf.events import Event +from mlpro.bf.mt import Task +from mlpro.bf.math import MSpace +from mlpro.bf.control import Controller, get_ctrl_data, ControlError, ControlVariable +from mlpro.bf.streams import InstDict, InstTypeNew +from mlpro.bf.ml import Model, Training, TrainingResults + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAController (Controller, Model): + """ + Template class for online-adaptive closed-loop controllers. Please implement methods _compute_action() + and _adapt() in child classes. + + Parameters + ---------- + p_ada : bool = True + Boolean switch for adaptivitiy. Default = True. + p_name : str = None + Optional name of the task. Default is None. + p_range_max : int = Task.C_RANGE_THREAD + Maximum range of asynchonicity. See class Range. Default is Task.C_RANGE_PROCESS. + p_visualize : bool = False + Boolean switch for visualisation. Default = False. + p_logging = Log.C_LOG_ALL + Log level (see constants of class Log). Default: Log.C_LOG_ALL + p_kwargs : dict + Further optional named parameters. + """ + + C_TYPE = 'OA Controller' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_input_space : MSpace, + p_output_space : MSpace, + p_ada: bool = True, + p_id = None, + p_name: str = None, + p_range_max = Task.C_RANGE_NONE, + p_visualize: bool = False, + p_logging=Log.C_LOG_ALL, + **p_kwargs ): + + Controller.__init__( self, + p_input_space = p_input_space, + p_output_space = p_output_space, + p_id = p_id, + p_name = p_name, + p_range_max = p_range_max, + p_visualize = p_visualize, + p_logging = False, + **p_kwargs ) + + Model.__init__( self, + p_ada = p_ada, + p_name = p_name, + p_visualize = p_visualize, + p_logging = p_logging ) + + +## ------------------------------------------------------------------------------------------------- + def _run(self, p_inst: InstDict): + """ + Computes the next action based on the current control error and adapts on further contextual + information like setpoint, state, action. + """ + + # 1 Get control error instance + ctrl_error = get_ctrl_data( p_inst = p_inst, p_type = ControlError, p_remove = True ) + if ctrl_error is None: + self.log(Log.C_LOG_TYPE_W, 'Control error instance is missing!') + return + + # 2 Compute the next action + ctrl_var = self.compute_output( p_ctrl_error = ctrl_error ) + p_inst[ctrl_var.id] = (InstTypeNew, ctrl_var) + + # 3 Adapt + self.adapt( p_ctrl_error = ctrl_error, p_ctrl_var = ctrl_var ) + + +## ------------------------------------------------------------------------------------------------- + def _adapt( self, p_ctrl_error: ControlError, p_ctrl_var: ControlVariable ) -> bool: + """ + Specialized custom method for online adaptation in closed-loop control systems. + + Parameters + ---------- + p_ctrl_error : ControlError + Control error. + p_ctrl_var : ControlVariable + Control variable. + """ + + raise NotImplementedError + + +## ------------------------------------------------------------------------------------------------- + def hdl_setpoint_changed(self, p_event_id:str, p_event_object:Event): + """ + Handler method to be registered for event ControlPanel.C_EVNET_ID_SETPOINT_CHG. Turns off + the adaptation for one cycle. + """ + + raise NotImplementedError + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAMultiController: # (MultiController, Model): + """ + """ + + C_TYPE = 'OA Multi-Controller' + C_NAME = '' + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControlledSystem: # (ControlledSystem, Model): + """ + Wrapper class for online-adaptive state-based systems. + """ + + C_TYPE = 'OA Controlled System' + C_NAME = '' + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControlPanel: # (ControlPanel): + """ + Enables external control of a closed-loop control. + """ + + C_TYPE = 'OA Control Panel' + C_NAME = '????' + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControlShared: # (ControlShared, OAControlPanel): + """ + ... + """ + + pass + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControlWorkflow: # (ControlWorkflow, Model): + """ + Container class for all tasks of a control workflow. + """ + + C_TYPE = 'OA Control Workflow' + C_NAME = '' + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControlSystem: # (ControlSystem, Model): + """ + ... + """ + + pass + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControlTrainingResults (TrainingResults): + """ + ... + """ + + pass + + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControlTraining (Training): + """ + ... + """ + + pass \ No newline at end of file diff --git a/src/mlpro/oa/control/controllers/__init__.py b/src/mlpro/oa/control/controllers/__init__.py new file mode 100644 index 000000000..54e122ed5 --- /dev/null +++ b/src/mlpro/oa/control/controllers/__init__.py @@ -0,0 +1,2 @@ +#from mlpro.oa.control.controllers.wrapper_fct import OAControllerFct +from mlpro.oa.control.controllers.wrapper_rl import OAControllerRL \ No newline at end of file diff --git a/src/mlpro/oa/control/controllers/wrapper_fct.py b/src/mlpro/oa/control/controllers/wrapper_fct.py new file mode 100644 index 000000000..323faf3f7 --- /dev/null +++ b/src/mlpro/oa/control/controllers/wrapper_fct.py @@ -0,0 +1,46 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.oa.control.controllers +## -- Module : wrapper_fct.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-09-19 0.0.0 DA Creation +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.0.0 (2024-09-19) + +This module provides a wrapper class for MLPro's adaptive functions. + +""" + + +from mlpro.oa.control import OAController + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControllerFct (OAController): + """ + Wrapper class for controllers based on an online-adaptive function mapping an error to an action. + + Parameters + ---------- + p_fct : Function + Function object mapping a control error to an action + + See class Controller for further parameters. + """ + + C_TYPE = 'OA Controller Fct' + C_NAME = '' + +## ------------------------------------------------------------------------------------------------- + def switch_adaptivity(self, p_ada: bool): + try: + self._fct.switch_adaptivity(p_ada) + except: + pass diff --git a/src/mlpro/oa/control/controllers/wrapper_rl.py b/src/mlpro/oa/control/controllers/wrapper_rl.py new file mode 100644 index 000000000..0b70257ab --- /dev/null +++ b/src/mlpro/oa/control/controllers/wrapper_rl.py @@ -0,0 +1,207 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.oa.control.controller +## -- Module : wrapper_rl.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-09-19 0.1.0 DA Initial implementation of class OAControllerRL +## -- 2024-10-09 0.2.0 DA Refactoring +## -- 2024-10-13 0.3.0 DA Refactoring +## -- 2024-10-16 0.3.1 DA/ASP Bugfix in method OAControllerRL._adapt() +## -- 2024-12-05 0.4.0 DA Class OAControllerRL: +## -- - implementation of plot methods +## .- - redefintion of method assign_so() +## -- 2024-12-06 0.4.1 DA Method OAControllerRL.__init__(): handling of the own name +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.4.1 (2024-12-06) + +This module provides a wrapper class for MLPro's RL policies. + +""" + + +from mlpro.bf.various import Log +from mlpro.bf.mt import Task +from mlpro.bf.control import ControlError, ControlVariable +from mlpro.oa.control import OAController + +from mlpro.rl import Action, State, SARSElement, FctReward, Policy + + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControllerRL (OAController): + """ + Wrapper class for online-adaptive closed-loop controllers reusing reinforcement learning objects + like a policy and a reward function. + + Parameters + ---------- + p_rl_policy : Policy + RL policy object. + p_rl_fct_reward : FctReward + RL reward function. + p_ada : bool + Boolean switch for adaptivitiy. Default = True. + p_name : str + Optional name of the task. Default is None. + p_range_max : int + Maximum range of asynchonicity. See class Range. Default is Range.C_RANGE_PROCESS. + p_visualize : bool + Boolean switch for visualisation. Default = False. + p_logging + Log level (see constants of class Log). Default: Log.C_LOG_ALL + p_kwargs : dict + Further optional named parameters. + """ + + C_TYPE = 'OA Controller RL' + +## ------------------------------------------------------------------------------------------------- + def __init__( self, + p_rl_policy : Policy, + p_rl_fct_reward : FctReward, + p_ada : bool = True, + p_name: str = None, + p_range_max=Task.C_RANGE_THREAD, + p_visualize: bool = False, + p_logging=Log.C_LOG_ALL, + **p_kwargs ): + + self._rl_policy : Policy = p_rl_policy + self._rl_fct_reward : FctReward = p_rl_fct_reward + self._state_old : State = None + self._action_old : Action = None + + if p_name is not None: + name = p_name + else: + name = p_rl_policy.get_name() + + super().__init__( p_ada = p_ada, + p_name = name, + p_range_max = p_range_max, + p_visualize = p_visualize, + p_logging = p_logging, + **p_kwargs ) + + +## ------------------------------------------------------------------------------------------------- + def assign_so(self, p_so): + super().assign_so( p_so = p_so ) + self._rl_policy.assign_so( p_so = p_so ) + + +## ------------------------------------------------------------------------------------------------- + def switch_adaptivity(self, p_ada: bool): + super().switch_adaptivity(p_ada=p_ada) + self._rl_policy.switch_adaptivity(p_ada=p_ada) + + +## ------------------------------------------------------------------------------------------------- + def compute_output( self, p_ctrl_error: ControlError ) -> ControlVariable: + + # 1 Convert control error to RL state + state = State( p_state_space = p_ctrl_error.value_space ) + state.values = p_ctrl_error.values + state.id = p_ctrl_error.id + state.tstamp = p_ctrl_error.tstamp + + # 2 Let the RL policy compute th next action + action = self._rl_policy.compute_action( p_obs = state ) + + # 3 Convert RL action to control variable and return + return ControlVariable( p_id = self.get_so().get_next_inst_id(), + p_value_space = action.get_feature_data().get_related_set(), + p_values = action.get_feature_data().get_values(), + p_tstamp = self.get_so().get_tstamp() ) + + +## ------------------------------------------------------------------------------------------------- + def _adapt(self, p_ctrl_error: ControlError, p_ctrl_var: ControlVariable) -> bool: + + # 0 Intro + adapted = False + + # 1 Convert control error to RL state + state_new = State( p_state_space = p_ctrl_error.value_space ) + state_new.id = p_ctrl_error.id + state_new.tstamp = p_ctrl_error.tstamp + state_new.values = p_ctrl_error.values + + # 2 Adaptation from the second cycle on + if self._state_old is None: + self._state_old = state_new + return False + + # 3 Call reward function + reward = self._rl_fct_reward.compute_reward( p_state_old = self._state_old, p_state_new = state_new ) + + # 4 Setup SARS element + sars_elem = SARSElement( p_state = self._state_old, + p_action = self._action_old, + p_reward = reward, + p_state_new = state_new ) + + # 5 Trigger adaptation of the RL policy + adapted = self._rl_policy.adapt( p_sars_elem = sars_elem ) + + # 6 Buffering of new state and action + self._state_old = state_new + self._action_old = Action( p_agent_id = self.id, + p_action_space = p_ctrl_var.value_space, + p_values = p_ctrl_var.values, + p_tstamp = p_ctrl_var.tstamp ) + + # 7 Outro + return adapted + + +## ------------------------------------------------------------------------------------------------- + def _init_plot_2d(self, p_figure, p_settings): + return self._rl_policy._init_plot_2d(p_figure, p_settings) + + +## ------------------------------------------------------------------------------------------------- + def _init_plot_3d(self, p_figure, p_settings): + return self._rl_policy._init_plot_3d(p_figure, p_settings) + + +## ------------------------------------------------------------------------------------------------- + def _init_plot_nd(self, p_figure, p_settings): + return self._rl_policy._init_plot_nd(p_figure, p_settings) + + +## ------------------------------------------------------------------------------------------------- + def _update_plot_2d(self, p_settings, p_inst, **p_kwargs): + return self._rl_policy._update_plot_2d(p_settings, p_inst, **p_kwargs) + + +## ------------------------------------------------------------------------------------------------- + def _update_plot_3d(self, p_settings, p_inst, **p_kwargs): + return self._rl_policy._update_plot_3d(p_settings, p_inst, **p_kwargs) + + +## ------------------------------------------------------------------------------------------------- + def _update_plot_nd(self, p_settings, p_inst, **p_kwargs): + return self._rl_policy._update_plot_nd(p_settings, p_inst, **p_kwargs) + + +## ------------------------------------------------------------------------------------------------- + def _remove_plot_2d(self): + return self._rl_policy._remove_plot_2d() + + +## ------------------------------------------------------------------------------------------------- + def _remove_plot_3d(self): + return self._rl_policy._remove_plot_3d() + + +## ------------------------------------------------------------------------------------------------- + def _remove_plot_nd(self): + return self._rl_policy._remove_plot_nd() \ No newline at end of file diff --git a/src/mlpro/oa/control/controlsystems/__init__.py b/src/mlpro/oa/control/controlsystems/__init__.py new file mode 100644 index 000000000..a8953cd8d --- /dev/null +++ b/src/mlpro/oa/control/controlsystems/__init__.py @@ -0,0 +1 @@ +from mlpro.oa.control.controlsystems.basic import OAControlSystemBasic \ No newline at end of file diff --git a/src/mlpro/oa/control/controlsystems/basic.py b/src/mlpro/oa/control/controlsystems/basic.py new file mode 100644 index 000000000..3da711bec --- /dev/null +++ b/src/mlpro/oa/control/controlsystems/basic.py @@ -0,0 +1,55 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.oa.control.control_scenarios +## -- Module : basic.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-10-07 0.1.0 DA Initial implementation +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.1.0 (2024-10-07) + +This module provides a simplified container class for a basic synchronous oa control scenario containing + +- a controller +- an oa control system +- an optional action cumulator + +""" + + +from mlpro.bf.various import Log +from mlpro.bf.control import ControlPanel, ControlWorkflow +from mlpro.bf.control.controlsystems import ControlSystemBasic +from mlpro.oa.control import OAControlSystem + + + +## ------------------------------------------------------------------------------------------------- +## ------------------------------------------------------------------------------------------------- +class OAControlSystemBasic: # (OAControlSystem, ControlSystemBasic): + """ + Simplified container class for a basic synchronous control system containing + + - a controller + - an oa control system + - an optional action incrementer + """ + + C_TYPE = 'OA Control Cycle Basic' + C_NAME = '' + +## ------------------------------------------------------------------------------------------------- + def _setup(self, p_mode, p_visualize: bool, p_logging) -> ControlWorkflow: + + control_workflow = ControlSystemBasic._setup(self, p_mode, p_visualize, p_logging) + + try: + control_workflow.get_control_panel().register_event_handler( p_event_id = ControlPanel.C_EVENT_ID_SETPOINT_CHG, + p_event_handler = self._controller.hdl_setpoint_changed ) + except: + self.log(Log.C_LOG_TYPE_W, 'Controller is not online adaptive') + + return control_workflow \ No newline at end of file diff --git a/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/clusterbased/basics.py b/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/clusterbased/basics.py index 4ba8cb90c..75c75f3b5 100644 --- a/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/clusterbased/basics.py +++ b/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/clusterbased/basics.py @@ -11,19 +11,24 @@ ## -- 2024-02-25 1.1.0 SK Visualisation update ## -- 2024-04-10 1.2.0 DA/SK Refactoring ## -- 2024-05-28 1.3.0 SK Refactoring +## -- 2024-12-11 1.3.1 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.3.0 (2024-05-28) +Ver. 1.3.1 (2024-12-11) This module provides a template class for cluster-based anomalies to be used in anomaly detection algorithms. """ +try: + from matplotlib.figure import Figure +except: + class Figure : pass + from mlpro.oa.streams.basics import Instance from mlpro.oa.streams.tasks.anomalydetectors.anomalies.basics import Anomaly from mlpro.bf.mt import Figure, PlotSettings from mlpro.oa.streams.tasks.clusteranalyzers.clusters.basics import Cluster -from matplotlib.figure import Figure diff --git a/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/group.py b/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/group.py index 55df7e1da..903f76a2d 100644 --- a/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/group.py +++ b/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/group.py @@ -13,20 +13,27 @@ ## -- 2024-05-07 1.2.1 SK Bug fix on groupanomaly visualisation ## -- 2024-05-22 1.3.0 SK Refactoring ## -- 2024-11-27 1.3.1 DA Bugfix in method GroupAnomaly.__init__() +## -- 2024-12-11 1.3.2 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.3.1 (2024-11-27) +Ver. 1.3.2 (2024-12-11) This module provides a template class for group anomaly to be used in anomaly detection algorithms. """ +try: + from matplotlib.figure import Figure + from matplotlib.text import Text + from matplotlib import patches +except: + class Figure : pass + class Text : pass + class patches : pass + from mlpro.bf.plot import PlotSettings from mlpro.bf.streams import Instance from mlpro.oa.streams.tasks.anomalydetectors.anomalies.basics import Anomaly -from matplotlib.figure import Figure -from matplotlib.text import Text -from matplotlib import patches diff --git a/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/point.py b/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/point.py index 104fef601..23bf65507 100644 --- a/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/point.py +++ b/src/mlpro/oa/streams/tasks/anomalydetectors/anomalies/point.py @@ -8,20 +8,26 @@ ## -- 2023-06-08 0.0.0 SK Creation ## -- 2023-09-12 1.0.0 SK Release ## -- 2024-04-10 1.2.0 DA/SK Refactoring +## -- 2024-12-11 1.2.1 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.2.0 (2024-04-10) +Ver. 1.2.1 (2024-12-11) This module provides a template class for point anomaly event to be used in anomaly detection algorithms. """ +try: + from matplotlib.figure import Figure + from matplotlib.text import Text +except: + class Figure : pass + class Text : pass + from mlpro.bf.plot import PlotSettings from mlpro.bf.streams import Instance from mlpro.oa.streams.tasks.anomalydetectors.anomalies.basics import Anomaly -from matplotlib.figure import Figure -from matplotlib.text import Text diff --git a/src/mlpro/oa/streams/tasks/anomalydetectors/basics.py b/src/mlpro/oa/streams/tasks/anomalydetectors/basics.py index 0fe6cbb72..352c1a244 100644 --- a/src/mlpro/oa/streams/tasks/anomalydetectors/basics.py +++ b/src/mlpro/oa/streams/tasks/anomalydetectors/basics.py @@ -14,19 +14,25 @@ ## -- forwarding of changes on ax limits ## -- 2024-05-22 1.4.0 SK Refactoring ## -- 2024-08-12 1.4.1 DA Correction in AnomalyDetector.update_plot() +## -- 2024-12-11 1.4.2 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.4.1 (2024-08-12) +Ver. 1.4.2 (2024-12-11) This module provides templates for anomaly detection to be used in the context of online adaptivity. """ from typing import List -from matplotlib.figure import Figure + +try: + from matplotlib.figure import Figure +except: + class Figure : pass + from mlpro.bf.math.properties import * from mlpro.bf.plot import PlotSettings -from mlpro.bf.streams import Instance, InstDict +from mlpro.bf.streams import InstDict from mlpro.bf.various import * from mlpro.bf.plot import * from mlpro.oa.streams import OAStreamTask diff --git a/src/mlpro/oa/streams/tasks/boundarydetector.py b/src/mlpro/oa/streams/tasks/boundarydetector.py index bbe8e1da4..1873d9d4e 100644 --- a/src/mlpro/oa/streams/tasks/boundarydetector.py +++ b/src/mlpro/oa/streams/tasks/boundarydetector.py @@ -35,10 +35,11 @@ ## -- 2024-10-29 1.5.0 DA - Refactoring ## -- - Pseudo-implementation of BoundaryDetector._adapt_reverse() ## -- 2024-11-05 1.5.1 DA Bugfix in method BoundaryDetector._upate_plot_nd() +## -- 2024-12-11 1.5.2 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.5.1 (2024-11-05) +Ver. 1.5.2 (2024-12-11) This module provides pool of boundary detector object further used in the context of online adaptivity. @@ -46,8 +47,11 @@ from itertools import repeat -import matplotlib.colors -from matplotlib.figure import Figure +try: + import matplotlib.colors + from matplotlib.figure import Figure +except: + class Figure : pass from mlpro.bf.various import Log from mlpro.bf.exceptions import ImplementationError diff --git a/src/mlpro/oa/streams/tasks/clusteranalyzers/basics.py b/src/mlpro/oa/streams/tasks/clusteranalyzers/basics.py index 9c10fcb37..e74325cc9 100644 --- a/src/mlpro/oa/streams/tasks/clusteranalyzers/basics.py +++ b/src/mlpro/oa/streams/tasks/clusteranalyzers/basics.py @@ -48,7 +48,11 @@ from typing import List, Tuple -from matplotlib.figure import Figure + +try: + from matplotlib.figure import Figure +except: + class Figure : pass from mlpro.bf.events import Event as MLProEvent from mlpro.bf.math.properties import * diff --git a/src/mlpro/oa/streams/tasks/clusteranalyzers/clusters/properties/centroid.py b/src/mlpro/oa/streams/tasks/clusteranalyzers/clusters/properties/centroid.py index 64323ebba..1a445e7fe 100644 --- a/src/mlpro/oa/streams/tasks/clusteranalyzers/clusters/properties/centroid.py +++ b/src/mlpro/oa/streams/tasks/clusteranalyzers/clusters/properties/centroid.py @@ -13,18 +13,26 @@ ## -- 2024-06-26 0.6.0 DA Refactoring ## -- 2024-07-13 0.7.0 DA Refactoring ## -- 2024-10-31 0.8.0 DA New parent class Crosshair +## -- 2024-12-11 0.8.1 DA Pseudo classes if matplotlib is not installed ## ------------------------------------------------------------------------------------------------- """ -Ver. 0.8.0 (2024-10-31) +Ver. 0.8.1 (2024-12-11) This module provides ... """ -from matplotlib.figure import Figure -from matplotlib.text import Text -from mpl_toolkits.mplot3d.art3d import Line3D, Text3D +try: + from matplotlib.figure import Figure + from matplotlib.text import Text + from mpl_toolkits.mplot3d.art3d import Line3D, Text3D +except: + class Figure : pass + class Text : pass + class Line3D : pass + class Text3D : pass + from mlpro.bf.mt import Figure, PlotSettings from mlpro.bf.various import * from mlpro.bf.plot import * diff --git a/src/mlpro/oa/streams/tasks/normalizers/minmax.py b/src/mlpro/oa/streams/tasks/normalizers/minmax.py index b1e0a6b18..d92b1a157 100644 --- a/src/mlpro/oa/streams/tasks/normalizers/minmax.py +++ b/src/mlpro/oa/streams/tasks/normalizers/minmax.py @@ -21,10 +21,11 @@ ## -- 2024-07-12 1.3.2 LSB Renormalization error ## -- 2024-10-29 1.3.3 DA - Refactoring of NormalizerMinMax._adapt_on_event() ## -- - Bugfix in NormalizerMinMax._update_plot_data_3d() +## -- 2024-12-16 1.4.0 DA Method NormalizerMinMax._run(): little code tuning ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.3.3 (2024-10-29) +Ver. 1.4.0 (2024-12-16) This module provides implementation for adaptive normalizers for MinMax Normalization. """ @@ -102,10 +103,13 @@ def _run(self, p_inst:InstDict): # Normalization of all incoming stream instances (order doesn't matter) for ids, (inst_type, inst) in p_inst.items(): + feature_data = inst.get_feature_data() + if self._param is None: - self.update_parameters( p_set = inst.get_feature_data().get_related_set() ) - normalized_element = self.normalize(inst.get_feature_data()) - inst.get_feature_data().set_values(normalized_element.get_values()) + self.update_parameters( p_set = feature_data.get_related_set() ) + + normalized_element = self.normalize(feature_data) + feature_data.set_values(normalized_element.get_values()) ## ------------------------------------------------------------------------------------------------- diff --git a/src/mlpro/oa/streams/tasks/normalizers/ztrans.py b/src/mlpro/oa/streams/tasks/normalizers/ztrans.py index 534704d26..8d974b88b 100644 --- a/src/mlpro/oa/streams/tasks/normalizers/ztrans.py +++ b/src/mlpro/oa/streams/tasks/normalizers/ztrans.py @@ -21,10 +21,12 @@ ## -- 2024-05-27 1.3.2 LSB Fixed Plotting ## -- 2024-05-28 1.3.3 LSB Fixing the plotting bugs ## -- 2024-05-28 1.3.4 LSB Fixed the denormalizing method when zero std +## -- 2024-12-05 1.3.5 DA Bugfix in method NormalizersZTransform._run() +## -- 2024-12-06 1.3.6 DA Fixes and optimization ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.3.4 (2024-05-27) +Ver. 1.3.6 (2024-12-06) This module provides implementation for adaptive normalizers for ZTransformation """ @@ -87,6 +89,7 @@ def __init__(self, p_name: str = None, self._plot_data_3d = None self._plot_data_nd = None + ## ------------------------------------------------------------------------------------------------- def _run(self, p_inst : InstDict): """ @@ -99,8 +102,11 @@ def _run(self, p_inst : InstDict): """ + if len( p_inst ) == 0: return + # 1 Online update of transformation parameters self.adapt( p_inst = p_inst ) + for inst_id, (inst_type, inst) in sorted(p_inst.items()): feature_data = inst.get_feature_data() @@ -108,10 +114,10 @@ def _run(self, p_inst : InstDict): if self._param is None: if inst_type == InstTypeNew: self.update_parameters( p_data_new = feature_data ) - self.update_plot_data() else: self.update_parameters( p_data_del = feature_data ) - self.update_plot_data() + + self.update_plot_data() feature_data.set_values( p_values = self.normalize(feature_data).get_values() ) @@ -211,9 +217,10 @@ def _update_plot_2d( self, self.update_plot_data() OAStreamTask._update_plot_2d( self, - p_settings = p_settings, - p_inst = p_inst, - **p_kwargs ) + p_settings = p_settings, + p_inst = p_inst, + **p_kwargs ) + ## ------------------------------------------------------------------------------------------------- def _update_plot_data_3d(self): @@ -240,6 +247,7 @@ def _update_plot_data_3d(self): self._parameters_updated = False + ## ------------------------------------------------------------------------------------------------- def _update_plot_3d( self, p_settings : PlotSettings, @@ -262,9 +270,9 @@ def _update_plot_3d( self, self._update_plot_data_3d() OAStreamTask._update_plot_3d( self, - p_settings = p_settings, - p_inst = p_inst, - **p_kwargs ) + p_settings = p_settings, + p_inst = p_inst, + **p_kwargs ) ## ------------------------------------------------------------------------------------------------- @@ -314,12 +322,14 @@ def _update_plot_nd( self, **p_kwargs ) - ## ------------------------------------------------------------------------------------------------- def update_plot_data(self): """ Updates the plot data. """ + + if not self.get_visualization(): return + try: self._update_plot_data_2d() except: diff --git a/src/setup.py b/src/setup.py index 32153c563..15dd7edcd 100644 --- a/src/setup.py +++ b/src/setup.py @@ -12,12 +12,13 @@ # Package dependencies for full installation extras_require={ "full": [ - "dill>=0.3.6", - "numpy>=1.23.5", - "torch>=1.13.1", - "matplotlib>=3.6.3", + "dill>=0.3.9", + "multiprocess>=0.70.17", + "numpy>=1.24.2", + "torch>=2.0.0", + "PySide6>=6.8.1", + "matplotlib>=3.10.0", "scipy>=1.8.1", - "multiprocess>=0.70.14", "pandas>=2.1.3" ], }, diff --git a/stepResponse.png b/stepResponse.png new file mode 100644 index 000000000..05f0e13aa Binary files /dev/null and b/stepResponse.png differ diff --git a/test/howtos/bf/control/howto_bf_control_001_basic_control_system.py b/test/howtos/bf/control/howto_bf_control_001_basic_control_system.py new file mode 100644 index 000000000..c7664e3b9 --- /dev/null +++ b/test/howtos/bf/control/howto_bf_control_001_basic_control_system.py @@ -0,0 +1,113 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control +## -- Module : howto_bf_control_001_basic_control_system.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-09-11 0.0.0 DA Creation +## -- 2024-10-09 0.1.0 DA Refactoring +## -- 2024-10-13 0.2.0 DA Refactoring +## -- 2024-11-09 1.0.0 DA Refactoring and documentation +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 1.0.0 (2024-11-09) + +Let's play fox and hunter! This howto demonstrates a basic control system consisting of a dummy +controller and a dummy controlled system. The dimensionality can be changed manually, where in 2d +and 3d mode, a spatial visualization is carried out. + +You will learn: + +1) How to set up a basic control system with a controller and a controlled system + +2) How to execute a control system and to change the setpoint from outside + +3) Different types of visualization depending on the dimensionality of the control system + +""" + +import numpy as np + +from mlpro.bf.various import Log +from mlpro.bf.plot import PlotSettings +from mlpro.bf.ops import Mode + +from mlpro.bf.systems.pool import Fox +from mlpro.bf.control.controllers import Hunter +from mlpro.bf.control.controlsystems import BasicControlSystem + + + + +# 1 Preparation of demo/unit test mode +if __name__ == '__main__': + # 1.1 Parameters for demo mode + cycle_limit = 500 + num_dim = 1 + logging = Log.C_LOG_ALL + visualize = True + step_rate = 2 + +else: + # 1.2 Parameters for internal unit test + cycle_limit = 5 + num_dim = 4 + logging = Log.C_LOG_NOTHING + visualize = False + step_rate = 1 + +if __name__ == '__main__': + i = input(f'\n\nDimensionality (press ENTER for {num_dim}): ') + if i != '': num_dim = int(i) + i = input(f'\n\nStep rate (visualization) (press ENTER for {step_rate}): ') + if i != '': step_rate = int(i) + + + +# 2 Setup the control system + +# 2.1 Controlled system +mycontrolledsystem = Fox( p_num_dim = num_dim, + p_visualize = visualize, + p_logging = logging ) + +# 2.2 Controller +mycontroller = Hunter( p_input_space = mycontrolledsystem.get_state_space(), + p_output_space = mycontrolledsystem.get_action_space(), + p_name = 'Hunter', + p_visualize = visualize, + p_logging = logging ) + +# 2.3 Basic control system +mycontrolsystem = BasicControlSystem( p_mode = Mode.C_MODE_SIM, + p_controller = mycontroller, + p_controlled_system = mycontrolledsystem, + p_name = 'Hunter and Fox', + p_ctrl_var_integration = False, + p_cycle_limit = cycle_limit, + p_visualize = visualize, + p_logging = logging ) + + + +# 3 Set initial setpoint values and reset the controlled system +mycontrolsystem.get_control_panels()[0][0].set_setpoint( p_values = np.zeros(shape=(num_dim)) ) +mycontrolledsystem.reset( p_seed = 1 ) + + + +# 4 Run some control cycles +if ( __name__ == '__main__' ) and visualize: + mycontrolsystem.init_plot( p_plot_settings=PlotSettings( p_view = PlotSettings.C_VIEW_ND, + p_view_autoselect = True, + p_step_rate = step_rate, + p_plot_horizon = 100 ) ) + input('\nPlease arrange all windows and press ENTER to start control processing...') + +mycontrolsystem.run() + +if __name__ == '__main__': + input('Press ENTER to exit...') + diff --git a/test/howtos/bf/control/howto_bf_control_002_basic_control_system_with_integrator.py b/test/howtos/bf/control/howto_bf_control_002_basic_control_system_with_integrator.py new file mode 100644 index 000000000..8106eb54e --- /dev/null +++ b/test/howtos/bf/control/howto_bf_control_002_basic_control_system_with_integrator.py @@ -0,0 +1,112 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control +## -- Module : howto_bf_control_002_basic_control_system_with_integrator.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-11-09 1.0.0 DA Creation +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 1.0.0 (2024-11-09) + +Let's play fox and hunter once again! This howto demonstrates a basic control system consisting of a dummy +controller and a dummy controlled system. Additionally an integrator is placed between controller and +controlled system. The dimensionality can be changed manually, where in 2d and 3d mode, a spatial +visualization is carried out. + +You will learn: + +1) How to set up a basic control system with a controller, a controlled system, and an integrator + +2) How to execute a control system and to change the setpoint from outside + +3) Different types of visualization depending on the dimensionality of the control system + +""" + + +import numpy as np + +from mlpro.bf.various import Log +from mlpro.bf.plot import PlotSettings +from mlpro.bf.ops import Mode + +from mlpro.bf.systems.pool import Fox +from mlpro.bf.control.controllers import Hunter +from mlpro.bf.control.controlsystems import BasicControlSystem + + + + +# 1 Preparation of demo/unit test mode +if __name__ == '__main__': + # 1.1 Parameters for demo mode + cycle_limit = 500 + num_dim = 1 + logging = Log.C_LOG_ALL + visualize = True + step_rate = 2 + +else: + # 1.2 Parameters for internal unit test + cycle_limit = 5 + num_dim = 4 + logging = Log.C_LOG_NOTHING + visualize = False + step_rate = 1 + +if __name__ == '__main__': + i = input(f'\n\nDimensionality (press ENTER for {num_dim}): ') + if i != '': num_dim = int(i) + i = input(f'\n\nStep rate (visualization) (press ENTER for {step_rate}): ') + if i != '': step_rate = int(i) + + + +# 2 Setup the control system + +# 2.1 Controlled system +mycontrolledsystem = Fox( p_num_dim = num_dim, + p_visualize = visualize, + p_logging = logging ) + +# 2.2 Controller +mycontroller = Hunter( p_input_space = mycontrolledsystem.get_state_space(), + p_output_space = mycontrolledsystem.get_action_space(), + p_name = 'Hunter', + p_visualize = visualize, + p_logging = logging ) + +# 2.3 Basic control system +mycontrolsystem = BasicControlSystem( p_mode = Mode.C_MODE_SIM, + p_controller = mycontroller, + p_controlled_system = mycontrolledsystem, + p_name = 'Hunter and Fox (+I)', + p_ctrl_var_integration = True, + p_cycle_limit = cycle_limit, + p_visualize = visualize, + p_logging = logging ) + + + +# 3 Set initial setpoint values and reset the controlled system +mycontrolsystem.get_control_panels()[0][0].set_setpoint( p_values = np.zeros(shape=(num_dim)) ) +mycontrolledsystem.reset( p_seed = 1 ) + + + +# 4 Run some control cycles +if ( __name__ == '__main__' ) and visualize: + mycontrolsystem.init_plot( p_plot_settings=PlotSettings( p_view = PlotSettings.C_VIEW_ND, + p_view_autoselect = True, + p_step_rate = step_rate, + p_plot_horizon = 100 ) ) + input('\nPlease arrange all windows and press ENTER to start control processing...') + +mycontrolsystem.run() + +if __name__ == '__main__': + input('Press ENTER to exit...') + diff --git a/test/howtos/bf/control/howto_bf_control_003_cascade_control_system.py b/test/howtos/bf/control/howto_bf_control_003_cascade_control_system.py new file mode 100644 index 000000000..3c3947378 --- /dev/null +++ b/test/howtos/bf/control/howto_bf_control_003_cascade_control_system.py @@ -0,0 +1,126 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.bf.control +## -- Module : howto_bf_control_003_cascade_control_system.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-11-09 1.0.0 DA Creation +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 1.0.0 (2024-11-09) + +Let's play fox and hunter once again but this time as a cascade control system! This howto demonstrates +a cascade control system consisting of two cascades with a dummy controller and a dummy controlled system. +The dimensionality can be changed manually, where in 2d and 3d mode, a spatial visualization is carried out. + +You will learn: + +1) How to set up a cascade control system with cascades consisting of a controller and a controlled system + +2) How to execute a cascade control system and to change the setpoint from outside + +3) Different types of visualization depending on the dimensionality of the control system + +""" + +import numpy as np +from datetime import timedelta + +from mlpro.bf.various import Log +from mlpro.bf.plot import PlotSettings +from mlpro.bf.ops import Mode + +from mlpro.bf.systems.pool import Fox +from mlpro.bf.control.controllers import Hunter +from mlpro.bf.control.controlsystems import CascadeControlSystem + + + + +# 1 Preparation of demo/unit test mode +if __name__ == '__main__': + # 1.1 Parameters for demo mode + cycle_limit = 500 + num_dim = 1 + logging = Log.C_LOG_ALL + visualize = True + step_rate = 2 + +else: + # 1.2 Parameters for internal unit test + cycle_limit = 5 + num_dim = 4 + logging = Log.C_LOG_NOTHING + visualize = False + step_rate = 1 + +if __name__ == '__main__': + i = input(f'\n\nDimensionality (press ENTER for {num_dim}): ') + if i != '': num_dim = int(i) + i = input(f'\nStep rate (visualization) (press ENTER for {step_rate}): ') + if i != '': step_rate = int(i) + + + +# 2 Setup the control system + +# 2.1 Controller and controlled system of the outer cascade +my_ctrl_sys_1 = Fox( p_num_dim = num_dim, + p_latency = timedelta( seconds = 5 ), + p_visualize = visualize, + p_logging = logging ) +my_ctrl_sys_1.reset( p_seed = 1 ) + +my_ctrl_1 = Hunter( p_input_space = my_ctrl_sys_1.get_state_space(), + p_output_space = my_ctrl_sys_1.get_action_space(), + p_name = 'Hunter1', + p_visualize = visualize, + p_logging = logging ) + + +# 2.2 Controller and controlled system of the inner cascade +my_ctrl_sys_2 = Fox( p_num_dim = num_dim, + p_latency = timedelta( seconds = 1 ), + p_visualize = visualize, + p_logging = logging ) +my_ctrl_sys_2.reset( p_seed = 2 ) + +my_ctrl_2 = Hunter( p_input_space = my_ctrl_sys_1.get_state_space(), + p_output_space = my_ctrl_sys_1.get_action_space(), + p_name = 'Hunter2', + p_visualize = visualize, + p_logging = logging ) + + +# 2.3 Cascade control system +mycontrolsystem = CascadeControlSystem( p_mode = Mode.C_MODE_SIM, + p_controllers = [ my_ctrl_1, my_ctrl_2 ], + p_controlled_systems = [ my_ctrl_sys_1, my_ctrl_sys_2 ], + p_name = 'Hunter and Fox', + p_cycle_limit = cycle_limit, + p_visualize = visualize, + p_logging = logging ) + + + +# 3 Set initial setpoint values for all control workflows (=cascades) of the control system +for panel_entry in mycontrolsystem.get_control_panels(): + panel_entry[0].set_setpoint( p_values = np.zeros(shape=(num_dim)) ) + + + +# 4 Run some control cycles +if ( __name__ == '__main__' ) and visualize: + mycontrolsystem.init_plot( p_plot_settings=PlotSettings( p_view = PlotSettings.C_VIEW_ND, + p_view_autoselect = True, + p_step_rate = step_rate, + p_plot_horizon = 100 ) ) + input('\nPlease arrange all windows and press ENTER to start control processing...') + +mycontrolsystem.run() + +if __name__ == '__main__': + input('Press ENTER to exit...') + diff --git a/test/howtos/bf/howto_bf_mt_002_tasks_and_workflows.py b/test/howtos/bf/howto_bf_mt_002_tasks_and_workflows.py index ae4671118..bff6c1e9f 100644 --- a/test/howtos/bf/howto_bf_mt_002_tasks_and_workflows.py +++ b/test/howtos/bf/howto_bf_mt_002_tasks_and_workflows.py @@ -79,6 +79,8 @@ def __init__( self, ## ------------------------------------------------------------------------------------------------- def _run(self, **p_kwargs): + print('Hello World') + tid = self.get_tid() # 1 Dummy implementation to simulate a busy sub-task diff --git a/test/howtos/bf/howto_bf_streams_004_native_stream_DoubleSpiral2D.py b/test/howtos/bf/howto_bf_streams_004_native_stream_DoubleSpiral2D.py index 932c2a26f..cc1df1e2a 100644 --- a/test/howtos/bf/howto_bf_streams_004_native_stream_DoubleSpiral2D.py +++ b/test/howtos/bf/howto_bf_streams_004_native_stream_DoubleSpiral2D.py @@ -95,4 +95,4 @@ def _setup(self, p_mode, p_visualize:bool, p_logging): myscenario.run() if __name__ == '__main__': - input('Press ENTER to exit...') \ No newline at end of file + input('Press ENTER to exit...') diff --git a/test/howtos/bf/howto_bf_streams_101_basics.py b/test/howtos/bf/howto_bf_streams_101_basics.py index 277f05bef..21e87671c 100644 --- a/test/howtos/bf/howto_bf_streams_101_basics.py +++ b/test/howtos/bf/howto_bf_streams_101_basics.py @@ -47,7 +47,7 @@ class MyTask (StreamTask): ## ------------------------------------------------------------------------------------------------- def _run(self, p_inst : InstDict): - pass + print('Hello World') @@ -111,6 +111,7 @@ def _setup(self, p_mode, p_visualize: bool, p_logging): if __name__ == '__main__': myscenario.init_plot() + myscenario.run() input('Press ENTER to start stream processing...') myscenario.run() diff --git a/test/howtos/bf/howto_bf_systems_001_demonstrating_native_systems.py b/test/howtos/bf/howto_bf_systems_001_demonstrating_native_systems.py index 99e487835..3135325fc 100644 --- a/test/howtos/bf/howto_bf_systems_001_demonstrating_native_systems.py +++ b/test/howtos/bf/howto_bf_systems_001_demonstrating_native_systems.py @@ -33,7 +33,7 @@ if __name__=='__main__': logging = Log.C_LOG_ALL visualize = True - cycle_limit = 10 + cycle_limit = 1000 else: logging = Log.C_LOG_NOTHING visualize = False diff --git a/test/howtos/oa/control/howto_oa_control_001_basic_control_loop.py.off b/test/howtos/oa/control/howto_oa_control_001_basic_control_loop.py.off new file mode 100644 index 000000000..7fab7f6f4 --- /dev/null +++ b/test/howtos/oa/control/howto_oa_control_001_basic_control_loop.py.off @@ -0,0 +1,102 @@ +## ------------------------------------------------------------------------------------------------- +## -- Project : MLPro - The integrative middleware framework for standardized machine learning +## -- Package : mlpro.oa.control +## -- Module : howto_oa_control_001_basic_control_loop.py +## ------------------------------------------------------------------------------------------------- +## -- History : +## -- yyyy-mm-dd Ver. Auth. Description +## -- 2024-10-13 0.0.0 DA Creation based on howto_bf_control_001_basic_control_loop.py +## ------------------------------------------------------------------------------------------------- + +""" +Ver. 0.0.0 (2024-10-13) + +Let's play fox and hunter! + +You will learn: + +1) How to ... + +2) How to ... + +3) How to ... + +""" + +import numpy as np + +from mlpro.bf.various import Log +from mlpro.bf.plot import PlotSettings +from mlpro.bf.ops import Mode + +from mlpro.oa.control import OAControlledSystem +from mlpro.oa.control.controlsystems import OAControlSystemBasic +from mlpro.bf.systems.pool import Fox +from mlpro.bf.control.controllers import Hunter + + + + + + +# 1 Preparation of demo/unit test mode +if __name__ == '__main__': + # 1.1 Parameters for demo mode + cycle_limit = 500 + num_dim = 2 + logging = Log.C_LOG_ALL + visualize = True + step_rate = 1 + +else: + # 1.2 Parameters for internal unit test + cycle_limit = 5 + num_dim = 2 + logging = Log.C_LOG_NOTHING + visualize = False + step_rate = 1 + + +# 2 Init control scenario + +# 2.1 Control system +mycontrolledsystem = OAControlledSystem( p_system = Fox( p_num_dim = num_dim ), + p_name = '"Fox"', + p_visualize = visualize, + p_logging = logging ) + +# 2.2 Controller +mycontroller = Hunter( p_input_space = mycontrolledsystem.system.get_state_space(), + p_output_space = mycontrolledsystem.system.get_action_space(), + p_name = '"Hunter"', + p_visualize = visualize, + p_logging = logging ) + +# 2.3 Basic control system +mycontrolsystem = OAControlSystemBasic( p_mode = Mode.C_MODE_SIM, + p_controller = mycontroller, + p_controlled_system = mycontrolledsystem, + p_ctrl_var_integration = False, + p_cycle_limit = cycle_limit, + p_visualize = visualize, + p_logging = logging ) + + +# 3 Set initial setpoint values and reset the controlled system +mycontrolsystem.get_control_panel().set_setpoint( p_values = np.zeros(shape=(num_dim)) ) +mycontrolledsystem.system.reset( p_seed = 1 ) + + +# 4 Run some control cycles +if __name__ == '__main__': + mycontrolsystem.init_plot( p_plot_settings=PlotSettings( p_view = PlotSettings.C_VIEW_ND, + p_view_autoselect = False, + p_step_rate = step_rate, + p_plot_horizon = 100 ) ) + input('\nPlease arrange all windows and press ENTER to start control processing...') + +mycontrolsystem.run() + +if __name__ == '__main__': + input('Press ENTER to exit...') + diff --git a/test/howtos/oa/howto_oa_streams_pp_121_complex_preprocessing.py b/test/howtos/oa/howto_oa_streams_pp_121_complex_preprocessing.py index fc94feb54..01ba70adf 100644 --- a/test/howtos/oa/howto_oa_streams_pp_121_complex_preprocessing.py +++ b/test/howtos/oa/howto_oa_streams_pp_121_complex_preprocessing.py @@ -9,10 +9,11 @@ ## -- 2024-05-02 1.1.0 DA Review/minor adjustments ## -- 2024-05-12 1.2.0 DA Event-based adaption Window->BoundaryDetector added ## -- 2024-07-19 1.2.1 SY Refactoring due to method Deriver._prepare_derivation() +## -- 2024-12-09 1.2.2 DA Bugfix ## ------------------------------------------------------------------------------------------------- """ -Ver. 1.2.1 (2024-07-19) +Ver. 1.2.2 (2024-12-09) This howto shows an example of complex preprocessing with parallel tasks. It addresses several problems like feature rearanging, numerical feature derivation, data windowing, and self-adapting @@ -149,6 +150,7 @@ def _setup(self, p_mode, p_ada: bool, p_visualize: bool, p_logging): # 2.6 Setup Z Transform-Normalizer in Parallel task6_norm_ztrans = NormalizerZTransform( p_name = '6 - Normalizer Z-Trans', p_ada = p_ada, + p_duplicate_data = True, # Important!! Avoids normalization of original instances p_visualize = p_visualize, p_logging = p_logging ) @@ -159,6 +161,7 @@ def _setup(self, p_mode, p_ada: bool, p_visualize: bool, p_logging): # 2.7 Setup MinMax-Normalizer task7_norm_minmax = NormalizerMinMax( p_name = '7 - Normalizer MinMax', p_ada = p_ada, + p_duplicate_data = True, # Important!! Avoids normalization of original instances p_visualize = p_visualize, p_logging = p_logging )