Overview

Dionysos aims to design a controller for a system $\mathcal{S}$ so that the closed-loop system satisfies the specification $\Sigma$ where:

So a control problem $(\mathcal{S},\Sigma)$ can be solved by $\mathcal{O}$ via the JuMP interface, with Dionysos inheriting JuMP's powerful and practical optimization framework.

Overview of the code structure

Description of the core of the Dionysos.jl package, the src folder:

SubfolderDescription
utilsContains useful functions, data structures, classic search algorithms, file management, ...
domainContains structures defining the domain of a system
systemContains a description of specific systems
problemContains control problems that can be solved by Dionysos solvers
symbolicContains the data structures needed to encode the abstractions
optimContains the solvers

Systems

The system types supported in Dionysos.jl are:

where $\mathcal{X}$ is the state constraint, $\mathcal{U}$ is the input constraint and $\mathcal{W}$ is the noise constraint.

Problems

The problem types supported in Dionysos.jl are:

TypeDescription
Reach-avoid optimal control problem$(\mathcal{S},\mathcal{I},\mathcal{T},\mathcal{V},\mathcal{C}, T)$, where $\mathcal{S}$ is the system, $\mathcal{I}$ is the initial set, $\mathcal{T}$ is the target set, $\mathcal{V}:\mathcal{X}\rightarrow \mathbb{R}$ is the cost state function, $\mathcal{C}:\mathcal{X}\times \mathcal{U}\rightarrow \mathbb{R}$ is the transition cost function, $T$ is the time limit to satisfy the specification.
Safety control problem$(\mathcal{S},\mathcal{I},\mathcal{S}, T)$, where $\mathcal{S}$ is the system, $\mathcal{I}$ is the initial set, $\mathcal{S}$ is the safe set, $T$ is the time during which safety must be ensured.

Extensions for linear temporal logic (LTL) specifications are currently being implemented.

Solvers

The following tables summarize the different solvers.

Abstraction-based solver types implemented in Dionysos.jl:

TypeFull vs partial discretizationPartition vs CoverShapeLocal controllerAbstractionSystemReference
Uniform grid abstractionFullPartitionHyperrectanglePiece-wise constantNon-determinisiticContinuous-timeSCOTS: A Tool for the Synthesis of Symbolic Controllers
Lazy abstractionPartialPartitionHyperrectanglePiece-wise constantNon-determinisiticContinuous-timeAlternating simulation on hierarchical abstractions
Hierarchical abstractionPartialPartitionHyperrectanglePiece-wise constantNon-determinisiticContinuous-timeAlternating simulation on hierarchical abstractions
Ellipsoid abstractionFullCoverEllipsoidPiece-wise affineDeterminisiticDiscrete-time affineState-feedback Abstractions for Optimal Control of Piecewise-affine Systems
Lazy ellipsoid abstractionPartialCoverEllipsoidPiece-wise affineDeterminisiticDiscrete-time non-linearNot yet published

Non abstraction-based solver types implemented in Dionysos.jl:

TypeDescriptionReference
Bemporad MorariOptimal control of hybrid systems via a predictive control scheme using mixed integer quadratic programming (MIQP) online optimization procedures.Control of systems integrating logic, dynamics, and constraints
BranchAndBoundOptimal control of hybrid systems via a predictive control scheme combining a branch and bound algorithm that can refine Q-functions using Lagrangian duality.Abstraction-based branch and bound approach to Q-learning for hybrid optimal control

Solver interface

Each solver is defined by a module which must implement the abstract type AbstractOptimizer and the Optimize! function. For example, for the UniformGridAbstraction solver, this structure and function are defined as follows

using JuMP

mutable struct Optimizer{T} <: MOI.AbstractOptimizer
    concrete_problem::Union{Nothing, PR.ProblemType}
    abstract_problem::Union{Nothing, PR.OptimalControlProblem, PR.SafetyProblem}
    abstract_system::Union{Nothing, SY.SymbolicModelList}
    abstract_controller::Union{Nothing, UT.SortedTupleSet{2, NTuple{2, Int}}}
    concrete_controller::Any
    state_grid::Union{Nothing, DO.Grid}
    input_grid::Union{Nothing, DO.Grid}
    function Optimizer{T}() where {T}
        return new{T}(nothing, nothing, nothing, nothing, nothing, nothing, nothing)
    end
end

and

function MOI.optimize!(optimizer::Optimizer)
    # Build the abstraction
    abstract_system = build_abstraction(
        optimizer.concrete_problem.system,
        optimizer.state_grid,
        optimizer.input_grid,
    )
    optimizer.abstract_system = abstract_system
    # Build the abstract problem
    abstract_problem = build_abstract_problem(optimizer.concrete_problem, abstract_system)
    optimizer.abstract_problem = abstract_problem
    # Solve the abstract problem
    abstract_controller = solve_abstract_problem(abstract_problem)
    optimizer.abstract_controller = abstract_controller
    # Solve the concrete problem
    optimizer.concrete_controller =
        solve_concrete_problem(abstract_system, abstract_controller)
    return
end

Running an example

In this section, we outline how to define and solve a control problem with Dionsysos. For an executable version of this example, see Solvers: Path planning problem in the documentation.

Define a control problem, i.e., the system and the specification of the desired closed loop behaviour. To do this, you can define new ones yourself or directly load an existing benchmark, for example

concrete_problem = PathPlanning.problem(; simple = true, approx_mode = PathPlanning.GROWTH);
concrete_system = concrete_problem.system;

Choose the solver you wish to use

using JuMP
optimizer = MOI.instantiate(AB.UniformGridAbstraction.Optimizer)

Define the solver's meta-parameters

x0 = SVector(0.0, 0.0, 0.0);
hx = SVector(0.2, 0.2, 0.2);
state_grid = DO.GridFree(x0, hx);
u0 = SVector(0.0, 0.0);
hu = SVector(0.3, 0.3);
input_grid = DO.GridFree(u0, hu);

Set the solver's meta-parameters

MOI.set(optimizer, MOI.RawOptimizerAttribute("concrete_problem"), concrete_problem)
MOI.set(optimizer, MOI.RawOptimizerAttribute("state_grid"), state_grid)
MOI.set(optimizer, MOI.RawOptimizerAttribute("input_grid"), input_grid)

Solve the control problem

MOI.optimize!(optimizer)

Get the results

abstract_system = MOI.get(optimizer, MOI.RawOptimizerAttribute("abstract_system"))
abstract_problem = MOI.get(optimizer, MOI.RawOptimizerAttribute("abstract_problem"))
abstract_controller = MOI.get(optimizer, MOI.RawOptimizerAttribute("abstract_controller"))
concrete_controller = MOI.get(optimizer, MOI.RawOptimizerAttribute("concrete_controller"))

In Dionysos, all the structures that could be relevant to plot (such as trajectories, state-space discretization, specifications, obstacles, etc.) have an associated @recipe function, which makes it very easy to plot all the results using the single common plot function of Plots.jl. For example

using Plots

plot!(concrete_system.X; color = :yellow, opacity = 0.5);
plot!(abstract_system.Xdom; color = :blue, opacity = 0.5);
plot!(concrete_problem.initial_set; color = :green, opacity = 0.2);
plot!(concrete_problem.target_set; dims = [1, 2], color = :red, opacity = 0.2);
plot!(control_trajectory; ms = 0.5)