Class: LpSolver::Drivers::NativeDriver
- Inherits:
-
Object
- Object
- LpSolver::Drivers::NativeDriver
- Defined in:
- lib/lpsolver/drivers/native_driver.rb
Overview
Solves a model using the native C extension.
This driver builds HiGHS data structures directly from the model and calls the native C API, bypassing LP file serialization. It requires the native extension to be compiled and loaded.
Instance Method Summary collapse
-
#solve(model) ⇒ Solution
Solves the model using the native C extension.
Instance Method Details
#solve(model) ⇒ Solution
Solves the model using the native C extension.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/lpsolver/drivers/native_driver.rb', line 18 def solve(model) unless defined?(LpSolver::HiGhSSolver) raise LoadError, 'Native extension not available. Compile with: rake compile' end num_col = model.var_counter num_row = model.constraints.size # Determine heading — native API always minimizes, so negate for maximize heading = model.heading == :maximize ? :maximize : :minimize # Build column arrays col_cost = Array.new(num_col, 0.0) col_lower = Array.new(num_col) col_upper = Array.new(num_col) col_integrality = Array.new(num_col, 0) model.var_bounds.each do |name, (lb, ub)| idx = model.variables[name].index col_lower[idx] = lb col_upper[idx] = ub col_integrality[idx] = model.var_types[name] == :integer ? 1 : 0 end model.objective.each do |idx, coeff| col_cost[idx] = heading == :maximize ? -coeff.to_f : coeff.to_f end # Build constraint arrays row_lower = Array.new(num_row) row_upper = Array.new(num_row) model.constraints.each_with_index do |constr, row_idx| row_lower[row_idx] = constr[:lb] row_upper[row_idx] = constr[:ub] end # Build matrix in CSC (column-wise) format # a_start[col] = index into a_index/a_value where column `col` starts # a_index[nz] = row index of non-zero element # a_value[nz] = value of non-zero element nz_per_col = Array.new(num_col, 0) nz_entries = [] model.constraints.each_with_index do |constr, row_idx| constr[:expr].each do |col_idx, coeff| nz_entries << [col_idx, row_idx, coeff.to_f] nz_per_col[col_idx] += 1 end end total_nz = nz_entries.size # Build a_start (cumulative column starts) a_start = [0] nz_per_col.each { |count| a_start << a_start.last + count } # Sort entries by column index for CSC format nz_entries.sort_by! { |col, row, _| col } aindex = nz_entries.map { |_, row, _| row } avalues = nz_entries.map { |_, _, val| val } # Call native solver (skip if no columns/rows) if num_col > 0 && num_row > 0 result = call_native( num_col, num_row, total_nz, col_cost, col_lower, col_upper, col_integrality, row_lower, row_upper, a_start, aindex, avalues ) else result = { status: :unbounded, objective: 0.0, col_value: [] } end # Parse result — variables are empty for infeasible/unbounded variables = {} unless [:infeasible, :unbounded, :unbounded_or_infeasible].include?(result[:status]) result[:col_value].each_with_index do |val, idx| var_name = model.variables.find { |_, v| v.index == idx }&.first variables[var_name.to_s] = val if var_name end end Solution.new( variables: variables, objective_value: heading == :maximize ? -result[:objective] : result[:objective], model_status: result[:status].to_s, iterations: 0 ) end |