Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fedad79
Remove need for the MOI fork
blegat Jan 6, 2026
d6b6156
Update ci
blegat Jan 6, 2026
06b3e00
Rename
blegat Jan 6, 2026
c871453
Fixes
blegat Jan 6, 2026
8e67e16
Correct typo and change model for Model
SophieL1 Jan 6, 2026
c7345b0
Fix tests
blegat Jan 6, 2026
e165a0e
Fix format
blegat Jan 6, 2026
b8f31cf
Fixes
blegat Jan 6, 2026
69dbb4f
Fix problems
SophieL1 Jan 8, 2026
8cc4f33
Remove need for the MOI fork
blegat Jan 6, 2026
daecc2b
Update ci
blegat Jan 6, 2026
678da5e
Rename
blegat Jan 6, 2026
e40b878
Fixes
blegat Jan 6, 2026
ae806d2
Correct typo and change model for Model
SophieL1 Jan 6, 2026
bb2d59d
Fix tests
blegat Jan 6, 2026
5ebb925
Fix format
blegat Jan 6, 2026
29fc603
Fixes
blegat Jan 6, 2026
98ee103
Fix problems
SophieL1 Jan 8, 2026
d56ae72
Merge branch 'main' into bl/fix_parse
SophieL1 Jan 8, 2026
48836eb
Merge remote-tracking branch 'origin/bl/fix_parse' into bl/fix_parse
SophieL1 Jan 8, 2026
41ca187
Change tests accordingly when merging with main
SophieL1 Jan 8, 2026
1239956
Correct format
SophieL1 Jan 8, 2026
3948bfe
Copy-paste OperatorRegistry, Model and Evaluator from MOI.Nonlinear
SophieL1 Jan 15, 2026
75432f3
Copy-paste functions we need from MOI.Nonlinear and change to ours
SophieL1 Jan 15, 2026
ba548b0
We seem to need this?
SophieL1 Jan 15, 2026
4ecd3ff
Change to our functions in ArrayDiff tests
SophieL1 Jan 15, 2026
e77013c
Change to our functions in ReverseAD tests
SophieL1 Jan 15, 2026
6d6cb7e
Delete double definition and change places
SophieL1 Jan 16, 2026
3ad14fc
Format and remove unused
SophieL1 Jan 16, 2026
08bcf64
Remove unused
SophieL1 Feb 3, 2026
95407fc
Reorganise MOI_Nonlinear_fork into operators, model, parse and evaluator
SophieL1 Feb 3, 2026
c034297
Format
SophieL1 Feb 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"

[compat]
DataStructures = "0.18, 0.19"
Expand Down
1 change: 1 addition & 0 deletions src/ArrayDiff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ForwardDiff
import MathOptInterface as MOI
const Nonlinear = MOI.Nonlinear
import SparseArrays
import OrderedCollections: OrderedDict

"""
Mode() <: AbstractAutomaticDifferentiation
Expand Down
117 changes: 108 additions & 9 deletions src/MOI_Nonlinear_fork.jl
Original file line number Diff line number Diff line change
@@ -1,19 +1,118 @@
# Inspired by MathOptInterface/src/Nonlinear/parse_expression.jl

const DEFAULT_MULTIVARIATE_OPERATORS = [
:+,
:-,
:*,
:^,
:/,
:ifelse,
:atan,
:min,
:max,
:vect,
:dot,
:hcat,
:vcat,
:norm,
:sum,
:row,
]

struct OperatorRegistry
# NODE_CALL_UNIVARIATE
univariate_operators::Vector{Symbol}
univariate_operator_to_id::Dict{Symbol,Int}
univariate_user_operator_start::Int
registered_univariate_operators::Vector{MOI.Nonlinear._UnivariateOperator}
# NODE_CALL_MULTIVARIATE
multivariate_operators::Vector{Symbol}
multivariate_operator_to_id::Dict{Symbol,Int}
multivariate_user_operator_start::Int
registered_multivariate_operators::Vector{
MOI.Nonlinear._MultivariateOperator,
}
# NODE_LOGIC
logic_operators::Vector{Symbol}
logic_operator_to_id::Dict{Symbol,Int}
# NODE_COMPARISON
comparison_operators::Vector{Symbol}
comparison_operator_to_id::Dict{Symbol,Int}
function OperatorRegistry()
univariate_operators = copy(DEFAULT_UNIVARIATE_OPERATORS)
multivariate_operators = copy(DEFAULT_MULTIVARIATE_OPERATORS)
logic_operators = [:&&, :||]
comparison_operators = [:<=, :(==), :>=, :<, :>]
return new(
# NODE_CALL_UNIVARIATE
univariate_operators,
Dict{Symbol,Int}(
op => i for (i, op) in enumerate(univariate_operators)
),
length(univariate_operators),
_UnivariateOperator[],
# NODE_CALL
multivariate_operators,
Dict{Symbol,Int}(
op => i for (i, op) in enumerate(multivariate_operators)
),
length(multivariate_operators),
_MultivariateOperator[],
# NODE_LOGIC
logic_operators,
Dict{Symbol,Int}(op => i for (i, op) in enumerate(logic_operators)),
# NODE_COMPARISON
comparison_operators,
Dict{Symbol,Int}(
op => i for (i, op) in enumerate(comparison_operators)
),
)
end
end

"""
Model()

