;Distributed recommender system ; by ;Jose M Vidal breeds [agents documents] ;preferences is a binary 2D vector, we map the first element to x and the second to y ;documents-read is the list of the who of the documents the agent has read ;documents-liked is list of lists ; item who-x documents-liked contains the list of the who of the documents the agent knows that who-x likes ; if who-x = who then this is the list of documents the agent knows it likes. ;recommendations is a history of the recommendations received from each agent, indexed by who. (i.e. a list of lists) ; the list is a binary vector, 0 if I had already read that document, 1 otherwise. First one is most recent. ;rec holds the latest recommendation ;x the payoff I expect from getting a recommendation from each agent, indexed by who of agent. ;utility is the utility for this round ;accrued-utility is the utility the agent has accrued ;accrued-gain ;expected-utility is what this agent can expect to gain by choosing a random document and reading it. agents-own [preferences documents-read documents-liked recommendations rec x utility accrued-utility accrued-gain expected-utility] ;description is the same structure as agents.preferences documents-own [description read-by] globals [time read-all] to set-coordinates-based-on-prefs [prefs] set xcor ((item 0 prefs) * screen-size-x) - screen-edge-x set ycor ((item 1 prefs) * screen-size-y) - screen-edge-y end to-report make-list [n v] locals [res] set res [] repeat n [ set res fput v res] report res end to setup locals [tmpag] ca set time 0 ask patches [set pcolor white] set-default-shape documents "box" repeat num-agents [ create-agent] create-documents num-documents set read-all 0 ;none has read them all ask agents [ ;these two are just temporary, so I can print them out set utility (count (documents in-radius like-radius)) * (reward-reading - cost-reading) set accrued-utility utility + ((num-documents - count (documents in-radius like-radius)) * (0 - cost-reading)) ;calculate expected utility. Notice that this value will be wrong ;if we start adding documents later on. set expected-utility (((num-documents - count (documents in-radius like-radius)) * (0 - cost-reading)) +((num-documents - count (documents in-radius like-radius)) * (reward-reading - cost-reading))) / num-documents ] type "Max utility=" print sum values-from agents [utility] type "Expected utility=" print sum values-from agents [accrued-utility] ask agents [ set utility 0 set accrued-utility 0] end to create-agent locals [other-prefs prefs] ifelse (random-int-or-float 1.0 < clustering-prob and any? agents)[ set other-prefs preferences-of (random-one-of agents) set prefs list (((random-int-or-float .1) - .05) + (item 0 other-prefs)) (((random-int-or-float .1) - .05) + (item 1 other-prefs))] [ set prefs list (random-int-or-float 1.0) (random-int-or-float 1.0)] create-custom-agents 1 [ set preferences prefs set-coordinates-based-on-prefs preferences set heading 0 set size 10 set color (who * 10) + 5 set documents-read [] set documents-liked make-list num-agents [] set recommendations make-list num-agents [] set x make-list num-agents 0 set utility 0 set accrued-utility 0 set accrued-gain 0 create-temporary-plot-pen "a" + who set-current-plot-pen "a" + who set-plot-pen-color color set rec -1] end to create-documents [n] create-custom-documents n [ set description list (random-int-or-float 1.0) (random-int-or-float 1.0) set-coordinates-based-on-prefs description set read-by [] set color blue set size 1 set heading 0] end to update locals [chosen-agent] set time time + 1 if (time > 30000)[stop] ; if (random 1.0 < .1) [create-documents 1] if (read-all = num-agents) [stop] set chosen-agent random-one-of agents ask chosen-agent [set utility 0] no-display set-current-plot "gain" ask chosen-agent [doit] display ask chosen-agent [set accrued-utility accrued-utility + utility] ask agents [ create-temporary-plot-pen "a" + who plot accrued-gain] set-current-plot "utility" plot sum values-from agents [accrued-utility] end ;reports a list which is the disjoint of l1 and l2 to-report disjoint [l1 l2] locals [res] set res [] while [not empty? l1][ if (member? (first l1) l2)[ set res fput (first l1) res] set l1 butfirst l1] report res end ;returns a list that contains all the elements of l1 except for those that are in l2 ; l1 - l2 to-report list-substract [l1 l2] locals [res] set res [] while [not empty? l1][ if (not member? (first l1) l2)[ set res fput (first l1) res] set l1 butfirst l1] report res end ;;; ;agents member functions to-report i-like? [doc] set (read-by-of doc) fput who (read-by-of doc) report distance doc <= like-radius end ;assumes I have not read doc, that is, not member? doc documents-read to read-document [doc recommended?] if (doc != nobody) [ ifelse (i-like? doc)[ set documents-liked replace-item who documents-liked (fput (who-of doc) (item who documents-liked)) ;draw a line to that document pen-down set heading towards doc fd distance doc pen-up set-coordinates-based-on-prefs preferences set heading 0 set utility utility + (reward-reading - cost-reading) if (recommended?) [ set accrued-gain accrued-gain + ((reward-reading - cost-reading - cost-message) - expected-utility)]] [ if (recommended?)[ set accrued-gain accrued-gain - cost-reading] set utility utility - cost-reading] set documents-read fput (who-of doc) documents-read if (length documents-read = num-documents) [ set read-all read-all + 1]] end to doit locals [chosen-who] ifelse (random-int-or-float 1.0 < document-explore)[ ;pick a doc at random and read it read-document random-one-of (documents with [not member? who (documents-read-of myself)]) false] [ ;ask someone about it ifelse (random-int-or-float 1.0 < agent-explore)[ ;pick agent at random exchange-recommendations-with random-one-of agents with [who != who-of myself]] [;pick best agent, only if there is someone that can provide expected utility greater than the cost of me sending a message. ifelse (max x > cost-message)[;ok, found the best guy exchange-recommendations-with (turtle position (max x) x)] [;if there is no greedy strategy available then fall back to reading a random document read-document random-one-of (documents with [not member? who (documents-read-of myself)]) false]]] end to-report i-know-that-i-like-it? [doc-who] report member? doc-who (item who documents-liked) end to-report i-have-read-it? [doc-who] report member? doc-who documents-read end ;recommend a document that this agent has read and liked ;but that agnt has *not* read. Pick randomly from among this set. ;A distributed implementation of this would require agnt to post the list of documents it has read, or tell it to this agent. to-report get-recommendation-for [agnt] locals [possible-recs] set possible-recs list-substract (item who documents-liked) (documents-read-of agnt) if (0 = length possible-recs)[ report -1] report item (random-int-or-float length possible-recs) possible-recs end ;recommend a document by chosing randomly from amont the documents that this agent knows that it likes. to-report get-random-recommendation-for [agnt] locals [possible-recs] set possible-recs item who documents-liked if (0 = length possible-recs)[ report -1] report item (random-int-or-float length possible-recs) possible-recs end ;ask other-agent to recommend a document ;update my info on him ;read document if I have not already to exchange-recommendations-with [other-agent] if (other-agent = nobody) [stop] ;set the rec of both me and ag-who to be the document we each recommend each other ask other-agent [set (rec-of myself) get-recommendation-for myself] set (rec-of other-agent) get-recommendation-for other-agent ;handle the received recommendations, reading documents if neccessary. process-recommendation other-agent ask other-agent [process-recommendation myself] set utility utility - cost-message end ;updates my history of the recommendations from other agents ;value=0 if I had already read the document recommended or there was no recommendation ;value=1 if the recommendation was for something I had not read (regardless of wether I liked it or not). to update-recommendations [other-agent-who value] set recommendations replace-item other-agent-who recommendations (fput value (item other-agent-who recommendations)) if (length item other-agent-who recommendations > 10)[ set recommendations replace-item other-agent-who recommendations (butlast item other-agent-who recommendations)] end ;handle a recommendation from other-agent saying that he likes rec to process-recommendation [other-agent] locals [other-agent-who] set other-agent-who who-of other-agent ifelse (rec = -1)[ update-recommendations other-agent-who 0] ;treat no-rec as a rec for a document I read [ set documents-liked replace-item other-agent-who documents-liked (fput rec (item other-agent-who documents-liked)) ifelse (i-have-read-it? rec)[ update-recommendations other-agent-who 0] [ update-recommendations other-agent-who 1 read-document (turtle rec) true]] set x replace-item other-agent-who x (generate-xi-value other-agent-who) end ;reports the utility I can expect from engaging with agent-who ;r = (#documents he recommended that I had not read)/(#documents he recommended) ; ;r *[ (|L_i n L_j| / |L_j|)*(R_r - C_r) + (1 - (|L_i n L_j| / |L_j|))*(0 - C_r) ] to-report generate-xi-value [agent-who] locals [r i-like he-likes] set r .5 set i-like item who documents-liked set he-likes item agent-who documents-liked if (length item agent-who recommendations > 0 and length he-likes > 0) [ set r (sum (item agent-who recommendations)) / length item agent-who recommendations report r * (((length disjoint i-like he-likes) / (length he-likes)) * (reward-reading - cost-reading) + (1 - ((length disjoint i-like he-likes) / (length he-likes)))*(0 - cost-reading)) ] report -1 end ;;; ;documents member functions ;Im not using this function to die-if-read-by-all if (length read-by = num-agents)[ ;I have been read by all ask agents [ set documents-read remove (who-of myself) documents-read set documents-liked replace-item who documents-liked (remove (who-of myself) (item who documents-liked))] die] end @#$#@#$#@ GRAPHICS-WINDOW 250 10 561 342 150 150 1.0 1 10 1 1 1 0 CC-WINDOW 5 407 771 502 Command Center BUTTON 4 42 85 75 NIL setup NIL 1 T OBSERVER T NIL BUTTON 86 42 167 75 NIL update NIL 1 T OBSERVER T NIL BUTTON 168 42 249 75 NIL update T 1 T OBSERVER T NIL SLIDER 4 75 176 108 num-agents num-agents 1 100 50 1 1 NIL SLIDER 4 175 176 208 like-radius like-radius 0 100 50 1 1 NIL SLIDER 4 208 190 241 document-explore document-explore 0 1 0.1 0.01 1 NIL SLIDER 4 142 179 175 num-documents num-documents 0 1000 1000 1 1 NIL SLIDER 6 282 178 315 reward-reading reward-reading 0 10 10.0 0.1 1 NIL SLIDER 6 314 178 347 cost-reading cost-reading 0 10 2.0 0.1 1 NIL SLIDER 6 347 178 380 cost-message cost-message 0 10 0.1 0.1 1 NIL SLIDER 4 242 176 275 agent-explore agent-explore 0 1 0.1 0.01 1 NIL MONITOR 561 42 618 91 gu sum values-from agents [utility] 0 1 PLOT 561 92 761 242 utility NIL NIL 0.0 100.0 0.0 100.0 true false SLIDER 4 109 176 142 clustering-prob clustering-prob 0 1 1.0 0.01 1 NIL PLOT 562 243 762 393 gain NIL NIL 0.0 10.0 0.0 1.0 true false @#$#@#$#@ Title: Distributed Recommender System Author: Jose M. Vidal Description: A prototype implementation of a distributed recommender system as seen in