# Optimal control example taken from:
# https://github.com/Pyomo/pyomo/blob/master/examples/dae/Optimal_Control.py
# https://github.com/Pyomo/pyomo/blob/master/examples/dae/run_Optimal_Control.py
#
#
# min X2(tf)
# s.t. X1_dot = u
# X2_dot = X1^2 + u^2
# tf = 1
# X1(0) = 1, X2(0) = 0
#
# set-up python version to use
reticulate::use_python("/opt/anaconda3/bin/python")
library(reticulate)
# import pyomo
pyo = import("pyomo.environ", convert = FALSE)
pyde = import("pyomo.dae", convert = FALSE)
bi = import_builtins()
# had signal handling issues when using solver. Turned
# off signal handling based on this discussion thread
# https://github.com/PyUtilib/pyutilib/issues/31
pyulib = import("pyutilib.subprocess.GlobalData")
pyulib$DEFINE_SIGNAL_HANDLERS_DEFAULT = FALSE
# printing model to a file, reading it back into R to print in R markdown
mdl_print = function(m) {
printstr = "
with open('tmp_model.txt', 'w') as f:
r.mdl.pprint(ostream=f)
f.close()
"
printstr = gsub("mdl", m, printstr)
py_run_string(printstr)
printstr_R = paste(readLines("tmp_model.txt"), collapse = "\n")
return(printstr_R)
}
# get operators
source("operators.R")
# load other libraries
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(ggplot2)
# create model object
M = pyo$ConcreteModel()
# set time variable
M$t = pyde$ContinuousSet(bounds = tuple(0, 1))
# set state variables x1, x2
M$x1 = pyo$Var(M$t, bounds = tuple(0, 1))
M$x2 = pyo$Var(M$t, bounds = tuple(0, 1))
# set control variable u
M$u = pyo$Var(M$t, initialize = 0)
# set variables denoting derivatives of state variables x1dot, x2dot
M$x1dot = pyde$DerivativeVar(M$x1, wrt = M$t)
M$x2dot = pyde$DerivativeVar(M$x2, wrt = M$t)
# objective: minimize x2 at tf = 1
M$obj = pyo$Objective(expr = M$x2[1], sense = pyo$minimize)
# define constraint x1dot = u
x1dot_rule = function(m, i) {
if (i == 0) {
return(py_get_attr(pyo$Constraint, "Skip"))
} else {
expr = m$x1dot[i] == m$u[i]
}
return(expr)
}
M$x1dot_cons = pyo$Constraint(M$t, rule = x1dot_rule)
# define constraint x2dot = x1^2 + u^2
x2dot_rule = function(m, i) {
if (i == 0) {
return(py_get_attr(pyo$Constraint, "Skip"))
} else {
expr = m$x2dot[i] == m$x1[i]^2 + m$u[i]^2
}
return(expr)
}
M$x2dot_cons = pyo$Constraint(M$t, rule = x2dot_rule)
# set initial conditions: x1(0) = 1, x2(0) = 0
M$init_x1 = pyo$Constraint(expr = M$x1[0] == 1.0)
M$init_x2 = pyo$Constraint(expr = M$x2[0] == 0.0)
# apply collocation
discretizer = pyo$TransformationFactory('dae.collocation')
discretizer$apply_to(M,nfe=20L,ncp=3L,scheme='LAGRANGE-RADAU')
## None
discretizer$reduce_collocation_points(M,var=M$u,ncp=1L,contset=M$t)
## unknown
solver = pyo$SolverFactory("ipopt")
results = solver$solve(M, logfile = "tmp.log")
cat(paste(readLines("tmp.log"), collapse = "\n"))
## Solver command line: ['/opt/anaconda3/bin/ipopt', '/var/folders/h0/0h0h0yrs6f76syrmv1_q13mw0000gn/T/tmpskotnynz.pyomo.nl', '-AMPL']
##
## Ipopt 3.12.12:
##
## ******************************************************************************
## This program contains Ipopt, a library for large-scale nonlinear optimization.
## Ipopt is released as open source code under the Eclipse Public License (EPL).
## For more information visit http://projects.coin-or.org/Ipopt
## ******************************************************************************
##
## This is Ipopt version 3.12.12, running with linear solver mumps.
## NOTE: Other linear solvers might be more efficient (see Ipopt documentation).
##
## Number of nonzeros in equality constraint Jacobian...: 982
## Number of nonzeros in inequality constraint Jacobian.: 0
## Number of nonzeros in Lagrangian Hessian.............: 120
##
## Total number of variables............................: 302
## variables with only lower bounds: 0
## variables with lower and upper bounds: 122
## variables with only upper bounds: 0
## Total number of equality constraints.................: 282
## Total number of inequality constraints...............: 0
## inequality constraints with only lower bounds: 0
## inequality constraints with lower and upper bounds: 0
## inequality constraints with only upper bounds: 0
##
## iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
## 0 9.9999900e-03 9.90e-01 1.39e-04 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0
## 1 5.8905427e-01 1.09e+01 1.46e+02 -1.7 1.92e+01 - 5.50e-04 3.21e-02F 1
## 2 4.2017983e-01 2.19e+01 7.14e+03 -1.7 4.65e+00 - 1.40e-04 9.90e-01f 1
## 3 6.8404869e-01 7.34e+00 7.78e+01 -1.7 7.47e+00 - 6.16e-01 9.90e-01h 1
## 4 8.0231348e-01 1.36e+00 3.62e+03 -1.7 1.14e+00 - 8.13e-01 9.90e-01h 1
## 5 8.2251267e-01 1.66e-02 3.17e-02 -1.7 9.48e-01 - 1.00e+00 1.00e+00h 1
## 6 8.1510746e-01 1.18e-02 5.27e+04 -3.8 3.10e-01 - 9.48e-01 1.00e+00h 1
## 7 7.1223952e-01 4.21e-01 1.01e+04 -3.8 1.80e+00 - 8.08e-01 1.00e+00h 1
## 8 7.6169962e-01 8.70e-05 1.37e+03 -3.8 4.12e-01 - 8.65e-01 1.00e+00h 1
## 9 7.6177628e-01 1.58e-04 1.52e-05 -3.8 1.87e-02 - 1.00e+00 1.00e+00h 1
## iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
## 10 7.6165391e-01 6.80e-04 1.27e-05 -5.7 3.92e-02 - 1.00e+00 1.00e+00h 1
## 11 7.6171721e-01 3.58e-07 1.30e-08 -5.7 5.98e-04 - 1.00e+00 1.00e+00h 1
## 12 7.6171723e-01 1.18e-07 2.23e-09 -8.6 5.00e-04 - 1.00e+00 1.00e+00h 1
## 13 7.6171725e-01 4.25e-14 5.15e-16 -8.6 1.00e-07 - 1.00e+00 1.00e+00h 1
##
## Number of Iterations....: 13
##
## (scaled) (unscaled)
## Objective...............: 7.6171724526877993e-01 7.6171724526877993e-01
## Dual infeasibility......: 5.1485543769626019e-16 5.1485543769626019e-16
## Constraint violation....: 2.8227360768296603e-14 4.2521541843143495e-14
## Complementarity.........: 2.5059036763221995e-09 2.5059036763221995e-09
## Overall NLP error.......: 2.5059036763221995e-09 2.5059036763221995e-09
##
##
## Number of objective function evaluations = 15
## Number of objective gradient evaluations = 14
## Number of equality constraint evaluations = 15
## Number of inequality constraint evaluations = 0
## Number of equality constraint Jacobian evaluations = 14
## Number of inequality constraint Jacobian evaluations = 0
## Number of Lagrangian Hessian evaluations = 13
## Total CPU secs in IPOPT (w/o function evaluations) = 0.067
## Total CPU secs in NLP function evaluations = 0.001
##
## EXIT: Optimal Solution Found.
##
# Get the results
#
# NOTE:
# the time points are in the list M$t but it needs to accessed using bi$list(M$t)
# state and control variables are index by elements of M$t.
# However accessing say x1 at time point i using M$x[i]() doesn't work.
# What seems to work is using M$x[tuple(i)]()
#
res_L = list()
k = 1
for(i in bi$list(M$t)) {
res_L[[k]] = list(t = i, x1 = py_to_r(M$x1[tuple(i)]()),
x2 = py_to_r(M$x2[tuple(i)]()),
u = py_to_r(M$u[tuple(i)]()))
k = k + 1
}
res_df = bind_rows(res_L)
# plot the state and control variables
p = ggplot(res_df) + geom_line(aes(x = t, y = x1, color = "x1"))
p = p + geom_line(aes(x = t, y = x2, color = "x2"))
p = p + geom_line(aes(x = t, y = u, color = "u"))
p = p + labs(x = "time", y = "")
p = p + theme_bw()
p

