"""
.. py:class:: Agent(willingness, competence, numfacts, numnoise,
spamminess, selfishness, trust_used,
inbox_trust_sorted, trust_filter_on, capacity)
Class for generating and implementing Agents.
:param int numfacts: number of facts in the simulation that are
valuable
:param int numnoise: number of facts in the simulation that are
noise
:param float willingness: how frequently an agent will act
:param float competence: how frequently the agent will a fact
as valuable or not correctly (1: always, p: p% of the time)
:pram float spamminess: how frequently the agent will send the
same fact to the same person (spammer=1 always, spammer=0 never)
:param float selfishness: how frequently the agent will drop a fact
and not send at all to a specific person (selfish=0 never,
selfish=1 always)
:param int capacity: how many actions an agent can take at each
simulation step. Capacity is 1 by default to implement agents
with limited cognitive resources.
"""
import random
import Trust
from simutil import *
class Agent(object):
[docs]
def __init__ (self, w=1, c=1, numfacts = 0, numnoise=0, \
[docs] spammer=0, selfish=0, \
trust_used = True, inbox_trust_sorted = True, \
trust_filter_on = True, capacity = 1):
## General constants used in simulation
self.NUM_FACTS = numfacts
self.NUM_NOISE = numnoise
## Agent properties
self.trust_used = trust_used
self.inbox_trust_sorted = inbox_trust_sorted
self.willingness = w
self.competence = c
self.selfish = selfish
self.spammer = spammer
self.capacity = capacity
self.trust_filter_on = trust_filter_on
self.spam_sensitivity = 0.2 # discount for spam behavior
# how much spamming will be counted as negative competence evidence
## Action history
self.inbox = [] ## list of (fact, sender_neighbor=None)
self.outbox = [] ## list of (trust_for_receiver, fact, receiver)
self.last_received_facts = []
self.all_received_facts = set([])
## all facts sent once to someone, used for performance optimization
self.sentfacts = set([])
self.numsent = 0 ## number of facts sent
self.knowledge = set([]) ## id of all facts known, spam or valuable
self.history = {} ## key:fact, value: set of neighbors fact is sent to
self.time_spent = 0 ## simulation time, number of times act is executed
## Network based properties, beliefs
self.neighbors = set([])
self.trust = {} ## Key: neighbor, Value: Trust object
self.neighbor_spamminess = {}
self.num_filtered = 0
def get_trust_for_neighbors( self ):
line = ""
for n in self.neighbors:
line += "%d/%r " %(self.trust[n].trust, self.trust[n].is_trusted)
return line
def add_fact(self, fact):
""" Add fact to knowledge. """
self.knowledge.add(fact)
def connect_to(self, neighbors, \
[docs] prior_comp = ('M','M'), \
prior_will=('M','M')):
"""
..py:function:: Agent.connect_to(neighbors, prior_comp, prior_will)
Example usage::
a.connect_to(lista, ('M','L'), ('H','H'))
Create a link to all Agents in the set neighbors.
Initialize prior trust for all neighbors if prior
competence and willigness is given.
:param set(Agent) neighbors: a set of Agent objects
that are neighbors of the current Agent
:param tuple(string, string) prior_comp: prior competence
belief for all neighbors, given as a pair of belief and
uncertainty. See :mod:`Trust` for more details.
:param tuple(string, string) prior_will: prior competence
belief for all neighbors, given as a pair of belief and
uncertainty. See :mod:`Trust` for more details.
"""
self.neighbors = set(neighbors)
for n in self.neighbors:
self.trust[n] = Trust.Trust(n, prior_comp, prior_will)
self.neighbor_spamminess[n] = 0
def stat(self):
""" Return basic stats for the agent. """
return (len(self.knowledge), len(self.neighbors))
def is_fact_valuable(self,fact):
""" Return the ground truth of whether the fact is valuable. """
if (0 <= fact < self.NUM_FACTS):
return True
elif (self.NUM_FACTS <= fact < (self.NUM_FACTS + self.NUM_NOISE)):
return False
else:
return None ## Invalid fact.
def process_fact(self, fact, sender_neighbor):
## sender_neighbor is None if the fact is from initial inbox
# Determine if the fact is valuable
is_good = self.is_fact_valuable(fact)
if random.random() > self.competence:
## process fact incorrectly
is_good = not is_good
# If trust is considered, add this fact as evidence and spamminess
if self.trust_used:
if sender_neighbor: ## there is a sender for the fact
## there is no sender for initial facts
self.last_received_facts.append( (sender_neighbor, is_good) )
if (fact, sender_neighbor) in self.all_received_facts:
self.neighbor_spamminess[sender_neighbor] += 1
else:
self.all_received_facts.add((fact, sender_neighbor))
if len(self.last_received_facts) > 10:
self.process_trust()
## Decide who to send the fact to based on spamminess and selfishness
if is_good:
self.knowledge.add(fact)
## x% spammer person will send the same fact to x% of contacts.
already_sent_tmp = list(self.history.get(fact,set()))
template = range(len(already_sent_tmp))
random.shuffle(template)
idx = int(len(template) * (1-self.spammer))
already_sent = []
for i in template[:idx]:
already_sent.append( already_sent_tmp[ template[i]] )
to_send = []
to_send_tmp = self.neighbors - set(already_sent)
to_send_tmp = list(to_send_tmp)
template = []
if self.trust_used: ##construct template based on trust
## and exclude people if trust filter is on
for i in range(len(to_send_tmp)):
n = to_send_tmp[i]
if self.trust_filter_on: ##only included trusted people
if self.trust[n].is_trusted:
template.append( (self.trust[n].trust, i) )
else:
self.num_filtered += 1
else:
template.append( (self.trust[n].trust, i) )
## choose the least trusted people from to_send_tmp to exclude
template.sort()
else: ## no trust used
for i in range(len(to_send_tmp)):
n = to_send_tmp[i]
template.append( (1, i) )
## choose random people from to_send_tmp to exclude
random.shuffle(template)
idx = int(len(template) * (1-self.selfish))
## find the items to send, sort by trust if trust is used
for (t,i) in template[:idx]:
to_send.append( (t, fact, to_send_tmp[i]) )
if self.trust_used:
to_send.sort(reverse = True)
self.outbox.extend( to_send )
def init_outbox(self):
""" Add all initial knowledge as a fact to send out. """
for fact in self.knowledge:
self.process_fact(fact, None) ## There is no sender
def receive(self, fact, neighbor):
""" Receive a fact from another neighbor. """
self.inbox.append((fact, neighbor))
def act(self):
"""
A single action for the agent:
- either send something from the outbox, or
- receive something from the inbox if outbox is empty.
"""
debug = True
self.time_spent += 1 ## simulation time incremented
actions_taken = []
for i in xrange(self.capacity):
### By willingness probability, decide whether to act or not
if random.random() <= self.willingness:
## Agent decided to act
decision = self.decide_action()
if decision == 'outbox':
### Take the first action from the outbox
self.numsent += 1
(trust, fact, n) = self.outbox.pop(0)
if fact in self.sentfacts:
self.history[fact].add(n)
else:
self.history[fact] = set([n])
self.sentfacts.add(fact)
actions_taken.append((n, fact))
elif len(self.inbox) != 0: # decision is inbox
### Process the first fact in the inbox and queue to outbox
(fact, neighbor) = self.inbox.pop(0)
self.process_fact(fact, neighbor)
return actions_taken ## No send action was taken
def decide_action(self) :
## choose outbox as long as there is something there.
# if len(self.outbox) != 0 and len(self.inbox) != 0:
# if random.random() < 0.5:
# return "outbox"
# else:
# return "inbox"
# elif len(self.outbox) != 0:
# return "outbox"
# else:
# return "inbox"
if len(self.outbox) != 0:
return "outbox"
else:
return "inbox"
def sort_inbox_by_trust(self) :
"""
Sort the inbox according to the current trust value of
each neighbor, send to most trusted first. Called from
process_trust, after updating trust if a flag is set.
We will simply take the trusted agents' message and put
them to top!
"""
new_inbox = []
unsorted_inbox = []
for (fact, sender) in self.inbox:
if self.trust[sender].is_trusted:
new_inbox.append( (self.trust[sender].trust, fact, sender) )
else:
unsorted_inbox.append( (fact, sender) )
new_inbox.sort(reverse = True)
self.inbox = []
for (t, fact, sender) in new_inbox:
self.inbox.append( (fact, sender) )
self.inbox.extend(unsorted_inbox)
def sort_outbox_by_trust(self) :
"""
Sort the outbox according to the current trust value of
each neighbor, send to most trusted first. Called from
process_trust, after updating trust.
"""
new_outbox = []
for (trust, fact, neighbor) in self.outbox:
new_outbox.append( (self.trust[neighbor].trust, fact, neighbor) )
new_outbox.sort(reverse = True)
self.outbox = new_outbox
def process_trust(self) :
"""
After a certain amount of evidence is collected,
update trust for each neighbor and resort the outbox.
"""
num_evidence = float(len(self.last_received_facts))
evidence = {}
for n in self.neighbors:
evidence[n] = [0,0,0]
for (n, is_good) in self.last_received_facts:
evidence[n][0] += 1
if is_good:
evidence[n][1] +=1
else:
evidence[n][2] += 1
all_will_evidence = []
for n in self.neighbors:
all_will_evidence.append ( evidence[n][0]/num_evidence )
evidence[n][2] += self.spam_sensitivity * self.neighbor_spamminess[n]
(m,s) = meanstd(all_will_evidence)
for n in self.neighbors:
x = evidence[n][0]/num_evidence
ev = 0
if x > m+s:
ev = 1
elif x >= m:
ev = 0.75
elif x >= m-s:
ev = 0.5
elif x >= m-2*s:
ev = 0.25
self.trust[n].get_will_evidence(self.time_spent, ev)
self.trust[n].get_comp_evidence(self.time_spent, \
evidence[n][1], \
evidence[n][2])
self.trust[n].get_trust() ## Update trust category for neighbor
self.last_received_facts = []
self.sort_outbox_by_trust()
if self.inbox_trust_sorted:
self.sort_inbox_by_trust()