The core datastructure for representing a nonlinear optimization problem.

It has the following fields:
* `objective::Union{Nothing,Expression}` : holds the nonlinear objective
function, if one exists, otherwise `nothing`.
* `expressions::Vector{Expression}` : a vector of expressions in the model.
* `constraints::OrderedDict{ConstraintIndex,Constraint}` : a map from
[`ConstraintIndex`](@ref) to the corresponding [`Constraint`](@ref). An
`OrderedDict` is used instead of a `Vector` to support constraint deletion.
* `parameters::Vector{Float64}` : holds the current values of the parameters.
* `operators::OperatorRegistry` : stores the operators used in the model.
"""
mutable struct Model
objective::Union{Nothing,MOI.Nonlinear.Expression}
expressions::Vector{MOI.Nonlinear.Expression}
constraints::OrderedDict{
MOI.Nonlinear.ConstraintIndex,
MOI.Nonlinear.Constraint,
}
parameters::Vector{Float64}
operators::OperatorRegistry
# This is a private field, used only to increment the ConstraintIndex.
last_constraint_index::Int64
function Model()
model = MOI.Nonlinear.Model()
ops = [:vect, :dot, :hcat, :vcat, :norm, :sum, :row]
start = length(model.operators.multivariate_operators)
append!(model.operators.multivariate_operators, ops)
for (i, op) in enumerate(ops)
model.operators.multivariate_operator_to_id[op] = start + i
end
return model
end
end

function set_objective(model::MOI.Nonlinear.Model, obj)
model.objective = parse_expression(model, obj)
return
end

function Model()
model = MOI.Nonlinear.Model()
append!(
model.operators.multivariate_operators,
[:vect, :dot, :hcat, :vcat, :norm, :sum, :row],
)
return model
end

function parse_expression(data::MOI.Nonlinear.Model, input)
expr = MOI.Nonlinear.Expression()
parse_expression(data, expr, input, -1)
Expand Down
5 changes: 2 additions & 3 deletions src/reverse_mode.jl
Original file line number Diff line number Diff line change
Expand Up @@ -430,9 +430,8 @@ function _reverse_eval(f::_SubexpressionStorage)
node = f.nodes[k]
children_indices = SparseArrays.nzrange(f.adj, k)
if node.type == MOI.Nonlinear.NODE_CALL_MULTIVARIATE
if node.index in
eachindex(MOI.Nonlinear.DEFAULT_MULTIVARIATE_OPERATORS)
op = MOI.Nonlinear.DEFAULT_MULTIVARIATE_OPERATORS[node.index]
if node.index in eachindex(DEFAULT_MULTIVARIATE_OPERATORS)
op = DEFAULT_MULTIVARIATE_OPERATORS[node.index]
if op == :vect
@assert _eachindex(f.sizes, k) ==
eachindex(children_indices)
Expand Down
7 changes: 2 additions & 5 deletions src/sizes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,11 @@ function _infer_sizes(
children_indices = SparseArrays.nzrange(adj, k)
N = length(children_indices)
if node.type == Nonlinear.NODE_CALL_MULTIVARIATE
if !(
node.index in
eachindex(MOI.Nonlinear.DEFAULT_MULTIVARIATE_OPERATORS)
)
if !(node.index in eachindex(DEFAULT_MULTIVARIATE_OPERATORS))
# TODO user-defined operators
continue
end
op = MOI.Nonlinear.DEFAULT_MULTIVARIATE_OPERATORS[node.index]
op = DEFAULT_MULTIVARIATE_OPERATORS[node.index]
if op == :vect
_assert_scalar_children(
sizes,
Expand Down
1 change: 1 addition & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to add it to the tests, it's just a dependency of ArrayDiff