Class: LpSolver::Drivers::CliDriver

Inherits:
Object
  • Object
show all
Defined in:
lib/lpsolver/drivers/cli_driver.rb

Overview

Solves a model using the HiGHS command-line interface.

This driver serializes the model to HiGHS LP format, invokes the HiGHS executable as a subprocess, and parses the solution file.

Constant Summary collapse

HIGHS_PATH =

The path to the HiGHS binary.

Resolution order:

1. Path passed to initialize
2. HIGHS_PATH environment variable
3. Bundled binary at lib/lpsolver/highs (from rake compile)
4. 'highs' on system PATH

Returns:

  • (String)

    The path to the HiGHS executable.

begin
  env_path = ENV.fetch('HIGHS_PATH', nil)
  if env_path
    env_path
  else
    bundled = File.expand_path('../../../lib/lpsolver/highs', __dir__)
    if File.exist?(bundled)
      bundled
    else
      'highs'
    end
  end
end

Instance Method Summary collapse

Constructor Details

#initialize(highs_path: nil) ⇒ CliDriver

Creates a new CLI driver.

Parameters:

  • highs_path (String, nil) (defaults to: nil)

    Path to the HiGHS binary. If nil, uses the default resolution order.



40
41
42
# File 'lib/lpsolver/drivers/cli_driver.rb', line 40

def initialize(highs_path: nil)
  @highs_path = highs_path || HIGHS_PATH
end

Instance Method Details

#solve(model) ⇒ Solution

Solves the model using the HiGHS CLI.

Parameters:

  • model (Model)

    The model to solve.

Returns:

Raises:

  • (SolverError)

    If the HiGHS solver encounters an error.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/lpsolver/drivers/cli_driver.rb', line 49

def solve(model)
  lp_content = model.to_lp
  lp_file = Tempfile.new(['model', '.lp'])
  lp_file.write(lp_content)
  lp_file.close

  solution_file = Tempfile.new(['solution', '.sol'])
  opts_file = Tempfile.new(['highs_opts', '.txt'])
  opts_file.write("log_to_console = false\noutput_flag = false\n")
  opts_file.close

  cmd = "#{@highs_path} " \
        "--model_file #{lp_file.path} " \
        "--options_file #{opts_file.path} " \
        "--solution_file #{solution_file.path}"

  output = `#{cmd} 2>&1`
  lp_file.unlink
  opts_file.unlink

  # HiGHS returns non-zero exit code for infeasible/unbounded problems,
  # but still writes a valid solution file. Check for valid status instead.
  solution_content = File.read(solution_file.path)
  status_match = solution_content.match(/Model status\s*\n\s*(\S+)/i)
  unless status_match
    raise SolverError, "HiGHS solver failed:\n#{output}" unless $?.success?
  end

  parse_solution(solution_file.path)
ensure
  solution_file&.unlink
  opts_file&.unlink
end