Source code for concepts.benchmark.logic_induction.boolean_normal_form
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
# File : boolean_normal_form.py
# Author : Jiayuan Mao
# Email : maojiayuan@gmail.com
# Date : 03/18/2018
#
# This file is part of Project Concepts.
# Distributed under terms of the MIT license.
import collections
import jacinle.random as random
__all__ = ['CNF', 'DNF', 'random_generate_cnf']
[docs]
class NormalForm(object):
merge1 = None
merge2 = None
init1 = None
init2 = None
split1 = None
split2 = None
[docs]
def __init__(self, nr_variables, exprs, varnames=None):
self.nr_variables = nr_variables
self.exprs = exprs
self.idx2name = varnames
[docs]
def __call__(self, assigns=None, **kwargs):
if assigns is None:
return self.eval(kwargs)
return self.eval(assigns)
[docs]
def eval(self, assigns):
assert self.nr_variables == len(assigns)
if isinstance(assigns, collections.Mapping):
assert self.idx2name is not None
assigns = [assigns[self.idx2name[k]] for k in range(self.nr_variables)]
else:
assert isinstance(assigns, collections.Sequence)
assigns = list(map(bool, assigns))
v1 = type(self).init1
for e1, neg1 in self.exprs:
v2 = type(self).init2
for e2, neg2 in e1:
if neg2:
v2 = type(self).merge2(v2, not assigns[e2])
else:
v2 = type(self).merge2(v2, assigns[e2])
if neg1:
v1 = type(self).merge1(v1, not v2)
else:
v1 = type(self).merge1(v1, v2)
return v1
[docs]
@classmethod
def from_string(cls, expr):
expr = expr.strip().replace(' ', '') # canonize
closures = []
variables = set()
ands = expr.split(cls.split1)
for a in ands:
this_closure = []
if a[0] == '!':
a = a[1:]
nega = True
else:
nega = False
assert a[0] == '(' and a[-1] == ')'
a = a[1:-1]
ors = a.split(cls.split2)
for o in ors:
if o.startswith('!'):
o = o[1:]
nego = True
else:
nego = False
this_closure.append([o, nego])
variables.add(o)
closures.append([this_closure, nega])
variables = sorted(variables)
var2idx = {v: i for i, v in enumerate(variables)}
nr_variables = len(variables)
for a, _ in closures:
for o in a:
o[0] = var2idx[o[0]]
return cls(nr_variables, closures, varnames=variables)
[docs]
def to_string(self):
if self.idx2name is not None:
idx2name = self.idx2name
else:
assert self.nr_variables <= 26
idx2name = [chr(97 + i) for i in range(26)]
s1 = ''
for e1, neg1 in self.exprs:
if len(s1) != 0:
s1 += type(self).split1
s2 = ''
for e2, neg2 in e1:
if len(s2) != 0:
s2 += type(self).split2
if neg2:
s2 += '!' + idx2name[e2]
else:
s2 += idx2name[e2]
if neg1:
s2 = '!(' + s2 + ')'
else:
s2 = '(' + s2 + ')'
s1 += s2
return s1
[docs]
class CNF(NormalForm):
init1 = True
init2 = False
split1 = '&'
split2 = '|'
merge1 = lambda x, y: x and y
merge2 = lambda x, y: x or y
[docs]
class DNF(NormalForm):
init1 = False
init2 = True
split1 = '|'
split2 = '&'
merge1 = lambda x, y: x or y
merge2 = lambda x, y: x and y
def _canonize_max(m):
if type(m) is int:
m = [max(m // 2, 1), max(m, 1)]
else:
assert m[0] > 0 and m[1] > 0
return m
[docs]
def random_generate_cnf(nr_variables, max_ands, max_ors):
max_ands = _canonize_max(max_ands)
max_ors = _canonize_max(max_ors)
closures = []
nr_ands = random.randint(max_ands[0], max_ands[1] + 1)
for i in range(nr_ands):
nr_ors = random.randint(max_ors[0], max_ors[1] + 1)
vars = random.randint(0, nr_variables, size=nr_ors)
nots = random.randint(0, 2, size=nr_ors)
this_closures = [(v, n) for v, n in zip(vars, nots)]
closures.append((this_closures, random.randint(0, 2)))
return CNF(nr_variables, closures)