Ejemplo n.º 1
0
def replace_allof(ast):
    for expr in ast:
        if isinstance(expr, Flag):
            yield expr
        elif isinstance(expr, Implication):
            condition = expr.condition
            constraint = list(replace_allof(expr.constraint))

            if any(isinstance(x, AllOfOperator) for x in condition):
                if all(x.enabled for x in condition):
                    yield Implication(list(replace_nary(condition)),
                                      constraint)
                else:
                    if any(x.enabled for x in condition):
                        raise NotImplementedError(
                            'Only pure negative or pure positive implication conditions supported'
                        )

                    # we need to replace !(a && b && c) -> !a || !b || !c
                    # per de Morgan's law, then convert to CNF
                    # (!a || !b) && (!c || !d) -> (!a && !c) || (!a && !d) || ...
                    for cset in itertools.product(
                            *expand_conditions(condition)):
                        yield Implication(list(cset), list(constraint))
            else:
                yield Implication(condition, constraint)
        elif isinstance(expr, NaryOperator):
            raise ValueError('Flat n-ary operators should be replaced already')
        else:
            raise ValueError('Unknown AST expr: %s' % expr)
Ejemplo n.º 2
0
def to_implication(expr):
    if isinstance(expr, Flag):
        return [Implication([], [expr], strict=True, stricter=True)]
    elif isinstance(expr, Implication):
        l = []
        for x in expr.constraint:
            l += to_implication(x)
        return [
            Implication(expr.condition + c.condition,
                        c.constraint,
                        strict=True,
                        stricter=True) for c in l
        ]
    elif isinstance(expr, AnyOfOperator):
        f = expr.constraint.pop(0)
        if len(expr.constraint) > 0:
            return list(
                merge_and_expand_implications(
                    merge(negate(AnyOfOperator(expr.constraint)),
                          to_implication(f))))
        else:
            return list(merge_and_expand_implications(to_implication(f)))
    elif isinstance(expr, AllOfOperator):
        r = []
        for i in expr.constraint:
            r += to_implication(i)
        return list(merge_and_expand_implications(r))
    else:
        raise ValueError('Invalid operator in %s' % expr)
Ejemplo n.º 3
0
def merge_and_expand_implications(ast):
    for expr in ast:
        if isinstance(expr, Implication):
            for i in expr.constraint:
                if isinstance(i, Implication):
                    for j in merge_and_expand_implications([i]):
                        yield Implication(expr.condition + j.condition,
                                          j.constraint)
                elif isinstance(i, AllOfOperator):
                    for j in i.constraint:
                        yield Implication(expr.condition, [j], strict=True)
                else:
                    yield Implication(expr.condition, [i], strict=True)
        else:
            yield expr
Ejemplo n.º 4
0
def replace_nary(ast):
    for expr in ast:
        if isinstance(expr, Flag):
            yield expr
        elif isinstance(expr, Implication):
            yield Implication(expr.condition,
                              list(replace_nary(expr.constraint)))
        elif isinstance(expr, AtMostOneOfOperator):
            # ?? ( a b c ... ) -> a? ( !b !c ... ) b? ( !c ... ) ...
            # -> && ( || ( ( !b !c ... ) !a ) || ( ( !c ... ) !b ) ... )
            constraint = [negate(x) for x in replace_nary(expr.constraint)]
            result = []
            while len(constraint) > 0:
                r = constraint.pop(0)
                l = AllOfOperator([x for x in constraint])
                result.append(AnyOfOperator([l, r]))
            yield AllOfOperator(result)
        elif isinstance(expr, ExactlyOneOfOperator):
            # ^^ ( a b c ... ) -> || ( a b c ... ) ?? ( a b c ... )
            constraint = list(replace_nary(expr.constraint))
            m = list(replace_nary([AtMostOneOfOperator(constraint)]))
            yield AllOfOperator([AnyOfOperator(constraint)] + m)
        elif isinstance(expr, AllOfOperator):
            yield AllOfOperator(list(replace_nary(expr.constraint)))
        elif isinstance(expr, AnyOfOperator):
            yield AnyOfOperator(list(replace_nary(expr.constraint)))
        else:
            raise ValueError('Unknown AST expr: %s' % expr)
Ejemplo n.º 5
0
def flatten_implications(ast, current_implications=[]):
    for expr in ast:
        if isinstance(expr, Flag):
            yield Implication(current_implications, [expr])
        elif isinstance(expr, Implication):
            for x in flatten_implications(
                    expr.constraint, current_implications + expr.condition):
                yield x
        elif isinstance(expr, NaryOperator):
            raise ValueError('N-ary operators should be replaced already')
        else:
            raise ValueError('Unknown AST expr: %s' % expr)
Ejemplo n.º 6
0
def simplify_with_immutables(ast, immutables):
    if type(ast) == list:
        r = []
        for x in ast:
            m = simplify_with_immutables(x, immutables)
            if m is True: continue
            if m is False: return False
            r.append(m)
        return r
    elif isinstance(ast, Flag):
        if ast.name in immutables:
            if ast.enabled: return immutables[ast.name]
            else: return not immutables[ast.name]
        else: return ast
    elif isinstance(ast, Implication):
        nc = []
        for c in ast.condition:
            if c.name in immutables:
                if not immutables[c.name]: return True
            else: nc.append(c)
        if len(nc) <= 0:
            return simplify_with_immutables(AllOfOperator(ast.constraint),
                                            immutables)
        ncons = []
        for c in ast.constraint:
            m = simplify_with_immutables(c, immutables)
            if m is True: continue
            if m is False: return False
            ncons.append(m)
        if len(ncons) <= 0: return True
        return Implication(nc, ncons)
    elif isinstance(ast, AllOfOperator):
        r = []
        for x in ast.constraint:
            m = simplify_with_immutables(x, immutables)
            if m is True: continue
            if m is False: return False
            r.append(m)
        if len(r) <= 0: return True
        if len(r) == 1: return r[0]
        return AllOfOperator(r)
    elif isinstance(ast, AnyOfOperator):
        r = []
        for x in ast.constraint:
            m = simplify_with_immutables(x, immutables)
            if m is True: return True
            if m is False: continue
            r.append(m)
        if len(r) <= 0: return False
        if len(r) == 1: return r[0]
        return AnyOfOperator(r)
    else:
        raise ValueError('Unknown AST expr: %s' % ast)
Ejemplo n.º 7
0
def selftest():
    check_equal('|| ( a b )',
                [Implication([Flag('b').negated()], [Flag('a')])])
    check_equal('?? ( a b )',
                [Implication([Flag('a')], [Flag('b').negated()])])
    check_equal('|| ( a b c? ( d ) )', [
        Implication(
            [Flag('b').negated(), Flag('c').negated()], [Flag('a')]),
        Implication(
            [Flag('b').negated(), Flag('d').negated()], [Flag('a')])
    ])
    check_equal(
        'a b? ( c )',
        [Implication([], [Flag('a')]),
         Implication([Flag('b')], [Flag('c')])])
    check_equal('^^ ( a b )', [
        Implication([Flag('b').negated()], [Flag('a')]),
        Implication([Flag('a')], [Flag('b').negated()])
    ])
Ejemplo n.º 8
0
def merge(cond, cons):
    if isinstance(cond, Flag):
        return [
            Implication([cond] + c.condition,
                        c.constraint,
                        strict=True,
                        stricter=True) for c in cons
        ]
    elif isinstance(cond, AnyOfOperator):
        r = []
        for c in cond.constraint:
            r += merge(c, cons)
        return list(merge_and_expand_implications(r))
    elif isinstance(cond, AllOfOperator):
        if len(cond.constraint) <= 0: return cons
        return list(
            merge_and_expand_implications(
                merge(cond.constraint[0],
                      merge(AllOfOperator(cond.constraint[1:]), cons))))
    else:
        raise ValueError('Invalid operator in %s' % cond)
Ejemplo n.º 9
0
def print_solutions(constraint_str, immutable_str):
    # sort n-ary expressions
    immutable_flags = parse_immutables(immutable_str)
    ast = sort_nary(validate_ast_passthrough(parse_string(constraint_str)),
            immutability_sort(immutable_str))
    ast = list(ast)
    print(ast)
    print()

    # implication variant
    impl_ast = []
    for c, e in flatten3(ast):
        if c:
            e = Implication(c, [e])
        impl_ast.append(e)

    all_flags = frozenset(x.name for x in get_all_flags(ast))

    # print flag names, vertically
    sorted_flags = sorted(all_flags)
    no_flags = len(sorted_flags)
    y_max = max(len(x) for x in sorted_flags)
    for y in range(0, y_max):
        for f in sorted_flags + ['|'] + sorted_flags:
            print(' %s' % (f[len(f)-y_max+y] if y >= y_max - len(f) else ' '), end='')
        print('')

    # solve for input = 000... to 111...
    max_iters = 0
    unsolvable = 0
    mismatched_solutions = 0
    for values in itertools.product((False, True), repeat=no_flags):
        inp_flags = dict(zip(sorted_flags, values))

        skip = False
        for k, v in immutable_flags.items():
            # skip mismatches for immutables
            if inp_flags[k] != v:
                skip = True
                break
        if skip:
            continue

        for f in sorted_flags:
            if f in immutable_flags:
                print('\033[33m', end='')
            print(' %d' % inp_flags[f], end='')
            if f in immutable_flags:
                print('\033[0m', end='')
        print(' |', end='')

        if validate_constraint(inp_flags, ast):
            print('\033[32m', end='')
            for f in sorted_flags:
                print(' %d' % inp_flags[f], end='')
            print(' (==)\033[0m')
        else:
            try:
                ret, iters = do_solving(sorted_flags, inp_flags, ast, immutable_flags)
            except (ImmutabilityError, InfiniteLoopError):
                unsolvable += 1
            else:
                if iters > max_iters:
                    max_iters = iters

                ret_impl, ret_iters = do_solving(sorted_flags, inp_flags,
                        impl_ast, immutable_flags, verbose=False)
                if ret != ret_impl:
                    mismatched_solutions += 1
                    print('%*s |\033[31m' % (len(sorted_flags) * 2, ''), end='')
                    for f in sorted_flags:
                        if ret_impl[f] != ret[f]:
                            print(' \033[1m%d\033[22m' % ret_impl[f], end='')
                        else:
                            print(' %d' % ret_impl[f], end='')
                    print(' [mismatch between implication and basic form]\033[0m')

    print()
    print('max iterations: %d;  unsolvable: %d;  mismatched solutions for transform: %d'
            % (max_iters, unsolvable, mismatched_solutions))