Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Si pextension #67

Merged
merged 6 commits into from
Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .JuliaFormatter.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Configuration file for JuliaFormatter.jl, see: https://domluna.github.io/JuliaFormatter.jl/stable/config/

always_for_in = true
always_use_return = true
margin = 80
remove_extra_newlines = true
short_to_long_function_def = true
15 changes: 15 additions & 0 deletions .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: TagBot
on:
issue_comment:
types:
- created
workflow_dispatch:
jobs:
TagBot:
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
runs-on: ubuntu-latest
steps:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
ssh: ${{ secrets.DOCUMENTER_KEY }}
23 changes: 23 additions & 0 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Documentation
on:
push:
branches: [master]
tags: '*'
pull_request:
types: [opened, synchronize, reopened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@latest
with:
# Build documentation on Julia 1.0
version: '1.0'
- name: Install dependencies
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()'
- name: Build and deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key
run: julia --project=docs/ docs/make.jl
68 changes: 68 additions & 0 deletions src/eago_semiinfinite/interface/optimizer.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Base.@kwdef mutable struct SIPOptimizer <: MOI.AbstractOptimizer
objective_sense::MOI.OptimizationSense = MOI.FEASIBLE_SENSE
time_limit::Float64 = 1000.0
is_silent::Bool = false
x::Vector{Float64} = Float64[]
obj_bound::Float64 = 0.0
obj_value::Float64 = 0.0
termination_status::MOI.TerminationStatusCode = MOI.OPTIMIZE_NOT_CALLED
result_status_code::MOI.ResultStatusCode = MOI.UNKNOWN_RESULT_STATUS
run_time::Float64 = 0.0
end

function MOI.empty!(m::SIPOptimizer)
end
function MOI.is_empty(m::Optimizer)
end

function MOI.set(m::SIPOptimizer, ::MOI.Silent, value)
m.is_silent = value
return nothing
end

function MOI.set(m::SIPOptimizer, ::MOI.TimeLimitSec, value::Nothing)
m.time_limit = Inf
return nothing
end
function MOI.set(m::SIPOptimizer, ::MOI.TimeLimitSec, value::Float64)
m.time_limit = value
return nothing
end

MOI.set(m::SIPOptimizer, p::MOI.RawParameter, value) = error("The SIPOptimizer does not have any raw parameters.")

function MOI.get(m::SIPOptimizer, ::MOI.ListOfVariableIndices)
end
function MOI.get(m::SIPOptimizer, ::MOI.NumberOfVariables)
end

function MOI.get(m::SIPOptimizer, ::MOI.ObjectiveValue)

end
function MOI.get(m::SIPOptimizer, ::MOI.ObjectiveBound)

end

MOI.get(m::SIPOptimizer, p::MOI.RawParameter) = error("The SIPOptimizer does not have any raw parameters.")


function MOI.get(m::SIPOptimizer, ::MOI.RelativeGap)
abs(objective_bound() - objective_value())/objective_value()
end
MOI.get(m::SIPOptimizer, ::MOI.SolverName) = "EAGO: SIPOptimizer"
MOI.get(m::SIPOptimizer, ::MOI.TerminationStatus) = m._termination_status_code
MOI.get(m::SIPOptimizer, ::MOI.PrimalStatus) = m.result_status_code
MOI.get(m::SIPOptimizer, ::MOI.SolveTime) = m.run_time
MOI.get(m::SIPOptimizer, ::MOI.ResultCount) = (m.result_status_code === MOI.FEASIBLE_POINT) ? 1 : 0
MOI.get(m::SIPOptimizer, ::MOI.TimeLimitSec) = m.time_limit

MOI.supports(::SIPOptimizer, ::MOI.TimeLimitSec) = true
MOI.supports(::SIPOptimizer, ::MOI.ObjectiveSense) = true
function MOI.set(m::SIPOptimizer, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense)
m.objective_sense = sense
return nothing
end


function MOI.optimize!(m::SIPOptimizer)
end
107 changes: 16 additions & 91 deletions src/eago_semiinfinite/interface/scratch_sheet.jl
Original file line number Diff line number Diff line change
@@ -1,104 +1,29 @@

#=
=#



#=
Basic idea behind syntax
mSIP = SIPModel()

@decision_variable(mSIP, 0.0 <= x <= 1.0)
@decision_variable(mSIP, z, Bin)
@uncertain_variable(mSIP, p, 0.0 <= x <= 1.0)


@constraint(mSIP, ...)
@objective(mSIP, Min, ...)

@NLconstraint(mSIP, ...)
@NLobjective(mSIP, Min, ...)

optimize!()
=#

using JuMP

import JuMP.object_dictionary
function pseudo_copy(m::JuMP.Model)

@enum(SIPCons, DECISION, UNCERTAIN, SEMIINFINITE, SIPNOTSET)
nlp_new = deepcopy(m.nlp_data)
nlp_new.evaluator = NLPEvaluator(m)
MOI.initialize(nlp_new.evaluator, Symbol[:ExprGraph])

"""
"""
Base.@kwdef mutable struct SIPModel <: JuMP.AbstractModel
m::JuMP.Model = JuMP.Model()
p::Dict{JuMP.VariableRef,Bool} = Dict{JuMP.VariableRef,Bool}()
constr_type::Dict{JuMP.ConstraintRef, SIPCons} = Dict{JuMP.ConstraintRef, SIPCons}()
nl_constraint_type::Dict{Int, SIPCons} = Dict{Int, SIPCons}()
nl_expression_type::Dict{Int, SIPCons} = Dict{Int, SIPCons}()
end
obj_expr = MOI.objective_expr(nlp_new.evaluator)
con_expr = MOI.constraint_expr(nlp_new.evaluator, 1)

#=
One option is to fully extend JuMP... That requires alot of function definitions
and the only new functionality we actually want to add is classifying constraints
based on is it a decision variable or is it a
=#
macro decision_variable(args...)
esc(quote
mSIP = $args[1]
inputs_2f = $(args[2:end]...)
@show mSIP
@show inputs_2f
#inputs = $(esc(args))
#@show inputs
#@show typeof(inputs)
vi = @variable($(args...))
#model = $inputs[1]
#for vi in variable_indices
# model.p[vi] = false
#end
# return vi
end)
end
@show obj_expr
@show con_expr

mSIP = SIPModel()
@decision_variable(mSIP, x)

#=
using MathOptInterface
return nothing
end

m = Model()
@variable(m, 0 <= y <= 1)
@variable(m, x)
@variable(m, a, Bin)
@variable(m, q[i=1:2])
@constraint(m, y^2 + y + x<= 0)
@constraint(m, [x, y-1, y-2] in SecondOrderCone())
@NLconstraint(m, sin(x) + cos(y) <= 0.0)
@constraint(m, 2x - 1 ⟂ x)
@constraint(m, q in SOS2([3,5]))
@constraint(m, a => {x + y <= 1})
@SDconstraint(m, [x 2x; 3x 4x] >= ones(2, 2))
@variable(m, x[1:3])
@variable(m, y)
@NLconstraint(m, x[1] + y^2 <= 0.0)
@NLobjective(m, Min, x[2]*x[3]*y^2)

A = [1 2; 3 4]
b = [5,6]
@constraint(m, con, A * x .== b)
nlp_data = m.nlp_data


list = list_of_constraint_types(m)

cons0 = all_constraints(m, VariableRef, MOI.LessThan{Float64})
cons1 = all_constraints(m, VariableRef, MOI.GreaterThan{Float64})
cons2 = all_constraints(m, GenericQuadExpr{Float64,VariableRef}, MOI.LessThan{Float64})
cons2 = all_constraints(m, GenericQuadExpr{Float64,VariableRef}, MOI.LessThan{Float64})

cons0_1 = cons0[1]
cons1_1 = cons1[1]
cons2_1 = cons2[1]
cons3_1 = cons2[1]

out = constraint_object(cons2_1).func
l_terms = linear_terms(out)
q_terms = quad_terms(out)
#all_consts = all_constraints(m, list[1][1], list[1][1])
=#
out = pseudo_copy(m)
117 changes: 106 additions & 11 deletions src/eago_semiinfinite/interface/sip_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,90 @@ for a discussion of the roadmap here.
=#

@enum(SIPCons, DECISION, UNCERTAIN, SEMIINFINITE, SIPNOTSET)
const MOIU_AUTO_CACHE = MOIU.CachingOptimizerMode.AUTOMATIC


"""
"""
Base.@kwdef mutable struct SIPData
p::Dict{JuMP.VariableRef,Bool} = Dict{JuMP.VariableRef,Bool}()
constr_type::Dict{JuMP.ConstraintRef, SIPCons} = Dict{JuMP.ConstraintRef, SIPCons}()
nl_constraint_type::Dict{Int, SIPCons} = Dict{Int, SIPCons}()
nl_expression_type::Dict{Int, SIPCons} = Dict{Int, SIPCons}()
model_decision::Model = Model(caching_mode = MOIU_AUTO_CACHE)
model_uncertain::Model = Model((aching_mode = MOIU_AUTO_CACHE)
model_semiinfinite::Model = Model(caching_mode = MOIU_AUTO_CACHE)
end
uncertain_variable_num(d::SIPData) = any(x -> x, d.p)

function _copy_nlp_objective!(out::JuMP.Model, m::JuMP.Model, nlp_data::JuMP._NLPData)
if nlp_data.nlobj !== nothing
# TODO CHECK THAT THE FORMULATION IS PEACHY
obj_expr = MOI.objective_expr(nlp_new.evaluator)
# obj_expr_jref = substitute moi variables to jump refs...
@NLobjective(m.ext[:sip].model_decision, Min, obj_expr_jref)
end
return nothing
end

function _copy_nlp_constraints!(m::JuMP.Model, nlp_data::JuMP._NLPData)
for i = 1:length(nlp_data.nlconstr)
con_expr = MOI.constraint_expr(nlp_data.evaluator, i)
end
return nothing
end

"""

Populates variables and constraints into one of three models based on classification:
1) store decision variables and constraints containing only decision variables in
`m.model_decision`, 2) store uncertain variables and constraints containing only
uncertain variables in `m.model_uncertain`, 3) store all variables and all
constraints containing both decision variables to `m.model_semiinfinite`.
"""
function initialize_pure_models!(m::JuMP.Model)
if haskey(m.ext, :sip)

# extract sip data and storage models
sip_data = _get_sip_data(m)::SIPData
m_dec = sip_data.model_decision
m_unc = sip_data.model_uncertain
m_sip = sip_data.model_semiinfinite

# get index map
indx_map_dec = MOI.copy_to(backend(m_dec), backend(m), copy_names = true)
indx_map_unc = MOI.copy_to(backend(m_unc), backend(m), copy_names = true)
indx_map_sip = MOI.copy_to(backend(m_sip), backend(m), copy_names = true)

# copy extension data
for (key, data) in m.ext
if key != :sip
m_dec.ext[key] = copy_extension_data(data, m_dec, m)
m_unc.ext[key] = copy_extension_data(data, m_unc, m)
m_sip.ext[key] = copy_extension_data(data, m_sip, m)
end
end

# copy objects (TODO need to check on SIP, DEC, UNC)
reference_map = ReferenceMap(new_model, index_map)
for (name, value) in object_dictionary(model)
new_model[name] = getindex.(reference_map, value)
end

if m.nlp_data !== nothing
nlp_data = deepcopy(m.nlp_data)
nlp_data.evaluator = NLPEvaluator(m)
MOI.initialize(nlp_data.evaluator, Symbol[:ExprGraph])
copy_nlp_objective!(m, nlp_data)
copy_nlp_expressions!(m, nlp_data)
copy_nlp_constraints!(m, nlp_data)
end

for (vi, is_uncertain) in m.ext.p
model_semiinfinite
model_decision_
end
return nothing
end


function initialize_sip_data(m::JuMP.Model)
m.ext[:sip] = SIPData()
end
Expand All @@ -32,7 +105,8 @@ function sip_optimizehook(m::JuMP.Model; kwargs...)
if uncertain_variable_num(data) == 0.0
ret = JuMP.optimize!(m::JuMP.Model, ignore_optimize_hook = true, kwargs...)
else
# ret = model_sip_solve(m)
initialize_pure_models!(m)
ret = sip_solve(data)
end
return ret
end
Expand All @@ -46,6 +120,7 @@ end

function ModelWithSIP(args...; kwargs...)
m = JuMP.Model(args...; kwargs...)
set_optimizer(SIPOptimizer)
enable_semiinfinite(m)
return m
end
Expand All @@ -58,16 +133,36 @@ arguments `kw_args` and returns the variable.

@uncertain_variable(m, expr, args..., kw_args...)

This macro simply denotes the variable as belonging to the set of descision
This macro simply denotes the variable as belonging to the set of uncertain
variables and then performs `@variable(m, expr, args..., kw_args...)` to
add the variable to the basic JuMP model for storage. All JuMP syntax is
supported.
"""
macro uncertain_variable(args...)
vi = @variable(args...)
model = esc(args[1])
for vi in variable_indices
model.p[vi] = true
end
return vi
esc(quote
vi = @variable($(args...))
$(args[1]).ext[:sip].p[vi] = true
vi
end)
end

"""
@decision_variable

Add an *anonymous* variable to `m::SIPModel` described by the keyword
arguments `kw_args` and returns the variable.

@decision_variable(m, expr, args..., kw_args...)

This macro simply denotes the variable as belonging to the set of decision
variables and then performs `@variable(m, expr, args..., kw_args...)` to
add the variable to the basic JuMP model for storage. All JuMP syntax is
supported.
"""
macro decision_variable(args...)
esc(quote
vi = @variable($(args...))
$(args[1]).ext[:sip].p[vi] = false
vi
end)
end