Skip to content

Commit

Permalink
Fix SigFig Bug (#344)
Browse files Browse the repository at this point in the history
* fix sigfig bug

* Fix api

* fix function name

* fix typo

* doc updates

* fix typo

* fix test
  • Loading branch information
pulsipher authored Jun 17, 2024
1 parent dc1e303 commit 0edc47e
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 175 deletions.
29 changes: 21 additions & 8 deletions docs/src/develop/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ template is provided in
[`./test/extensions/infinite_domain.jl`](https://github.com/infiniteopt/InfiniteOpt.jl/blob/master/test/extensions/infinite_domain.jl).
The extension steps employed are:
1. Define the new `struct` infinite domain type (only thing required as bare minimum)
2. Extend [`InfiniteOpt.supports_in_domain`](@ref) (enables error checking of supports)
3. Extend [`InfiniteOpt.generate_support_values`](@ref) (enables support generation via `num_supports` keyword arguments)
4. If a lower bound and upper bound can be reported, extend `JuMP` lower bound and upper bound methods (enables automatic bound detection in `integral`)
5. Extend [`InfiniteOpt.MeasureToolbox.generate_expect_data`](@ref) (enables the use of `expect`)
2. Extend[`InfiniteOpt.round_domain`](@ref) (enables safe use of significant digit rounding)
3. Extend [`InfiniteOpt.supports_in_domain`](@ref) (enables error checking of supports)
4. Extend [`InfiniteOpt.generate_support_values`](@ref) (enables support generation via `num_supports` keyword arguments)
5. If a lower bound and upper bound can be reported, extend `JuMP` lower bound and upper bound methods (enables automatic bound detection in `integral`)
6. Extend [`InfiniteOpt.MeasureToolbox.generate_expect_data`](@ref) (enables the use of `expect`)

As an example, let's create a univariate disjoint interval domain as an infinite
domain type. This corresponds to the domain ``[lb_1, ub_1] \cup [lb_2, ub_2]``
Expand Down Expand Up @@ -82,13 +83,25 @@ to extend [`generate_integral_data`](@ref). See [`Measure Evaluation Techniques`
for details.

To enable support domain checking which is useful to avoid strange bugs, we will
extend [`InfiniteOpt.supports_in_domain`](@ref). This returns a `Bool` to
indicate if a vector of supports are in the domain:
extend [`InfiniteOpt.round_domain`](@ref) which rounds the domain to use proper
significant digits and [`InfiniteOpt.supports_in_domain`](@ref) which returns a
`Bool` whether a vector of supports is in the domain:
```jldoctest domain_ext; output = false
function InfiniteOpt.round_domain(
domain::DisjointDomain,
sig_digits::Int
)
lb1 = round(domain.lb1, sigdigits = sig_digits)
ub1 = round(domain.ub1, sigdigits = sig_digits)
lb2 = round(domain.lb2, sigdigits = sig_digits)
ub2 = round(domain.ub2, sigdigits = sig_digits)
return DisjointDomain(lb1, ub1, lb2, ub2)
end
function InfiniteOpt.supports_in_domain(
supports::Union{Number, Vector{<:Number}},
domain::DisjointDomain
)::Bool
)
return all((domain.lb1 .<= supports .<= domain.ub1) .| (domain.lb2 .<= supports .<= domain.ub2))
end
Expand All @@ -113,7 +126,7 @@ function InfiniteOpt.generate_support_values(
domain::DisjointDomain;
num_supports::Int = InfiniteOpt.DefaultNumSupports,
sig_digits::Int = InfiniteOpt.DefaultSigDigits
)::Tuple{Vector{Float64}, DataType}
)
length_ratio = (domain.ub1 - domain.lb1) / (domain.ub1 - domain.lb1 + domain.ub2 - domain.lb2)
num_supports1 = Int64(ceil(length_ratio * num_supports))
num_supports2 = num_supports - num_supports1
Expand Down
2 changes: 1 addition & 1 deletion docs/src/guide/constraint.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ argument. This includes a wealth of constraint types including:
For example, we could define the following semi-definite constraint:
```jldoctest constrs
julia> @constraint(model, [yb 2yb; 3yb 4yb] >= ones(2, 2), PSDCone())
[yb(t) - 1 2 yb(t) - 1;
[yb(t) - 1 2 yb(t) - 1
3 yb(t) - 1 4 yb(t) - 1] ∈ PSDCone(), ∀ t ∈ [0, 10]
```
See [`JuMP`'s constraint documentation](https://jump.dev/JuMP.jl/v1/manual/constraints/)
Expand Down
4 changes: 2 additions & 2 deletions docs/src/guide/derivative.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
```@meta
DocTestFilters = [r"≥|>=", r" == | = ", r" ∈ | in ", r" for all | ∀ ", r"d|∂",
r"integral|∫", r".*scalar_parameters.jl:783"]
r"integral|∫", r".*scalar_parameters.jl:790"]
```

# [Derivative Operators](@id deriv_docs)
Expand Down Expand Up @@ -502,7 +502,7 @@ julia> derivative_constraints(d1)
julia> add_supports(t, 0.2)
┌ Warning: Support/method changes will invalidate existing derivative evaluation constraints that have been added to the InfiniteModel. Thus, these are being deleted.
└ @ InfiniteOpt ~/work/infiniteopt/InfiniteOpt.jl/src/scalar_parameters.jl:783
└ @ InfiniteOpt ~/work/infiniteopt/InfiniteOpt.jl/src/scalar_parameters.jl:790
julia> has_derivative_constraints(d1)
false
Expand Down
1 change: 1 addition & 0 deletions docs/src/manual/domains.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ JuMP.set_lower_bound(::AbstractInfiniteDomain, ::Real)
JuMP.has_upper_bound(::AbstractInfiniteDomain)
JuMP.upper_bound(::AbstractInfiniteDomain)
JuMP.set_upper_bound(::AbstractInfiniteDomain, ::Real)
InfiniteOpt.round_domain
```

## Support Point Labels
Expand Down
55 changes: 30 additions & 25 deletions src/array_parameters.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,38 @@
# CORE DISPATCHVARIABLEREF METHOD EXTENSIONS
################################################################################
# Extend dispatch_variable_ref
function dispatch_variable_ref(model::InfiniteModel,
index::DependentParameterIndex
)::DependentParameterRef
function dispatch_variable_ref(
model::InfiniteModel,
index::DependentParameterIndex
)
return DependentParameterRef(model, index)
end

# Extend _add_data_object
function _add_data_object(model::InfiniteModel,
object::MultiParameterData
)::DependentParametersIndex
function _add_data_object(
model::InfiniteModel,
object::MultiParameterData
)
index = MOIUC.add_item(model.dependent_params, object)
push!(model.param_object_indices, index)
return index
end

# Extend _data_dictionary (type based)
function _data_dictionary(model::InfiniteModel,
::Type{DependentParameters})::MOIUC.CleverDict
function _data_dictionary(
model::InfiniteModel,
::Type{DependentParameters}
)
return model.dependent_params
end

# Extend _data_dictionary (ref based)
function _data_dictionary(pref::DependentParameterRef)::MOIUC.CleverDict
function _data_dictionary(pref::DependentParameterRef)
return JuMP.owner_model(pref).dependent_params
end

# Extend _data_object
function _data_object(pref::DependentParameterRef)::MultiParameterData
function _data_object(pref::DependentParameterRef)
object = get(_data_dictionary(pref), JuMP.index(pref).object_index, nothing)
if isnothing(object)
error("Invalid dependent parameter reference, cannot find ",
Expand All @@ -40,17 +44,17 @@ function _data_object(pref::DependentParameterRef)::MultiParameterData
end

# Extend _core_variable_object
function _core_variable_object(pref::DependentParameterRef)::DependentParameters
function _core_variable_object(pref::DependentParameterRef)
return _data_object(pref).parameters
end

# Return the number of dependent parameters involved
function _num_parameters(pref::DependentParameterRef)::Int
function _num_parameters(pref::DependentParameterRef)
return length(_data_object(pref).names)
end

# Extend _delete_data_object
function _delete_data_object(vref::DependentParameterRef)::Nothing
function _delete_data_object(vref::DependentParameterRef)
delete!(_data_dictionary(vref), JuMP.index(vref).object_index)
return
end
Expand All @@ -59,7 +63,7 @@ end
# PARAMETER DEFINITION
################################################################################
# Check that multi-dimensional domains are all the same
function _check_same_domain(_error::Function, domains)::Nothing
function _check_same_domain(_error::Function, domains)
if !_allequal(domains)
_error("Conflicting infinite domains. Only one multi-dimensional ",
"can be specified. Otherwise, scalar domains can be used ",
Expand All @@ -74,7 +78,7 @@ function _make_array_domain(
_error::Function,
domains::Vector{T},
inds::Collections.ContainerIndices
)::T where {T <: MultiDistributionDomain}
) where {T <: MultiDistributionDomain}
_check_same_domain(_error, domains)
dist = first(domains).distribution
if size(dist) != size(inds)
Expand All @@ -99,7 +103,7 @@ function _make_array_domain(
_error::Function,
domains::Vector{T},
inds::Collections.ContainerIndices
)::T where {T <: CollectionDomain}
) where {T <: CollectionDomain}
_check_same_domain(_error, domains)
if length(collection_domains(first(domains))) != length(inds)
_error("The dimensions of the parameters and the specified ",
Expand All @@ -125,7 +129,7 @@ function _make_array_domain(
_error::Function,
domains::Vector{T},
inds::Collections.ContainerIndices
)::T where {T <: InfiniteArrayDomain}
) where {T <: InfiniteArrayDomain}
_check_same_domain(_error, domains)
return first(domains)
end
Expand All @@ -135,7 +139,7 @@ function _make_array_domain(
_error::Function,
domains::Vector{T},
inds::Collections.ContainerIndices
)::CollectionDomain{T} where {T <: InfiniteScalarDomain}
) where {T <: InfiniteScalarDomain}
return CollectionDomain(domains)
end

Expand All @@ -151,7 +155,7 @@ function _process_supports(
supps::Vector{<:Real},
domain,
sig_digits
)::Dict{Vector{Float64}, Set{DataType}}
)
if !supports_in_domain(reshape(supps, length(supps), 1), domain)
_error("Support violates the infinite domain.")
end
Expand All @@ -165,7 +169,7 @@ function _process_supports(
vect_supps::Vector{<:Vector{<:Real}},
domain,
sig_digits
)::Dict{Vector{Float64}, Set{DataType}}
)
len = length(first(vect_supps))
if any(length(s) != len for s in vect_supps)
_error("Inconsistent support dimensions.")
Expand All @@ -185,7 +189,7 @@ function _process_derivative_methods(
_error::Function,
methods::V,
domains
)::V where {V <: Vector{<:NonGenerativeDerivativeMethod}}
) where {V <: Vector{<:NonGenerativeDerivativeMethod}}
return methods
end

