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
15
21
22 - def add(self, 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
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
63 - def getP(self): return self.P
64 - def getQ(self): return self.Q
67 - def getQoS(self): return self.QoS
68 - def getTime(self): return self.time
69
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
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
89
91 self = self+y
92 return self
93
95 if not issubclass(y.__class__,Case): return False
96 if self.services!= y.services: return False
97
98 if sorted(self.P) != sorted(y.P):
99 return False
100
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
111
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
117
120
122
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
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
158
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
175 '''returns all the cases of the plan'''
176 return self.cases
177
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
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
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
211 for c in self.cases:
212 if c.getID()==ID: return c
213 return None
214
216 t=0
217 for case in self.cases:t+=case.getTime()
218 return t
219
221 q=1
222 for case in self.cases:
223 q = q * case.getQoS()
224 return q
225
227 return len(self.cases)
228
230 return "{P: "+str(self.P)+ " Q: "+ str(self.Q) + " I: "+str(self.inputs)+" O: "+ str(self.outputs) + " CASES: "+str(self.cases)+"}"
233
234
236
237 - def __init__(self, rl = {'maxth':0.95, 'limit':10000}):
238 self.db = CaseDB()
239 self.services = dict()
240
241
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
253
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
268 self.addCase(case)
269
270 return case
271
272
277 '''returns a DF.Service'''
278 if self.services.has_key(name):
279
280
281
282 return self.services[name]['s']
283 return None
284
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
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
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
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
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
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
359 for i in case.getInputs():
360 if kb.get(i)==None:
361 Inputs_matched=False
362 break
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
371
372
373
374 MAXPLANS=1000
375
376 results = []
377 plans = Queue.Queue()
378
379 t1 = time.time()
380
381 try:
382 try:
383
384 for case in self.getCases(Q=[Goal.expression]):
385 if tout!=-1 and (time.time()-t1)>tout: raise TimeOut
386
387 new_plan = Plan(cases=[case])
388
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()):
400
401 new_plan = deepcopy(p)
402 new_plan.insertCase(case)
403 if self.planMatchesInKB(new_plan,kb):
404
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
413 except TooMuchPlans:
414 pass
415
416
417 finally:
418 pass
419
420 if results ==[]:
421
422 return None
423 sumT=0
424 sumQ=0
425 for plan in results:
426 sumT+=plan.getTime()
427 sumQ+=plan.getQoS()
428
429
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
461 if use_rl:
462 explore = random.random()
463 if explore > self.threshold:
464
465 best_plan = random.choice(plans)
466 else:
467
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
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
497 case = self.getCase(c)
498 if case!=None:
499 case.reward()
500 return case
501 return False
502
504 case = self.getCase(c)
505 if case!=None:
506 case.punish()
507 return case
508 return False
509