Skip to content

network

Classes:

Mapper

Mapper(mcs_data)

Methods:

Source code in ties/modules/network.py
27
28
def __init__(self, mcs_data):
    self.full_G = nx.read_graphml(mcs_data)

node_to_dst_from_origin

node_to_dst_from_origin(paths)

Get the :param paths: :return:

Source code in ties/modules/network.py
81
82
83
84
85
86
87
88
89
90
91
92
def node_to_dst_from_origin(self, paths):
    """
    Get the
    :param paths:
    :return:
    """

    dsts = defaultdict(lambda: float("inf"))
    for path in paths:
        dsts[path[0]] = min(len(path), dsts[path[0]])

    return dsts

mult_dst_cycle

mult_dst_cycle(path)

So 0.10.101 > 0.2*0.05 * 0.05 So a larger number is better

:param path: :return:

Source code in ties/modules/network.py
138
139
140
141
142
143
144
145
146
def mult_dst_cycle(self, path):
    """
    So 0.1*0.1*01 > 0.2*0.05 * 0.05
    So a larger number is better

    :param path:
    :return:
    """
    return np.prod(self.get_dsts(list(path) + [path[0]]))

norm_mult_dst_cycle

norm_mult_dst_cycle(path)

So 0.10.101 > 0.2*0.05 * 0.05 So a larger number is better

:param path: :return:

Source code in ties/modules/network.py
148
149
150
151
152
153
154
155
156
def norm_mult_dst_cycle(self, path):
    """
    So 0.1*0.1*01 > 0.2*0.05 * 0.05
    So a larger number is better

    :param path:
    :return:
    """
    return np.prod(self.get_dsts(list(path) + [path[0]])) * self.get_dst_cycle(path)

select_path

select_path(paths, paths_to_ignore=None, limit=0.35)

Always use path with better perturbations.

:param paths: :return:

Source code in ties/modules/network.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
def select_path(self, paths, paths_to_ignore=None, limit=0.35):
    """
    Always use path with better perturbations.

    :param paths:
    :return:
    """

    if paths_to_ignore is None:
        paths_to_ignore = []

    perturbation_quality_limits = np.linspace(0.10, limit, round(limit / 0.05 - 1))
    for pert_quality in perturbation_quality_limits:
        for path in paths:
            if path in paths_to_ignore:
                continue

            # below soft, number of steps is the best
            if self.get_max_step(path) < pert_quality:
                return path

    # worst case scenario
    return paths[0]

add_shortest_paths_hub

add_shortest_paths_hub()

HUB approach.

Take a list of nodes that have more than "1 edge". This means that they have other edges going via them.

Hub size
  • 1 extra node: just add one edge (cumulative)
  • 2 or more nodes: add 2 more edges
Hub distance
  • 1 (direct): add one edge
  • 2: add two edges
  • 3: 4 edges
  • 4: 5 edges

