UniGen almost-uniform sampler (pysat.allies.unigen)#
List of classes#
A wrapper for UniGen3, a state-of-the-art almost-uniform sampler. |
Module description#
This module provides interface to UniGen3, a state-of-the-art almost-uniform sampler utilising an improved version of CryptoMiniSat to handle problems of size and complexity that were not possible before. . The original work on UniGen3 has been published in [1], [2], and [3].
Note that to be functional, the module requires package pyunigen to be
installed:
$ pip install pyunigen
The interface gives access to Sampler, which expects a formula in
CNF as input. Given a few additional (optional) arguments,
including a random seed, tolerance factor \(\varepsilon\),
confidence \(\delta\) (to be used by ApproxMC), and uniformity
parameter \(\kappa\), the class can be used to get apply
almost-uniform sampling and to obtain a requested number of samples as a
result, subject to the given tolerance factor and confidence parameter.
Namely, given a CNF formula \(\mathcal{F}\) with the set of satisfying assignments (or models) denoted by \(sol(\mathcal{F})\) and parameter \(\varepsilon\in(0,1]\), a uniform sampler outputs a model \(y\in sol(\mathcal{F})\) such that \(\textrm{Pr}\left[y \textrm{ is output}\right]=\frac{1}{|sol(\mathcal{F})|}\). Almost-uniform sampling relaxes the uniformity guarantee and ensures that \(\frac{1}{(1+\varepsilon)|sol(\mathcal{F})|} \leq \textrm{Pr}\left[y \textrm{ is output}\right] \leq \frac{1+\varepsilon}{|sol(\mathcal{F})|}\).
The implementation can be used as an executable (the list of available
command-line options can be shown using unigen.py -h) in the
following way:
$ xzcat formula.cnf.xz
p cnf 6 2
1 5 0
1 6 0
$ unigen.py -n 4 formula.cnf.xz
v +1 -2 +3 -4 -5 -6 0
v +1 +2 +3 -4 +5 +6 0
v +1 -2 -3 -4 +5 -6 0
v -1 -2 -3 -4 +5 +6 0
Alternatively, the algorithm can be accessed and invoked through the
standard import interface of Python, e.g.
>>> from pysat.allies.unigen import Sampler
>>> from pysat.formula import CNF
>>>
>>> cnf = CNF(from_file='formula.cnf.xz')
>>>
>>> with Sampler(cnf) as sampler:
... print(sampler.sample(nof_samples=4, sample_over=[1, 2, 3])
[[1, 2, 3, 4, 5], [1, -2, -3, -4, -5], [1, -2, -3, -4, 5], [1, 2, -3, 4, 5]]
As can be seen in the above example, sampling can be done over a user-defined set of variables (rather than the complete set of variables).
Module details#
- class allies.unigen.Sampler(formula=None, seed=1, epsilon=0.8, delta=0.2, kappa=0.638, verbose=0)#
A wrapper for UniGen3, a state-of-the-art almost-uniform sampler. Given a formula in
CNF, this class can be used to apply almost-uniform sampling of the formula’s models, subject to a few input parameters.The class initialiser receives a number of input arguments. The
formulaargument can be left unspecified at this stage. In this case, a user is expected to add all the relevant clauses usingadd_clause().Additional parameters a user may want to specify include integer
seed(used by ApproxMC), tolerance factorepsilon(used in the probabilistic guarantees of almost-uniformity), confidence parameterdelta(used by ApproxMC), and uniformity parameterkappa(see [2]).- Parameters:
formula (
CNF) – CNF formulaseed (int) – seed value
epsilon (float) – tolerance factor
delta (float) – confidence parameter (used by ApproxMC)
kappa (float) – uniformity parameter
verbose (int) – verbosity level
>>> from pysat.allies.unigen import Sampler >>> from pysat.formula import CNF >>> >>> cnf = CNF(from_file='some-formula.cnf') >>> with Sampler(formula=cnf, epsilon=0.1, delta=0.9) as sampler: ... for model in sampler.sample(nof_samples=100): ... print(model) # printing 100 result samples
- add_clause(clause)#
The method for adding a clause to the problem formula. Although the input formula can be specified as an argument of the constructor of
Sampler, adding clauses may also be helpful afterwards, on the fly.The clause to add can be any iterable over integer literals.
- Parameters:
clause (iterable(int)) – a clause to add
>>> from pysat.allies.unigen import Sampler >>> >>> with Sampler() as sampler: ... sampler.add_clause(range(1, 4)) ... sampler.add_clause([3, 4]) ... ... print(sampler.sample(nof_samples=4)) [[1, 2, -3, 4], [-1, 2, -3, 4], [1, 2, 3, -4], [-1, 2, 3, 4]]
- delete()#
Explicit destructor of the internal Sampler oracle. Delete the actual sampler object and sets it to
None.
- sample(nof_samples, sample_over=None, counts=None)#
Given the formula provided by the user either in the constructor of
Sampleror through a series of calls toadd_clause(), this method runs the UniGen3 sampler with the specified values of tolerance \(\varepsilon\), confidence \(\delta\) parameters, and uniformity parameter \(kappa\) as well as the randomseedvalue, and outputs a requested number of samples.A user may specify an argument
sample_over, which is a list of integers specifying the variables with respect to which sampling should be performed. Ifsample_overis left asNone, almost-uniform sampling is done wrt. all the variables of the input formula.Finally, argument
countscan be specified as a pair of integer values: cell count and hash count (in this order) used during sampling. If left undefined (None), the values are determined by ApproxMC.- Parameters:
nof_samples (int) – number of samples to output
sample_over (list(int)) – variables to sample over
counts ([int, int]) – cell count and hash count values
- Returns:
a list of samples
>>> from pysat.allies.unigen import Sampler >>> from pysat.card import CardEnc, EncType >>> >>> # cardinality constraint with auxiliary variables >>> # there are exactly 6 models for the constraint >>> # over the 6 original variables >>> cnf = CardEnc.equals(lits=range(1, 5), bound=2, encoding=EncType.totalizer) >>> >>> with Sampler(formula=cnf, epsilon=0.05, delta=0.95) as sampler: ... for model in sampler.sample(nof_samples=3): ... print(model) [1, -2, 3, -4, 5, 6, -7, -8, 9, -10, 11, -12, 13, 14, -15, 16, 17, -18, 19, -20] [1, -2, -3, 4, 5, 6, -7, -8, 9, -10, 11, -12, 13, 14, -15, 16, 17, -18, 19, -20] [1, 2, -3, -4, 5, 6, -7, 8, -9, -10, 11, 12, 13, 14, -15, 16, 17, 18, -19, -20] >>> >>> # now, sampling over the original variables >>> with Sampler(formula=cnf, epsilon=0.05, delta=0.95) as sampler: ... for model in sampler.sample(nof_samples=3, sample_over=range(1, 5)): ... print(model) [1, 2, -3, -4] [1, -2, 3, -4] [-1, 2, 3, -4]