| 1158 | ############################## |
| 1159 | # Fractional Chromatic index # |
| 1160 | ############################## |
| 1161 | |
| 1162 | cpdef fractional_chromatic_index(gr, constraint_log = 0,solver_log = 0): |
| 1163 | r""" |
| 1164 | Computes the fractional chromatic index of ``gr`` |
| 1165 | |
| 1166 | INPUT: |
| 1167 | |
| 1168 | - ``gr`` -- a graph |
| 1169 | |
| 1170 | - ``constraint_log`` -- whether to print information on the |
| 1171 | constraints generated |
| 1172 | |
| 1173 | - ``solver_log`` -- level of verbosity required from the solver |
| 1174 | |
| 1175 | |
| 1176 | TODO: |
| 1177 | |
| 1178 | Can be improved by computing matchings through a LP formulation, |
| 1179 | and not using the Python implementation of Edmonds' algorithm |
| 1180 | (which requires to copy the graph, etc). With a LP formulation, as |
| 1181 | we are computing a matching at each loop by only changing the |
| 1182 | weight of the vertices, it is more efficient to create a LP and |
| 1183 | update its costs at each turn. |
| 1184 | |
| 1185 | EXAMPLE: |
| 1186 | |
| 1187 | The fractional chromatic index of a `C_5` is `5/2`:: |
| 1188 | |
| 1189 | sage: from sage.graphs.generic_graph_pyx import fractional_chromatic_index |
| 1190 | sage: g = graphs.CycleGraph(5) |
| 1191 | sage: fractional_chromatic_index(g) |
| 1192 | 2.5 |
| 1193 | """ |
| 1194 | |
| 1195 | g = gr.copy() |
| 1196 | cdef int m = g.size() |
| 1197 | cdef list indices |
| 1198 | cdef list values |
| 1199 | cdef float weight |
| 1200 | cdef list matching |
| 1201 | cdef int i |
| 1202 | |
| 1203 | # Associating its variable number to each edge of the graph |
| 1204 | cdef dict edge_id = dict( [(x,y) for (y,x) |
| 1205 | in enumerate(g.edges(labels = False))] ) |
| 1206 | |
| 1207 | cdef GenericBackend p |
| 1208 | from sage.numerical.backends.generic_backend import get_solver |
| 1209 | p = get_solver() |
| 1210 | |
| 1211 | p.add_variables(m) |
| 1212 | p.set_sense(+1) |
| 1213 | |
| 1214 | p.set_objective([1]*m) |
| 1215 | |
| 1216 | p.set_verbosity(solver_log) |
| 1217 | |
| 1218 | # Let us add, as our first set of matching, a singleton for each |
| 1219 | # edge |
| 1220 | |
| 1221 | for 0 <= i < m: |
| 1222 | p.add_linear_constraint([i], [1], 1, 1) |
| 1223 | |
| 1224 | p.solve() |
| 1225 | |
| 1226 | while (1): |
| 1227 | # Computing a matching of maximum weight... |
| 1228 | |
| 1229 | # Updating the value on the edges of g |
| 1230 | for u,v in g.edges(labels = False): |
| 1231 | g.set_edge_label(u,v,p.get_variable_value(edge_id[(u,v)])) |
| 1232 | |
| 1233 | matching = g.matching() |
| 1234 | |
| 1235 | # Summing the weights |
| 1236 | weight = 0 |
| 1237 | for u,v,_ in matching: |
| 1238 | weight += g.edge_label(u,v) |
| 1239 | |
| 1240 | |
| 1241 | # If the maximum matching has weight at most 1, we are done ! |
| 1242 | if weight <= 1: |
| 1243 | break |
| 1244 | |
| 1245 | # Otherwise, we add it.. |
| 1246 | |
| 1247 | if constraint_log: |
| 1248 | print "Adding a constraint on matching : ",matching |
| 1249 | |
| 1250 | indices = [] |
| 1251 | for u,v,_ in matching: |
| 1252 | indices.append(edge_id[(u,v)]) |
| 1253 | |
| 1254 | values = [1] * len(indices) |
| 1255 | p.add_linear_constraint(indices, values, 1, 1) |
| 1256 | |
| 1257 | # And solve again |
| 1258 | p.solve() |
| 1259 | |
| 1260 | # Getting the objective back |
| 1261 | obj = p.get_objective_value() |
| 1262 | |
| 1263 | # Accomplished ! |
| 1264 | return obj |
| 1265 | |
| 1266 | |