Skip to content

Commit 9f7c7fa

Browse files
authored
[Nonlinear] Fix JacVec products (#1966)
1 parent 73b86f2 commit 9f7c7fa

File tree

2 files changed

+74
-4
lines changed

2 files changed

+74
-4
lines changed

src/Nonlinear/ReverseAD/mathoptinterface_api.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ function MOI.eval_constraint_jacobian_product(d::NLPEvaluator, y, x, w)
223223
d.jac_storage[col] = 0.0
224224
end
225225
_extract_reverse_pass(d.jac_storage, d, expr)
226-
for (k, col) in enumerate(expr.grad_sparsity)
227-
y[row] += d.jac_storage[k] * w[col]
226+
for col in expr.grad_sparsity
227+
y[row] += d.jac_storage[col] * w[col]
228228
end
229229
end
230230
return
@@ -243,8 +243,8 @@ function MOI.eval_constraint_jacobian_transpose_product(
243243
d.jac_storage[col] = 0.0
244244
end
245245
_extract_reverse_pass(d.jac_storage, d, expr)
246-
for (k, col) in enumerate(expr.grad_sparsity)
247-
y[col] += d.jac_storage[k] * w[row]
246+
for col in expr.grad_sparsity
247+
y[col] += d.jac_storage[col] * w[row]
248248
end
249249
end
250250
return

test/Nonlinear/ReverseAD.jl

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,76 @@ function _test_odd_chunks_Hessian_products(N)
918918
return
919919
end
920920

921+
function _dense_jacobian(jacobian_sparsity, V, m, n)
922+
I = [i for (i, j) in jacobian_sparsity]
923+
J = [j for (i, j) in jacobian_sparsity]
924+
raw = SparseArrays.sparse(I, J, V, m, n)
925+
return Matrix(raw)
926+
end
927+
928+
function test_jacobians_and_jacvec()
929+
model = Nonlinear.Model()
930+
x = MOI.VariableIndex.(1:3)
931+
a, b, c = x
932+
Nonlinear.set_objective(model, :($a * $b + $c^2))
933+
Nonlinear.add_constraint(model, :($c * $b), MOI.LessThan(1.0))
934+
Nonlinear.add_constraint(model, :($a^2 / 2), MOI.LessThan(1.0))
935+
evaluator = Nonlinear.Evaluator(model, Nonlinear.SparseReverseMode(), x)
936+
MOI.initialize(evaluator, [:Jac, :JacVec])
937+
values = [1.0, 2.0, 3.0] # For a, b, c.
938+
jacobian_sparsity = MOI.jacobian_structure(evaluator)
939+
V = zeros(length(jacobian_sparsity))
940+
MOI.eval_constraint_jacobian(evaluator, V, values)
941+
correct_jacobian = [0.0 3.0 2.0; 1.0 0.0 0.0]
942+
@test _dense_jacobian(jacobian_sparsity, V, 2, 3) correct_jacobian
943+
v = [2.4, 3.5, 1.2]
944+
product_storage = zeros(2)
945+
MOI.eval_constraint_jacobian_product(evaluator, product_storage, values, v)
946+
@test product_storage correct_jacobian * v
947+
w = [0.6, 4.3]
948+
product_storage = zeros(3)
949+
MOI.eval_constraint_jacobian_transpose_product(
950+
evaluator,
951+
product_storage,
952+
values,
953+
w,
954+
)
955+
@test product_storage correct_jacobian' * w
956+
return
957+
end
958+
959+
function test_jacobians_and_jacvec_with_subexpressions()
960+
model = Nonlinear.Model()
961+
x = MOI.VariableIndex.(1:3)
962+
a, b, c = x
963+
bc = Nonlinear.add_expression(model, :($b * $c))
964+
Nonlinear.set_objective(model, :($a * $b + $c^2))
965+
Nonlinear.add_constraint(model, :($bc), MOI.LessThan(1.0))
966+
Nonlinear.add_constraint(model, :($a^2 / 2), MOI.LessThan(1.0))
967+
evaluator = Nonlinear.Evaluator(model, Nonlinear.SparseReverseMode(), x)
968+
MOI.initialize(evaluator, [:Jac, :JacVec])
969+
values = [1.0, 2.0, 3.0] # For a, b, c.
970+
jacobian_sparsity = MOI.jacobian_structure(evaluator)
971+
V = zeros(length(jacobian_sparsity))
972+
MOI.eval_constraint_jacobian(evaluator, V, values)
973+
correct_jacobian = [0.0 3.0 2.0; 1.0 0.0 0.0]
974+
@test _dense_jacobian(jacobian_sparsity, V, 2, 3) correct_jacobian
975+
v = [2.4, 3.5, 1.2]
976+
product_storage = zeros(2)
977+
MOI.eval_constraint_jacobian_product(evaluator, product_storage, values, v)
978+
@test product_storage correct_jacobian * v
979+
w = [0.6, 4.3]
980+
product_storage = zeros(3)
981+
MOI.eval_constraint_jacobian_transpose_product(
982+
evaluator,
983+
product_storage,
984+
values,
985+
w,
986+
)
987+
@test product_storage correct_jacobian' * w
988+
return
989+
end
990+
921991
end # module
922992

923993
TestReverseAD.runtests()

0 commit comments

Comments
 (0)