@@ -23,6 +23,16 @@ field, which should be interpreted as follows:
2323 * `NODE_CALL_UNIVARIATE_BROADCASTED`: the index into `operators.univariate_operators`
2424 * `NODE_CALL_MULTIVARIATE_BROADCASTED`: the index into `operators.multivariate_operators`
2525 * `NODE_CALL_REDUCE`: the index into `operators.multivariate_operators`
26+ * `NODE_MOI_VARIABLE_BLOCK`: a contiguous block of `MOI.VariableIndex`. The
27+ `index` field is the value of the FIRST `MOI.VariableIndex` in the block;
28+ the shape `(m, n)` is stored in `Expression.block_shapes`. The block holds
29+ `m * n` variables in column-major order.
30+ * `NODE_VARIABLE_BLOCK`: same as `NODE_MOI_VARIABLE_BLOCK` but after MOI
31+ variable indices have been remapped to consecutive 1-based internal
32+ indices. `index` is the FIRST internal index of the block.
33+ * `NODE_VALUE_BLOCK`: a contiguous block of constants. `index` is the start
34+ index in the `.values` field; the next `m * n` entries (column-major) are
35+ the block's data. Shape stored in `Expression.block_shapes`.
2636"""
2737@enum (
2838 NodeType,
@@ -51,6 +61,12 @@ field, which should be interpreted as follows:
5161 NODE_CALL_MULTIVARIATE_BROADCASTED,
5262 # Index into the multivariate operators, with reduction
5363 NODE_CALL_REDUCE,
64+ # Block-of-variables node, before MOI → internal index remap.
65+ NODE_MOI_VARIABLE_BLOCK,
66+ # Block-of-variables node, after MOI → internal index remap.
67+ NODE_VARIABLE_BLOCK,
68+ # Block-of-constants node.
69+ NODE_VALUE_BLOCK,
5470)
5571
5672@enum (Linearity, CONSTANT, LINEAR, PIECEWISE_LINEAR, NONLINEAR)
@@ -96,6 +112,16 @@ function _replace_moi_variables(
96112 moi_index_to_consecutive_index[MOI. VariableIndex (node. index)],
97113 node. parent,
98114 )
115+ elseif node. type == NODE_MOI_VARIABLE_BLOCK
116+ # `node.index` is the FIRST MOI variable index in the block. For an
117+ # `ArrayOfContiguousVariables`, all variables in the block are
118+ # contiguous in MOI's ordering, so we just remap the first index
119+ # and reuse it as the consecutive offset. The block's length comes
120+ # from `Expression.block_shapes[i]`, which the caller threads
121+ # through separately.
122+ first_consec =
123+ moi_index_to_consecutive_index[MOI. VariableIndex (node. index)]
124+ new_nodes[i] = Node (NODE_VARIABLE_BLOCK, first_consec, node. parent)
99125 else
100126 new_nodes[i] = node
101127 end
@@ -122,10 +148,10 @@ function _classify_linearity(
122148 children_arr = SparseArrays. rowvals (adj)
123149 for k in length (nodes): - 1 : 1
124150 node = nodes[k]
125- if node. type == NODE_VARIABLE
151+ if node. type == NODE_VARIABLE || node . type == NODE_VARIABLE_BLOCK
126152 linearity[k] = LINEAR
127153 continue
128- elseif node. type == NODE_VALUE
154+ elseif node. type == NODE_VALUE || node . type == NODE_VALUE_BLOCK
129155 linearity[k] = CONSTANT
130156 continue
131157 elseif node. type == NODE_PARAMETER
@@ -218,24 +244,29 @@ function _classify_linearity(
218244end
219245
220246"""
221- _compute_gradient_sparsity!(
222- indices::Coloring.IndexedSet,
223- nodes::Vector{Nonlinear.Node},
224- )
247+ _compute_gradient_sparsity!(indices::Coloring.IndexedSet, f)
248+
249+ Compute the sparsity pattern of the gradient of an expression (that is, a list
250+ of which variable indices are present).
225251
226- Compute the sparsity pattern of the gradient of an expression (that is, a list of
227- which variable indices are present).
252+ `f` is duck-typed (as its type is defined later) to a
253+ `_SubexpressionStorage`-like object exposing `f.nodes` and `f.sizes`.
254+ For `NODE_VARIABLE_BLOCK` nodes, the block's length is read from `f.sizes`
255+ (via `_length`), and the block contributes that many consecutive variable
256+ indices starting at `nodes[k].index`.
228257"""
229- function _compute_gradient_sparsity! (
230- indices:: Coloring.IndexedSet ,
231- nodes:: Vector{Node} ,
232- )
233- for node in nodes
258+ function _compute_gradient_sparsity! (indices:: Coloring.IndexedSet , f)
259+ for (k, node) in enumerate (f. nodes)
234260 if node. type == NODE_VARIABLE
235261 push! (indices, node. index)
236- elseif node. type == NODE_MOI_VARIABLE
262+ elseif node. type == NODE_VARIABLE_BLOCK
263+ len = _length (f. sizes, k)
264+ for i in 0 : (len - 1 )
265+ push! (indices, node. index + i)
266+ end
267+ elseif node. type == NODE_MOI_VARIABLE || node. type == NODE_MOI_VARIABLE_BLOCK
237268 error (
238- " Internal error: Invalid to compute sparsity if NODE_MOI_VARIABLE " *
269+ " Internal error: Invalid to compute sparsity if $(node . type) " *
239270 " nodes are present." ,
240271 )
241272 end
@@ -341,6 +372,7 @@ function _compute_hessian_sparsity(
341372 child_group_variables = Dict {Int,Set{Int}} ()
342373 for (k, node) in enumerate (nodes)
343374 @assert node. type != NODE_MOI_VARIABLE
375+ @assert node. type != NODE_MOI_VARIABLE_BLOCK
344376 if input_linearity[k] == CONSTANT
345377 continue # No hessian contribution from constant nodes
346378 end
@@ -376,6 +408,13 @@ function _compute_hessian_sparsity(
376408 child_group_variables[child_group_idx],
377409 nodes[r]. index,
378410 )
411+ elseif nodes[r]. type == NODE_VARIABLE_BLOCK
412+ # TODO `NODE_VARIABLE_BLOCK` would need its block shape to
413+ # enumerate the variable indices.
414+ error (
415+ " Internal error: Hessian sparsity for " *
416+ " NODE_VARIABLE_BLOCK is not yet supported." ,
417+ )
379418 elseif nodes[r]. type == NODE_SUBEXPRESSION
380419 sub_vars = subexpression_variables[nodes[r]. index]
381420 if ! haskey (child_group_variables, child_group_idx)
0 commit comments