Hub size/distance -> edges: - 0/* -> 0 - 1/1 -> 0 - 2/1 -> 1 - =>3/1 -> 2

  • 1/2 -> 1
  • 1/3 -> 1

  • 2/2 -> 2

  • 2/3 -> 3 ?

  • 3/2 -> 3

  • 2/>3 -> 3

  • 3/3 -> 4

  • 3/>3 -> 5

It will be important that these new paths will have cycles.

Source code in ties/modules/network.py
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
def add_shortest_paths_hub(self):
    """
    HUB approach.

    Take a list of nodes that have more than "1 edge".
    This means that they have other edges going via them.

    Hub size:
     - 1 extra node: just add one edge (cumulative)
     - 2 or more nodes: add 2 more edges

    Hub distance:
     - 1 (direct): add one edge
     - 2: add two edges
     - 3: 4 edges
     - 4: 5 edges

    Hub size/distance -> edges:
     - 0/*  -> 0
     - 1/1  -> 0
     - 2/1  -> 1
     - =>3/1 -> 2

     - 1/2  -> 1
     - 1/3  -> 1

     - 2/2  -> 2


     - 2/3  -> 3 ?
     - >3/2 -> 3
     - 2/>3 -> 3

     - 3/3  -> 4
     - >3/>3 -> 5

    It will be important that these new paths will have cycles.
    """
    G = mapper.rev_sol_diG
    # start with 0
    original_G = copy.deepcopy(G)
    for source, node in nx.bfs_edges(original_G, self.active):
        # ignore if not a hub
        hub_size = original_G.out_degree[node]
        # the original (best) distance
        dst = [len(p) - 1 for p in self.selected_paths if p[0] == node][0]

        add_paths = 0
        if hub_size == 0:
            continue
        if hub_size == 1 and dst == 1:
            continue
        elif {hub_size, dst} == {2, 1}:
            add_paths = 1
        elif hub_size == 1 and dst in {2, 3}:
            add_paths = 2
        elif {hub_size, dst} == {2} or hub_size > 3 and dst == 1:
            add_paths = 2
        elif hub_size == 2 and dst == 3:
            add_paths = 3
        elif hub_size >= 3 and dst == 2:
            add_paths = 3
        elif hub_size == 2 and dst > 3:
            add_paths = 3
        elif {hub_size, dst} == {3}:
            add_paths = 4
        elif hub_size > 3 and dst > 3:
            add_paths = 5
        else:
            print("there is some other option?", hub_size, dst)

        for i in range(add_paths):
            path = self.select_path(
                shortest_paths[node], self.selected_paths, limit=0.30
            )
            self.add_path(path)

cycle_closure_hub

cycle_closure_hub()

Cycle Closure

For hubs, we need cycle closure. Ideally, it would not an "edge" cycle closure.

Find all simple cycles

Source code in ties/modules/network.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
def cycle_closure_hub(self):
    """
    Cycle Closure

    For hubs, we need cycle closure. Ideally, it would not an "edge" cycle closure.

    Find all simple cycles
    """
    rev_sol_diG = mapper.rev_sol_diG
    G03 = self.remove_edges(self.full_G, threshold=0.3)
    all_cycles = nx.cycle_basis(G03)
    dst_to_0 = self.node_to_dst_from_origin(self.selected_paths)

    def get_min_node_to0(path):
        # is this MST basically?
        return min(dst_to_0[p] for p in path)

    # get for each node, the distance it is from the active

    # start with 0
    original_G = copy.deepcopy(rev_sol_diG)
    cycles_added = 0
    for source, node in nx.bfs_edges(original_G, self.active):
        hub_size = original_G.out_degree[node]
        if hub_size < 1:
            continue

        # print(node)

        cycles = [c for c in all_cycles if node in c and len(c) == 3]

        # add and order by the cycle score (path length)
        cycle_score = OrderedDict(
            sorted(
                [(tuple(c), self.get_dst_cycle(c)) for c in cycles],
                key=lambda x: x[1],
            )[:5]
        )

        # for cycle, score in cycle_score.items():
        #     if len(cycle) > 3:
        #         continue
        #     print(
        #         "close",
        #         [dst_to_0[n] for n in cycle],
        #         "norm mult",
        #         norm_mult_dst_cycle(cycle),
        #         "mult",
        #         mult_dst_cycle(cycle),
        #         "dst",
        #         get_dst_cycle(cycle),
        #         "all dsts",
        #         get_cycle_dsts(cycle),
        #     )

        # pick the cycle that ideally takes you closer
        # pick the one that either is closer, or the first item

        if len(cycle_score) == 0:
            print("No cycles found for", node)
            continue

        shortest_cycle, dst = sorted(
            cycle_score.items(), key=lambda x: get_min_node_to0(x[0])
        )[0]
        self.add_path(list(shortest_cycle) + [shortest_cycle[0]])
        cycles_added += 1

    return cycles_added