Skip to content

Commit e2abc77

Browse files
committed
general constraint run_ci
1 parent dfd2314 commit e2abc77

File tree

14 files changed

+328
-132
lines changed

14 files changed

+328
-132
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ minimize gT(xT; wT) + sum(gt(xt, ut; wt))
99
xt, ut
1010
subject to xt+1 = ft(xt, ut; wt) , t = 1,...,T-1
1111
ct(xt, ut; wt) {<,=} 0, t = 1,...,T
12-
xt_L <= xt <= xt_U, t = 1,...,T
13-
ut_L <= ut <= ut_U, t = 1,...,T-1.
12+
xt_L < xt < xt_U, t = 1,...,T
13+
ut_L < ut < ut_U, t = 1,...,T-1.
1414
```
1515

1616
with direct trajectory optimization.

examples/acrobot/acrobot.jl

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
# ## Setup
66

7-
using DirectTrajectoryOptimization
87
using LinearAlgebra
98
using Plots
9+
using DirectTrajectoryOptimization
1010

1111
# ## horizon
1212
T = 101
@@ -105,6 +105,34 @@ cons = [Constraint() for t = 1:T]
105105
# ## problem
106106
p = ProblemData(obj, dyn, cons, bnds, options=Options())
107107

108+
@variables z[1:p.nlp.num_var]
109+
110+
function gen_con(z)
111+
z[1:2] - z[end-1:end]
112+
end
113+
114+
gc = gen_con(z)
115+
jac = Symbolics.sparsejacobian(gc, z)
116+
gc_func = eval(Symbolics.build_function(gc, z)[2])
117+
gc_jac_func = eval(Symbolics.build_function(jac.nzval, z)[2])
118+
nc = length(gc)
119+
nj = length(jac.nzval)
120+
sp_jac = [findnz(jac)[1:2]...]
121+
if false
122+
@variables λ[1:nc]
123+
lag_con = dot(λ, gc)
124+
hess = Symbolics.sparsehessian(lag_con, z)
125+
hess_func = eval(Symbolics.build_function(hess.nzval, z, λ)[2])
126+
sp_hess = [findnz(hess)[1:2]...]
127+
nh = length(hess.nzval)
128+
else
129+
hess_func = Expr(:null)
130+
sp_hess = [Int[]]
131+
nh = 0
132+
end
133+
134+
135+
108136
# ## initialize
109137
x_interpolation = linear_interpolation(x1, xT, T)
110138
u_guess = [1.0 * randn(nu) for t = 1:T-1]

src/DirectTrajectoryOptimization.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const MOI = MathOptInterface
1111
include("objective.jl")
1212
include("constraints.jl")
1313
include("bounds.jl")
14+
include("general_constraint.jl")
1415
include("dynamics.jl")
1516
include("solver.jl")
1617
include("data.jl")
@@ -21,7 +22,7 @@ include("utils.jl")
2122
export Cost
2223

2324
# constraints
24-
export Bound, Bounds, Constraint, Constraints
25+
export Bound, Bounds, Constraint, Constraints, GeneralConstraint
2526

2627
# dynamics
2728
export Dynamics

src/constraints.jl

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,11 @@ end
6767

6868
function eval_hess_lag!(h, idx, cons::Constraints{T}, x, u, w, λ) where T
6969
for (t, con) in enumerate(cons)
70-
con.hess(con.hess_cache, x[t], u[t], w[t], λ[t])
71-
@views h[idx[t]] .+= con.hess_cache
72-
fill!(con.hess_cache, 0.0) # TODO: confirm this is necessary
70+
if !isempty(con.hess_cache)
71+
con.hess(con.hess_cache, x[t], u[t], w[t], λ[t])
72+
@views h[idx[t]] .+= con.hess_cache
73+
fill!(con.hess_cache, 0.0) # TODO: confirm this is necessary
74+
end
7375
end
7476
end
7577

@@ -134,15 +136,3 @@ function hessian_indices(cons::Constraints{T}, key::Vector{Tuple{Int,Int}}, nx::
134136
end
135137
return idx
136138
end
137-
138-
# struct ConstraintSet{T}
139-
# bounds::Bounds{T}
140-
# stage::Constraints{T}
141-
# end
142-
143-
# ConstraintSet() = ConstraintSet([Bound()], [Constraint()])
144-
# ConstraintSet(stage::Constraints{T}) where T = ConstraintSet([Bound()], stage)
145-
# ConstraintSet(bounds::Bounds{T}) where T = ConstraintSet(bounds, [Constraint()])
146-
147-
# num_con(cons::ConstraintSet{T}) where T = sum([con.nc * length(con.idx) for con in cons.stage])
148-
# num_jac(cons::ConstraintSet{T}) where T = sum([con.nj * length(con.idx) for con in cons.stage])

src/data.jl

Lines changed: 59 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,42 @@ struct TrajectoryOptimizationIndices
3737
stage_con::Vector{Vector{Int}}
3838
stage_jac::Vector{Vector{Int}}
3939
stage_hess::Vector{Vector{Int}}
40+
gen_con::Vector{Int}
41+
gen_jac::Vector{Int}
42+
gen_hess::Vector{Int}
4043
x::Vector{Vector{Int}}
4144
u::Vector{Vector{Int}}
4245
xu::Vector{Vector{Int}}
4346
xuy::Vector{Vector{Int}}
4447
end
4548

46-
function indices(obj::Objective{T}, dyn::Vector{Dynamics{T}}, cons::Constraints{T}, key::Vector{Tuple{Int,Int}},
47-
nx::Vector{Int}, nu::Vector{Int}) where T
49+
function indices(obj::Objective{T}, dyn::Vector{Dynamics{T}}, cons::Constraints{T}, gc::GeneralConstraint{T},
50+
key::Vector{Tuple{Int,Int}}, nx::Vector{Int}, nu::Vector{Int}, nz::Int) where T
4851
# Jacobians
4952
dyn_con = constraint_indices(dyn, shift=0)
5053
dyn_jac = jacobian_indices(dyn, shift=0)
5154
stage_con = constraint_indices(cons, shift=num_con(dyn))
5255
stage_jac = jacobian_indices(cons, shift=num_jac(dyn))
56+
gen_con = constraint_indices(gc, shift=(num_con(dyn) + num_con(cons)))
57+
gen_jac = jacobian_indices(gc, shift=(num_jac(dyn) + num_jac(cons)))
5358

5459
# Hessian of Lagrangian
5560
obj_hess = hessian_indices(obj, key, nx, nu)
5661
dyn_hess = hessian_indices(dyn, key, nx, nu)
5762
stage_hess = hessian_indices(cons, key, nx, nu)
63+
gen_hess = hessian_indices(gc, key, nz)
5864

5965
# indices
6066
x_idx = x_indices(dyn)
6167
u_idx = u_indices(dyn)
6268
xu_idx = xu_indices(dyn)
6369
xuy_idx = xuy_indices(dyn)
6470

65-
return TrajectoryOptimizationIndices(obj_hess, dyn_con, dyn_jac, dyn_hess, stage_con, stage_jac, stage_hess,
71+
return TrajectoryOptimizationIndices(
72+
obj_hess,
73+
dyn_con, dyn_jac, dyn_hess,
74+
stage_con, stage_jac, stage_hess,
75+
gen_con, gen_jac, gen_hess,
6676
x_idx, u_idx, xu_idx, xuy_idx)
6777
end
6878

@@ -78,6 +88,9 @@ struct NLPData{T} <: MOI.AbstractNLPEvaluator
7888
sp_jac
7989
sp_hess_lag
8090
hess_lag::Bool
91+
gc::GeneralConstraint{T}
92+
w::Vector{T}
93+
λ_gen::Vector{T}
8194
end
8295

8396
function primal_bounds(bnds::Bounds{T}, nz::Int, x_idx::Vector{Vector{Int}}, u_idx::Vector{Vector{Int}}) where T
@@ -91,55 +104,79 @@ function primal_bounds(bnds::Bounds{T}, nz::Int, x_idx::Vector{Vector{Int}}, u_i
91104
return zl, zu
92105
end
93106

94-
function constraint_bounds(cons::Constraints{T}, nc::Int, idx::TrajectoryOptimizationIndices) where T
107+
function constraint_bounds(cons::Constraints{T}, gc::GeneralConstraint{T},
108+
nc_dyn::Int, nc_con::Int, idx::TrajectoryOptimizationIndices) where T
109+
# total constraints
110+
nc = nc_dyn + nc_con + gc.nc
111+
# bounds
95112
cl, cu = zeros(nc), zeros(nc)
113+
# stage
96114
for (t, con) in enumerate(cons)
97115
cl[idx.stage_con[t][con.idx_ineq]] .= -Inf
98116
end
117+
# general
118+
cl[collect(nc_dyn + nc_con .+ gc.idx_ineq)] .= -Inf
99119
return cl, cu
100120
end
101121

102-
function NLPData(trajopt::TrajectoryOptimizationData; eval_hess=false)
122+
function NLPData(trajopt::TrajectoryOptimizationData;
123+
eval_hess=false,
124+
general_constraint=GeneralConstraint())
125+
103126
# number of variables
104127
nz = sum(trajopt.x_dim) + sum(trajopt.u_dim)
105128

106129
# number of constraints
107130
nc_dyn = num_con(trajopt.dyn)
108131
nc_con = num_con(trajopt.cons)
109-
nc = nc_dyn + nc_con
132+
nc_gen = num_con(general_constraint)
133+
nc = nc_dyn + nc_con + nc_gen
110134

111135
# number of nonzeros in constraint Jacobian
112136
nj_dyn = num_jac(trajopt.dyn)
113137
nj_con = num_jac(trajopt.cons)
114-
nj = nj_dyn + nj_con
138+
nj_gen = num_jac(general_constraint)
139+
nj = nj_dyn + nj_con + nj_gen
115140

116141
# number of nonzeros in Hessian of Lagrangian
117142
nh = 0
118143

119144
# constraint Jacobian sparsity
120145
sp_dyn = sparsity_jacobian(trajopt.dyn, trajopt.x_dim, trajopt.u_dim, row_shift=0)
121146
sp_con = sparsity_jacobian(trajopt.cons, trajopt.x_dim, trajopt.u_dim, row_shift=nc_dyn)
122-
sp_jac = collect([sp_dyn..., sp_con...])
147+
sp_gen = sparsity_jacobian(general_constraint, nz, row_shift=(nc_dyn + nc_con))
148+
sp_jac = collect([sp_dyn..., sp_con..., sp_gen...])
123149

124150
# Hessian of Lagrangian sparsity
125151
sp_obj_hess = sparsity_hessian(trajopt.obj, trajopt.x_dim, trajopt.u_dim)
126152
sp_dyn_hess = sparsity_hessian(trajopt.dyn, trajopt.x_dim, trajopt.u_dim)
127153
sp_con_hess = sparsity_hessian(trajopt.cons, trajopt.x_dim, trajopt.u_dim)
128-
sp_hess_lag = [sp_obj_hess..., sp_dyn_hess..., sp_con_hess...]
154+
sp_gen_hess = sparsity_hessian(general_constraint, nz)
155+
sp_hess_lag = [sp_obj_hess..., sp_dyn_hess..., sp_con_hess..., sp_gen_hess...]
129156
sp_hess_lag = !isempty(sp_hess_lag) ? sp_hess_lag : Tuple{Int,Int}[]
130157
sp_hess_key = sort(unique(sp_hess_lag))
131-
# sp_hess_status = [!isempty(sp_obj_hess), !isempty(sp_dyn_hess), !isempty(sp_con_hess)]
132158

133159
# indices
134-
idx = indices(trajopt.obj, trajopt.dyn, trajopt.cons, sp_hess_key, trajopt.x_dim, trajopt.u_dim)
160+
idx = indices(trajopt.obj, trajopt.dyn, trajopt.cons,
161+
general_constraint,
162+
sp_hess_key,
163+
trajopt.x_dim, trajopt.u_dim, nz)
135164

136165
# primal variable bounds
137166
zl, zu = primal_bounds(trajopt.bnds, nz, idx.x, idx.u)
138167

139168
# nonlinear constraint bounds
140-
cl, cu = constraint_bounds(trajopt.cons, nc, idx)
141-
142-
NLPData(trajopt, nz, nc, nj, nh, [zl, zu], [cl, cu], idx, sp_jac, sp_hess_key, eval_hess)
169+
cl, cu = constraint_bounds(trajopt.cons, general_constraint, nc_dyn, nc_con, idx)
170+
171+
NLPData(trajopt,
172+
nz, nc, nj, nh,
173+
[zl, zu], [cl, cu],
174+
idx,
175+
sp_jac, sp_hess_key,
176+
eval_hess,
177+
general_constraint,
178+
vcat(trajopt.w...),
179+
zeros(general_constraint.nc))
143180
end
144181

145182
struct SolverData
@@ -186,14 +223,15 @@ function trajectory!(x::Vector{Vector{T}}, u::Vector{Vector{T}}, z::Vector{T},
186223
end
187224
end
188225

189-
function duals!(λ_dyn::Vector{Vector{T}}, λ_stage::Vector{Vector{T}}, λ,
190-
idx_dyn::Vector{Vector{Int}}, idx_stage::Vector{Vector{Int}}) where T
226+
function duals!(λ_dyn::Vector{Vector{T}}, λ_stage::Vector{Vector{T}}, λ_gen::Vector{T}, λ,
227+
idx_dyn::Vector{Vector{Int}}, idx_stage::Vector{Vector{Int}}, idx_gen::Vector{Int}) where T
191228
for (t, idx) in enumerate(idx_dyn)
192229
λ_dyn[t] .= @views λ[idx]
193230
end
194231
for (t, idx) in enumerate(idx_stage)
195232
λ_stage[t] .= @views λ[idx]
196233
end
234+
λ_gen .= λ[idx_gen]
197235
end
198236

199237
struct ProblemData{T} <: MOI.AbstractNLPEvaluator
@@ -202,11 +240,13 @@ struct ProblemData{T} <: MOI.AbstractNLPEvaluator
202240
end
203241

204242
function ProblemData(obj::Objective{T}, dyn::Vector{Dynamics{T}}, cons::Constraints{T}, bnds::Bounds{T};
205-
eval_hess=false, options=Options(),
243+
eval_hess=false,
244+
general_constraint=GeneralConstraint(),
245+
options=Options(),
206246
w=[[zeros(nw) for nw in dimensions(dyn)[3]]..., zeros(0)]) where T
207247

208248
trajopt = TrajectoryOptimizationData(obj, dyn, cons, bnds, w=w)
209-
nlp = NLPData(trajopt, eval_hess=eval_hess)
249+
nlp = NLPData(trajopt, general_constraint=general_constraint, eval_hess=eval_hess)
210250
s_data = SolverData(nlp, options=options)
211251

212252
ProblemData(nlp, s_data)
@@ -236,49 +276,4 @@ end
236276

237277
function solve!(p::ProblemData)
238278
MOI.optimize!(p.s_data.solver)
239-
end
240-
241-
# struct Solver{T}
242-
# p::Problem{T}
243-
# nlp_bounds::Vector{MOI.NLPBoundsPair}
244-
# block_data::MOI.NLPBlockData
245-
# solver::Ipopt.Optimizer
246-
# z::Vector{MOI.VariableIndex}
247-
# end
248-
249-
# function Solver(trajopt::TrajectoryOptimizationProblem; eval_hess=false, options=Options())
250-
# p = Problem(trajopt, eval_hess=eval_hess)
251-
252-
# nlp_bounds = MOI.NLPBoundsPair.(p.con_bnds...)
253-
# block_data = MOI.NLPBlockData(nlp_bounds, NLPEvaluator(), true)
254-
255-
# # instantiate NLP solver
256-
# solver = Ipopt.Optimizer()
257-
258-
# # set NLP solver options
259-
# for name in fieldnames(typeof(options))
260-
# solver.options[String(name)] = getfield(options, name)
261-
# end
262-
263-
# z = MOI.add_variables(solver, nz)
264-
265-
# for i = 1:p.num_var
266-
# MOI.add_constraint(solver, z[i], MOI.LessThan(p.var_bnds[2][i]))
267-
# MOI.add_constraint(solver, z[i], MOI.GreaterThan(p.var_bnds[1][i]))
268-
# end
269-
270-
# MOI.set(solver, MOI.NLPBlock(), block_data)
271-
# MOI.set(solver, MOI.ObjectiveSense(), MOI.MIN_SENSE)
272-
273-
# return Solver(p, nlp_bounds, block_data, solver, z)
274-
# end
275-
276-
# using MathOptInterface
277-
# const MOI = MathOptInterface
278-
279-
# MOI.AbstractNLPEvaluator
280-
281-
# nlp_bounds = MOI.NLPBoundsPair.([-ones(10), ones(10)]...)
282-
283-
# struct MOIEval <: MOI.AbstractNLPEvaluator end
284-
# block_data = MOI.NLPBlockData(nlp_bounds, a(), true)
279+
end

src/dynamics.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,14 @@ function eval_jac!(j, idx, cons::Vector{Dynamics{T}}, x, u, w) where T
8585
fill!(con.jac_cache, 0.0) # TODO: confirm this is necessary
8686
end
8787
end
88-
88+
isempty(ones(1))
8989
function eval_hess_lag!(h, idx, cons::Vector{Dynamics{T}}, x, u, w, λ) where T
9090
for (t, con) in enumerate(cons)
91-
con.hess(con.hess_cache, x[t+1], x[t], u[t], w[t], λ[t])
92-
@views h[idx[t]] .+= con.hess_cache
93-
fill!(con.hess_cache, 0.0) # TODO: confirm this is necessary
91+
if !isempty(con.hess_cache)
92+
con.hess(con.hess_cache, x[t+1], x[t], u[t], w[t], λ[t])
93+
@views h[idx[t]] .+= con.hess_cache
94+
fill!(con.hess_cache, 0.0) # TODO: confirm this is necessary
95+
end
9496
end
9597
end
9698

0 commit comments

Comments
 (0)