@@ -5,9 +5,10 @@ Internal layout for NLP variables:
55 ..,
66 X_N-1, U_N-1, K_N-1^1..K_N-1^s,
77 X_N, U_N, V]
8- with s the stage number and U given by either linear interpolation in [t_i, t_i+1]
9- or constant interpolation for 1-stage methods or if specfied (U_N might end up unused)
10- Path constraints are all evaluated at time steps
8+ with s the stage number and U piecewise constant equal to U_i in [t_i, t_i+1]
9+ or, for methods with s>1, piecewise linear if option control_type set to :linear
10+ NB. U_N may be removed at some point if we disable piecewise linear control
11+ Path constraints are all evaluated at time steps, including final time.
1112=#
1213
1314
@@ -57,9 +58,9 @@ struct Gauss_Legendre_2 <: GenericIRK
5758 _step_variables_block:: Int
5859 _state_stage_eqs_block:: Int
5960 _step_pathcons_block:: Int
60- _constant_control :: Bool
61+ _control_type :: Symbol
6162
62- function Gauss_Legendre_2 (dim_NLP_steps, dim_NLP_x, dim_NLP_u, dim_NLP_v, dim_u_cons, dim_x_cons, dim_xu_cons, dim_boundary_cons, dim_v_cons, constant_control )
63+ function Gauss_Legendre_2 (dim_NLP_steps, dim_NLP_x, dim_NLP_u, dim_NLP_v, dim_u_cons, dim_x_cons, dim_xu_cons, dim_boundary_cons, dim_v_cons, control_type )
6364
6465 stage = 2
6566
@@ -70,7 +71,7 @@ struct Gauss_Legendre_2 <: GenericIRK
7071 [0.5 , 0.5 ],
7172 [(0.5 - sqrt (3 ) / 6 ), (0.5 + sqrt (3 ) / 6 )],
7273 step_variables_block, state_stage_eqs_block, step_pathcons_block,
73- constant_control
74+ control_type
7475 )
7576
7677 return disc, dim_NLP_variables, dim_NLP_constraints
@@ -93,9 +94,9 @@ struct Gauss_Legendre_3 <: GenericIRK
9394 _step_variables_block:: Int
9495 _state_stage_eqs_block:: Int
9596 _step_pathcons_block:: Int
96- _constant_control :: Bool
97+ _control_type :: Symbol
9798
98- function Gauss_Legendre_3 (dim_NLP_steps, dim_NLP_x, dim_NLP_u, dim_NLP_v, dim_u_cons, dim_x_cons, dim_xu_cons, dim_boundary_cons, dim_v_cons, constant_control )
99+ function Gauss_Legendre_3 (dim_NLP_steps, dim_NLP_x, dim_NLP_u, dim_NLP_v, dim_u_cons, dim_x_cons, dim_xu_cons, dim_boundary_cons, dim_v_cons, control_type )
99100
100101 stage = 3
101102
@@ -107,7 +108,7 @@ struct Gauss_Legendre_3 <: GenericIRK
107108 (5 / 36 + sqrt (15 ) / 30 ) (2 / 9 + sqrt (15 ) / 15 ) (5.0 / 36.0 )],
108109 [5.0 / 18.0 , 4.0 / 9.0 , 5.0 / 18.0 ],
109110 [0.5 - 0.1 * sqrt (15 ), 0.5 , 0.5 + 0.1 * sqrt (15 )],
110- step_variables_block, state_stage_eqs_block, step_pathcons_block, constant_control
111+ step_variables_block, state_stage_eqs_block, step_pathcons_block, control_type
111112 )
112113
113114 return disc, dim_NLP_variables, dim_NLP_constraints
@@ -183,7 +184,7 @@ function get_OCP_control_at_time_step(xu, docp::DOCP{ <: GenericIRK, <: ScalVect
183184 return @view xu[(offset + 1 ): (offset + docp. dim_NLP_u)]
184185end
185186function get_OCP_control_at_time_stage (xu, docp:: DOCP{ <: GenericIRK, <: ScalVect, ScalVariable, <: ScalVect} , i, cj)
186- if (docp. discretization. stage == 1 ) || (docp. discretization. _constant_control )
187+ if (docp. discretization. stage == 1 ) || (docp. discretization. _control_type == :constant )
187188 # constant interpolation on step
188189 return get_OCP_control_at_time_step (xu, docp, i)
189190 else
@@ -194,7 +195,7 @@ function get_OCP_control_at_time_stage(xu, docp::DOCP{ <: GenericIRK, <: ScalVec
194195 end
195196end
196197function get_OCP_control_at_time_stage (xu, docp:: DOCP{ <: GenericIRK, <: ScalVect, VectVariable, <: ScalVect} , i, cj)
197- if (docp. discretization. stage == 1 ) || (docp. discretization. _constant_control )
198+ if (docp. discretization. stage == 1 ) || (docp. discretization. _control_type == :constant )
198199 # constant interpolation on step
199200 return get_OCP_control_at_time_step (xu, docp, i)
200201 else
@@ -251,8 +252,8 @@ $(TYPEDSIGNATURES)
251252Set work array for all dynamics and lagrange cost evaluations
252253"""
253254function setWorkArray (docp:: DOCP{ <: GenericIRK} , xu, time_grid, v)
254- # work array layout: [x_ij ; sum_bk ; u_ij] ?
255- work = similar (xu, docp. dim_OCP_x + docp. dim_NLP_x + docp . dim_NLP_u )
255+ # work array layout: [x_ij ; sum_bk]
256+ work = similar (xu, docp. dim_OCP_x + docp. dim_NLP_x)
256257 return work
257258end
258259
@@ -264,12 +265,9 @@ Convention: 1 <= i <= dim_NLP_steps (+1)
264265"""
265266function setStepConstraints! (docp:: DOCP{ <: GenericIRK} , c, xu, v, time_grid, i, work)
266267
267- # work array layout: [x_ij ; sum_bk ; u_ij] ?
268+ # work array layout: [x_ij ; sum_bk]
268269 work_xij = @view work[1 : docp. dim_OCP_x]
269270 work_sumbk = @view work[docp. dim_OCP_x+ 1 : docp. dim_OCP_x+ docp. dim_NLP_x]
270- # work_sumbk .= zero(eltype(xu)) AD bug when affecting constant values...
271- @views @. work_sumbk[1 : docp. dim_NLP_x] = xu[1 : docp. dim_NLP_x] * 0.
272- # work_uij ?
273271
274272 # offset for previous steps
275273 offset = (i- 1 )* (docp. discretization. _state_stage_eqs_block + docp. discretization. _step_pathcons_block)
@@ -297,7 +295,11 @@ function setStepConstraints!(docp::DOCP{ <: GenericIRK}, c, xu, v, time_grid, i,
297295 kij = get_stagevars_at_time_step (xu, docp, i, j)
298296
299297 # update sum b_j k_i^j (w/ lagrange term) for state equation after loop
300- @views @. work_sumbk[1 : docp. dim_NLP_x] = work_sumbk[1 : docp. dim_NLP_x] + docp. discretization. butcher_b[j] * kij[1 : docp. dim_NLP_x]
298+ if j == 1
299+ @views @. work_sumbk[1 : docp. dim_NLP_x] = docp. discretization. butcher_b[j] * kij[1 : docp. dim_NLP_x]
300+ else
301+ @views @. work_sumbk[1 : docp. dim_NLP_x] = work_sumbk[1 : docp. dim_NLP_x] + docp. discretization. butcher_b[j] * kij[1 : docp. dim_NLP_x]
302+ end
301303
302304 # state at stage: x_i^j = x_i + h_i sum a_jl k_i^l
303305 # +++ still some allocations here
@@ -312,8 +314,7 @@ function setStepConstraints!(docp::DOCP{ <: GenericIRK}, c, xu, v, time_grid, i,
312314 xij = work_xij
313315 end
314316
315- # control at stage: interpolation between u_i and u_i+1
316- # +++ use work aray to reduce allocs ?
317+ # control at stage
317318 uij = get_OCP_control_at_time_stage (xu, docp, i, cj)
318319
319320 # stage equations k_i^j = f(t_i^j, x_i^j, u_i, v) as c[] = k - f
@@ -342,3 +343,199 @@ function setStepConstraints!(docp::DOCP{ <: GenericIRK}, c, xu, v, time_grid, i,
342343 setPathConstraints! (docp, c, ti, xi, ui, v, offset)
343344
344345end
346+
347+
348+ """
349+ $(TYPEDSIGNATURES)
350+
351+ Build sparsity pattern for Jacobian of constraints
352+ """
353+ function DOCP_Jacobian_pattern (docp:: DOCP{ <: GenericIRK} )
354+
355+ if docp. discretization. _control_type != :constant
356+ error (" Manual Jacobian sparsity pattern not supported for IRK scheme with piecewise linear control" )
357+ end
358+
359+ # vector format for sparse matrix
360+ Is = Vector {Int} (undef, 0 )
361+ Js = Vector {Int} (undef, 0 )
362+
363+ s = docp. discretization. stage
364+
365+ # index alias for v
366+ v_start = docp. dim_NLP_variables - docp. dim_NLP_v + 1
367+ v_end = docp. dim_NLP_variables
368+
369+ # 1. main loop over steps
370+ for i = 1 : docp. dim_NLP_steps
371+
372+ # constraints block and offset: state equation, stage equations, path constraints
373+ c_block = docp. discretization. _state_stage_eqs_block + docp. discretization. _step_pathcons_block
374+ c_offset = (i- 1 )* c_block
375+
376+ # contiguous variables blocks will be used when possible
377+ # x_i (l_i) u_i k_i x_i+1 (l_i+1)
378+ var_offset = (i- 1 )* docp. discretization. _step_variables_block
379+ xi_start = var_offset + 1
380+ xi_end = var_offset + docp. dim_OCP_x
381+ ui_start = var_offset + docp. dim_NLP_x + 1
382+ ui_end = var_offset + docp. dim_NLP_x + docp. dim_NLP_u
383+ ki_start = var_offset + docp. dim_NLP_x + docp. dim_NLP_u + 1
384+ ki_end = var_offset + docp. discretization. _step_variables_block
385+ xip1_end = var_offset + docp. discretization. _step_variables_block + docp. dim_OCP_x
386+ li = var_offset + docp. dim_NLP_x
387+ lip1 = var_offset + docp. discretization. _step_variables_block + docp. dim_NLP_x
388+
389+ # 1.1 state eq 0 = x_i+1 - (x_i + h_i sum_j b_j k_ij)
390+ # depends on x_i, k_ij, x_i+1, and v for h_i in variable times case !
391+ # skip l_i, u_i; should skip k_i[n+1] also but annoying...
392+ add_nonzero_block! (Is, Js, c_offset+ 1 , c_offset+ docp. dim_OCP_x, xi_start, xi_end)
393+ add_nonzero_block! (Is, Js, c_offset+ 1 , c_offset+ docp. dim_OCP_x, ki_start, xip1_end)
394+ add_nonzero_block! (Is, Js, c_offset+ 1 , c_offset+ docp. dim_OCP_x, v_start, v_end)
395+ # 1.2 lagrange part l_i+1 = l_i + h_i (sum_j b_j k_ij)[n+1]
396+ # depends on l_i, k_ij[n+1], l_i+1, and v for h_i in variable times case !
397+ if docp. is_lagrange
398+ add_nonzero_block! (Is, Js, c_offset+ docp. dim_NLP_x, li)
399+ add_nonzero_block! (Is, Js, c_offset+ docp. dim_NLP_x, lip1)
400+ for i= 1 : s
401+ kij_l = var_offset + docp. dim_NLP_x + docp. dim_NLP_u + i* docp. dim_NLP_x
402+ add_nonzero_block! (Is, Js, c_offset+ docp. dim_NLP_x, kij_l)
403+ end
404+ add_nonzero_block! (Is, Js, c_offset+ docp. dim_NLP_x, c_offset+ docp. dim_NLP_x, v_start, v_end)
405+ end
406+
407+ # 1.3 stage equations k_ij = f(t_ij, x_ij, u_ij, v) (with lagrange part)
408+ # with x_ij = x_i + sum_l a_il k_jl and assuming u_ij = u_i
409+ # depends on x_i, u_i, k_i, and v; skip l_i (could skip k_ij[n+1] too...)
410+ add_nonzero_block! (Is, Js, c_offset+ docp. dim_NLP_x+ 1 , c_offset+ (s+ 1 )* docp. dim_NLP_x, xi_start, xi_end)
411+ add_nonzero_block! (Is, Js, c_offset+ docp. dim_NLP_x+ 1 , c_offset+ (s+ 1 )* docp. dim_NLP_x, ui_start, ki_end)
412+ add_nonzero_block! (Is, Js, c_offset+ docp. dim_NLP_x+ 1 , c_offset+ (s+ 1 )* docp. dim_NLP_x, v_start, v_end)
413+
414+ # 1.4 path constraint g(t_i, x_i, u_i, v)
415+ # depends on x_i, u_i, v; skip l_i
416+ add_nonzero_block! (Is, Js, c_offset+ (s+ 1 )* docp. dim_NLP_x+ 1 , c_offset+ c_block, xi_start, xi_end)
417+ add_nonzero_block! (Is, Js, c_offset+ (s+ 1 )* docp. dim_NLP_x+ 1 , c_offset+ c_block, ui_start, ui_end)
418+ add_nonzero_block! (Is, Js, c_offset+ (s+ 1 )* docp. dim_NLP_x+ 1 , c_offset+ c_block, v_start, v_end)
419+ end
420+
421+ # 2. final path constraints (xf, uf, v)
422+ c_offset = docp. dim_NLP_steps * (docp. discretization. _state_stage_eqs_block + docp. discretization. _step_pathcons_block)
423+ c_block = docp. discretization. _step_pathcons_block
424+ var_offset = docp. dim_NLP_steps* docp. discretization. _step_variables_block
425+ xf_start = var_offset + 1
426+ xf_end = var_offset + docp. dim_OCP_x
427+ uf_start = var_offset + docp. dim_NLP_x + 1
428+ uf_end = var_offset + docp. dim_NLP_x + docp. dim_NLP_u
429+ add_nonzero_block! (Is, Js, c_offset+ 1 , c_offset+ c_block, xf_start, xf_end)
430+ add_nonzero_block! (Is, Js, c_offset+ 1 ,c_offset+ c_block, uf_start, uf_end)
431+ add_nonzero_block! (Is, Js, c_offset+ 1 , c_offset+ c_block, v_start, v_end)
432+
433+ # 3. boundary constraints (x0, xf, v)
434+ c_offset = docp. dim_NLP_steps * (docp. discretization. _state_stage_eqs_block + docp. discretization. _step_pathcons_block) + docp. discretization. _step_pathcons_block
435+ c_block = docp. dim_boundary_cons + docp. dim_v_cons
436+ x0_start = 1
437+ x0_end = docp. dim_OCP_x
438+ add_nonzero_block! (Is, Js, c_offset+ 1 , c_offset+ c_block, x0_start, x0_end)
439+ add_nonzero_block! (Is, Js, c_offset+ 1 , c_offset+ c_block, xf_start, xf_end)
440+ add_nonzero_block! (Is, Js, c_offset+ 1 , c_offset+ c_block, v_start, v_end)
441+ # 3.4 null initial condition for lagrangian cost state l0
442+ if docp. is_lagrange
443+ add_nonzero_block! (Is, Js, docp. dim_NLP_constraints, docp. dim_NLP_x)
444+ end
445+
446+ # build and return sparse matrix
447+ nnzj = length (Is)
448+ Vs = ones (Bool, nnzj)
449+ return sparse (Is, Js, Vs, docp. dim_NLP_constraints, docp. dim_NLP_variables)
450+ end
451+
452+
453+ """
454+ $(TYPEDSIGNATURES)
455+
456+ Build sparsity pattern for Hessian of Lagrangian
457+ """
458+ function DOCP_Hessian_pattern (docp:: DOCP{ <: GenericIRK} )
459+
460+ if docp. discretization. _control_type != :constant
461+ error (" Manual Hessian sparsity pattern not supported for IRK scheme with piecewise linear control" )
462+ end
463+
464+ # NB. need to provide full pattern for coloring, not just upper/lower part
465+ Is = Vector {Int} (undef, 0 )
466+ Js = Vector {Int} (undef, 0 )
467+
468+ s = docp. discretization. stage
469+
470+ # index alias for v
471+ v_start = docp. dim_NLP_variables - docp. dim_NLP_v + 1
472+ v_end = docp. dim_NLP_variables
473+
474+ # 0. objective
475+ # 0.1 mayer cost (x0, xf, v)
476+ # -> grouped with term 3. for boundary conditions
477+ # 0.2 lagrange case (lf)
478+ # -> 2nd order term is zero
479+
480+ # 1. main loop over steps
481+ # 1.0 v / v term
482+ add_nonzero_block! (Is, Js, v_start, v_end, v_start, v_end)
483+
484+ for i = 1 : docp. dim_NLP_steps
485+
486+ # contiguous variables blocks will be used when possible
487+ # x_i (l_i) u_i k_i x_i+1 (l_i+1)
488+ var_offset = (i- 1 )* docp. discretization. _step_variables_block
489+ xi_start = var_offset + 1
490+ xi_end = var_offset + docp. dim_OCP_x
491+ ui_start = var_offset + docp. dim_NLP_x + 1
492+ ui_end = var_offset + docp. dim_NLP_x + docp. dim_NLP_u
493+ ki_start = var_offset + docp. dim_NLP_x + docp. dim_NLP_u + 1
494+ ki_end = var_offset + (s+ 1 )* docp. dim_NLP_x + docp. dim_NLP_u
495+
496+ # 1.1 state eq 0 = x_i+1 - (x_i + h_i sum_j b_j k_ij)
497+ # -> 2nd order terms are zero
498+ # 1.2 lagrange part 0 = l_i+1 - (l_i + h_i (sum_j b_j k_ij[n+1]))
499+ # -> 2nd order terms are zero
500+
501+ # 1.3 stage equations 0 = k_ij - f(t_ij, x_ij, u_ij, v) (with lagrange part)
502+ # with x_ij = x_i + sum_l a_il k_jl and assuming u_ij = u_i
503+ # depends on x_i, u_i, k_i, and v; skip l_i (could skip k_ij[n+1] too...)
504+ add_nonzero_block! (Is, Js, xi_start, xi_end, xi_start, xi_end)
505+ add_nonzero_block! (Is, Js, ui_start, ki_end, ui_start, ki_end)
506+ add_nonzero_block! (Is, Js, xi_start, xi_end, ui_start, ki_end; sym= true )
507+ add_nonzero_block! (Is, Js, xi_start, xi_end, v_start, v_end; sym= true )
508+ add_nonzero_block! (Is, Js, ui_start, ki_end, v_start, v_end; sym= true )
509+
510+ # 1.4 path constraint g(t_i, x_i, u_i, v)
511+ # -> included in 1.3
512+ end
513+
514+ # 2. final path constraints (xf, uf, v) (assume present)
515+ var_offset = docp. dim_NLP_steps* docp. discretization. _step_variables_block
516+ xf_start = var_offset + 1
517+ xf_end = var_offset + docp. dim_OCP_x
518+ uf_start = var_offset + docp. dim_NLP_x + 1
519+ uf_end = var_offset + docp. dim_NLP_x + docp. dim_NLP_u
520+ add_nonzero_block! (Is, Js, xf_start, xf_end, xf_start, xf_end)
521+ add_nonzero_block! (Is, Js, uf_start, uf_end, uf_start, uf_end)
522+ add_nonzero_block! (Is, Js, xf_start, xf_end, uf_start, uf_end; sym= true )
523+ add_nonzero_block! (Is, Js, xf_start, xf_end, v_start, v_end; sym= true )
524+ add_nonzero_block! (Is, Js, uf_start, uf_end, v_start, v_end; sym= true )
525+
526+ # 3. boundary constraints (x0, xf, v) or mayer cost g0(x0, xf, v) (assume present)
527+ # -> x0 / x0, x0 / v terms included in first loop iteration
528+ # -> xf / xf, xf / v terms included in 2.
529+ x0_start = 1
530+ x0_end = docp. dim_OCP_x
531+ add_nonzero_block! (Is, Js, x0_start, x0_end, xf_start, xf_end; sym= true )
532+
533+ # 3.1 null initial condition for lagrangian cost state l0
534+ # -> 2nd order term is zero
535+
536+ # build and return sparse matrix
537+ nnzj = length (Is)
538+ Vs = ones (Bool, nnzj)
539+ return sparse (Is, Js, Vs, docp. dim_NLP_variables, docp. dim_NLP_variables)
540+
541+ end
0 commit comments