comparison org/lpsolve.org @ 16:7698e9bdff2b

upgraded pokemon-types to clojure version 1.3
author Robert McIntyre <rlm@mit.edu>
date Mon, 06 Aug 2012 17:22:39 -0400
parents ecb6e3f9b7d6
children 0f6ace87343a
comparison
equal deleted inserted replaced
15:da4c47650d38 16:7698e9bdff2b
135 : 135 :
136 : Actual values of the variables: 136 : Actual values of the variables:
137 : wheat 21.875 137 : wheat 21.875
138 : barley 53.125 138 : barley 53.125
139 139
140
140 This shows that the farmer can maximize his profit by planting 21.875 141 This shows that the farmer can maximize his profit by planting 21.875
141 of the available acres with wheat and the remaining 53.125 acres with 142 of the available acres with wheat and the remaining 53.125 acres with
142 barley; by doing so, he will make $6315.62(5) in profit. 143 barley; by doing so, he will make $6315.62(5) in profit.
143 144
144 * Incorporating =lp_solve= into Clojure 145 * Incorporating =lp_solve= into Clojure
150 ** The Farmer's Problem in Clojure 151 ** The Farmer's Problem in Clojure
151 152
152 We are going to solve the same problem involving wheat and barley, 153 We are going to solve the same problem involving wheat and barley,
153 that we did above, but this time using clojure and the =lp_solve= API. 154 that we did above, but this time using clojure and the =lp_solve= API.
154 155
155 #+srcname: intro 156 #+name: intro
156 #+begin_src clojure :results silent 157 #+begin_src clojure :results silent
157 (ns pokemon.lpsolve 158 (ns pokemon.lpsolve
158 (:use [clojure.contrib def set [seq :only [indexed]] pprint]) 159 ;;(:use [clojure.contrib def set [seq :only [indexed]] pprint])
159 (:import lpsolve.LpSolve) 160 (:import lpsolve.LpSolve)
160 (:require pokemon.types) 161 (:require pokemon.types)
161 (:require incanter.core trans) 162 (:require incanter.core)
162 (:require rlm.map-utils)) 163 (:require rlm.map-utils))
163 #+end_src 164 #+end_src
164 165
165 The =lp_solve= Java interface is available from the same site as 166 The =lp_solve= Java interface is available from the same site as
166 =lp_solve= itself, http://lpsolve.sourceforge.net/ Using it is the 167 =lp_solve= itself, http://lpsolve.sourceforge.net/ Using it is the
170 libraries to your export =LD_LIBRARY_PATH= if you are using Linux. For 171 libraries to your export =LD_LIBRARY_PATH= if you are using Linux. For
171 example, in my =.bashrc= file, I have the line 172 example, in my =.bashrc= file, I have the line
172 =LD_LIBRARY_PATH=$HOME/roBin/lpsolve:$LD_LIBRARY_PATH=. If everything 173 =LD_LIBRARY_PATH=$HOME/roBin/lpsolve:$LD_LIBRARY_PATH=. If everything
173 is set-up correctly, 174 is set-up correctly,
174 175
175 #+srcname: body 176 #+name: body
176 #+begin_src clojure :results verbatim :exports both 177 #+begin_src clojure :results verbatim :exports both
177 (import 'lpsolve.LpSolve) 178 (import 'lpsolve.LpSolve)
178 #+end_src 179 #+end_src
179 180
180 #+results: body 181 #+results: body
203 - intelligible, immutable output 204 - intelligible, immutable output
204 205
205 To deal with these issues I'll create four functions for interfacing 206 To deal with these issues I'll create four functions for interfacing
206 with =LpSolve= 207 with =LpSolve=
207 208
208 #+srcname: declares 209 #+name: declares
209 #+begin_src clojure :results silent 210 #+begin_src clojure :results silent
210 (in-ns 'pokemon.lpsolve) 211 (in-ns 'pokemon.lpsolve)
211 212
212 ;; deal with automatic memory management for LpSolve instance. 213 ;; deal with automatic memory management for LpSolve instance.
213 (declare linear-program) 214 (declare linear-program)
224 225
225 Every instance of =LpSolve= must be manually garbage collected via a 226 Every instance of =LpSolve= must be manually garbage collected via a
226 call to =deleteLP=. I use a non-hygienic macro similar to =with-open= 227 call to =deleteLP=. I use a non-hygienic macro similar to =with-open=
227 to ensure that =deleteLP= is always called. 228 to ensure that =deleteLP= is always called.
228 229
229 #+srcname: memory-management 230 #+name: memory-management
230 #+begin_src clojure :results silent 231 #+begin_src clojure :results silent
231 (in-ns 'pokemon.lpsolve) 232 (in-ns 'pokemon.lpsolve)
232 (defmacro linear-program 233 (defmacro linear-program
233 "solve a linear programming problem using LpSolve syntax. 234 "solve a linear programming problem using LpSolve syntax.
234 within the macro, the variable =lps= is bound to the LpSolve instance." 235 within the macro, the variable =lps= is bound to the LpSolve instance."
249 *** Sensible Results 250 *** Sensible Results
250 The =linear-program= macro deletes the actual =lps= object once it is 251 The =linear-program= macro deletes the actual =lps= object once it is
251 done working, so it's important to collect the important results and 252 done working, so it's important to collect the important results and
252 add return them in an immutable structure at the end. 253 add return them in an immutable structure at the end.
253 254
254 #+srcname: get-results 255 #+name: get-results
255 #+begin_src clojure :results silent 256 #+begin_src clojure :results silent
256 (in-ns 'pokemon.lpsolve) 257 (in-ns 'pokemon.lpsolve)
257 258
258 (defrecord LpSolution 259 (defrecord LpSolution
259 [objective-value 260 [objective-value
304 =model= function which returns the problem in a form that can be 305 =model= function which returns the problem in a form that can be
305 solved by other linear programming packages. 306 solved by other linear programming packages.
306 307
307 *** Solution Status of an LpSolve Object 308 *** Solution Status of an LpSolve Object
308 309
309 #+srcname: solve 310 #+name: solve
310 #+begin_src clojure :results silent 311 #+begin_src clojure :results silent
311 (in-ns 'pokemon.lpsolve) 312 (in-ns 'pokemon.lpsolve)
312 313
313 (defn static-integer? 314 (defn static-integer?
314 "does the field represent a static integer constant?" 315 "does the field represent a static integer constant?"
317 (integer? (.get field nil)))) 318 (integer? (.get field nil))))
318 319
319 (defn integer-constants [class] 320 (defn integer-constants [class]
320 (filter static-integer? (.getFields class))) 321 (filter static-integer? (.getFields class)))
321 322
322 (defn-memo constant-map 323 (defn constant-map
323 "Takes a class and creates a map of the static constant integer 324 "Takes a class and creates a map of the static constant integer
324 fields with their names. This helps with C wrappers where they have 325 fields with their names. This helps with C wrappers where they have
325 just defined a bunch of integer constants instead of enums." 326 just defined a bunch of integer constants instead of enums."
326 [class] 327 [class]
327 (let [integer-fields (integer-constants class)] 328 (let [integer-fields (integer-constants class)]
328 (into (sorted-map) 329 (into (sorted-map)
329 (zipmap (map #(.get % nil) integer-fields) 330 (zipmap (map #(.get % nil) integer-fields)
330 (map #(.getName %) integer-fields))))) 331 (map #(.getName %) integer-fields)))))
332
333 (alter-var-root #'constant-map memoize)
331 334
332 (defn solve 335 (defn solve
333 "Solve an instance of LpSolve and return a string representing the 336 "Solve an instance of LpSolve and return a string representing the
334 status of the computation. Will only solve a particular LpSolve 337 status of the computation. Will only solve a particular LpSolve
335 instance once." 338 instance once."
349 352
350 Now we can implement a nicer version of the examples from the 353 Now we can implement a nicer version of the examples from the
351 [[http://lpsolve.sourceforge.net/][=lp\_solve= website]]. The following is a more or less 354 [[http://lpsolve.sourceforge.net/][=lp\_solve= website]]. The following is a more or less
352 line-by-line translation of the Java code from that example. 355 line-by-line translation of the Java code from that example.
353 356
354 #+srcname: farmer-example 357 #+name: farmer-example
355 #+begin_src clojure :results silent 358 #+begin_src clojure :results silent
356 (in-ns 'pokemon.lpsolve) 359 (in-ns 'pokemon.lpsolve)
357 (defn farmer-example [] 360 (defn farmer-example []
358 (linear-program 361 (linear-program
359 (results 362 (results
411 the particular instance of =lp_solve=. Why not make a version of 414 the particular instance of =lp_solve=. Why not make a version of
412 =linear-program= that takes care of initialization? 415 =linear-program= that takes care of initialization?
413 416
414 417
415 418
416 #+srcname: lp-solve 419 #+name: lp-solve
417 #+begin_src clojure :results silent 420 #+begin_src clojure :results silent
418 (in-ns 'pokemon.lpsolve) 421 (in-ns 'pokemon.lpsolve)
419 (defn initialize-lpsolve-row-oriented 422 (defn initialize-lpsolve-row-oriented
420 "fill in an lpsolve instance using a constraint matrix =A=, the 423 "fill in an lpsolve instance using a constraint matrix =A=, the
421 objective function =c=, and the right-hand-side =b=" 424 objective function =c=, and the right-hand-side =b="
460 #+end_src 463 #+end_src
461 464
462 Now, we can use a much more functional approach to solving the 465 Now, we can use a much more functional approach to solving the
463 farmer's problem: 466 farmer's problem:
464 467
465 #+srcname: better-farmer 468 #+name: better-farmer
466 #+begin_src clojure :results silent 469 #+begin_src clojure :results silent
467 (in-ns 'pokemon.lpsolve) 470 (in-ns 'pokemon.lpsolve)
468 (defn better-farmer-example [] 471 (defn better-farmer-example []
469 (lp-solve [[120 210] 472 (lp-solve [[120 210]
470 [110 30] 473 [110 30]
549 susceptance of each type, then finding an immortal type corresponds to 552 susceptance of each type, then finding an immortal type corresponds to
550 setting each constraint (the $b$ vector) to -1 (since log_2(1/2) = -1) 553 setting each constraint (the $b$ vector) to -1 (since log_2(1/2) = -1)
551 and setting the constraint vector $c$ to all ones, which means that we 554 and setting the constraint vector $c$ to all ones, which means that we
552 want to find the immortal type which uses the least amount of types. 555 want to find the immortal type which uses the least amount of types.
553 556
554 #+srcname: pokemon-lp 557 #+name: pokemon-lp
555 #+begin_src clojure :results silent 558 #+begin_src clojure :results silent
556 (in-ns 'pokemon.lpsolve) 559 (in-ns 'pokemon.lpsolve)
557 560
558 (defn log-clamp-matrix [matrix] 561 (defn log-clamp-matrix [matrix]
559 ;; we have to clamp the Infinities to a more reasonable negative 562 ;; we have to clamp the Infinities to a more reasonable negative
607 610
608 (defn set-variable-names 611 (defn set-variable-names
609 "sets the variable names of the problem given a vector of names" 612 "sets the variable names of the problem given a vector of names"
610 [#^LpSolve lps names] 613 [#^LpSolve lps names]
611 (dorun 614 (dorun
612 (map (fn [[index name]] 615 (keep-indexed
613 (.setColName lps (inc index) (str name))) 616 (fn [index name]
614 ;; ONE based indexing!!! 617 (.setColName lps (inc index) (str name)))
615 (indexed names)))) 618 ;; ONE based indexing!!!
619 names)))
616 620
617 (defn poke-solve 621 (defn poke-solve
618 ([poke-matrix target objective-function constraint min-num-types] 622 ([poke-matrix target objective-function constraint min-num-types]
619 ;; must have at least one type 623 ;; must have at least one type
620 (let [poke-matrix 624 (let [poke-matrix
643 #+end_src 647 #+end_src
644 648
645 With this, we are finally able to get some results. 649 With this, we are finally able to get some results.
646 650
647 ** Results 651 ** Results
648 #+srcname: results 652 #+name: results
649 #+begin_src clojure :results silent 653 #+begin_src clojure :results silent
650 (in-ns 'pokemon.lpsolve) 654 (in-ns 'pokemon.lpsolve)
651 655
652 (defn best-defense-type 656 (defn best-defense-type
653 "finds a type combination which is resistant to all attacks." 657 "finds a type combination which is resistant to all attacks."
972 976
973 Unfortunately, all of the tools that we've written so far are focused 977 Unfortunately, all of the tools that we've written so far are focused
974 on defense type combinations. However, it is possible to make every 978 on defense type combinations. However, it is possible to make every
975 tool attack-oriented via a simple macro. 979 tool attack-oriented via a simple macro.
976 980
977 #+srcname: attack-oriented 981 #+name: attack-oriented
978 #+begin_src clojure :results silent 982 #+begin_src clojure :results silent
979 (in-ns 'pokemon.lpsolve) 983 (in-ns 'pokemon.lpsolve)
980 984
981 (defmacro attack-mode [& forms] 985 (defmacro attack-mode [& forms]
982 `(let [attack-strengths# pokemon.types/attack-strengths 986 `(let [attack-strengths# pokemon.types/attack-strengths