| 9829 | def degree_constrained_subgraph(self, bounds=None): |
| 9830 | r""" |
| 9831 | Returns a degree-constrained subgraph. |
| 9832 | |
| 9833 | Given a graph `G` and two functions `f, g:V(G)\rightarrow \mathbb Z` |
| 9834 | such that `f \leq g`, a degree-constrained subgraph in `G` is |
| 9835 | a subgraph `G' \subseteq G` such that for any vertex `v \in G`, |
| 9836 | `f(v) \leq d_{G'}(v) \leq g(v)`. |
| 9837 | |
| 9838 | INPUT: |
| 9839 | |
| 9840 | - ``bounds`` -- Two possibilities : |
| 9841 | - A dictionary whose keys are the vertices, and values a pair of |
| 9842 | real values ``(min,max)`` corresponding to the values `(f(v),g(v))`. |
| 9843 | - A function associating to each vertex a pair of |
| 9844 | real values ``(min,max)`` corresponding to the values `(f(v),g(v))`. |
| 9845 | |
| 9846 | OUTPUT: |
| 9847 | |
| 9848 | - When a solution exists, this method outputs the degree-constained subgraph |
| 9849 | as a Graph object. |
| 9850 | - When no solution exists, returns ``False`` |
| 9851 | |
| 9852 | NOTES: |
| 9853 | |
| 9854 | - This algorithm computes the degree-constrained subgraph of minimum weight. |
| 9855 | - If the graph's edges are weighted, these are taken into account. |
| 9856 | - This problem can be solved in polynomial time. |
| 9857 | |
| 9858 | EXAMPLES :: |
| 9859 | |
| 9860 | Is there a perfect matching in an even cycle ? |
| 9861 | |
| 9862 | sage: g = graphs.CycleGraph(6) |
| 9863 | sage: bounds = lambda x : [1,1] |
| 9864 | sage: m=g.degree_constrained_subgraph(bounds=bounds) # optional - requires GLPK or CBC |
| 9865 | sage: m.size() |
| 9866 | 3 |
| 9867 | """ |
| 9868 | |
| 9869 | from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException |
| 9870 | |
| 9871 | p = MixedIntegerLinearProgram(maximization=False) |
| 9872 | b = p.new_variable() |
| 9873 | |
| 9874 | reorder = lambda x: (min(x[0],x[1]),max(x[0],x[1]),x[2]) |
| 9875 | |
| 9876 | if bounds == None: |
| 9877 | raise ValueError,"The `bounds` keyword can not be equal to None" |
| 9878 | elif isinstance(bounds,dict): |
| 9879 | f_bounds = lambda x : bounds[x] |
| 9880 | else: |
| 9881 | f_bounds = bounds |
| 9882 | |
| 9883 | |
| 9884 | if self.weighted(): |
| 9885 | weight = lambda x: x[2] if x[2] != None else 1 |
| 9886 | else: |
| 9887 | weight = lambda x: 1 |
| 9888 | |
| 9889 | for v in self: |
| 9890 | minimum,maximum = f_bounds(v) |
| 9891 | p.add_constraint(sum([ b[reorder(e)]*weight(e) for e in self.edges_incident(v)]),min=minimum, max=maximum) |
| 9892 | |
| 9893 | p.set_objective(sum([ b[reorder(e)]*weight(e) for e in self.edge_iterator()])) |
| 9894 | p.set_binary(b) |
| 9895 | |
| 9896 | try: |
| 9897 | p.solve() |
| 9898 | g = self.copy() |
| 9899 | b = p.get_values(b) |
| 9900 | g.delete_edges([e for e in g.edge_iterator() if b[reorder(e)] == 0]) |
| 9901 | return g |
| 9902 | |
| 9903 | |
| 9904 | except MIPSolverException: |
| 9905 | return False |
| 9906 | |
| 9907 | |
| 9908 | |
| 9909 | |
| 9910 | |