ruby-cbc 0.3.18 → 0.3.19
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 +4 -4
 - data/.rubocop.yml +3 -1
 - data/.rubocop_todo.yml +51 -0
 - data/.ruby-style.yml +6 -1346
 - data/.travis.yml +3 -1
 - data/Gemfile +1 -1
 - data/README.md +53 -10
 - data/Rakefile +6 -6
 - data/lib/ruby-cbc/conflict_solver.rb +9 -8
 - data/lib/ruby-cbc/ilp/constraint.rb +1 -1
 - data/lib/ruby-cbc/ilp/term_array.rb +2 -2
 - data/lib/ruby-cbc/model.rb +9 -9
 - data/lib/ruby-cbc/problem.rb +26 -26
 - data/lib/ruby-cbc/version.rb +1 -1
 - data/lib/ruby-cbc.rb +2 -2
 - data/ruby-cbc.gemspec +10 -9
 - metadata +43 -15
 
    
        data/.travis.yml
    CHANGED
    
    
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,4 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            [](https://travis-ci.org/gverger/ruby-cbc)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       2 
3 
     | 
    
         
             
            # Ruby-Cbc
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
       4 
5 
     | 
    
         
             
            This gem is using Cbc, an Integer Linear Programming Library, to provide optimization problems solving
         
     | 
| 
         @@ -8,6 +9,13 @@ It uses the version 2.9.9 of Cbc, and requires the version 2.9.9 of gem cbc-wrap 
     | 
|
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
            ## Installation
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
      
 12 
     | 
    
         
            +
            You need to have `cbc` installed for `ruby-cbc` to work.
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            - On a mac, you can execute `brew install cbc`
         
     | 
| 
      
 15 
     | 
    
         
            +
            - On Debian and Ubuntu, use `apt-get install coinor-libcbc-dev`
         
     | 
| 
      
 16 
     | 
    
         
            +
            - On Archlinux, use `pacman -S coin-or-cbc`
         
     | 
| 
      
 17 
     | 
    
         
            +
            - On docker you can check the dockerfiles in test/installation-tests/
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       11 
19 
     | 
    
         
             
            Add this line to your application's Gemfile:
         
     | 
| 
       12 
20 
     | 
    
         | 
| 
       13 
21 
     | 
    
         
             
            ```ruby
         
     | 
| 
         @@ -22,16 +30,25 @@ Or install it yourself as: 
     | 
|
| 
       22 
30 
     | 
    
         | 
| 
       23 
31 
     | 
    
         
             
                $ gem install ruby-cbc
         
     | 
| 
       24 
32 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 33 
     | 
    
         
            +
            ### Heroku
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            On Heroku, you will need to tweak your installation a bit: you can install the cbc library with
         
     | 
| 
      
 36 
     | 
    
         
            +
            the [Apt](https://github.com/heroku/heroku-buildpack-apt) buildpack with an Aptfile of:
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            ```
         
     | 
| 
      
 39 
     | 
    
         
            +
            coinor-libcbc-dev
         
     | 
| 
      
 40 
     | 
    
         
            +
            ```
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            You will need to set LD_LIBRARY_PATH so it can find LAPACK and BLAS (see this [issue](https://github.com/heroku/heroku-buildpack-apt/issues/35)).
         
     | 
| 
       28 
43 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
      
 44 
     | 
    
         
            +
            ```
         
     | 
| 
      
 45 
     | 
    
         
            +
            heroku config:set LD_LIBRARY_PATH=/app/.apt/usr/lib/x86_64-linux-gnu/lapack:/app/.apt/usr/lib/x86_64-linux-gnu/blas
         
     | 
| 
      
 46 
     | 
    
         
            +
            ```
         
     | 
| 
       30 
47 
     | 
    
         | 
| 
       31 
48 
     | 
    
         
             
            ## Usage
         
     | 
| 
       32 
49 
     | 
    
         | 
| 
       33 
50 
     | 
    
         
             
            ```ruby
         
     | 
| 
       34 
     | 
    
         
            -
            # The same Brief Example as found in section 1.3 of 
     | 
| 
      
 51 
     | 
    
         
            +
            # The same Brief Example as found in section 1.3 of
         
     | 
| 
       35 
52 
     | 
    
         
             
            # glpk-4.44/doc/glpk.pdf.
         
     | 
| 
       36 
53 
     | 
    
         
             
            #
         
     | 
| 
       37 
54 
     | 
    
         
             
            # maximize
         
     | 
| 
         @@ -52,39 +69,45 @@ m.maximize(10 * x1 + 6 * x2 + 4 * x3) 
     | 
|
| 
       52 
69 
     | 
    
         | 
| 
       53 
70 
     | 
    
         
             
            m.enforce(x1 + x2 + x3 <= 100)
         
     | 
| 
       54 
71 
     | 
    
         
             
            m.enforce(10 * x1 + 4 * x2 + 5 * x3 <= 600)
         
     | 
| 
       55 
     | 
    
         
            -
            m.enforce(2 * x1 + 2 * x2 + 6* x3 <= 300)
         
     | 
| 
      
 72 
     | 
    
         
            +
            m.enforce(2 * x1 + 2 * x2 + 6 * x3 <= 300)
         
     | 
| 
       56 
73 
     | 
    
         | 
| 
       57 
74 
     | 
    
         
             
            p = m.to_problem
         
     | 
| 
       58 
75 
     | 
    
         | 
| 
       59 
76 
     | 
    
         
             
            p.solve
         
     | 
| 
       60 
77 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
      
 78 
     | 
    
         
            +
            unless p.proven_infeasible?
         
     | 
| 
       62 
79 
     | 
    
         
             
              puts "x1 = #{p.value_of(x1)}"
         
     | 
| 
       63 
80 
     | 
    
         
             
              puts "x2 = #{p.value_of(x2)}"
         
     | 
| 
       64 
81 
     | 
    
         
             
              puts "x3 = #{p.value_of(x3)}"
         
     | 
| 
       65 
82 
     | 
    
         
             
            end
         
     | 
| 
       66 
83 
     | 
    
         
             
            ```
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
       67 
85 
     | 
    
         
             
            ### Modelling
         
     | 
| 
       68 
86 
     | 
    
         | 
| 
       69 
87 
     | 
    
         
             
            Let's have a model :
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
       70 
89 
     | 
    
         
             
            ```ruby
         
     | 
| 
       71 
90 
     | 
    
         
             
            model = Cbc::Model.new
         
     | 
| 
       72 
91 
     | 
    
         
             
            ```
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
       73 
93 
     | 
    
         
             
            #### The variables
         
     | 
| 
       74 
94 
     | 
    
         | 
| 
       75 
95 
     | 
    
         
             
            3 variable kinds are available:
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
       76 
97 
     | 
    
         
             
            ```ruby
         
     | 
| 
       77 
98 
     | 
    
         
             
            x = model.bin_var(name: "x")        # a binary variable (i.e. can take values 0 and 1)
         
     | 
| 
       78 
99 
     | 
    
         
             
            y = model.int_var(L..U, name: "y")  # a integer variable, L <= y <= U
         
     | 
| 
       79 
100 
     | 
    
         
             
            z = model.cont_var(L..U, name: "z") # a continuous variable, L <= z <= U
         
     | 
| 
       80 
101 
     | 
    
         
             
            ```
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
       81 
103 
     | 
    
         
             
            Name is optional and used only when displaying the model.
         
     | 
| 
       82 
104 
     | 
    
         | 
| 
       83 
105 
     | 
    
         
             
            If you don't specify the range, the variables are free.
         
     | 
| 
       84 
     | 
    
         
            -
            You can also use  
     | 
| 
      
 106 
     | 
    
         
            +
            You can also use `Cbc::INF` as the infinity bound.
         
     | 
| 
       85 
107 
     | 
    
         | 
| 
       86 
108 
     | 
    
         
             
            Each one of these 3 kinds have also an array method that generate several variables.
         
     | 
| 
       87 
109 
     | 
    
         
             
            For instance to generate 3 positive integer variables named x, y and z :
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
       88 
111 
     | 
    
         
             
            ```ruby
         
     | 
| 
       89 
112 
     | 
    
         
             
            x, y, z = model.int_var_array(3, 0..Cbc::INF, names: ["x", "y", "z"])
         
     | 
| 
       90 
113 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -92,12 +115,15 @@ x, y, z = model.int_var_array(3, 0..Cbc::INF, names: ["x", "y", "z"]) 
     | 
|
| 
       92 
115 
     | 
    
         
             
            #### The constraints
         
     | 
| 
       93 
116 
     | 
    
         | 
| 
       94 
117 
     | 
    
         
             
            You can enforce constraints:
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
       95 
119 
     | 
    
         
             
            ```ruby
         
     | 
| 
       96 
120 
     | 
    
         
             
            model.enforce(x + y - z <= 10)
         
     | 
| 
       97 
121 
     | 
    
         
             
            ```
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
       98 
123 
     | 
    
         
             
            You are not restricted to usual linear programming rules when writing a constraint.
         
     | 
| 
       99 
     | 
    
         
            -
            Usually you would have to write  
     | 
| 
      
 124 
     | 
    
         
            +
            Usually you would have to write `x - y = 0` to express `x = y`.
         
     | 
| 
       100 
125 
     | 
    
         
             
            Ruby-Cbc allows you to put variables and constants on both sides of the comparison operator. You can write
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
       101 
127 
     | 
    
         
             
            ```ruby
         
     | 
| 
       102 
128 
     | 
    
         
             
            model.enforce(x - y == 0)
         
     | 
| 
       103 
129 
     | 
    
         
             
            model.enforce(x == y)
         
     | 
| 
         @@ -107,12 +133,14 @@ model.enforce(0 == x - y) 
     | 
|
| 
       107 
133 
     | 
    
         | 
| 
       108 
134 
     | 
    
         
             
            Ruby-Cbc allows you to name your constraints. Beware that their name is not an unique id. It is only a helper
         
     | 
| 
       109 
135 
     | 
    
         
             
            for human readability, and several constraints can share the same function name.
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
       110 
137 
     | 
    
         
             
            ```ruby
         
     | 
| 
       111 
138 
     | 
    
         
             
            model.enforce(my_function_name: x + y <= 50)
         
     | 
| 
       112 
139 
     | 
    
         
             
            model.constraints.map(&:to_function_s) # => ["my_function_name(x, y)"]
         
     | 
| 
       113 
140 
     | 
    
         
             
            ```
         
     | 
| 
       114 
141 
     | 
    
         | 
| 
       115 
142 
     | 
    
         
             
            Linear constraints are usually of the form
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
       116 
144 
     | 
    
         
             
            ```ruby
         
     | 
| 
       117 
145 
     | 
    
         
             
            a1 * x1 + a2 * x2 + ... + an * xn <= C
         
     | 
| 
       118 
146 
     | 
    
         
             
            a1 * x1 + a2 * x2 + ... + an * xn >= C
         
     | 
| 
         @@ -120,14 +148,17 @@ a1 * x1 + a2 * x2 + ... + an * xn == C 
     | 
|
| 
       120 
148 
     | 
    
         
             
            ```
         
     | 
| 
       121 
149 
     | 
    
         | 
| 
       122 
150 
     | 
    
         
             
            With Ruby-Cbc you can write
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
       123 
152 
     | 
    
         
             
            ```ruby
         
     | 
| 
       124 
153 
     | 
    
         
             
            2 * (2 + 5 * x) + 4 * 5 + 1 == 1 + 4 * 5 * y
         
     | 
| 
       125 
154 
     | 
    
         
             
            ```
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
       126 
156 
     | 
    
         
             
            The (in)equation must still be a **linear** (in)equation, you cannot multiply two variables !
         
     | 
| 
       127 
157 
     | 
    
         | 
| 
       128 
158 
     | 
    
         
             
            #### Objective
         
     | 
| 
       129 
159 
     | 
    
         | 
| 
       130 
160 
     | 
    
         
             
            You can set the objective:
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
       131 
162 
     | 
    
         
             
            ```ruby
         
     | 
| 
       132 
163 
     | 
    
         
             
            model.maximize(3 * x + 2 * y)
         
     | 
| 
       133 
164 
     | 
    
         
             
            model.minimize(3 * x + 2 * y)
         
     | 
| 
         @@ -136,12 +167,15 @@ model.minimize(3 * x + 2 * y) 
     | 
|
| 
       136 
167 
     | 
    
         
             
            #### Displaying the model
         
     | 
| 
       137 
168 
     | 
    
         | 
| 
       138 
169 
     | 
    
         
             
            the `Model` instances have a `to_s` method. You can then run
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
       139 
171 
     | 
    
         
             
            ```ruby
         
     | 
| 
       140 
172 
     | 
    
         
             
            puts model
         
     | 
| 
       141 
173 
     | 
    
         
             
            ```
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
       142 
175 
     | 
    
         
             
            The model will be printed in LP format.
         
     | 
| 
       143 
176 
     | 
    
         | 
| 
       144 
177 
     | 
    
         
             
            For instance:
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
       145 
179 
     | 
    
         
             
            ```
         
     | 
| 
       146 
180 
     | 
    
         
             
            Maximize
         
     | 
| 
       147 
181 
     | 
    
         
             
              + 10 x1 + 6 x2 + 4 x3
         
     | 
| 
         @@ -167,25 +201,30 @@ End 
     | 
|
| 
       167 
201 
     | 
    
         
             
            ### Solving
         
     | 
| 
       168 
202 
     | 
    
         | 
| 
       169 
203 
     | 
    
         
             
            To solve the model, you need to first transform it to a problem.
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
       170 
205 
     | 
    
         
             
            ```ruby
         
     | 
| 
       171 
206 
     | 
    
         
             
            problem = model.to_problem
         
     | 
| 
       172 
207 
     | 
    
         
             
            ```
         
     | 
| 
       173 
208 
     | 
    
         | 
| 
       174 
209 
     | 
    
         
             
            You can define a time limit to the resolution
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
       175 
211 
     | 
    
         
             
            ```ruby
         
     | 
| 
       176 
212 
     | 
    
         
             
            problem.set_time_limit(nb_seconds)
         
     | 
| 
       177 
213 
     | 
    
         
             
            ```
         
     | 
| 
       178 
214 
     | 
    
         | 
| 
       179 
215 
     | 
    
         
             
            You can solve the Linear Problem
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
       180 
217 
     | 
    
         
             
            ```ruby
         
     | 
| 
       181 
218 
     | 
    
         
             
            problem.solve
         
     | 
| 
       182 
219 
     | 
    
         
             
            ```
         
     | 
| 
       183 
220 
     | 
    
         | 
| 
       184 
221 
     | 
    
         
             
            You can specify arguments that match the cbc command line
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
       185 
223 
     | 
    
         
             
            ```ruby
         
     | 
| 
       186 
224 
     | 
    
         
             
            problem.solve(sec: 60) # equivalent to $ cbc -sec 60
         
     | 
| 
       187 
225 
     | 
    
         
             
            problem.solve(log: 1)  # equivalent to $ cbc -log 1
         
     | 
| 
       188 
226 
     | 
    
         
             
            ```
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
       189 
228 
     | 
    
         
             
            For more examples of available options, if `coinor-cbc` is installed run
         
     | 
| 
       190 
229 
     | 
    
         | 
| 
       191 
230 
     | 
    
         
             
                $ cbc
         
     | 
| 
         @@ -193,6 +232,7 @@ For more examples of available options, if `coinor-cbc` is installed run 
     | 
|
| 
       193 
232 
     | 
    
         
             
            then type `?`
         
     | 
| 
       194 
233 
     | 
    
         | 
| 
       195 
234 
     | 
    
         
             
            Once `problem.solve` has finished you can query the status:
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
       196 
236 
     | 
    
         
             
            ```ruby
         
     | 
| 
       197 
237 
     | 
    
         
             
            problem.proven_infeasible?  # will tell you if the problem has no solution
         
     | 
| 
       198 
238 
     | 
    
         
             
            problem.proven_optimal?     # will tell you if the problem is solved optimally
         
     | 
| 
         @@ -200,6 +240,7 @@ problem.time_limit_reached? # Will tell you if the solve timed out 
     | 
|
| 
       200 
240 
     | 
    
         
             
            ```
         
     | 
| 
       201 
241 
     | 
    
         | 
| 
       202 
242 
     | 
    
         
             
            To have the different values, do
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
       203 
244 
     | 
    
         
             
            ```ruby
         
     | 
| 
       204 
245 
     | 
    
         
             
            problem.objective_value # Will tell you the value of the best objective
         
     | 
| 
       205 
246 
     | 
    
         
             
            problem.best_bound      # Will tell you the best known bound
         
     | 
| 
         @@ -211,16 +252,19 @@ problem.value_of(var)   # will tell you the computed value or a variable 
     | 
|
| 
       211 
252 
     | 
    
         | 
| 
       212 
253 
     | 
    
         
             
            Sometimes a problem has no feasible solution. In this case, one may wonder what is the minimum subset of conflicting
         
     | 
| 
       213 
254 
     | 
    
         
             
            inequations. For this prupose, you can use
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
       214 
256 
     | 
    
         
             
            ```ruby
         
     | 
| 
       215 
257 
     | 
    
         
             
            problem.find_conflict      # Will return an array of constraints that form an unsatifiable set
         
     | 
| 
       216 
258 
     | 
    
         
             
            problem.find_conflict_vars # Will return all variables involved in the unsatisfiable minimum set of constraints
         
     | 
| 
       217 
259 
     | 
    
         
             
            ```
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
       218 
261 
     | 
    
         
             
            It finds a minimum subset of constraints that make the problem unsatisfiable. Note that there could be several of them,
         
     | 
| 
       219 
262 
     | 
    
         
             
            but the solver only computes the first one it finds. Note also that it does so by solving several instances
         
     | 
| 
       220 
263 
     | 
    
         
             
            of relaxed versions of the problem. It might take some time! It is based on QuickXplain
         
     | 
| 
       221 
264 
     | 
    
         
             
            (http://dl.acm.org/citation.cfm?id=1597177).
         
     | 
| 
       222 
265 
     | 
    
         | 
| 
       223 
266 
     | 
    
         
             
            One way to see the results nicely could be
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
       224 
268 
     | 
    
         
             
            ```ruby
         
     | 
| 
       225 
269 
     | 
    
         
             
            problem.find_conflict.map(&:to_function_s)
         
     | 
| 
       226 
270 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -228,4 +272,3 @@ problem.find_conflict.map(&:to_function_s) 
     | 
|
| 
       228 
272 
     | 
    
         
             
            ## Contributing
         
     | 
| 
       229 
273 
     | 
    
         | 
| 
       230 
274 
     | 
    
         
             
            Bug reports and pull requests are welcome on GitHub at https://github.com/gverger/ruby-cbc.
         
     | 
| 
       231 
     | 
    
         
            -
             
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require "bundler/gem_tasks"
         
     | 
| 
       2 
2 
     | 
    
         
             
            require "rspec/core/rake_task"
         
     | 
| 
       3 
     | 
    
         
            -
            require  
     | 
| 
      
 3 
     | 
    
         
            +
            require "rake/extensiontask"
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            def in_dir(path)
         
     | 
| 
       6 
6 
     | 
    
         
             
              original_dir = Dir.pwd
         
     | 
| 
         @@ -12,14 +12,14 @@ end 
     | 
|
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
            RSpec::Core::RakeTask.new(:spec)
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
            spec = Gem::Specification.load( 
     | 
| 
       16 
     | 
    
         
            -
            Rake::ExtensionTask.new( 
     | 
| 
       17 
     | 
    
         
            -
              ext.lib_dir =  
     | 
| 
      
 15 
     | 
    
         
            +
            spec = Gem::Specification.load("ruby-cbc.gemspec")
         
     | 
| 
      
 16 
     | 
    
         
            +
            Rake::ExtensionTask.new("ruby-cbc", spec) do |ext|
         
     | 
| 
      
 17 
     | 
    
         
            +
              ext.lib_dir = "lib/ruby-cbc"
         
     | 
| 
       18 
18 
     | 
    
         
             
              ext.tmp_dir = "tmp"
         
     | 
| 
       19 
     | 
    
         
            -
              ext.name =  
     | 
| 
      
 19 
     | 
    
         
            +
              ext.name = "cbc_wrapper"
         
     | 
| 
       20 
20 
     | 
    
         
             
            end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
            task : 
     | 
| 
      
 22 
     | 
    
         
            +
            task default: [:spec]
         
     | 
| 
       23 
23 
     | 
    
         | 
| 
       24 
24 
     | 
    
         
             
            task :benchmark do
         
     | 
| 
       25 
25 
     | 
    
         
             
              ruby "test/benchmark.rb"
         
     | 
| 
         @@ -19,7 +19,8 @@ module Cbc 
     | 
|
| 
       19 
19 
     | 
    
         
             
                  max_iter = 1
         
     | 
| 
       20 
20 
     | 
    
         
             
                  conflict_set_size = 0
         
     | 
| 
       21 
21 
     | 
    
         
             
                  loop do
         
     | 
| 
       22 
     | 
    
         
            -
                    range_idxs = 
     | 
| 
      
 22 
     | 
    
         
            +
                    range_idxs =
         
     | 
| 
      
 23 
     | 
    
         
            +
                      first_failing(conflict_set_size, crs, continuous: continuous, max_iterations: max_iter)
         
     | 
| 
       23 
24 
     | 
    
         
             
                    break if range_idxs.nil?
         
     | 
| 
       24 
25 
     | 
    
         
             
                    crs = crs.restrict_to_n_constraints(range_idxs.max + 1)
         
     | 
| 
       25 
26 
     | 
    
         
             
                    crs.move_constraint_to_start(range_idxs)
         
     | 
| 
         @@ -43,7 +44,9 @@ module Cbc 
     | 
|
| 
       43 
44 
     | 
    
         
             
                      nb_clusters_one_constraint += 1
         
     | 
| 
       44 
45 
     | 
    
         
             
                    end
         
     | 
| 
       45 
46 
     | 
    
         
             
                    if nb_clusters_one_constraint > 0
         
     | 
| 
       46 
     | 
    
         
            -
                      crs.move_constraint_to_start( 
     | 
| 
      
 47 
     | 
    
         
            +
                      crs.move_constraint_to_start(
         
     | 
| 
      
 48 
     | 
    
         
            +
                        (crs.nb_constraints - nb_clusters_one_constraint)..(crs.nb_constraints - 1)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      )
         
     | 
| 
       47 
50 
     | 
    
         
             
                      clusters[nb_clusters_one_constraint, clusters.size - nb_clusters_one_constraint] =
         
     | 
| 
       48 
51 
     | 
    
         
             
                        clusters[0, clusters.size - nb_clusters_one_constraint]
         
     | 
| 
       49 
52 
     | 
    
         
             
                      clusters[0, nb_clusters_one_constraint] = Array.new(nb_clusters_one_constraint, 1)
         
     | 
| 
         @@ -83,14 +86,12 @@ module Cbc 
     | 
|
| 
       83 
86 
     | 
    
         
             
                      min_idx = half_constraint_idx + 1
         
     | 
| 
       84 
87 
     | 
    
         
             
                      # puts "                                FEAS"
         
     | 
| 
       85 
88 
     | 
    
         
             
                    end
         
     | 
| 
       86 
     | 
    
         
            -
                    if max_idx  
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                      return min_idx..max_idx
         
     | 
| 
       90 
     | 
    
         
            -
                    end
         
     | 
| 
      
 89 
     | 
    
         
            +
                    next if max_idx != min_idx
         
     | 
| 
      
 90 
     | 
    
         
            +
                    return nil if max_idx > crs.nb_constraints
         
     | 
| 
      
 91 
     | 
    
         
            +
                    return min_idx..max_idx
         
     | 
| 
       91 
92 
     | 
    
         
             
                  end
         
     | 
| 
       92 
93 
     | 
    
         
             
                  # Shouldn't come here if the whole problem is infeasible
         
     | 
| 
       93 
     | 
    
         
            -
                   
     | 
| 
      
 94 
     | 
    
         
            +
                  nil
         
     | 
| 
       94 
95 
     | 
    
         
             
                end
         
     | 
| 
       95 
96 
     | 
    
         | 
| 
       96 
97 
     | 
    
         
             
                def infeasible?(problem)
         
     | 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require  
     | 
| 
      
 1 
     | 
    
         
            +
            require "forwardable"
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module Ilp
         
     | 
| 
       4 
4 
     | 
    
         
             
              class TermArray
         
     | 
| 
         @@ -33,7 +33,7 @@ module Ilp 
     | 
|
| 
       33 
33 
     | 
    
         
             
                end
         
     | 
| 
       34 
34 
     | 
    
         | 
| 
       35 
35 
     | 
    
         
             
                def *(other)
         
     | 
| 
       36 
     | 
    
         
            -
                  raise ArgumentError,  
     | 
| 
      
 36 
     | 
    
         
            +
                  raise ArgumentError, "Argument is not numeric" unless other.is_a? Numeric
         
     | 
| 
       37 
37 
     | 
    
         
             
                  new_terms = terms.map { |term| term * other }
         
     | 
| 
       38 
38 
     | 
    
         
             
                  TermArray.new(new_terms)
         
     | 
| 
       39 
39 
     | 
    
         
             
                end
         
     | 
    
        data/lib/ruby-cbc/model.rb
    CHANGED
    
    | 
         @@ -91,7 +91,7 @@ module Cbc 
     | 
|
| 
       91 
91 
     | 
    
         
             
                  constraints.each do |cons|
         
     | 
| 
       92 
92 
     | 
    
         
             
                    str << "  #{cons}\n"
         
     | 
| 
       93 
93 
     | 
    
         
             
                  end
         
     | 
| 
       94 
     | 
    
         
            -
                  bounded_vars = vars. 
     | 
| 
      
 94 
     | 
    
         
            +
                  bounded_vars = vars.reject { |v| v.kind == Ilp::Var::BINARY_KIND }
         
     | 
| 
       95 
95 
     | 
    
         
             
                  unless bounded_vars.empty?
         
     | 
| 
       96 
96 
     | 
    
         
             
                    str << "\nBounds\n"
         
     | 
| 
       97 
97 
     | 
    
         
             
                    bounded_vars.each do |v|
         
     | 
| 
         @@ -133,16 +133,16 @@ module Cbc 
     | 
|
| 
       133 
133 
     | 
    
         
             
                  v
         
     | 
| 
       134 
134 
     | 
    
         
             
                end
         
     | 
| 
       135 
135 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                def lb_to_s( 
     | 
| 
       137 
     | 
    
         
            -
                  return "-inf" if  
     | 
| 
       138 
     | 
    
         
            -
                  return "+inf" if  
     | 
| 
       139 
     | 
    
         
            -
                   
     | 
| 
      
 136 
     | 
    
         
            +
                def lb_to_s(lower_bound)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  return "-inf" if lower_bound.nil? || lower_bound == -Cbc::INF
         
     | 
| 
      
 138 
     | 
    
         
            +
                  return "+inf" if lower_bound == Cbc::INF
         
     | 
| 
      
 139 
     | 
    
         
            +
                  lower_bound.to_s
         
     | 
| 
       140 
140 
     | 
    
         
             
                end
         
     | 
| 
       141 
141 
     | 
    
         | 
| 
       142 
     | 
    
         
            -
                def ub_to_s( 
     | 
| 
       143 
     | 
    
         
            -
                  return "+inf" if  
     | 
| 
       144 
     | 
    
         
            -
                  return "-inf" if  
     | 
| 
       145 
     | 
    
         
            -
                   
     | 
| 
      
 142 
     | 
    
         
            +
                def ub_to_s(upper_bound)
         
     | 
| 
      
 143 
     | 
    
         
            +
                  return "+inf" if upper_bound.nil? || upper_bound == Cbc::INF
         
     | 
| 
      
 144 
     | 
    
         
            +
                  return "-inf" if upper_bound == -Cbc::INF
         
     | 
| 
      
 145 
     | 
    
         
            +
                  upper_bound.to_s
         
     | 
| 
       146 
146 
     | 
    
         
             
                end
         
     | 
| 
       147 
147 
     | 
    
         
             
              end
         
     | 
| 
       148 
148 
     | 
    
         
             
            end
         
     | 
    
        data/lib/ruby-cbc/problem.rb
    CHANGED
    
    | 
         @@ -56,9 +56,6 @@ module Cbc 
     | 
|
| 
       56 
56 
     | 
    
         
             
                end
         
     | 
| 
       57 
57 
     | 
    
         | 
| 
       58 
58 
     | 
    
         
             
                def create_cbc_problem(continuous: false)
         
     | 
| 
       59 
     | 
    
         
            -
                  @int_arrays = []
         
     | 
| 
       60 
     | 
    
         
            -
                  @double_arrays = []
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
59 
     | 
    
         
             
                  ccs = self.class.crs_to_ccs(@crs)
         
     | 
| 
       63 
60 
     | 
    
         
             
                  objective = Array.new(ccs.nb_vars, 0)
         
     | 
| 
       64 
61 
     | 
    
         
             
                  if model.objective
         
     | 
| 
         @@ -69,10 +66,18 @@ module Cbc 
     | 
|
| 
       69 
66 
     | 
    
         | 
| 
       70 
67 
     | 
    
         
             
                  @cbc_model = Cbc_wrapper.Cbc_newModel
         
     | 
| 
       71 
68 
     | 
    
         
             
                  Cbc_wrapper.Cbc_loadProblem(
         
     | 
| 
       72 
     | 
    
         
            -
                    @cbc_model, 
     | 
| 
       73 
     | 
    
         
            -
                     
     | 
| 
       74 
     | 
    
         
            -
                     
     | 
| 
       75 
     | 
    
         
            -
                     
     | 
| 
      
 69 
     | 
    
         
            +
                    @cbc_model,
         
     | 
| 
      
 70 
     | 
    
         
            +
                    ccs.nb_vars,
         
     | 
| 
      
 71 
     | 
    
         
            +
                    @crs.nb_constraints,
         
     | 
| 
      
 72 
     | 
    
         
            +
                    to_int_array(ccs.col_ptr),
         
     | 
| 
      
 73 
     | 
    
         
            +
                    to_int_array(ccs.row_idx),
         
     | 
| 
      
 74 
     | 
    
         
            +
                    to_double_array(ccs.values),
         
     | 
| 
      
 75 
     | 
    
         
            +
                    nil,
         
     | 
| 
      
 76 
     | 
    
         
            +
                    nil,
         
     | 
| 
      
 77 
     | 
    
         
            +
                    to_double_array(objective),
         
     | 
| 
      
 78 
     | 
    
         
            +
                    nil,
         
     | 
| 
      
 79 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 80 
     | 
    
         
            +
                  )
         
     | 
| 
       76 
81 
     | 
    
         | 
| 
       77 
82 
     | 
    
         
             
                  # Segmentation errors when setting name
         
     | 
| 
       78 
83 
     | 
    
         
             
                  # Cbc_wrapper.Cbc_setProblemName(@cbc_model, model.name) if model.name
         
     | 
| 
         @@ -106,22 +111,20 @@ module Cbc 
     | 
|
| 
       106 
111 
     | 
    
         
             
                    idx += 1
         
     | 
| 
       107 
112 
     | 
    
         
             
                  end
         
     | 
| 
       108 
113 
     | 
    
         | 
| 
       109 
     | 
    
         
            -
                  ObjectSpace.define_finalizer(self, self.class.finalizer(@cbc_model 
     | 
| 
      
 114 
     | 
    
         
            +
                  ObjectSpace.define_finalizer(self, self.class.finalizer(@cbc_model))
         
     | 
| 
       110 
115 
     | 
    
         | 
| 
       111 
     | 
    
         
            -
                  @default_solve_params = {
         
     | 
| 
       112 
     | 
    
         
            -
                    log: 0
         
     | 
| 
       113 
     | 
    
         
            -
                  }
         
     | 
| 
      
 116 
     | 
    
         
            +
                  @default_solve_params = { log: 0 }
         
     | 
| 
       114 
117 
     | 
    
         
             
                end
         
     | 
| 
       115 
118 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
                def set_constraint_bounds( 
     | 
| 
       117 
     | 
    
         
            -
                  case  
     | 
| 
      
 119 
     | 
    
         
            +
                def set_constraint_bounds(constraint, idx)
         
     | 
| 
      
 120 
     | 
    
         
            +
                  case constraint.type
         
     | 
| 
       118 
121 
     | 
    
         
             
                  when Ilp::Constraint::LESS_OR_EQ
         
     | 
| 
       119 
     | 
    
         
            -
                    Cbc_wrapper.Cbc_setRowUpper(@cbc_model, idx,  
     | 
| 
      
 122 
     | 
    
         
            +
                    Cbc_wrapper.Cbc_setRowUpper(@cbc_model, idx, constraint.bound)
         
     | 
| 
       120 
123 
     | 
    
         
             
                  when Ilp::Constraint::GREATER_OR_EQ
         
     | 
| 
       121 
     | 
    
         
            -
                    Cbc_wrapper.Cbc_setRowLower(@cbc_model, idx,  
     | 
| 
      
 124 
     | 
    
         
            +
                    Cbc_wrapper.Cbc_setRowLower(@cbc_model, idx, constraint.bound)
         
     | 
| 
       122 
125 
     | 
    
         
             
                  when Ilp::Constraint::EQUALS
         
     | 
| 
       123 
     | 
    
         
            -
                    Cbc_wrapper.Cbc_setRowUpper(@cbc_model, idx,  
     | 
| 
       124 
     | 
    
         
            -
                    Cbc_wrapper.Cbc_setRowLower(@cbc_model, idx,  
     | 
| 
      
 126 
     | 
    
         
            +
                    Cbc_wrapper.Cbc_setRowUpper(@cbc_model, idx, constraint.bound)
         
     | 
| 
      
 127 
     | 
    
         
            +
                    Cbc_wrapper.Cbc_setRowLower(@cbc_model, idx, constraint.bound)
         
     | 
| 
       125 
128 
     | 
    
         
             
                  end
         
     | 
| 
       126 
129 
     | 
    
         
             
                end
         
     | 
| 
       127 
130 
     | 
    
         | 
| 
         @@ -131,8 +134,6 @@ module Cbc 
     | 
|
| 
       131 
134 
     | 
    
         
             
                  end
         
     | 
| 
       132 
135 
     | 
    
         
             
                  Cbc_wrapper.Cbc_solve(@cbc_model)
         
     | 
| 
       133 
136 
     | 
    
         
             
                  @solution = Cbc_wrapper::DoubleArray.frompointer(Cbc_wrapper.Cbc_getColSolution(@cbc_model))
         
     | 
| 
       134 
     | 
    
         
            -
                  @double_arrays << @solution
         
     | 
| 
       135 
     | 
    
         
            -
                  @solution
         
     | 
| 
       136 
137 
     | 
    
         
             
                end
         
     | 
| 
       137 
138 
     | 
    
         | 
| 
       138 
139 
     | 
    
         
             
                def value_of(var)
         
     | 
| 
         @@ -145,9 +146,12 @@ module Cbc 
     | 
|
| 
       145 
146 
     | 
    
         
             
                  end
         
     | 
| 
       146 
147 
     | 
    
         
             
                end
         
     | 
| 
       147 
148 
     | 
    
         | 
| 
      
 149 
     | 
    
         
            +
                # Keep this one for back compatibility
         
     | 
| 
      
 150 
     | 
    
         
            +
                # rubocop:disable Naming/AccessorMethodName
         
     | 
| 
       148 
151 
     | 
    
         
             
                def set_time_limit(seconds)
         
     | 
| 
       149 
152 
     | 
    
         
             
                  @default_solve_params[:sec] = seconds
         
     | 
| 
       150 
153 
     | 
    
         
             
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
                # rubocop:enable Naming/AccessorMethodName
         
     | 
| 
       151 
155 
     | 
    
         | 
| 
       152 
156 
     | 
    
         
             
                def proven_optimal?
         
     | 
| 
       153 
157 
     | 
    
         
             
                  Cbc_wrapper.Cbc_isProvenOptimal(@cbc_model) == 1
         
     | 
| 
         @@ -175,18 +179,16 @@ module Cbc 
     | 
|
| 
       175 
179 
     | 
    
         
             
                end
         
     | 
| 
       176 
180 
     | 
    
         | 
| 
       177 
181 
     | 
    
         
             
                def find_conflict
         
     | 
| 
       178 
     | 
    
         
            -
                  @ 
     | 
| 
      
 182 
     | 
    
         
            +
                  @find_conflict ||= ConflictSolver.new(self).find_conflict
         
     | 
| 
       179 
183 
     | 
    
         
             
                end
         
     | 
| 
       180 
184 
     | 
    
         | 
| 
       181 
185 
     | 
    
         
             
                def find_conflict_vars
         
     | 
| 
       182 
     | 
    
         
            -
                  @ 
     | 
| 
      
 186 
     | 
    
         
            +
                  @find_conflict_vars ||= find_conflict.map(&:vars).flatten.uniq
         
     | 
| 
       183 
187 
     | 
    
         
             
                end
         
     | 
| 
       184 
188 
     | 
    
         | 
| 
       185 
     | 
    
         
            -
                def self.finalizer(cbc_model 
     | 
| 
      
 189 
     | 
    
         
            +
                def self.finalizer(cbc_model)
         
     | 
| 
       186 
190 
     | 
    
         
             
                  proc do
         
     | 
| 
       187 
191 
     | 
    
         
             
                    Cbc_wrapper.Cbc_deleteModel(cbc_model)
         
     | 
| 
       188 
     | 
    
         
            -
                    int_arrays.each { |ar| Cbc_wrapper.delete_intArray(ar) }
         
     | 
| 
       189 
     | 
    
         
            -
                    double_arrays.each { |ar| Cbc_wrapper.delete_doubleArray(ar) }
         
     | 
| 
       190 
192 
     | 
    
         
             
                  end
         
     | 
| 
       191 
193 
     | 
    
         
             
                end
         
     | 
| 
       192 
194 
     | 
    
         | 
| 
         @@ -203,7 +205,6 @@ module Cbc 
     | 
|
| 
       203 
205 
     | 
    
         
             
                    c_array[idx] = array[idx]
         
     | 
| 
       204 
206 
     | 
    
         
             
                    idx += 1
         
     | 
| 
       205 
207 
     | 
    
         
             
                  end
         
     | 
| 
       206 
     | 
    
         
            -
                  @int_arrays << c_array
         
     | 
| 
       207 
208 
     | 
    
         
             
                  c_array
         
     | 
| 
       208 
209 
     | 
    
         
             
                end
         
     | 
| 
       209 
210 
     | 
    
         | 
| 
         @@ -214,7 +215,6 @@ module Cbc 
     | 
|
| 
       214 
215 
     | 
    
         
             
                    c_array[idx] = array[idx]
         
     | 
| 
       215 
216 
     | 
    
         
             
                    idx += 1
         
     | 
| 
       216 
217 
     | 
    
         
             
                  end
         
     | 
| 
       217 
     | 
    
         
            -
                  @double_arrays << c_array
         
     | 
| 
       218 
218 
     | 
    
         
             
                  c_array
         
     | 
| 
       219 
219 
     | 
    
         
             
                end
         
     | 
| 
       220 
220 
     | 
    
         
             
              end
         
     | 
    
        data/lib/ruby-cbc/version.rb
    CHANGED
    
    
    
        data/lib/ruby-cbc.rb
    CHANGED
    
    | 
         @@ -4,7 +4,7 @@ require "cbc-wrapper" 
     | 
|
| 
       4 
4 
     | 
    
         
             
            module Cbc
         
     | 
| 
       5 
5 
     | 
    
         
             
            end
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            files = %w 
     | 
| 
      
 7 
     | 
    
         
            +
            files = %w[
         
     | 
| 
       8 
8 
     | 
    
         
             
              conflict_solver
         
     | 
| 
       9 
9 
     | 
    
         
             
              model
         
     | 
| 
       10 
10 
     | 
    
         
             
              problem
         
     | 
| 
         @@ -16,7 +16,7 @@ files = %w( 
     | 
|
| 
       16 
16 
     | 
    
         
             
              ilp/term_array
         
     | 
| 
       17 
17 
     | 
    
         
             
              ilp/var
         
     | 
| 
       18 
18 
     | 
    
         
             
              utils/compressed_row_storage
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 19 
     | 
    
         
            +
            ]
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
            files.each do |file|
         
     | 
| 
       22 
22 
     | 
    
         
             
              require File.expand_path("../ruby-cbc/#{file}", __FILE__)
         
     | 
    
        data/ruby-cbc.gemspec
    CHANGED
    
    | 
         @@ -1,7 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
       2 
     | 
    
         
            -
            lib = File.expand_path('../lib', __FILE__)
         
     | 
| 
      
 1 
     | 
    
         
            +
            lib = File.expand_path("lib", __dir__)
         
     | 
| 
       3 
2 
     | 
    
         
             
            $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
         
     | 
| 
       4 
     | 
    
         
            -
            require  
     | 
| 
      
 3 
     | 
    
         
            +
            require "ruby-cbc/version"
         
     | 
| 
       5 
4 
     | 
    
         | 
| 
       6 
5 
     | 
    
         
             
            Gem::Specification.new do |spec|
         
     | 
| 
       7 
6 
     | 
    
         
             
              spec.name          = "ruby-cbc"
         
     | 
| 
         @@ -19,14 +18,16 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       19 
18 
     | 
    
         
             
              spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
         
     | 
| 
       20 
19 
     | 
    
         
             
              spec.require_paths = ["lib"]
         
     | 
| 
       21 
20 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
              spec.add_development_dependency " 
     | 
| 
       23 
     | 
    
         
            -
              spec.add_development_dependency " 
     | 
| 
       24 
     | 
    
         
            -
              spec.add_development_dependency "rspec"
         
     | 
| 
       25 
     | 
    
         
            -
              spec.add_development_dependency "rake-compiler"
         
     | 
| 
      
 21 
     | 
    
         
            +
              spec.add_development_dependency "benchmark-ips"
         
     | 
| 
      
 22 
     | 
    
         
            +
              spec.add_development_dependency "bundler", "~> 2.3"
         
     | 
| 
       26 
23 
     | 
    
         
             
              spec.add_development_dependency "pry"
         
     | 
| 
       27 
24 
     | 
    
         
             
              spec.add_development_dependency "pry-byebug"
         
     | 
| 
       28 
     | 
    
         
            -
              spec.add_development_dependency " 
     | 
| 
      
 25 
     | 
    
         
            +
              spec.add_development_dependency "rake", "~> 10.0"
         
     | 
| 
      
 26 
     | 
    
         
            +
              spec.add_development_dependency "rake-compiler"
         
     | 
| 
      
 27 
     | 
    
         
            +
              spec.add_development_dependency "rspec"
         
     | 
| 
      
 28 
     | 
    
         
            +
              spec.add_development_dependency "rubocop"
         
     | 
| 
      
 29 
     | 
    
         
            +
              spec.add_development_dependency "rubocop-rspec"
         
     | 
| 
       29 
30 
     | 
    
         
             
              spec.add_development_dependency "ruby-prof"
         
     | 
| 
       30 
31 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
              spec.add_dependency "cbc-wrapper",  
     | 
| 
      
 32 
     | 
    
         
            +
              spec.add_dependency "cbc-wrapper", "~> 2.9.9.2"
         
     | 
| 
       32 
33 
     | 
    
         
             
            end
         
     |