Mercurial > pokemon-types
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 |