| 2209 | def minimum_outdegree_orientation(self, use_edge_labels=False): |
| 2210 | r""" |
| 2211 | Returns a DiGraph which is an orientation with the smallest |
| 2212 | possible maximum outdegree of the current graph. |
| 2213 | |
| 2214 | Given a Graph `G`, is is polynomial to compute an orientation |
| 2215 | `D` of the edges of `G` such that the maximum out-degree in `D` |
| 2216 | is minimized. This problem, though, is NP-complete in the |
| 2217 | weighted case [AMOZ06]_. |
| 2218 | |
| 2219 | INPUT: |
| 2220 | |
| 2221 | - ``use_edge_labels`` (boolean) |
| 2222 | |
| 2223 | - When set to ``True``, uses edge labels as weights to |
| 2224 | compute the orientation and assumes a weight of `1` |
| 2225 | when there is no value available for a given edge. |
| 2226 | |
| 2227 | - When set to ``False`` (default), gives a weight of 1 |
| 2228 | to all the edges. |
| 2229 | |
| 2230 | EXAMPLE: |
| 2231 | |
| 2232 | Given a complete bipartite graph `K_{n,m}`, the maximum out-degree |
| 2233 | of an optimal orientation is |
| 2234 | `\left\lceil \frac {nm} {n+m}\right\rceil`:: |
| 2235 | |
| 2236 | sage: g = graphs.CompleteBipartiteGraph(3,4) |
| 2237 | sage: o = g.minimum_outdegree_orientation() # optional - requires GLPK or CBC |
| 2238 | sage: max(o.out_degree()) == ceil((4*3)/(3+4)) # optional - requires GLPK or CBC |
| 2239 | True |
| 2240 | |
| 2241 | |
| 2242 | |
| 2243 | REFERENCES: |
| 2244 | |
| 2245 | .. [AMOZ06] Asahiro, Y. and Miyano, E. and Ono, H. and Zenmyo, K. |
| 2246 | Graph orientation algorithms to minimize the maximum outdegree |
| 2247 | Proceedings of the 12th Computing: The Australasian Theroy Symposium |
| 2248 | Volume 51, page 20 |
| 2249 | Australian Computer Society, Inc. 2006 |
| 2250 | |
| 2251 | """ |
| 2252 | |
| 2253 | if self.is_directed(): |
| 2254 | raise ValueError("Cannot compute an orientation of a DiGraph. "+\ |
| 2255 | "Please convert it to a Graph if you really mean it.") |
| 2256 | |
| 2257 | if use_edge_labels: |
| 2258 | weight = lambda u,v : self.edge_label(u,v) if self.edge_label(u,v)!=None else 1 |
| 2259 | else: |
| 2260 | weight = lambda u,v : 1 |
| 2261 | |
| 2262 | from sage.numerical.mip import MixedIntegerLinearProgram |
| 2263 | |
| 2264 | p = MixedIntegerLinearProgram(maximization=False) |
| 2265 | |
| 2266 | # The orientation of an edge is boolean |
| 2267 | # and indicates whether the edge uv |
| 2268 | # with u<v goes from u to v ( equal to 0 ) |
| 2269 | # or from v to u ( equal to 1) |
| 2270 | orientation = p.new_variable(dim=2) |
| 2271 | |
| 2272 | degree = p.new_variable() |
| 2273 | |
| 2274 | # Whether an edge adjacent to a vertex u counts |
| 2275 | # positively or negatively |
| 2276 | outgoing = lambda u,v,variable : (1-variable) if u>v else variable |
| 2277 | |
| 2278 | for u in self: |
| 2279 | p.add_constraint(sum([weight(u,v)*outgoing(u,v,orientation[min(u,v)][max(u,v)]) for v in self.neighbors(u)])-degree['max'],max=0) |
| 2280 | |
| 2281 | p.set_objective(degree['max']) |
| 2282 | |
| 2283 | p.set_binary(orientation) |
| 2284 | |
| 2285 | p.solve() |
| 2286 | |
| 2287 | orientation = p.get_values(orientation) |
| 2288 | |
| 2289 | # All the edges from self are doubled in O |
| 2290 | # ( one in each direction ) |
| 2291 | O = DiGraph(self) |
| 2292 | |
| 2293 | # Builds the list of edges that should be removed |
| 2294 | edges=[] |
| 2295 | |
| 2296 | for u,v in self.edge_iterator(labels=None): |
| 2297 | # assumes u<v |
| 2298 | if u>v: |
| 2299 | u,v=v,u |
| 2300 | |
| 2301 | if orientation[min(u,v)][max(u,v)] == 1: |
| 2302 | edges.append((max(u,v),min(u,v))) |
| 2303 | else: |
| 2304 | edges.append((min(u,v),max(u,v))) |
| 2305 | |
| 2306 | O.delete_edges(edges) |
| 2307 | |
| 2308 | return O |
| 2309 | |