| 2175 | def bounded_outdegree_orientation(self, bound): |
| 2176 | r""" |
| 2177 | Computes an orientation of ``self`` such that every vertex `v` |
| 2178 | has out-degree less than `b(v)` |
| 2179 | |
| 2180 | INPUT: |
| 2181 | |
| 2182 | - ``bound`` -- Maximum bound on the out-degree. Can be of |
| 2183 | three different types : |
| 2184 | |
| 2185 | * An integer `k`. In this case, computes an orientation |
| 2186 | whose maximum out-degree is less than `k`. |
| 2187 | |
| 2188 | * A dictionary associating to each vertex its associated |
| 2189 | maximum out-degree. |
| 2190 | |
| 2191 | * A function associating to each vertex its associated |
| 2192 | maximum out-degree. |
| 2193 | |
| 2194 | OUTPUT: |
| 2195 | |
| 2196 | A DiGraph representing the orientation if it exists. A |
| 2197 | ``ValueError`` exception is raised otherwise. |
| 2198 | |
| 2199 | ALGORITHM: |
| 2200 | |
| 2201 | The problem is solved through a maximum flow : |
| 2202 | |
| 2203 | Given a graph `G`, we create a ``DiGraph`` `D` defined on |
| 2204 | `E(G)\cup V(G)\cup \{s,t\}`. We then link `s` to all of `V(G)` |
| 2205 | (these edges having a capacity equal to the bound associated |
| 2206 | to each element of `V(G)`), and all the elements of `E(G)` to |
| 2207 | `t` . We then link each `v \in V(G)` to each of its incident |
| 2208 | edges in `G`. A maximum integer flow of value `|E(G)|` |
| 2209 | corresponds to an admissible orientation of `G`. Otherwise, |
| 2210 | none exists. |
| 2211 | |
| 2212 | EXAMPLES: |
| 2213 | |
| 2214 | There is always an orientation of a graph `G` such that a |
| 2215 | vertex `v` has out-degree at most `\lceil \frac {d(v)} 2 |
| 2216 | \rceil`:: |
| 2217 | |
| 2218 | sage: g = graphs.RandomGNP(40, .4) |
| 2219 | sage: b = lambda v : ceil(g.degree(v)/2) |
| 2220 | sage: D = g.bounded_outdegree_orientation(b) |
| 2221 | sage: all( D.out_degree(v) <= b(v) for v in g ) |
| 2222 | True |
| 2223 | |
| 2224 | |
| 2225 | Chvatal's graph, being 4-regular, can be oriented in such a |
| 2226 | way that its maximum out-degree is 2:: |
| 2227 | |
| 2228 | sage: g = graphs.ChvatalGraph() |
| 2229 | sage: D = g.bounded_outdegree_orientation(2) |
| 2230 | sage: max(D.out_degree()) |
| 2231 | 2 |
| 2232 | |
| 2233 | For any graph `G`, it is possible to compute an orientation |
| 2234 | such that the maximum out-degree is at most the maximum |
| 2235 | average degree of `G` divided by 2. Anything less, though, is |
| 2236 | impossible. |
| 2237 | |
| 2238 | sage: g = graphs.RandomGNP(40, .4) |
| 2239 | sage: mad = g.maximum_average_degree() |
| 2240 | |
| 2241 | Hence this is possible :: |
| 2242 | |
| 2243 | sage: d = g.bounded_outdegree_orientation(ceil(mad/2)) |
| 2244 | |
| 2245 | While this is not:: |
| 2246 | |
| 2247 | sage: try: |
| 2248 | ... g.bounded_outdegree_orientation(ceil(mad/2-1)) |
| 2249 | ... print "Error" |
| 2250 | ... except ValueError: |
| 2251 | ... pass |
| 2252 | |
| 2253 | TESTS: |
| 2254 | |
| 2255 | As previously for random graphs, but more intensively:: |
| 2256 | |
| 2257 | sage: for i in xrange(30): # long |
| 2258 | ... g = graphs.RandomGNP(40, .4) # long |
| 2259 | ... b = lambda v : ceil(g.degree(v)/2) # long |
| 2260 | ... D = g.bounded_outdegree_orientation(b) # long |
| 2261 | ... if not ( # long |
| 2262 | ... all( D.out_degree(v) <= b(v) for v in g ) or # long |
| 2263 | ... D.size() != g.size()): # long |
| 2264 | ... print "Something wrong happened" # long |
| 2265 | |
| 2266 | """ |
| 2267 | from sage.graphs.all import DiGraph |
| 2268 | n = self.order() |
| 2269 | |
| 2270 | if n == 0: |
| 2271 | return DiGraph() |
| 2272 | |
| 2273 | vertices = self.vertices() |
| 2274 | vertices_id = dict(map(lambda (x,y):(y,x), list(enumerate(vertices)))) |
| 2275 | |
| 2276 | b = {} |
| 2277 | |
| 2278 | |
| 2279 | # Checking the input type. We make a dictionay out of it |
| 2280 | if isinstance(bound, dict): |
| 2281 | b = bound |
| 2282 | else: |
| 2283 | try: |
| 2284 | b = dict(zip(vertices,map(bound, vertices))) |
| 2285 | |
| 2286 | except TypeError: |
| 2287 | b = dict(zip(vertices, [bound]*n)) |
| 2288 | |
| 2289 | d = DiGraph() |
| 2290 | |
| 2291 | # Adding the edges (s,v) and ((u,v),t) |
| 2292 | d.add_edges( ('s', vertices_id[v], b[v]) for v in vertices) |
| 2293 | |
| 2294 | d.add_edges( ((vertices_id[u], vertices_id[v]), 't', 1) |
| 2295 | for (u,v) in self.edges(labels=None) ) |
| 2296 | |
| 2297 | # each v is linked to its incident edges |
| 2298 | |
| 2299 | for u,v in self.edges(labels = None): |
| 2300 | u,v = vertices_id[u], vertices_id[v] |
| 2301 | d.add_edge(u, (u,v), 1) |
| 2302 | d.add_edge(v, (u,v), 1) |
| 2303 | |
| 2304 | # Solving the maximum flow |
| 2305 | value, flow = d.flow('s','t', value_only = False, integer = True, use_edge_labels = True) |
| 2306 | |
| 2307 | if value != self.size(): |
| 2308 | raise ValueError("No orientation exists for the given bound") |
| 2309 | |
| 2310 | D = DiGraph() |
| 2311 | D.add_vertices(vertices) |
| 2312 | |
| 2313 | # The flow graph may not contain all the vertices, if they are |
| 2314 | # not part of the flow... |
| 2315 | |
| 2316 | for u in [x for x in range(n) if x in flow]: |
| 2317 | |
| 2318 | for (uu,vv) in flow.neighbors_out(u): |
| 2319 | v = vv if vv != u else uu |
| 2320 | D.add_edge(vertices[u], vertices[v]) |
| 2321 | |
| 2322 | # I do not like when a method destroys the embedding ;-) |
| 2323 | |
| 2324 | D.set_pos(self.get_pos()) |
| 2325 | |
| 2326 | return D |
| 2327 | |
| 2328 | |