Session Info
sessionInfo()
## R version 4.0.1 (2020-06-06)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS High Sierra 10.13.6
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] ggplot2_3.3.2 dplyr_1.0.0 reticulate_1.16
##
## loaded via a namespace (and not attached):
## [1] Rcpp_1.0.4.6 knitr_1.28 magrittr_1.5 munsell_0.5.0
## [5] tidyselect_1.1.0 colorspace_1.4-1 lattice_0.20-41 R6_2.4.1
## [9] rlang_0.4.6 stringr_1.4.0 highr_0.8 tools_4.0.1
## [13] grid_4.0.1 gtable_0.3.0 xfun_0.14 withr_2.2.0
## [17] htmltools_0.5.0 ellipsis_0.3.1 yaml_2.2.1 digest_0.6.25
## [21] tibble_3.0.1 lifecycle_0.2.0 crayon_1.3.4 Matrix_1.2-18
## [25] farver_2.0.3 purrr_0.3.4 vctrs_0.3.1 glue_1.4.1
## [29] evaluate_0.14 rmarkdown_2.3 labeling_0.3 stringi_1.4.6
## [33] compiler_4.0.1 pillar_1.4.4 scales_1.1.1 generics_0.0.2
## [37] jsonlite_1.6.1 pkgconfig_2.0.3
# pyomo version used is 5.7