Change to support PCI unchangeable cells
[optf/osdf.git] / apps / pci / optimizers / solver / optimizer.py
1 # -------------------------------------------------------------------------
2 #   Copyright (c) 2018 AT&T Intellectual Property
3 #
4 #   Licensed under the Apache License, Version 2.0 (the "License");
5 #   you may not use this file except in compliance with the License.
6 #   You may obtain a copy of the License at
7 #
8 #       http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #   Unless required by applicable law or agreed to in writing, software
11 #   distributed under the License is distributed on an "AS IS" BASIS,
12 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 #   See the License for the specific language governing permissions and
14 #   limitations under the License.
15 #
16 # -------------------------------------------------------------------------
17 #
18
19 import itertools
20 import os
21 from collections import defaultdict
22 import pymzn
23
24 from .pci_utils import get_id,mapping
25
26 BASE_DIR = os.path.dirname(__file__)
27 cell_id_mapping = dict()
28 id_cell_mapping = dict()
29
30 def pci_optimize(network_cell_info, cell_info_list, request_json):
31     global cell_id_mapping, id_cell_mapping
32     cell_id_mapping, id_cell_mapping = mapping(network_cell_info)
33     original_pcis = get_original_pci_list(network_cell_info)
34     unchangeable_pcis = get_ids_of_fixed_pci_cells(request_json['cellInfo'].get('fixedPCICells', []))
35     neighbor_edges = get_neighbor_list(network_cell_info)
36     second_level_edges = get_second_level_neighbor(network_cell_info)
37     ignorable_links = get_ignorable_links(network_cell_info, request_json)
38     anr_flag = is_anr(request_json)
39
40     dzn_data = build_dzn_data(cell_info_list, ignorable_links, neighbor_edges, second_level_edges, anr_flag, original_pcis, unchangeable_pcis)
41
42     return build_pci_solution(dzn_data, ignorable_links, anr_flag)
43
44 def get_ids_of_fixed_pci_cells(fixed_pci_list):
45     fixed_pci_ids = set()
46     for cell in fixed_pci_list:
47         fixed_pci_ids.add(cell_id_mapping[cell])
48     return fixed_pci_ids
49
50
51 def get_cell_id_pci_mapping(network_cell_info):
52     original_pcis = dict()
53     for cell in network_cell_info['cell_list']:
54         for nbr in cell['nbr_list']:
55             if cell_id_mapping[nbr['targetCellId']] not in original_pcis:
56                 original_pcis[cell_id_mapping[nbr['targetCellId']]] = nbr['pciValue']
57     return original_pcis
58
59
60 def get_original_pci_list(network_cell_info):
61     cell_id_pci_mapping = get_cell_id_pci_mapping(network_cell_info)
62     original_pcis_list = []
63     for i in range(len(cell_id_pci_mapping)):
64         original_pcis_list.append(cell_id_pci_mapping.get(i))
65     return original_pcis_list
66
67
68 def build_pci_solution(dzn_data, ignorable_links, anr_flag):
69     mzn_solution = solve(get_mzn_model(anr_flag), dzn_data)
70     if mzn_solution == 'UNSATISFIABLE':
71         return mzn_solution
72     solution = {'pci': mzn_solution[0]['pci']}
73
74     if anr_flag:
75         removables = defaultdict(list)
76         used_ignorables = mzn_solution[0]['used_ignorables']
77         index = 0
78         for i in ignorable_links:
79             if used_ignorables[index] > 0:
80                 removables[i[0]].append(i[1])
81             index += 1
82         solution['removables'] = removables
83     return solution
84
85
86 def build_dzn_data(cell_info_list, ignorable_links, neighbor_edges, second_level_edges, anr_flag,original_pcis, unchangeable_pcis):
87     dzn_data = {
88         'NUM_NODES': len(cell_info_list),
89         'NUM_PCIS': len(cell_info_list),
90         'NUM_NEIGHBORS': len(neighbor_edges),
91         'NEIGHBORS': get_list(neighbor_edges),
92         'NUM_SECOND_LEVEL_NEIGHBORS': len(second_level_edges),
93         'SECOND_LEVEL_NEIGHBORS': get_list(second_level_edges),
94         'PCI_UNCHANGEABLE_CELLS': unchangeable_pcis,
95         'ORIGINAL_PCIS': original_pcis
96     }
97     if anr_flag:
98         dzn_data['NUM_IGNORABLE_NEIGHBOR_LINKS'] = len(ignorable_links)
99         dzn_data['IGNORABLE_NEIGHBOR_LINKS'] = get_list(ignorable_links)
100     return dzn_data
101
102
103 def get_mzn_model(anr_flag):
104     if anr_flag:
105         mzn_model = os.path.join(BASE_DIR, 'min_confusion_inl.mzn')
106     else:
107         mzn_model = os.path.join(BASE_DIR, 'no_conflicts_no_confusion.mzn')
108     return mzn_model
109
110
111 def is_anr(request_json):
112     return 'pci-anr' in request_json["requestInfo"]["optimizers"]
113
114
115 def get_list(edge_list):
116     array_list = []
117     for s in edge_list:
118         array_list.append([s[0], s[1]])
119     return sorted(array_list)
120
121
122 def solve(mzn_model, dzn_data):
123     return pymzn.minizinc(mzn=mzn_model, data=dzn_data)
124
125
126 def get_neighbor_list(network_cell_info):
127     neighbor_list = set()
128     for cell in network_cell_info['cell_list']:
129         add_to_neighbor_list(network_cell_info, cell, neighbor_list)
130     return neighbor_list
131
132
133 def add_to_neighbor_list(network_cell_info, cell, neighbor_list):
134     for nbr in cell.get('nbr_list', []):
135         host_id = cell['id']
136         nbr_id = get_id(network_cell_info, nbr['targetCellId'])
137         if nbr_id and host_id != nbr_id:
138             neighbor_list.add((host_id, nbr_id))
139
140
141 def get_second_level_neighbor(network_cell_info):
142     second_neighbor_list = set()
143     for cell in network_cell_info['cell_list']:
144         comb_list = build_second_level_list(network_cell_info, cell)
145         for comb in comb_list:
146             if comb[0] and comb[1]:
147                 second_neighbor_list.add((comb[0], comb[1]))
148     return sorted(second_neighbor_list)
149
150
151 def build_second_level_list(network_cell_info, cell):
152     second_nbr_list = []
153     for nbr in cell.get('nbr_list', []):
154         second_nbr_list.append(get_id(network_cell_info, nbr['targetCellId']))
155     return [list(elem) for elem in list(itertools.combinations(second_nbr_list, 2))]
156
157
158 def get_ignorable_links(network_cell_info, request_json):
159     ignorable_list = set()
160     anr_input_list = request_json["cellInfo"].get('anrInputList', [])
161     if anr_input_list:
162         for anr_info in anr_input_list:
163             cell_id = get_id(network_cell_info, anr_info['cellId'])
164             anr_removable = anr_info.get('removeableNeighbors', [])
165             for anr in anr_removable:
166                 ignorable_list.add((cell_id, get_id(network_cell_info, anr)))
167     return ignorable_list