Skip to content

Added more options to gurobi solver - #14

Open
areleu wants to merge 8 commits into
rust-or:masterfrom
areleu:rebase-upstream
Open

areleu wants to merge 8 commits into
rust-or:masterfrom
areleu:rebase-upstream

Conversation

@areleu

@areleu areleu commented Jul 8, 2026

Copy link
Copy Markdown

Hi, I had these features of the Gurobi solver laying around for a while and I thought it wouldn't be bad to have them in the main repo. Let me know if you need some changes.

Comment thread src/solvers/gurobi.rs
Comment on lines +80 to +81
/// Add solving method using gurobi syntax
pub fn with_algorithm(&self, algorithm: u32) -> GurobiSolver {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

algorithm: u32 is obscure. This should probably be an enum, or at least very clearly documented.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I could just rename it to method: https://docs.gurobi.com/projects/optimizer/en/current/reference/parameters.html#method I don't quite remember why I called it like that, probably for it to be consistent with other APIs

Comment thread src/solvers/gurobi.rs
Comment on lines +92 to +93
/// Tell gurobi to use crossover
pub fn with_crossover(&self, crossover: u32) -> GurobiSolver {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same here, crossover: u32 does not make sense to me. And the doc comment is way too short

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should expose it as native rust types, not mirror the C interface, and have our own documentation for our own types. In this case, it might look like this

/// Controls whether and how the solver converts an interior-point solution into
/// a basic solution.
///
/// Barrier and PDHG algorithms usually produce an *interior* solution: a solution
/// that satisfies the model well, but is not necessarily positioned on a clear
/// set of active variable bounds and constraints.
///
/// Some users need a *basic* solution instead. A basic solution is the kind of
/// corner-point solution typically produced by simplex methods. It is often more
/// useful when inspecting basis status, reduced costs, shadow prices, or when
/// passing the solution into later simplex-based work.
///
/// Crossover is the post-processing step that performs this conversion. It first
/// tries to move selected primal or dual quantities to their bounds, then uses a
/// simplex cleanup phase to repair any remaining infeasibility.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Crossover {
    /// Let the solver choose the crossover behavior automatically.
    ///
    /// This is usually the best choice unless there is a specific reason to
    /// control the crossover sequence. The solver may choose based on model
    /// structure, numerical behavior, and the algorithm used to solve the
    /// relaxation.
    #[default]
    Auto,

    /// Do not run crossover.
    ///
    /// With this setting, the solver returns the interior solution produced by
    /// barrier or PDHG directly. This may be faster when a basic solution is not
    /// needed, but the returned solution can be less suitable for workflows that
    /// rely on basis information or simplex-style post-processing.
    ///
    /// Disabling crossover is not available for every model type or algorithm
    /// combination. Those compatibility rules should be checked when the
    /// parameter is applied to a model.
    Disabled,

    /// Run crossover using an explicit push order and cleanup method.
    ///
    /// This gives callers control over two decisions:
    ///
    /// - whether primal or dual quantities are pushed to bounds first;
    /// - whether the final cleanup is performed with primal or dual simplex.
    ///
    /// Most users should prefer [`Crossover::Auto`]. This option is useful when
    /// reproducing solver behavior, comparing strategies, or tuning difficult
    /// models.
    Strategy {
        /// The order in which crossover performs its two push phases.
        ///
        /// A push phase tries to move either primal or dual quantities onto
        /// bounds so that the interior solution becomes closer to a basic
        /// solution before simplex cleanup begins.
        push_order: PushPhaseOrder,

        /// The simplex algorithm used for the final cleanup phase.
        ///
        /// Cleanup removes any remaining primal or dual infeasibilities after
        /// the push phases have finished.
        cleanup: CrossoverCleanup,
    },
}

/// Specifies which crossover push phase runs first.
///
/// Crossover has two push phases:
///
/// - a *primal* push, which works on the model variables directly;
/// - a *dual* push, which works on dual information associated with constraints
///   and bounds.
///
/// The order can affect performance and numerical behavior, but it does not
/// change the mathematical model being solved.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PushPhaseOrder {
    /// Run the dual push phase first, followed by the primal push phase.
    ///
    /// This prioritizes making the dual side of the solution more basis-like
    /// before pushing primal variables to bounds.
    DualThenPrimal,

    /// Run the primal push phase first, followed by the dual push phase.
    ///
    /// This prioritizes moving model variables toward bounds before working on
    /// the dual side of the solution.
    PrimalThenDual,
}

/// Specifies which simplex method is used during crossover cleanup.
///
/// After the push phases, the solution may still have small primal or dual
/// infeasibilities. The cleanup phase uses simplex to finish the conversion to a
/// basic solution.
///
/// Primal and dual simplex are closely related algorithms. Either can be a good
/// choice depending on the model and the state of the solution after the push
/// phases.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CrossoverCleanup {
    /// Use primal simplex for the cleanup phase.
    ///
    /// Primal simplex focuses on maintaining primal feasibility while improving
    /// the remaining basis structure. It can be preferable when the post-push
    /// solution is already close to primal feasible.
    PrimalSimplex,

    /// Use dual simplex for the cleanup phase.
    ///
    /// Dual simplex focuses on maintaining dual feasibility while repairing the
    /// remaining primal side. It can be preferable when the post-push solution is
    /// already close to dual feasible.
    DualSimplex,
}

Comment thread src/solvers/gurobi.rs
}

/// Toggle verbose
pub fn set_verbose(&self, verbose: u32) -> GurobiSolver {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same as above, we want to provide a rust interface, documented in its own right

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for logging, we probably want a bool that defaults to false

Comment thread src/solvers/gurobi.rs
}

/// Toggle predual
pub fn set_predual(&self, predual: u32) -> GurobiSolver {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

idem

Comment thread src/solvers/gurobi.rs
}

/// Toggle predual
pub fn with_barconvtol(&self, barconvtol: f32) -> GurobiSolver {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

idem

@lovasoa

lovasoa commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

I think we need better docs and better types.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants