Skip to content

Instantly share code, notes, and snippets.

@Engelberg
Created March 5, 2014 23:45
Show Gist options
  • Save Engelberg/9379157 to your computer and use it in GitHub Desktop.
Save Engelberg/9379157 to your computer and use it in GitHub Desktop.
Optimizing allocation with loco
(ns mark.loco.grants
(:use loco.core loco.constraints))
; Use whatever scoring function you want for the various
; applicants, but ultimately, you want to assemble this
; information into a data structure.
(def applicants
[{:name "Alex", :score 5, :grant-request 120}
{:name "David", :score 4, :grant-request 100}
{:name "Mark", :score 3, :grant-request 80}])
(def n (count applicants))
(def budget 200)
; We'll be dealing with fractions, but our values need to be integers,
; so we need a scaling factor to multiply the fractions by before
; truncating to integers.
(def scale 1000)
; variable [:allocation n] is how much money to allocate to
; the applicant at the nth position of the applicants vector
(def allocation-vars
(for [i (range n)] [:allocation i]))
(def grant-constraints
; Don't give any person more than they asked for
(for [i (range n)]
($in [:allocation i] 0 (:grant-request (applicants i)))))
(def budget-constraint
; Don't spend more than the budget permits
($<= (apply $+ allocation-vars) budget))
(def all-constraints (conj grant-constraints budget-constraint))
; If you assume that the probability of someone coming
; is the allocation they received divided by the grant
; requested, then the "value" of each dollar given to
; a person is their score divded by the size of their grant request.
(def grant-values
(for [i (range n)]
(int (* scale (/ (:score (applicants i))
(:grant-request (applicants i)))))))
(solution all-constraints
:maximize (apply $+ (map $* allocation-vars grant-values)))
; The global scalar constraint should be equivalent and more efficient
; but there appears to be a bug in loco that prevents scalar
; from being used with maximize. I have filed an issue on this.
;(solution all-constraints
; :maximize ($scalar allocation-vars grant-values))
@Engelberg
Copy link
Author

One could also scale the grant-values dynamically, rather than by a hard-coded value, by whatever scaling factor gives you a spread of, say, 100, between the minimum and maximum values:

(def grant-fractional-values
  (for [i (range n)]
    (/ (:score (applicants i))
       (:grant-request (applicants i)))))

(def scale (/ 100 (- (apply max grant-fractional-values) (apply min grant-fractional-values))))

(def grant-values (map #(long (* scale %)) grant-fractional-values))

@Engelberg
Copy link
Author

The :maximize ($scalar allocation-vars grant-values) version works in 0.1.1-SNAPSHOT,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment