-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathagent.py
144 lines (114 loc) · 4.81 KB
/
agent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from graph import Graph
from strategies import Strategy
from strategies.random_strategy import RandomStrategy
import math
class Agent:
def __init__(self, graph: Graph, group: str, config) -> None:
"""
Args:
graph (Graph): The graph to be labeled
group (str): The group of nodes that the agent is responsible for
config: simulation configuration
"""
self.graph = graph
self.config = config
self.group = group
self.steps_count = 0 # number of steps since the start
self.cost = 0
self.costs = self.config['costs']
self.nodes_added = 0
self.edges_added = 0
self.steps_config = config['steps_config'] if 'steps_config' in config else {}
self.step_on_each_node = self.steps_config['on_each_node'] if 'on_each_node' in self.steps_config else False
self.step_on_each_edge = self.steps_config['on_each_edge'] if 'on_each_edge' in self.steps_config else False
self.max_atomic_steps = self.steps_config['max_atomic_steps'] if 'max_atomic_steps' in self.steps_config else -1
self.max_steps = self.steps_config['max_steps'] if 'max_steps' in self.steps_config else -1
# atomic step is for example adding a node, a step can have multiple atomic steps
self.atomic_steps_per_step = -1
self.atomic_steps_count = 0
self.atomic_nodes_steps_count = 0
self.atomic_edges_steps_count = 0
# Plan saves possible nodes and edges to be added (These are IDs for
# nodes, and couples of (ID, ID) for edges
self.plan = {'nodes': [], 'edges': []}
self.executed_plan = {'nodes': [], 'edges': []}
self.strategy = RandomStrategy()
self.initialized = False
def initialize_plan(self, respect_date_added=False):
print('initialize plan')
nodes = self.graph.get_nodes_with_group(
self.group, random_order=True, respect_date_added=respect_date_added)
for id in nodes:
self.plan['nodes'].append(id[0])
edges = self.graph.get_edges_with_group(self.group, active=False, random_order=True)
for e in edges:
self.plan['edges'].append(e)
self.graph.set_active_edges_with_group(self.group, False)
self.graph.set_active_nodes_with_group(self.group, False)
number_of_atomic_steps = len(self.plan['nodes']) + len(self.plan['edges'])
if self.max_atomic_steps != -1:
if number_of_atomic_steps > self.max_atomic_steps:
if number_of_atomic_steps % self.max_atomic_steps == 0:
self.atomic_steps_per_step = number_of_atomic_steps // self.max_atomic_steps
else:
self.atomic_steps_per_step = number_of_atomic_steps // (self.max_atomic_steps - 1)
self.strategy.initialize_strategy(self)
self.initialized = True
def set_strategy(self, strategy: Strategy):
self.strategy = strategy
def cost_insert_node(self, page_rank):
cost = math.exp(page_rank)
return cost
def atomic_step(self):
# get a node from the plan according to a strategy
res = self.strategy.step()
if res is None: # the strategy ended
return None
for node_id in res['nodes']:
self.atomic_nodes_steps_count += 1
if node_id == -1: # the strategy passes
continue
cost = self.cost_insert_node(self.graph.get_node_ranking(node_id))
if self.costs["budget"] > 0 and self.cost + cost > self.costs["budget"]:
return None
self.nodes_added += 1
self.graph.set_node_active(node_id, True, commit=False)
self.cost += cost
for edge in res['edges']:
self.atomic_edges_steps_count += 1
if edge[0] == -1 and edge[0] == -1: # the strategy passes
continue
self.edges_added += 1
self.graph.set_edge_active(edge[0], edge[1], True, commit=False)
self.graph.conn.commit()
return res
def atomic_steps_done(self, last_res):
if self.atomic_steps_count == 0:
return False
if last_res is None or \
(self.atomic_steps_per_step > 0 and self.atomic_steps_count > self.atomic_steps_per_step) or \
(self.step_on_each_edge and self.atomic_edges_steps_count > 0) or \
(self.step_on_each_node and self.atomic_nodes_steps_count > 0) or \
(self.costs["budget"] > 0 and self.cost >= self.costs["budget"]):
return True
return False
def step(self):
if not self.initialized:
raise Exception("Agent not initialized")
if self.costs["budget"] > 0 and self.cost >= self.costs["budget"]:
print("budget over", self.group)
return None
self.steps_count += 1
if self.max_steps > 0 and self.steps_count > self.max_steps:
return None
res = None
self.atomic_steps_count = 0
self.atomic_nodes_steps_count = 0
self.atomic_edges_steps_count = 0
while not self.atomic_steps_done(res):
res = self.atomic_step()
self.atomic_steps_count += 1
return res