OpenCtrl 1.0.16
A library for Optimal Control and System Dynamics
Requires Python: >=3.8

![OpenCtrl](https://github.com/shoham-roy31/OpenCtrl/blob/main/OpenCtrlLogo.png?raw=true)
OpenCtrl is an open source library for Control System Dynamics and Optimal Control Alogrithms.

The library facilitates:
 - System Objects
 - Multi-Horizon Optimization
 - Control Algorithms
 - Real-time visualization

## How to install
Run the flowing code using pip:
`pip install OpenCtrl`

For specific version *X* use:
`pip install OpenCtrl==X`

## Quick Guide

Version Check:
`python -c "import OpenCtrl print(OpenCtrl.__version__)"`

### Create a Linear System
To create Linear System:
```
import numpy as np
from OpenCtrl.SystemDynamicExample import LinearSystem
''' define your input space parameters '''
input_space = { '1' : {'discrete' : [0,512]},
                '2' : {'random' : ['uniform',-128,127]},
                '3' : {'continuous' : [-10,10]},
                '4' : {'continuous' : [512,1024]},
              }
''' Instantiate Linear System Object '''
linear_sys = LinearSystem(sys_dim = 4,
                          input_dim = 4,
                          input_space = input_space,
                          disturbance_type = 'uniform',
                          disturbance_scale = [-255, 255],
                          disturbance_params = None
                          )
''' Info : sys_dim equals input_dim since we considered we can control 4 degrees of freedom, if the case is different like we have 1 degree of freedom then input_space can only have { '1' : {'discrete' : [0,512] } } only. For disturbance_params some degree of freedom can have no impact by disturbance if only 1 degree is impacted the disturbance_params will be 1. '''
u_o = np.array([np.random.randint(0,512), 
              np.random.uniform(-128,127), 
              np.random.uniform(-10,10), 
              np.random.uniform(512,1024)]
            )
linear_sys.step(u_o)
print(linear_sys.x)
```
### Create an Optimizer
To create an optimizer object to optimize the system:
```
from OpenCtrl.optim import VanillaOptim
''' Let's create 3 different types of Optimizers '''
cost_func = 'quadratic'
horizon = 5
max_iteration = 50
tolerance_step = 10

def get_predictions(horizon):
    return [u_o for _ in range(horizon)]

def print_metrics(type,cost,u):
    print(f"For optimizer type {type} \nCost: {cost}\nOptimal Input : {u}")

''' Random Search '''
optimizer_random = VanillaOptim( system = linear_sys,
                                 horizon = horizon,
                                 cost_function = cost_func,
                                 optimizer_type = 'random',
                                 max_iterations = max_iteration,
                                 tolerance_step = tolerance_step
                                )
cost_random, u_random = optimizer_random.optimize( preds = get_predictions(horizon),
                                                    verbose = True
                                                  )
print_metrics('random',cost_random,u_random)     

''' Gradient Descent '''
optimizer_gradient = VanillaOptim( system = linear_sys,
                                   horizon = 5,
                                   cost_function = 'quadratic',
                                   optimizer_type = 'gradient',
                                   alpha = 1e-3,
                                   max_iterations = max_iteration,
                                   tolerance_step = tolerance_step
                                )
cost_gradient,u_gradient = optimizer_gradient.optimize(preds = get_predictions(horizon),
verbose = True )
print_metrics('gradient',cost_gradient,u_gradient)
''' Genetic Algorithm '''
optimizer_genetic = VanillaOptim( system = linear_sys,
                                  horiozn = horizon,
                                  cost_function = cost_func,
                                  optimizer_type = 'genetic',
                                  population_size = 1000,
                                  cross_over_rate = 0.42,
                                  mutation_rate = 0.23,
                                  cut_off_rate = 0.5,
                                  max_iterations = max_iteration,
                                  tolerance_step = tolerance_step
                                  )
cost_genetic,u_genetic = optimizer_genetic.optimize(pred = get_predictios(horizon),
verbose = True)
print_metrics('genetic',cost_genetic,u_genetic)
```
![OptimPlot](https://github.com/shoham-roy31/OpenCtrl/blob/main/optim_plot.gif?raw=true)
### Use Control Algorithm
To use a control algorith to tune the system:
```
from OpenCtrl.controls import LAC
control_horizon = 10
lac = LAC(system = linear_sys,
          optimizer = optimizer_gradient,
          horizon = horizon,
          nominal_disturbance = 'baseline'
          )
''' nominal_disturbance is a conventional method of predicting disturbance '''
for _ in range(control_horizon):
    preds = get_predictions(horizon)
    cost,u = lac.tune(preds = preds,
                      verbose = True,
                      base_line= _
                     )
    print_metrics('gradient',cost, u)

```
![ControlMetrics](https://github.com/shoham-roy31/OpenCtrl/blob/main/control_metrics.gif?raw=true)
## To Create Extension of Algos and System Dynamics
*** Developer Documentation in Progress ***
## To Contribute 
You can check out the dev repo of OpenCtrl:
[OpenCtrl](https://github.com/shoham-roy31/OpenCtrl)