Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
38be01b
Add ColGen interface and generic functions
guimarqu Jul 29, 2025
4aedcf0
feat(colgen): implement column generation framework with default impl…
guimarqu Jul 30, 2025
5d1fb64
feat(colgen): add MOI helper functions with keyword-based API
guimarqu Jul 30, 2025
4e8ad38
Implement artificial variables for column generation Phase 1
guimarqu Jul 31, 2025
c2b9814
Refactor column generation implementation
guimarqu Jul 31, 2025
64a6e3c
Reorganize artificial variable tests into dedicated dw_colgen.jl test…
guimarqu Jul 31, 2025
0c67c2c
Implement type-safe artificial variable tracking and comprehensive te…
guimarqu Jul 31, 2025
4d282f8
Add configurable convexity artificial variable cost to MixedPhase1and2
guimarqu Jul 31, 2025
4e85fa2
feat(ColGen): separate column generation into modular components
guimarqu Jul 31, 2025
afd2376
feat(ColGen): add dual solution collection for master problem
guimarqu Aug 1, 2025
eb267c3
feat(ColGen): add optimizer validation for master problem
guimarqu Aug 1, 2025
1a5bc41
feat(ColGen): store variable values in MasterPrimalSolution
guimarqu Aug 1, 2025
5dd31b2
feat(ColGen): implement reduced costs computation for column generation
guimarqu Aug 2, 2025
84ffd9c
test(ColGen): add unit test for update_reduced_costs! method
guimarqu Aug 2, 2025
5c0a290
refactor(ColGen): enhance column generation implementation with prici…
guimarqu Aug 2, 2025
a2b08cb
fix(ColGen): correct pricing solution bound handling and add subprobl…
guimarqu Aug 2, 2025
bae6ef6
feat(ColGen): implement complete insert_columns! function with unit t…
guimarqu Aug 2, 2025
5460107
feat(ColGen): implement dual bound computation for Dantzig-Wolfe deco…
guimarqu Aug 3, 2025
1fc1068
feat(ColGen): add column filtering and iteration logging
guimarqu Aug 3, 2025
1d3a256
refactor(ColGen): implement parametric DantzigWolfeColGenImpl for sim…
guimarqu Aug 4, 2025
1b84791
test(ColGen): add Wolsey integration test for manual column generation
guimarqu Aug 4, 2025
f52cff0
Remove use of JuMP.index from colgen impl
guimarqu Aug 5, 2025
5cb8b45
refactor(ColGen): convert wolsey test to use JuMP models for readability
guimarqu Aug 5, 2025
e8cdfcf
fix(ColGen): correct dual bound computation and reduced cost calculation
guimarqu Aug 6, 2025
c6717d5
feat(ColGen): add MasterPrimalSolution printing with JuMP and MOI mod…
guimarqu Aug 6, 2025
ba6bf49
feat(ColGen): add MasterDualSolution printing with variable bounds en…
guimarqu Aug 6, 2025
18c3de3
feat(ColGen): add recompute_cost method and comprehensive unit tests
guimarqu Aug 6, 2025
c920005
feat(ColGen): add comprehensive GAP E2E tests for column generation
guimarqu Aug 7, 2025
71497e0
refactor(ColGen): clean up test structure and add HiGHS dependency
guimarqu Aug 7, 2025
45d6afe
refactor(ColGen): implement unified solution architecture with type-s…
guimarqu Aug 7, 2025
6e03f68
refactor(ColGen): modularize source and test architecture
guimarqu Aug 9, 2025
61add63
objective constant in recompute_dual_solution
guimarqu Aug 9, 2025
6aee21a
take into account objective sense when retriving dual solution
guimarqu Aug 9, 2025
6a5e9c7
bug in reduced cost computation
guimarqu Aug 9, 2025
243622d
fixed bugs in reduced cost computation & dual bound computation
guimarqu Aug 9, 2025
de7f988
fix(ColGen): remove redundant │
guimarqu Aug 9, 2025
eb2bc34
update Project file
guimarqu Aug 9, 2025
91570a2
update Project
guimarqu Aug 9, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.claude
6 changes: 6 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ version = "0.1.0"
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
GLPK = "60bf3e95-4087-53dc-ae20-288a0d20c6a6"
HiGHS = "87dc4568-4c63-4d18-b0c0-bb2238e4078b"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
ReformulationKit = "ccf8701a-f1e2-43fb-9872-b69ec7886d0a"
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[sources]
ReformulationKit = {url = "https://github.com/nablarise/ReformulationKit.jl.git", rev = "main"}
50 changes: 50 additions & 0 deletions src/ColGen/ColGen.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

