Skip to content

Conversation

@guimarqu
Copy link
Member

@guimarqu guimarqu commented May 6, 2025

PR Type

Enhancement, Tests


Description

  • Introduce integrality state tracking for integer and binary variables.

    • Add integrality_state.jl for integrality relaxation/restriction.
    • Track original integer and binary variables in DomainChangeTrackerHelper.
  • Enhance domain change tracking to support integrality and binary constraints.

    • Register and manage integer/binary constraints in helper.
    • Update change application logic for integrality.
  • Add comprehensive tests for integrality and domain change tracking.

    • Test tracking and relaxation/restriction for continuous, integer, and binary variables.
    • Test integrality relaxation and recovery in models.
  • Add a new example: Circle Packing Optimization.

    • Implements a JuMP-based circle packing model.
    • Integrates with NablaMatheuristicKit for tree search and DOT visualization.

Changes walkthrough 📝

Relevant files
Enhancement
integrality_state.jl
Add integrality state tracking and change management         

src/MathOptState/integrality_state.jl

  • Introduces integrality state tracker for integer and binary variables.
  • Implements relaxation/restriction logic for integrality and binary
    constraints.
  • Defines change types and diff structures for integrality changes.
  • Provides methods to apply and merge integrality changes.
  • +129/-0 
    var_bounds_state.jl
    Enhance domain change tracker for integrality/binary support

    src/MathOptState/var_bounds_state.jl

  • Extends DomainChangeTrackerHelper to track integer and binary
    constraints.
  • Registers original integer/binary variables for branching.
  • Adds constraint registration for integer and binary types.
  • Adds debug logging for bound changes.
  • Adds constructor for DomainChangeDiff from change vectors.
  • +39/-37 
    MathOptState.jl
    Include integrality state tracking module                               

    src/MathOptState/MathOptState.jl

    • Includes new integrality_state.jl for integrality state tracking.
    +1/-0     
    circle_packing.jl
    Add circle packing optimization example with tree search 

    examples/circle_packing.jl

  • Implements a full JuMP-based circle packing optimization example.
  • Integrates with NablaMatheuristicKit for tree search and branching.
  • Tracks integrality and binary variables for branching.
  • Generates DOT files for decision tree visualization.
  • +438/-0 
    Tests
    MathOptStateTests.jl
    Add tests for integrality and domain change tracking         

    test/MathOptStateTests/MathOptStateTests.jl

  • Adds tests for domain change tracker with continuous, binary, and
    integer variables.
  • Adds tests for integrality relaxation and recovery.
  • Verifies correct registration and state transitions for
    integrality/binary variables.
  • +199/-0 

    Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • @guimarqu guimarqu merged commit 59165e9 into main May 6, 2025
    2 checks passed
    @guimarqu guimarqu deleted the circle_packing_example branch May 6, 2025 13:47
    @qodo-code-review
    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Commented Code

    There are commented-out merge functions for integrality changes that should either be implemented or removed. These functions would be needed for proper merging of integrality changes.

    # merge_forward_change_diff(
    #     parent_forward_diff::IntegralityChangeDiff,
    #     local_forward_change::IntegralityChangeDiff
    # ) = IntegralityChangeDiff(
    #     vcat(parent_forward_diff.integrality_changes, local_forward_change.integrality_changes)
    # )
    
    # merge_backward_change_diff(
    #     parent_backward_diff::IntegralityChangeDiff,
    #     local_backward_change::IntegralityChangeDiff
    # ) = IntegralityChangeDiff(
    #     vcat(parent_backward_diff.integrality_changes, local_backward_change.integrality_changes)
    # )
    Debugging Code

    The example contains commented-out code and debug statements like @show that should be removed before merging to maintain code cleanliness.

    #container_radii = [20.0, 15.0]
    
    # Define 15 items with different radii and rewards
    num_items = 15
    #num_items = 7
    Random.seed!(42)  # For reproducibility
    
    # Generate random radii between 5 and 15
    item_radii = round.(5.0 .+ 10.0 .* rand(num_items))
    
    @show container_radii
    @show item_radii
    Error Handling

    The circle packing example lacks error handling for solver failures or infeasible problems, which could lead to crashes in production use.

    function solve_circle_packing(problem::CirclePackingProblem; time_limit_seconds::Float64=600.0)
        model = build_jump_model(problem)
        tracker = NMK.MathOptState.DomainChangeTracker()
        helper = NMK.MathOptState.transform_model!(tracker, JuMP.backend(model))
    
        original_state = NMK.MathOptState.root_state(tracker, JuMP.backend(model))
        relaxed_state = NMK.MathOptState.relax_integrality!(JuMP.backend(model), helper)
        NMK.MathOptState.recover_state!(JuMP.backend(model), original_state, relaxed_state, helper)
    
        root_state = NMK.MathOptState.root_state(tracker, JuMP.backend(model))
    
        space = CirclePackingSearchSpace(problem, model, helper, root_state, tracker; node_limit=1000)
    
        strategy = BestValueSearchStrategy()
        result = NMK.TreeSearch.search(strategy, space)
    end

    @qodo-code-review
    Copy link

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Implement proper search priority

    The current implementation returns node.value_guess which is always 0.0 as set
    in the children function. This makes the best-first search ineffective.
    Implement a proper priority calculation based on the objective value or a
    heuristic estimate.

    examples/circle_packing.jl [394-396]

     function NMK.TreeSearch.get_priority(::BestValueSearchStrategy, space::CirclePackingSearchSpace, node::CirclePackingNode)
    -    return node.value_guess
    +    # Apply the node's state to evaluate its objective value
    +    NMK.MathOptState.apply_change!(JuMP.backend(space.model), NMK.MathOptState.forward(node.state), space.helper)
    +    optimize!(space.model)
    +    
    +    # Higher objective value is better for maximization problems
    +    priority = JuMP.objective_value(space.model)
    +    
    +    # Restore the model state
    +    NMK.MathOptState.apply_change!(JuMP.backend(space.model), NMK.MathOptState.backward(node.state), space.helper)
    +    
    +    return priority
     end
    • Apply / Chat
    Suggestion importance[1-10]: 9

    __

    Why: The suggestion correctly identifies a critical issue: node.value_guess is always initialized to 0.0, rendering the BestValueSearchStrategy ineffective as all nodes would have the same priority. The proposed improved_code provides a standard and correct way to calculate node priority by solving the optimization problem at the node's state, which is essential for a meaningful best-first search.

    High
    Uncomment merge functions

    Uncomment the merge functions for IntegralityChangeDiff. These functions are
    essential for properly combining parent and local changes during tree search
    operations. Without them, integrality changes won't propagate correctly through
    the search tree.

    src/MathOptState/integrality_state.jl [96-108]

    -# merge_forward_change_diff(
    -#     parent_forward_diff::IntegralityChangeDiff,
    -#     local_forward_change::IntegralityChangeDiff
    -# ) = IntegralityChangeDiff(
    -#     vcat(parent_forward_diff.integrality_changes, local_forward_change.integrality_changes)
    -# )
    +merge_forward_change_diff(
    +    parent_forward_diff::IntegralityChangeDiff,
    +    local_forward_change::IntegralityChangeDiff
    +) = IntegralityChangeDiff(
    +    vcat(parent_forward_diff.integrality_changes, local_forward_change.integrality_changes)
    +)
     
    -# merge_backward_change_diff(
    -#     parent_backward_diff::IntegralityChangeDiff,
    -#     local_backward_change::IntegralityChangeDiff
    -# ) = IntegralityChangeDiff(
    -#     vcat(parent_backward_diff.integrality_changes, local_backward_change.integrality_changes)
    -# )
    +merge_backward_change_diff(
    +    parent_backward_diff::IntegralityChangeDiff,
    +    local_backward_change::IntegralityChangeDiff
    +) = IntegralityChangeDiff(
    +    vcat(parent_backward_diff.integrality_changes, local_backward_change.integrality_changes)
    +)
    • Apply / Chat
    Suggestion importance[1-10]: 7

    __

    Why: The suggestion to uncomment merge_forward_change_diff and merge_backward_change_diff for IntegralityChangeDiff is likely correct. These provide specific implementations for merging IntegralityChangeDiff objects, which is important for the new integrality state tracking if the framework relies on multiple dispatch for these merge operations. Without them, changes to integrality state might not propagate correctly during tree search.

    Medium
    • More

    @codecov
    Copy link

    codecov bot commented May 6, 2025

    Welcome to Codecov 🎉

    Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

    ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

    Thanks for integrating Codecov - We've got you covered ☂️

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants