Skip to content

Add Extension Friendly Variable Creation Methods#123

Merged
pulsipher merged 10 commits intoinfiniteopt:masterfrom
dnguyen227:make_variable
Sep 22, 2025
Merged

Add Extension Friendly Variable Creation Methods#123
pulsipher merged 10 commits intoinfiniteopt:masterfrom
dnguyen227:make_variable

Conversation

@dnguyen227
Copy link
Copy Markdown
Contributor

For the purposes of copying variables from one model to another, while also having the flexibility to modify properties before copy with the VariableProperties. Simple example below.

using JuMP
import DisjunctiveProgramming as DP

# Create original variable with properties
original_model = Model()
@variable(original_model, 2 <= x <= 10, start = 5.0)

# Extract variable properties
props = DP.VariableProperties(x)

#Capability to change property before  copying onto another model.
props.is_integer = true

# Create new model and recreate variable with same properties
new_model = Model()
x_copy = DP._variable_from_properties(new_model, props)

@pulsipher
Copy link
Copy Markdown
Collaborator

This is a good start, I would suggest the following:

# Stores all the info that could be used to create a JuMP variable
mutable struct VariableProperties{L, U, F, S, SET, T}
    info::JuMP.VariableInfo{L, U, F, S}
    name::String
    set::SET
    variable_type::T # this is critical for extensions
end 

# Default JuMP constructor (extensions will need to define this)
function VariableProperties(vref::JuMP.GenericVariableRef{T}) where T
    info = JuMP.VariableInfo(
        JuMP.has_lower_bound(vref),
        JuMP.has_lower_bound(vref) ? JuMP.lower_bound(vref) : zero(T),
        JuMP.has_upper_bound(vref),
        JuMP.has_upper_bound(vref) ? JuMP.upper_bound(vref) : zero(T),
        JuMP.is_fixed(vref),
        JuMP.is_fixed(vref) ? JuMP.fix_value(vref) : zero(T),
        !isnothing(JuMP.start_value(vref)),
        JuMP.start_value(vref),
        JuMP.is_binary(vref),
        JuMP.is_integer(vref)
    )
    name = JuMP.name(vref)
    set = JuMP.is_variable_in_set(vref) ? JuMP.moi_set(JuMP.constraint_object(JuMP.VariableInSetRef(vref))) : nothing
    return VariableProperties(info, name, set, nothing)
end

# Function for converting VariableProperties into variables (can be extended, but set up to avoid needing to do so)
function create_variable(model::JuMP.AbstractModel, props::VariableProperties)
    var = _make_variable_object(props)
    if !isnothing(props.set)
        var = JuMP.build_variable(error, var, props.set)
    end
    return JuMP.add_variable(model, var, props.name)
end

function _make_variable_object(props::VariableProperties{L, U, F, S, SET, Nothing}) where {L, U, F, S, SET}
    return JuMP.build_variable(error, props.info)
end
function _make_variable_object(props::VariableProperties)
    return JuMP.build_variable(error, props.info, props.variable_type)
end

Then we can use this to define useful functions for the reformulations, like copy:

function variable_copy(model::JuMP.AbstractModel, vref::JuMP.AbstractVariableRef)
    props = VariableProperties(vref)
    return create_variable(model, props)
end 

Such that we can do something like:

using JuMP

m = Model()
@variable(m, 0 <= x <= 4, Int)
@variable(m, p in Parameter(42))
x_copy = copy_variable(m, x)
m2 =Model()
p_copy = copy_variable(m2, p)

@dnguyen227
Copy link
Copy Markdown
Contributor Author

dnguyen227 commented Sep 20, 2025

Change has been made to use JuMP.VariableInfo to store the data.
*Note, modifications I make require another call of VariableProperties to create a new instance (JuMP.VariableInfo creates an immutable). Helper functions for changing to be included next week.

For example in my tests:

function test_make_variable_object()
    model = Model()
    
    @variable(model, x, lower_bound = 1.0, upper_bound = 5.0)
    props = DP.VariableProperties(x)


    # props.info.upper_bound = 10.0 <= can not set because Variable.Info is immutable

    modified_info = JuMP.VariableInfo(
        props.info.has_lb,
        props.info.lower_bound,
        props.info.has_ub,
        10.0,  # Modified upper bound
        props.info.has_fix,
        props.info.fixed_value,
        props.info.has_start,
        props.info.start,
        props.info.binary,
        props.info.integer
    )

    # Create new VariableProperties with modified info
    modified_props = DP.VariableProperties(modified_info, props.name, props.set, props.variable_type)

    var_obj = DP._make_variable_object(modified_props)
    @test var_obj.info.upper_bound == 10.0
end

Copy link
Copy Markdown
Collaborator

@pulsipher pulsipher left a comment

Choose a reason for hiding this comment

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

This is coming along. This PR should also update the existing transformation methods (e.g., Hull) to use these methods as appropriate.

Comment thread src/datatypes.jl Outdated
Comment thread src/variables.jl
Comment thread test/variables/creation.jl Outdated
@pulsipher pulsipher changed the title Add VariableProperties and _variable_from_properties. Add Extension Friendly Variable Creation Methods Sep 22, 2025
@pulsipher pulsipher added the enhancement New feature or request label Sep 22, 2025
Copy link
Copy Markdown
Collaborator

@pulsipher pulsipher left a comment

Choose a reason for hiding this comment

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

Almost there, just a few minor things and need to fix the tests.

Comment thread src/hull.jl Outdated
Comment thread src/hull.jl Outdated
Comment thread src/variables.jl
…w) tests, added variable docstrings, updated hull to include zero(T)
@dnguyen227
Copy link
Copy Markdown
Contributor Author

Almost there, just a few minor things and need to fix the tests.

Please give the workflows another try 🤞

@pulsipher pulsipher merged commit 2b964d4 into infiniteopt:master Sep 22, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants