GitLab Repo

amachine.am_optimize.am_optimize_node_permutation_cpsat

 1import warnings
 2
 3import networkx as nx
 4import numpy as np
 5
 6from ..am_random import resolve_rng
 7
 8def _calculate_score(
 9    G: nx.DiGraph,
10    instance_cohesion: np.ndarray,
11    perm: list[int]
12) -> float:
13
14    nodes = list(G.nodes())
15    node_to_idx = {node: i for i, node in enumerate(nodes)}
16
17    total_score = 0.0
18    for u, v in G.edges():
19        i = node_to_idx[u]
20        j = node_to_idx[v]
21        
22        assigned_instance_u = perm[i]
23        assigned_instance_v = perm[j]
24        
25        total_score += instance_cohesion[assigned_instance_u, assigned_instance_v]
26
27    return total_score
28
29def optimize_node_permutation_cpsat(
30    G: nx.DiGraph,
31    cohesion_matrix: np.ndarray,
32    max_search_time : int,
33    np_rng : np.random.Generator ) -> list[int] :
34
35    from ortools.sat.python import cp_model
36
37    np_rng = resolve_rng( np_rng )
38    random_seed = np_rng.integers(0, 2**31 - 1)
39
40    N = len(list(G.nodes()))
41    node_to_idx = {node: i for i, node in enumerate(G.nodes())}
42
43    SCALE = 1_000_000
44    cohesion_int = np.round(cohesion_matrix * SCALE).astype(int)
45    flat = cohesion_int.flatten().tolist()
46    score_lo, score_hi = int(cohesion_int.min()), int(cohesion_int.max())
47
48    model = cp_model.CpModel()
49    perm = [model.NewIntVar(0, N - 1, f"p{i}") for i in range(N)]
50    model.AddAllDifferent(perm)
51
52    edge_scores = []
53    for u, v in G.edges():
54        i, j = node_to_idx[u], node_to_idx[v]
55
56        flat_idx = model.NewIntVar(0, N * N - 1, f"idx_{i}_{j}")
57        model.Add(flat_idx == N * perm[i] + perm[j])
58
59        score = model.NewIntVar(score_lo, score_hi, f"s_{i}_{j}")
60        model.AddElement(flat_idx, flat, score)
61        edge_scores.append(score)
62
63    model.Maximize(cp_model.LinearExpr.Sum(edge_scores))
64
65    solver = cp_model.CpSolver()
66    solver.parameters.num_search_workers  = 3
67    solver.parameters.max_time_in_seconds = max_search_time
68    solver.parameters.linearization_level = 2
69    solver.parameters.random_seed = int(random_seed)
70
71    status = solver.Solve(model)
72
73    best_permutation = [ i for i in range( N ) ]
74    print( f"initial node permuation score {_calculate_score(G,cohesion_matrix,best_permutation)}" )
75
76    if status in (cp_model.OPTIMAL, cp_model.FEASIBLE) :
77        msg = "OPTIMAL" if status == cp_model.OPTIMAL else "FEASIBLE"
78        print(f"{msg} solution found. Objective = {solver.ObjectiveValue():.0f}")
79        best_permutation = [solver.Value(perm[i]) for i in range(N)]
80        print( f"cp-sat node permuation score {_calculate_score(G,cohesion_matrix,best_permutation)}" )
81    else:
82        warnings.warn("optimization found no solution within time limit.")
83
84    return best_permutation
def optimize_node_permutation_cpsat( G: networkx.classes.digraph.DiGraph, cohesion_matrix: numpy.ndarray, max_search_time: int, np_rng: numpy.random._generator.Generator) -> list[int]:
30def optimize_node_permutation_cpsat(
31    G: nx.DiGraph,
32    cohesion_matrix: np.ndarray,
33    max_search_time : int,
34    np_rng : np.random.Generator ) -> list[int] :
35
36    from ortools.sat.python import cp_model
37
38    np_rng = resolve_rng( np_rng )
39    random_seed = np_rng.integers(0, 2**31 - 1)
40
41    N = len(list(G.nodes()))
42    node_to_idx = {node: i for i, node in enumerate(G.nodes())}
43
44    SCALE = 1_000_000
45    cohesion_int = np.round(cohesion_matrix * SCALE).astype(int)
46    flat = cohesion_int.flatten().tolist()
47    score_lo, score_hi = int(cohesion_int.min()), int(cohesion_int.max())
48
49    model = cp_model.CpModel()
50    perm = [model.NewIntVar(0, N - 1, f"p{i}") for i in range(N)]
51    model.AddAllDifferent(perm)
52
53    edge_scores = []
54    for u, v in G.edges():
55        i, j = node_to_idx[u], node_to_idx[v]
56
57        flat_idx = model.NewIntVar(0, N * N - 1, f"idx_{i}_{j}")
58        model.Add(flat_idx == N * perm[i] + perm[j])
59
60        score = model.NewIntVar(score_lo, score_hi, f"s_{i}_{j}")
61        model.AddElement(flat_idx, flat, score)
62        edge_scores.append(score)
63
64    model.Maximize(cp_model.LinearExpr.Sum(edge_scores))
65
66    solver = cp_model.CpSolver()
67    solver.parameters.num_search_workers  = 3
68    solver.parameters.max_time_in_seconds = max_search_time
69    solver.parameters.linearization_level = 2
70    solver.parameters.random_seed = int(random_seed)
71
72    status = solver.Solve(model)
73
74    best_permutation = [ i for i in range( N ) ]
75    print( f"initial node permuation score {_calculate_score(G,cohesion_matrix,best_permutation)}" )
76
77    if status in (cp_model.OPTIMAL, cp_model.FEASIBLE) :
78        msg = "OPTIMAL" if status == cp_model.OPTIMAL else "FEASIBLE"
79        print(f"{msg} solution found. Objective = {solver.ObjectiveValue():.0f}")
80        best_permutation = [solver.Value(perm[i]) for i in range(N)]
81        print( f"cp-sat node permuation score {_calculate_score(G,cohesion_matrix,best_permutation)}" )
82    else:
83        warnings.warn("optimization found no solution within time limit.")
84
85    return best_permutation