Package spade :: Module tbcbp
[hide private]
[frames] | no frames]

Source Code for Module spade.tbcbp

  1  import Queue 
  2  import time 
  3  import random 
  4  from odict import odict 
  5  from copy import copy, deepcopy 
  6  from uuid import uuid4 
  7  from math import sqrt, pow, ceil, log, floor 
  8  import math 
  9  import types 
 10   
 11  import dblite 
 12   
13 -class TimeOut(Exception): pass
14 -class TooMuchPlans(Exception): pass
15
16 -class CaseDB:
17 - def __init__(self):
18 self.db = dblite.Base("caseDB") 19 self.db.create("P","Q","inputs","outputs","case", mode="override") 20 self.db.open()
21
22 - def add(self, case):
23 P = sorted(case.getP()) 24 Q = sorted(case.getQ()) 25 I = sorted(case.getInputs()) 26 O = sorted(case.getOutputs()) 27 28 self.db.insert(P=P,Q=Q,inputs=I,outputs=O,case=case)
29
30 - def get(self, P=None,Q=None,inputs=None,outputs=None):
31 res = [] 32 query = "self.db(" 33 if P!=None: query+="P="+str(sorted(P))+"," 34 if Q!=None: query+="Q="+str(sorted(Q))+"," 35 if inputs!=None: query+="inputs="+str(sorted(inputs))+"," 36 if outputs!=None: query+="outputs="+str(sorted(outputs)) 37 query+=")" 38 for record in eval(query): 39 res.append(record["case"]) 40 41 return res
42
43 -class Case(dict):
44
45 - def __init__(self,P=[], Q=[], inputs=[], outputs=[], services=[], QoS=1, ID=None):
46 dict.__init__(self) 47 self.P = P 48 self.Q = Q 49 self.inputs = inputs 50 self.outputs = outputs 51 self.QoS = QoS 52 self.services = services 53 self.time = 0 54 if ID == None: self.ID = str(uuid4()) 55 else: self.ID = ID 56 57 self.rewards = 0 58 self.punishments= 0 59 self.executions = 0 60 self.trust = 0
61
62 - def getID(self): return self.ID
63 - def getP(self): return self.P
64 - def getQ(self): return self.Q
65 - def getInputs(self): return self.inputs
66 - def getOutputs(self): return self.outputs
67 - def getQoS(self): return self.QoS
68 - def getTime(self): return self.time
69
70 - def reward(self):
71 self.rewards+=1 72 self.executions+=1 73 self.trust = min(1.0,float(self.rewards)/float(self.executions)) 74 self.QoS = self.trust
75
76 - def punish(self):
77 if self.rewards>0: self.rewards-=1 78 self.punishments+=1 79 self.executions+=1 80 self.trust = max(0.0,float(self.rewards)/float(self.executions)) 81 self.QoS = self.trust
82
83 - def __add__(self,y):
84 #if str(self.Q)!=str(y.getP()): 85 for sentence in self.outputs: 86 if sentence not in y.inputs: 87 raise TypeError 88 return Case(P=self.P,Q=y.getQ(),inputs=self.getInputs(),outputs=y.getOutputs(), services=self.services+y.services,QoS=self.QoS*y.QoS, ID=str(uuid4()))
89
90 - def __iadd__(self,y):
91 self = self+y 92 return self
93
94 - def __eq__(self,y):
95 if not issubclass(y.__class__,Case): return False 96 if self.services!= y.services: return False 97 #if self.P!=y.getP(): 98 if sorted(self.P) != sorted(y.P): 99 return False 100 #if self.Q!=y.getQ(): 101 if sorted(self.Q) != sorted(y.Q): 102 return False 103 if sorted(self.inputs) != sorted(y.inputs): 104 return False 105 if sorted(self.outputs) != sorted(y.outputs): 106 return False 107 return True
108
109 - def __ne__(self,y):
110 return not self==y
111
112 - def __str__(self):
113 return "{ID: "+self.ID+" P: "+str(self.P)+ " Q: "+str(self.Q)+" I: "+str(self.inputs)+" O: "+str(self.outputs)+" SERVICES:"+str(self.services)+" QoS: "+str(self.QoS)+"}"
114
115 - def __repr__(self):
116 return self.__str__()
117
118 - def __len__(self):
119 return len(self.services)
120
121 -class Plan(dict):
122
123 - def __init__(self,cases):
124 ''' 125 Creates a new plan composed of a list of cases 126 Usage: 127 cases: a list of cases with at least 1 case 128 ''' 129 dict.__init__(self) 130 131 if type(cases)!= types.ListType: 132 raise TypeError 133 if len(cases)<=0: 134 raise Exception("cases must have at least one case") 135 136 self.P=(cases[0]).getP() 137 self.Q=(cases[-1:][0]).getQ() 138 self.inputs=(cases[0]).getInputs() 139 self.outputs=(cases[-1:][0]).getOutputs() 140 self.cases=cases 141 142 self.active_case = 0 143 self.active_service = -1
144
145 - def getP(self): return self.P
146 - def getQ(self): return self.Q
147 - def getInputs(self): return self.inputs
148 - def getOutputs(self): return self.outputs
149 150 '''def match(self,P=[],Q=[]): 151 if (P!=[] and sorted(P)==sorted(self.P)) and (Q!=[] and sorted(Q)==sorted(self.Q)): return True 152 elif (P==None and Q==self.Q): return True 153 elif (Q==None and P==self.P): return True 154 else: return False 155 ''' 156
157 - def getActiveCase(self): return self.active_case
158
159 - def getNextService(self):
160 if len(self.cases)<=0: return None 161 162 self.active_service+=1 163 if self.active_service < len(self.cases[self.active_case].services): 164 return self.cases[self.active_case].services[self.active_service] 165 else: 166 self.active_service=0 167 self.active_case+=1 168 if self.active_case >= len(self.cases): 169 self.active_case=0 170 self.active_service=-1 171 return None 172 return deepcopy(self.cases[self.active_case].services[self.active_service])
173
174 - def getCases(self):
175 '''returns all the cases of the plan''' 176 return self.cases
177
178 - def getCase(self):
179 ''' 180 compose a new composite case by joining all the cases of the plan 181 returns a Case 182 ''' 183 if len(self.cases)==0: return None 184 case = self.cases[0] 185 for c in self.cases[1:]: case += c 186 return case
187
188 - def getServices(self):
189 ''' 190 returns all the services of the cases that compose the plan 191 returns a list of DF.Service 192 ''' 193 services=[] 194 for c in self.cases: 195 for s in c.services: services.append(s) 196 return services
197
198 - def insertCase(self,case):
199 ''' 200 inserts a new case at the beginning of the plan 201 the outputs of the new case MUST match with the inputs of the plan 202 returns the Plan with the new case added 203 ''' 204 if sorted(case.getOutputs())!=sorted(self.inputs): raise Exception 205 self.cases.insert(0,case) 206 self.P = case.getP() 207 self.inputs = case.getInputs() 208 return self
209
210 - def getCasebyID(self,ID):
211 for c in self.cases: 212 if c.getID()==ID: return c 213 return None
214
215 - def getTime(self):
216 t=0 217 for case in self.cases:t+=case.getTime() 218 return t
219
220 - def getQoS(self):
221 q=1 222 for case in self.cases: 223 q = q * case.getQoS() 224 return q
225
226 - def __len__(self):
227 return len(self.cases)
228
229 - def __str__(self):
230 return "{P: "+str(self.P)+ " Q: "+ str(self.Q) + " I: "+str(self.inputs)+" O: "+ str(self.outputs) + " CASES: "+str(self.cases)+"}"
231 - def __repr__(self):
232 return self.__str__()
233 234
235 -class TBCBP:
236
237 - def __init__(self, rl = {'maxth':0.95, 'limit':10000}):
238 self.db = CaseDB() #dict() 239 self.services = dict() 240 241 #RL values 242 self.threshold = 0.0 243 self.max_threshold = rl['maxth'] 244 self.limit_steps = rl['limit'] 245 self.steps = 1 246 self.RLlimit = log(self.limit_steps)
247
248 - def getCaseTime(self,case):
249 t = 0 250 for name in case.services: 251 t += self.services[name]["time"] 252 return t
253
254 - def registerService(self, service, time=1, QoS=1):
255 ''' 256 registers a DF.Service 257 time and QoS parameters are optional 258 ''' 259 name = service.getName() 260 P = service.getP() 261 Q = service.getQ() 262 I = service.getInputs() 263 O = service.getOutputs() 264 self.services[name]={'name':name,'s':service,"P":P,"Q":Q,"inputs":I, "outputs":O, "time":time,"QoS":QoS} 265 266 case = Case(P=P,Q=Q,inputs=I,outputs=O, services=[name],QoS=QoS) 267 case.time = time #self.getCaseTime(case) 268 self.addCase(case) 269 270 return case
271 #print "TBCBP::Service Registered: "+str(case) 272
273 - def delService(self, name):
274 #TODO 275 pass
276 - def getService(self, name):
277 '''returns a DF.Service''' 278 if self.services.has_key(name): 279 #print color_red + str(self.services[name]['s']) + color_none 280 #s = self.services[name]["s"](P=self.services[name]["P"],Q=self.services[name]["Q"],inputs=self.services[name]["inputs"],outputs=self.services[name]["outputs"],name=name) 281 #return s 282 return self.services[name]['s'] 283 return None
284
285 - def getServiceInfo(self,name):
286 ''' 287 returns info of a service 288 Usage: 289 name - string with the name of the service 290 returns a dict with the info of the service 291 ''' 292 if self.services.has_key(name): 293 return self.services[name]
294
295 - def addCase(self, case):
296 ''' 297 inserts a new case in the case-base 298 services of the case MUST be registered in the TBCBP 299 ''' 300 for s in case.services: 301 if s not in self.services.keys(): 302 return False 303 case.time = self.getCaseTime(case) 304 305 self.db.add(case) 306 307 return True
308
309 - def addPlan(self,plan):
310 case = plan.getCase() 311 self.addCase(case)
312 313
314 - def getCases(self, P=None, Q=None, inputs=None, outputs=None):
315 ''' 316 returns a list of cases where the P,Q,inputs and outputs match. 317 it only compares parameters if they are not None 318 ''' 319 return self.db.get(P,Q,inputs,outputs)
320
321 - def getCase(self,case):
322 ''' 323 returns the case of the case-base whose parameters (P,Q,inputs and outputs) match with the 'case' parameters 324 ''' 325 try: 326 res = self.db.get(P=case.getP(),Q=case.getQ(),inputs=case.getInputs(),outputs=case.getOutputs()) 327 except: 328 return None 329 for r in res: 330 if r.services == case.services: 331 return r 332 return None
333
334 - def getCaseOfService(self, name):
335 ''' 336 returns the case that represents the service 'name' in the case-base 337 Usage: 338 name - string with the name of the service 339 ''' 340 service = self.getService(name) 341 if service==None: return None 342 case = self.getCase(Case(P=service.getP(),Q=service.getQ(),inputs=service.getInputs(),outputs=service.getOutputs(),services=[name])) 343 return case
344
345 - def planMatchesInKB(self, case, kb):
346 ''' 347 returns True if all the Preconditions and Inputs of the case are true in the knowledge-base 348 otherwise returns False 349 Usage: 350 case - the case that we want to compare. instance of Case 351 kb - the knowledge base of the agent. instance of kb.KB 352 ''' 353 Preconditions_matched=True 354 Inputs_matched=True 355 for p in case.getP(): 356 if kb.ask(p) == False: 357 Preconditions_matched=False 358 break #preconditions do not match 359 for i in case.getInputs(): 360 if kb.get(i)==None: 361 Inputs_matched=False 362 break #inputs do not match 363 if Preconditions_matched and Inputs_matched: 364 return True 365 else: 366 return False
367
368 - def composePlan(self, Goal, kb, tout=20, use_rl=True):
369 370 #print "TBCBP::DB:: "+str(self.db) 371 #print "TBCBP::SERVICES:: "+str(self.services) 372 #print "TBCBP::composePlan::GOAL::"+str(Goal)+"::KB::"+str(kb.kb.clauses)+"::TOUT::"+str(tout) 373 374 MAXPLANS=1000 375 376 results = [] 377 plans = Queue.Queue() 378 379 t1 = time.time() 380 381 try: 382 try: 383 #print "TBCBP::composePlan::getCases for "+str(Goal.expression)+" -> "+str(self.getCases(Q=[Goal.expression])) 384 for case in self.getCases(Q=[Goal.expression]): #.values(): 385 if tout!=-1 and (time.time()-t1)>tout: raise TimeOut 386 #for case in cases: 387 new_plan = Plan(cases=[case]) 388 #if Preconditions match and have all inputs, the plan is finished 389 if self.planMatchesInKB(case,kb): 390 results.append(new_plan) 391 else: plans.put(new_plan) 392 393 if tout!=-1 and (time.time()-t1)>tout: raise TimeOut 394 if len(results)>MAXPLANS: raise TooMuchPlans 395 396 while plans.qsize() > 0: 397 if len(results)>MAXPLANS: raise TooMuchPlans 398 p = plans.get() 399 for case in self.getCases(outputs=p.getInputs()): #.values(): 400 #for case in cases: 401 new_plan = deepcopy(p) 402 new_plan.insertCase(case) 403 if self.planMatchesInKB(new_plan,kb): 404 #if new_plan not in results: 405 results.append(new_plan) 406 else: 407 plans.put(new_plan) 408 if tout!=-1 and (time.time()-t1)>tout: raise TimeOut 409 410 except TimeOut: 411 pass 412 #print "TIMEOUT!" 413 except TooMuchPlans: 414 pass 415 #print "TOOMUCHPLANS!" 416 417 finally: 418 pass 419 420 if results ==[]: 421 #print "NO PLANS FOUND!" 422 return None 423 sumT=0 424 sumQ=0 425 for plan in results: 426 sumT+=plan.getTime() 427 sumQ+=plan.getQoS() 428 429 #print "selecting best plan" 430 plans = [] 431 exploit= odict() 432 last=0 433 normalize=10000 434 435 best_plan = results[0] 436 if sumT==0: T=0.0 437 else: T = (1.0/float(best_plan.getTime())/float(sumT)) 438 if sumQ==0: Q=0.0 439 else: Q = (float(best_plan.getQoS())/float(sumQ)) 440 f = T+Q 441 plans.append(best_plan) 442 if int(f*normalize) > 0: 443 exploit[int(f*normalize)] = best_plan 444 last=int(f*normalize) 445 446 447 for plan in results[1:]: 448 if sumT==0: T=0.0 449 else: T = (1.0/float(plan.getTime())/float(sumT)) 450 if sumQ==0: Q=0.0 451 else: Q = (float(plan.getQoS())/float(sumQ)) 452 if T+Q > f: 453 best_plan = plan 454 f = T+Q 455 plans.append(plan) 456 if int(last+(f*normalize))>0: 457 exploit[int(last+(f*normalize))] = plan 458 last = last+int(f*normalize) 459 460 #mini reinforcement learning 461 if use_rl: 462 explore = random.random() 463 if explore > self.threshold: 464 #explore! 465 best_plan = random.choice(plans) 466 else: 467 #exploit! 468 if last==0: 469 best_plan= random.choice(plans) 470 else: 471 i = random.randint(1,last) 472 for k in exploit.keys(): 473 if i<=k: 474 best_plan=exploit[k] 475 break 476 477 478 #adjust threshold 479 self.steps+=1 480 if self.steps > self.limit_steps: self.threshold = self.max_threshold 481 else: self.threshold = (self.max_threshold * log(self.steps)) / self.RLlimit 482 483 self.addPlan(best_plan) 484 return best_plan
485 486 487
488 - def retain(self, case, QoS=None):
489 if QoS!=None: case.QoS=QoS 490 491 c = self.getCase(case) 492 if c: 493 c.QoS=case.QoS
494 495
496 - def reward(self,c):
497 case = self.getCase(c) 498 if case!=None: 499 case.reward() 500 return case 501 return False
502
503 - def punish(self,c):
504 case = self.getCase(c) 505 if case!=None: 506 case.punish() 507 return case 508 return False
509