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

Fix SigFig Bug #344

Merged
merged 7 commits into from
Jun 17, 2024
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
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
Loading