Expand Down Expand Up @@ -219,6 +223,7 @@ function _build_parameters(
end
# process the infinite domain
domain = _make_array_domain(_error, domains, orig_inds)
domain = round_domain(domain, sig_digits)
# we have supports
if !isempty(supports)
supp_dict = _process_supports(_error, supports, domain, sig_digits)
Expand Down Expand Up @@ -272,7 +277,7 @@ function add_parameters(
model::InfiniteModel,
params::DependentParameters,
names::Vector{String}
)::Vector{GeneralVariableRef}
)
# get the number of parameters
num_params = length(params.domain)
# process the names
Expand All @@ -297,7 +302,7 @@ end
# NAMING
################################################################################
# Get the parameter index in the DependentParameters object
_param_index(pref::DependentParameterRef)::Int = JuMP.index(pref).param_index
_param_index(pref::DependentParameterRef) = JuMP.index(pref).param_index

"""
JuMP.name(pref::DependentParameterRef)::String
Expand All @@ -311,7 +316,7 @@ julia> name(pref)
"par_name"
```
"""
function JuMP.name(pref::DependentParameterRef)::String
function JuMP.name(pref::DependentParameterRef)
object = get(_data_dictionary(pref), JuMP.index(pref).object_index, nothing)
return isnothing(object) ? "" : object.names[_param_index(pref)]
end
Expand All @@ -330,7 +335,7 @@ julia> name(vref)
"para_name"
```
"""
function JuMP.set_name(pref::DependentParameterRef, name::String)::Nothing
function JuMP.set_name(pref::DependentParameterRef, name::String)
_data_object(pref).names[_param_index(pref)] = name
JuMP.owner_model(pref).name_to_param = nothing
return
Expand Down
Loading

0 comments on commit 0edc47e

Please sign in to comment.