lpsolver 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +345 -0
- data/exe/README.md +1 -0
- data/lib/lpsolver/constraint_spec.rb +96 -0
- data/lib/lpsolver/exception.rb +55 -0
- data/lib/lpsolver/linear_expression.rb +194 -0
- data/lib/lpsolver/model.rb +520 -0
- data/lib/lpsolver/quadratic_expression.rb +164 -0
- data/lib/lpsolver/solution.rb +123 -0
- data/lib/lpsolver/variable.rb +221 -0
- data/lib/lpsolver/version.rb +8 -0
- data/lib/lpsolver.rb +41 -0
- data/lpsolver.gemspec +29 -0
- metadata +65 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LpSolver
|
|
4
|
+
# Represents the solution returned by solving a model.
|
|
5
|
+
#
|
|
6
|
+
# The Solution object contains the optimal (or best-found) values for
|
|
7
|
+
# all decision variables, the optimal objective value, and metadata
|
|
8
|
+
# about the solver's execution.
|
|
9
|
+
#
|
|
10
|
+
# @example Accessing solution values
|
|
11
|
+
# solution = model.solve
|
|
12
|
+
# solution[:x] # => 4.0 (value of variable x)
|
|
13
|
+
# solution[:y] # => 0.0 (value of variable y)
|
|
14
|
+
# solution.objective_value # => 12.0 (optimal objective)
|
|
15
|
+
#
|
|
16
|
+
# @example Checking solution status
|
|
17
|
+
# solution.feasible? # => true
|
|
18
|
+
# solution.infeasible? # => false
|
|
19
|
+
# solution.unbounded? # => false
|
|
20
|
+
class Solution
|
|
21
|
+
# @return [Hash{String => Float}] Maps variable names to their optimal values.
|
|
22
|
+
# The keys are the variable names as strings (as produced by HiGHS),
|
|
23
|
+
# and the values are the optimal decision variable values.
|
|
24
|
+
attr_reader :variables
|
|
25
|
+
|
|
26
|
+
# @return [Float] The optimal objective function value.
|
|
27
|
+
# For minimization problems, this is the minimum value.
|
|
28
|
+
# For maximization problems, this is the maximum value.
|
|
29
|
+
attr_reader :objective_value
|
|
30
|
+
|
|
31
|
+
# @return [String] The status of the model as reported by HiGHS.
|
|
32
|
+
# Possible values include:
|
|
33
|
+
# - "optimal": An optimal solution was found.
|
|
34
|
+
# - "infeasible": No feasible solution exists.
|
|
35
|
+
# - "unbounded": The objective can be improved without bound.
|
|
36
|
+
# - "unknown": The solver could not determine the status.
|
|
37
|
+
attr_reader :model_status
|
|
38
|
+
|
|
39
|
+
# @return [Integer] The number of iterations the solver performed.
|
|
40
|
+
# This is a diagnostic metric; may be 0 for some solver types.
|
|
41
|
+
attr_reader :iterations
|
|
42
|
+
|
|
43
|
+
# Creates a new Solution object.
|
|
44
|
+
#
|
|
45
|
+
# @param variables [Hash{String => Float}] Maps variable names to values.
|
|
46
|
+
# @param objective_value [Float] The optimal objective value.
|
|
47
|
+
# @param model_status [String] The solver-reported model status.
|
|
48
|
+
# @param iterations [Integer] The number of solver iterations.
|
|
49
|
+
def initialize(variables:, objective_value:, model_status:, iterations:)
|
|
50
|
+
@variables = variables
|
|
51
|
+
@objective_value = objective_value
|
|
52
|
+
@model_status = model_status
|
|
53
|
+
@iterations = iterations
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Retrieves the value of a variable by name.
|
|
57
|
+
#
|
|
58
|
+
# @param name [Symbol, String] The variable name.
|
|
59
|
+
# @return [Float] The optimal value of the variable.
|
|
60
|
+
# @raise [KeyError] If the variable name is not found in the solution.
|
|
61
|
+
# @example
|
|
62
|
+
# solution[:x] # => 4.0
|
|
63
|
+
def [](name)
|
|
64
|
+
variables[name.to_s]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Retrieves values for multiple variables by name.
|
|
68
|
+
#
|
|
69
|
+
# @param *names [Symbol, String] The variable names to retrieve.
|
|
70
|
+
# @return [Array<Float>] An array of variable values in the same order.
|
|
71
|
+
# @example
|
|
72
|
+
# solution.values_at(:x, :y) # => [4.0, 0.0]
|
|
73
|
+
# @param [Array<Object>] names
|
|
74
|
+
def values_at(*names)
|
|
75
|
+
names.map { |name| variables[name.to_s] }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Checks if the solution is feasible.
|
|
79
|
+
#
|
|
80
|
+
# A solution is feasible if the solver found a solution that satisfies
|
|
81
|
+
# all constraints and bounds.
|
|
82
|
+
#
|
|
83
|
+
# @return [Boolean] True if the model status is "optimal".
|
|
84
|
+
def feasible?
|
|
85
|
+
@model_status == 'optimal'
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Checks if the model is infeasible.
|
|
89
|
+
#
|
|
90
|
+
# A model is infeasible if no solution exists that satisfies all
|
|
91
|
+
# constraints simultaneously.
|
|
92
|
+
#
|
|
93
|
+
# @return [Boolean] True if the model status is "infeasible".
|
|
94
|
+
def infeasible?
|
|
95
|
+
@model_status == 'infeasible'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Checks if the model is unbounded.
|
|
99
|
+
#
|
|
100
|
+
# A model is unbounded if the objective can be improved indefinitely
|
|
101
|
+
# without violating any constraints.
|
|
102
|
+
#
|
|
103
|
+
# @return [Boolean] True if the model status is "unbounded".
|
|
104
|
+
def unbounded?
|
|
105
|
+
@model_status == 'unbounded'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Returns a string representation of the solution.
|
|
109
|
+
#
|
|
110
|
+
# @return [String] A formatted string showing variable values and
|
|
111
|
+
# the objective value.
|
|
112
|
+
# @example
|
|
113
|
+
# puts solution
|
|
114
|
+
# # x = 4.0
|
|
115
|
+
# # y = 0.0
|
|
116
|
+
# # Objective: 12.0
|
|
117
|
+
def to_s
|
|
118
|
+
lines = variables.map { |name, value| "#{name} = #{value}" }
|
|
119
|
+
lines << "Objective: #{objective_value}"
|
|
120
|
+
lines.join("\n")
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LpSolver
|
|
4
|
+
# Represents a decision variable in an LP/MIP/QP model.
|
|
5
|
+
#
|
|
6
|
+
# Variables are the building blocks of optimization models. Each variable
|
|
7
|
+
# represents a quantity to be determined by the solver (e.g., production
|
|
8
|
+
# levels, investment amounts, resource allocations).
|
|
9
|
+
#
|
|
10
|
+
# Variables support arithmetic operators for building expressions and
|
|
11
|
+
# comparison operators for creating constraints. The operator overloading
|
|
12
|
+
# enables a natural, mathematical DSL:
|
|
13
|
+
#
|
|
14
|
+
# x = model.add_variable(:x, lb: 0)
|
|
15
|
+
# model.add_constraint(:budget, (x * 2 + y) <= 100)
|
|
16
|
+
#
|
|
17
|
+
# @note Variable * Variable produces a QuadraticExpression (for QP).
|
|
18
|
+
# Variable * Scalar produces a LinearExpression (for LP).
|
|
19
|
+
#
|
|
20
|
+
# @example Creating a variable
|
|
21
|
+
# x = model.add_variable(:x, lb: 0, ub: 100, integer: false)
|
|
22
|
+
# x.index # => 0
|
|
23
|
+
# x.name # => :x
|
|
24
|
+
#
|
|
25
|
+
# @example Using in expressions
|
|
26
|
+
# expr = x * 2 + y * 3 # LinearExpression
|
|
27
|
+
# quad = x * x + y * y # QuadraticExpression
|
|
28
|
+
# spec = (x * 2 + y) <= 100 # ConstraintSpec
|
|
29
|
+
class Variable
|
|
30
|
+
# @return [Integer] The internal index of this variable in the model.
|
|
31
|
+
# This is used internally to map the variable to a column in the
|
|
32
|
+
# solver's constraint matrix.
|
|
33
|
+
attr_reader :index
|
|
34
|
+
|
|
35
|
+
# @return [Symbol] The human-readable name of this variable.
|
|
36
|
+
attr_reader :name
|
|
37
|
+
|
|
38
|
+
# Creates a new Variable instance.
|
|
39
|
+
#
|
|
40
|
+
# @param index [Integer] The internal index assigned by the model.
|
|
41
|
+
# @param name [Symbol] The human-readable name of this variable.
|
|
42
|
+
def initialize(index, name)
|
|
43
|
+
@index = index
|
|
44
|
+
@name = name
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Multiplies this variable by a scalar or another variable.
|
|
48
|
+
#
|
|
49
|
+
# When multiplied by a Numeric, returns a LinearExpression with this
|
|
50
|
+
# variable scaled by the given coefficient. When multiplied by another
|
|
51
|
+
# Variable, returns a QuadraticExpression representing the product term
|
|
52
|
+
# (used for quadratic objectives in QP).
|
|
53
|
+
#
|
|
54
|
+
# @param other [Numeric, Variable] The multiplier.
|
|
55
|
+
# @return [LinearExpression] When +other+ is a Numeric.
|
|
56
|
+
# Example: `x * 2` → LinearExpression with terms {0 => 2.0}
|
|
57
|
+
# @return [QuadraticExpression] When +other+ is a Variable.
|
|
58
|
+
# Example: `x * y` → QuadraticExpression with terms [[0, 1, 1.0]]
|
|
59
|
+
def *(other)
|
|
60
|
+
if other.is_a?(Variable)
|
|
61
|
+
QuadraticExpression.new({}, [[@index, other.index, 1.0]])
|
|
62
|
+
else
|
|
63
|
+
LinearExpression.new({ @index => other.to_f })
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Adds another variable, expression, or constant to this variable.
|
|
68
|
+
#
|
|
69
|
+
# Creates a new LinearExpression containing the sum of this variable
|
|
70
|
+
# and the other operand.
|
|
71
|
+
#
|
|
72
|
+
# @param other [Variable, LinearExpression, Numeric] The operand to add.
|
|
73
|
+
# @return [LinearExpression] A new expression representing the sum.
|
|
74
|
+
# @example Adding two variables
|
|
75
|
+
# (x + y).terms # => {0 => 1.0, 1 => 1.0}
|
|
76
|
+
# @example Adding a constant
|
|
77
|
+
# (x + 5).constant # => 5.0
|
|
78
|
+
def +(other)
|
|
79
|
+
if other.is_a?(Variable)
|
|
80
|
+
LinearExpression.new({ @index => 1.0, other.index => 1.0 })
|
|
81
|
+
elsif other.is_a?(LinearExpression)
|
|
82
|
+
LinearExpression.new(merge_terms({ @index => 1.0 }, other.terms), other.constant)
|
|
83
|
+
else
|
|
84
|
+
LinearExpression.new({ @index => 1.0 }, other.to_f)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Subtracts another variable, expression, or constant from this variable.
|
|
89
|
+
#
|
|
90
|
+
# Creates a new LinearExpression containing the difference between this
|
|
91
|
+
# variable and the other operand.
|
|
92
|
+
#
|
|
93
|
+
# @param other [Variable, LinearExpression, Numeric] The operand to subtract.
|
|
94
|
+
# @return [LinearExpression] A new expression representing the difference.
|
|
95
|
+
# @example Subtracting two variables
|
|
96
|
+
# (x - y).terms # => {0 => 1.0, 1 => -1.0}
|
|
97
|
+
# @example Subtracting a constant
|
|
98
|
+
# (x - 5).constant # => -5.0
|
|
99
|
+
def -(other)
|
|
100
|
+
if other.is_a?(Variable)
|
|
101
|
+
LinearExpression.new({ @index => 1.0, other.index => -1.0 })
|
|
102
|
+
elsif other.is_a?(LinearExpression)
|
|
103
|
+
LinearExpression.new(negate_add_terms({ @index => 1.0 }, other.terms), -other.constant)
|
|
104
|
+
else
|
|
105
|
+
LinearExpression.new({ @index => 1.0 }, -other.to_f)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns a LinearExpression with this variable negated.
|
|
110
|
+
#
|
|
111
|
+
# @return [LinearExpression] A new expression with negated coefficients.
|
|
112
|
+
# @example
|
|
113
|
+
# (-x).terms # => {0 => -1.0}
|
|
114
|
+
def -@
|
|
115
|
+
LinearExpression.new({ @index => -1.0 })
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Creates a less-than-or-equal-to constraint specification.
|
|
119
|
+
#
|
|
120
|
+
# This is used with Model#add_constraint to define upper bounds on
|
|
121
|
+
# linear expressions. The constraint represents: expression <= value.
|
|
122
|
+
#
|
|
123
|
+
# @param value [Numeric] The right-hand side upper bound.
|
|
124
|
+
# @return [ConstraintSpec] A constraint specification with operator :le.
|
|
125
|
+
# @example
|
|
126
|
+
# model.add_constraint(:budget, (x * 2 + y) <= 100)
|
|
127
|
+
# @param [Object] other
|
|
128
|
+
def <=(other)
|
|
129
|
+
ConstraintSpec.new(:le, { @index => 1.0 }, 0, other.to_f)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Creates a greater-than-or-equal-to constraint specification.
|
|
133
|
+
#
|
|
134
|
+
# This is used with Model#add_constraint to define lower bounds on
|
|
135
|
+
# linear expressions. The constraint represents: expression >= value.
|
|
136
|
+
#
|
|
137
|
+
# @param value [Numeric] The right-hand side lower bound.
|
|
138
|
+
# @return [ConstraintSpec] A constraint specification with operator :ge.
|
|
139
|
+
# @example
|
|
140
|
+
# model.add_constraint(:demand, (x + y * 2) >= 50)
|
|
141
|
+
# @param [Object] other
|
|
142
|
+
def >=(other)
|
|
143
|
+
ConstraintSpec.new(:ge, { @index => 1.0 }, 0, other.to_f)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Creates an equality constraint specification.
|
|
147
|
+
#
|
|
148
|
+
# This is used with Model#add_constraint to define exact values for
|
|
149
|
+
# linear expressions. The constraint represents: expression == value.
|
|
150
|
+
#
|
|
151
|
+
# @param value [Numeric] The exact value the expression must equal.
|
|
152
|
+
# @return [ConstraintSpec] A constraint specification with operator :eq.
|
|
153
|
+
# @example
|
|
154
|
+
# model.add_constraint(:weights, (x + y + z) == 1)
|
|
155
|
+
# @param [Object] other
|
|
156
|
+
def ==(other)
|
|
157
|
+
ConstraintSpec.new(:eq, { @index => 1.0 }, 0, other.to_f)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Checks if two variables refer to the same underlying variable.
|
|
161
|
+
#
|
|
162
|
+
# @param other [Object] The object to compare against.
|
|
163
|
+
# @return [Boolean] True if +other+ is a Variable with the same index.
|
|
164
|
+
def equals?(other)
|
|
165
|
+
other.is_a?(Variable) && other.index == @index
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Returns a string representation of this variable.
|
|
169
|
+
#
|
|
170
|
+
# @return [String] A string in the format "@name(index)".
|
|
171
|
+
# @example
|
|
172
|
+
# x.to_s # => "@x(0)"
|
|
173
|
+
def to_s
|
|
174
|
+
"@#{@name}(#{@index})"
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Converts this variable's name to a Symbol.
|
|
178
|
+
#
|
|
179
|
+
# @return [Symbol] The variable's name.
|
|
180
|
+
# @example
|
|
181
|
+
# x.to_sym # => :x
|
|
182
|
+
def to_sym
|
|
183
|
+
@name
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Returns the hash code based on this variable's index.
|
|
187
|
+
#
|
|
188
|
+
# @return [Integer] The hash code of the variable's index.
|
|
189
|
+
def hash
|
|
190
|
+
@index.hash
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
alias eql? equals?
|
|
194
|
+
|
|
195
|
+
private
|
|
196
|
+
|
|
197
|
+
# Merges two term hashes, combining coefficients for duplicate indices.
|
|
198
|
+
#
|
|
199
|
+
# @param a [Hash{Integer => Float}] First term hash.
|
|
200
|
+
# @param b [Hash{Integer => Float}] Second term hash.
|
|
201
|
+
# @return [Hash{Integer => Float}] The merged term hash with zero coefficients removed.
|
|
202
|
+
def merge_terms(a, b)
|
|
203
|
+
merged = a.dup
|
|
204
|
+
b.each { |idx, coeff| merged[idx] = (merged[idx] || 0) + coeff }
|
|
205
|
+
merged.reject! { |_, v| v.zero? }
|
|
206
|
+
merged
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Merges two term hashes with subtraction (a - b).
|
|
210
|
+
#
|
|
211
|
+
# @param a [Hash{Integer => Float}] First term hash.
|
|
212
|
+
# @param b [Hash{Integer => Float}] Second term hash to subtract.
|
|
213
|
+
# @return [Hash{Integer => Float}] The difference term hash with zero coefficients removed.
|
|
214
|
+
def negate_add_terms(a, b)
|
|
215
|
+
merged = a.dup
|
|
216
|
+
b.each { |idx, coeff| merged[idx] = (merged[idx] || 0) - coeff }
|
|
217
|
+
merged.reject! { |_, v| v.zero? }
|
|
218
|
+
merged
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
data/lib/lpsolver.rb
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# LpSolver - A Ruby gem for solving Linear Programming (LP), Quadratic
|
|
4
|
+
# Programming (QP), and Mixed Integer Programming (MIP) problems.
|
|
5
|
+
#
|
|
6
|
+
# This gem provides a Ruby DSL for building optimization models and
|
|
7
|
+
# interfaces with the HiGHS solver via its command-line interface.
|
|
8
|
+
#
|
|
9
|
+
# == Quick Start
|
|
10
|
+
#
|
|
11
|
+
# require 'lpsolver'
|
|
12
|
+
#
|
|
13
|
+
# model = LpSolver::Model.new
|
|
14
|
+
# x = model.add_variable(:x, lb: 0)
|
|
15
|
+
# y = model.add_variable(:y, lb: 0)
|
|
16
|
+
#
|
|
17
|
+
# model.add_constraint(:budget, (x * 2 + y) <= 100)
|
|
18
|
+
# model.minimize
|
|
19
|
+
# model.set_objective(x * 3 + y * 5)
|
|
20
|
+
#
|
|
21
|
+
# solution = model.solve
|
|
22
|
+
# puts solution.objective_value # => 12.0
|
|
23
|
+
#
|
|
24
|
+
# @see LpSolver::Model
|
|
25
|
+
# @see LpSolver::Variable
|
|
26
|
+
# @see LpSolver::Solution
|
|
27
|
+
module LpSolver
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
require_relative 'lpsolver/version'
|
|
31
|
+
require_relative 'lpsolver/exception'
|
|
32
|
+
|
|
33
|
+
# Core DSL classes (loaded in dependency order)
|
|
34
|
+
require_relative 'lpsolver/constraint_spec'
|
|
35
|
+
require_relative 'lpsolver/variable'
|
|
36
|
+
require_relative 'lpsolver/linear_expression'
|
|
37
|
+
require_relative 'lpsolver/quadratic_expression'
|
|
38
|
+
|
|
39
|
+
# Solvers and data classes
|
|
40
|
+
require_relative 'lpsolver/solution'
|
|
41
|
+
require_relative 'lpsolver/model'
|
data/lpsolver.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/lpsolver/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'lpsolver'
|
|
7
|
+
spec.version = LpSolver::VERSION
|
|
8
|
+
spec.authors = ['David Siaw']
|
|
9
|
+
spec.email = ['874280+davidsiaw@users.noreply.github.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'HiGHS LP/MIP/QP solver for Ruby'
|
|
12
|
+
spec.description = 'A Ruby gem providing access to the HiGHS linear, quadratic, and mixed-integer programming solver via CLI.'
|
|
13
|
+
spec.homepage = 'https://github.com/davidsiaw/lpsolver'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.0')
|
|
16
|
+
|
|
17
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
18
|
+
|
|
19
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/davidsiaw/lpsolver'
|
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/davidsiaw/lpsolver'
|
|
22
|
+
spec.metadata['documentation_uri'] = 'https://davidsiaw.github.io/lpsolver'
|
|
23
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
24
|
+
|
|
25
|
+
spec.files = Dir['{exe,data,lib}/**/*'] + %w[Gemfile lpsolver.gemspec README.md LICENSE.txt]
|
|
26
|
+
spec.bindir = 'exe'
|
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
28
|
+
spec.require_paths = ['lib']
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: lpsolver
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- David Siaw
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A Ruby gem providing access to the HiGHS linear, quadratic, and mixed-integer
|
|
14
|
+
programming solver via CLI.
|
|
15
|
+
email:
|
|
16
|
+
- 874280+davidsiaw@users.noreply.github.com
|
|
17
|
+
executables:
|
|
18
|
+
- README.md
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- Gemfile
|
|
23
|
+
- LICENSE.txt
|
|
24
|
+
- README.md
|
|
25
|
+
- exe/README.md
|
|
26
|
+
- lib/lpsolver.rb
|
|
27
|
+
- lib/lpsolver/constraint_spec.rb
|
|
28
|
+
- lib/lpsolver/exception.rb
|
|
29
|
+
- lib/lpsolver/linear_expression.rb
|
|
30
|
+
- lib/lpsolver/model.rb
|
|
31
|
+
- lib/lpsolver/quadratic_expression.rb
|
|
32
|
+
- lib/lpsolver/solution.rb
|
|
33
|
+
- lib/lpsolver/variable.rb
|
|
34
|
+
- lib/lpsolver/version.rb
|
|
35
|
+
- lpsolver.gemspec
|
|
36
|
+
homepage: https://github.com/davidsiaw/lpsolver
|
|
37
|
+
licenses:
|
|
38
|
+
- MIT
|
|
39
|
+
metadata:
|
|
40
|
+
allowed_push_host: https://rubygems.org
|
|
41
|
+
homepage_uri: https://github.com/davidsiaw/lpsolver
|
|
42
|
+
source_code_uri: https://github.com/davidsiaw/lpsolver
|
|
43
|
+
changelog_uri: https://github.com/davidsiaw/lpsolver
|
|
44
|
+
documentation_uri: https://davidsiaw.github.io/lpsolver
|
|
45
|
+
rubygems_mfa_required: 'true'
|
|
46
|
+
post_install_message:
|
|
47
|
+
rdoc_options: []
|
|
48
|
+
require_paths:
|
|
49
|
+
- lib
|
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.0'
|
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '0'
|
|
60
|
+
requirements: []
|
|
61
|
+
rubygems_version: 3.5.22
|
|
62
|
+
signing_key:
|
|
63
|
+
specification_version: 4
|
|
64
|
+
summary: HiGHS LP/MIP/QP solver for Ruby
|
|
65
|
+
test_files: []
|