module ColGen

using MathOptInterface, ReformulationKit, JuMP
const MOI = MathOptInterface
const MOIU = MathOptInterface.Utilities
const RK = ReformulationKit

include("helpers.jl")
include("coluna.jl")
include("moi_solutions.jl")
include("dw_colgen.jl")
include("master_optimization.jl")
include("reduced_costs.jl")
include("pricing_optimization.jl")
include("dual_bounds.jl")
include("column_insertion.jl")
include("ip_management.jl")
include("dw_colgen_iteration.jl")
include("dw_stabilization.jl")

# Export helper functions
export add_variable!, add_constraint!


#### reformulation API
function get_master end
function get_reform end
function is_minimization end
function get_pricing_subprobs end

function run_column_generation(reformulation)
# Validate optimizer is attached before proceeding
master_moi = JuMP.backend(RK.master(reformulation))
if MOIU.state(master_moi) == MOIU.NO_OPTIMIZER
throw(ErrorException(
"""
No optimizer attached to the master problem.
Please attach an optimizer to the master model before running column generation.
Example: JuMP.set_optimizer(ReformulationKit.master(reformulation), HiGHS.Optimizer)
"""
))
end

context = DantzigWolfeColGenImpl(reformulation)
ip_primal_sol = nothing
run!(context, ip_primal_sol)
end

end
84 changes: 84 additions & 0 deletions src/ColGen/column_insertion.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Copyright (c) 2025 Nablarise. All rights reserved.
# Author: Guillaume Marques <guillaume@nablarise.com>
# SPDX-License-Identifier: Proprietary

function _compute_original_column_cost(column::PricingPrimalMoiSolution, original_cost_mapping::RK.OriginalCostMapping)
# Compute the original cost of the column using costs from the compact formulation
# This is ∑(c_i * x_i) where c_i are original variable costs and x_i are solution values
original_cost = 0.0
for (var_index, var_value) in column.solution.variable_values
if haskey(original_cost_mapping, var_index)
original_cost += original_cost_mapping[var_index] * var_value
end
end
return original_cost
end

function _compute_master_constraint_membership(
column::PricingPrimalMoiSolution,
coupling_mapping::RK.CouplingConstraintMapping,
master::Master
)
constraint_coeffs = Dict{MOI.ConstraintIndex, Float64}()
sp_id = column.subproblem_id

# Compute coupling constraint memberships (A * x for each constraint)
for (var_index, var_value) in column.solution.variable_values
coefficients = RK.get_variable_coefficients(coupling_mapping, var_index)
for (constraint_type, constraint_value, coeff) in coefficients
constraint_ref = constraint_type(constraint_value)
constraint_coeffs[constraint_ref] = get(constraint_coeffs, constraint_ref, 0.0) + coeff * var_value
end
end

# Add convexity constraint membership (coefficient = 1.0)
if haskey(master.convexity_constraints_ub, sp_id)
conv_constraint_ref = master.convexity_constraints_ub[sp_id]
constraint_coeffs[conv_constraint_ref] = 1.0
end
if haskey(master.convexity_constraints_lb, sp_id)
conv_constraint_ref = master.convexity_constraints_lb[sp_id]
constraint_coeffs[conv_constraint_ref] = 1.0
end

return constraint_coeffs
end

function insert_columns!(context::DantzigWolfeColGenImpl, ::MixedPhase1and2, columns_to_insert::PricingPrimalMoiSolutionToInsert)
master = get_master(context)
master_moi = moi_master(master)
pricing_subprobs = get_pricing_subprobs(context)

cols_inserted = 0

for column in columns_to_insert.collection
# Get subproblem information
sp_id = column.subproblem_id
pricing_sp = pricing_subprobs[sp_id]

# Compute original column cost (from compact formulation variable costs)
original_cost = _compute_original_column_cost(column, pricing_sp.original_cost_mapping)

# Compute master constraint membership (how much this solution contributes to each constraint)
constraint_memberships = _compute_master_constraint_membership(
column,
pricing_sp.coupling_constr_mapping,
master
)

# Add the column variable to master
# - Lower bound 0.0: convex combination coefficients are non-negative
# - Constraint coeffs: membership values computed above
# - Objective coeff: original cost from compact formulation
column_var = add_variable!(
master_moi;
lower_bound = 0.0,
constraint_coeffs = constraint_memberships,
objective_coeff = original_cost
)

cols_inserted += 1
end

return cols_inserted
end
Loading
Loading