Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" Subsets satisfying a hereditary property """ #***************************************************************************** # Copyright (C) 2014 Nathann Cohen <nathann.cohen@gmail.com> # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #******************************************************************************
r""" Return all subsets `S` of `X` such that `f(S)` is true.
The boolean function `f` must be decreasing, i.e. `f(S)\Rightarrow f(S')` if `S'\subseteq S`.
This function is implemented to call `f` as few times as possible. More precisely, `f` will be called on all sets `S` such that `f(S)` is true, as well as on all inclusionwise minimal sets `S` such that `f(S)` is false.
The problem that this function answers is also known as the learning problem on monotone boolean functions, or as computing the set of winning coalitions in a simple game.
INPUT:
- ``f`` -- a boolean function which takes as input a list of elements from ``X``.
- ``X`` -- a list/iterable.
- ``max_obstruction_size`` (integer) -- if you know that there is a `k` such that `f(S)` is true if and only if `f(S')` is true for all `S'\subseteq S` with `S'\leq k`, set ``max_obstruction_size=k``. It may dramatically decrease the number of calls to `f`. Set to ``None`` by default, meaning `k=|X|`.
- ``ncpus`` -- number of cpus to use for this computation. Note that changing the value from `1` (default) to anything different *enables* parallel computations which can have a cost by itself, so it is not necessarily a good move. In some cases, however, it is a *great* move. Set to ``None`` to automatically detect and use the maximum number of cpus available.
.. NOTE::
Parallel computations are performed through the :func:`~sage.parallel.decorate.parallel` decorator. See its documentation for more information, in particular with respect to the memory context.
EXAMPLES:
Sets whose elements all have the same remainder mod 2::
sage: from sage.combinat.subsets_hereditary import subsets_with_hereditary_property sage: f = lambda x: (not x) or all(xx%2 == x[0]%2 for xx in x) sage: list(subsets_with_hereditary_property(f,range(4))) [[], [0], [1], [2], [3], [0, 2], [1, 3]]
Same, on two threads::
sage: sorted(list(subsets_with_hereditary_property(f,range(4),ncpus=2))) [[], [0], [0, 2], [1], [1, 3], [2], [3]]
One can use this function to compute the independent sets of a graph. We know, however, that in this case the maximum obstructions are the edges, and have size 2. We can thus set ``max_obstruction_size=2``, which reduces the number of calls to `f` from 91 to 56::
sage: num_calls=0 sage: g = graphs.PetersenGraph() sage: def is_independent_set(S): ....: global num_calls ....: num_calls+=1 ....: return g.subgraph(S).size()==0 sage: l1=list(subsets_with_hereditary_property(is_independent_set,g.vertices())) sage: num_calls 91 sage: num_calls=0 sage: l2=list(subsets_with_hereditary_property(is_independent_set,g.vertices(),max_obstruction_size=2)) sage: num_calls 56 sage: l1==l2 True
TESTS::
sage: list(subsets_with_hereditary_property(lambda x:False,range(4))) [] sage: list(subsets_with_hereditary_property(lambda x:len(x)<1,range(4))) [[]] sage: list(subsets_with_hereditary_property(lambda x:True,range(2))) [[], [0], [1], [0, 1]] """ # About the implementation: # # 1) We work on X={0,...,n-1} but remember X to return correctly # labelled answers. # # 2) We maintain a list of sets S such that f(S)=0 (i.e. 'no-sets'), in # order to filter out larger sets for which f is necessarily False. # # 3) Those sets are stored in an array: bs[i] represents the set of all # no-sets S we found such that i is NOT in S. Why ? Because it makes it # easy to filter out sets: if a set S' whose *complement* is # {i1,i2,...,ik} is such that bs[i1]&bs[i2]&...&bs[ik] is nonempty then # f(S') is necessarily False.
r""" Explores the successors of a set s.
The successors of a set s are all the sets s+[i] where max(s)<i. This function returns them all as a partition `(yes_sets,no_sets)`. """
# Filter a no-set using the data collected so far.
# If we cannot decide yet we must call f(S) else:
# The empty set else:
# All sets of size 0, then size 1, then ...
else:
# Update bs with the new no-sets
# Did we forget to return X itself ? # # If we did, this was probably the worst choice of algorithm for we computed # f(X) for all 2^n sets X, but well... |