예제 #1
0
def power_iteration(matrix_a,
                    variables=None,
                    initial_vector=None,
                    iterations=100,
                    delta=10**-3):
    """
    Computes the pagerank of a matrix.
    By convention, the variables in the matrix are prefixed with r_ if they are used as row variables and c_ if they are
    used as column variables.
    :param Matrix matrix_a: The square, stochastic adjacency matrix
    :param variables: The variables of the matrix (if None it is inferred from the matrix)
    :param damping_factor: The damping factor used to prevent problems due to dangling or disconnected nodes
    :param Matrix initial_vector: The initial column vector (if None it is initialized with 1/N, where N is the size of the matrix)
    :param iterations: The maximal number of iterations before the algorithm stops
    :param delta: The threshold when change is considered negligible
    :return: The page rank vector
    """

    build = Builder(matrix_a.diagram.pool)

    if variables is None:
        raise RuntimeError("Variables computation not yet implemented.")
    names = [t[0] for t in variables]

    if initial_vector is None:
        initial_diagram = build.exp(1.0 / matrix_a.height)
        for name, lb, ub in variables:
            initial_diagram = initial_diagram * build.limit(
                "r_" + name, lb, ub)
        row_vars = [("r_" + name, lb, ub) for name, lb, ub in variables]
        initial_vector = Matrix(initial_diagram, row_vars, [])

    initial_vector = initial_vector.rename({"r_" + var: var for var in names})

    previous_vector = None
    new_vector = initial_vector

    matrix_a = matrix_a.rename({"c_" + var: var for var in names})

    for i in range(iterations):
        # Check for convergence
        if previous_vector is not None:
            # Calculate difference vector
            difference_vector = new_vector - previous_vector

            # Compare norm of difference with given delta
            if difference_vector.norm() < delta:
                return new_vector, i

        # Save previous vector
        previous_vector = new_vector

        # Compute next iteration
        new_vector = matrix_a * new_vector

        # Rename column variables to row variables
        new_vector = new_vector.rename({"r_" + var: var
                                        for var in names}).reduce()

    return new_vector, iterations
예제 #2
0
def dampen(matrix_a, damping_factor, reduce_result=True):
    """
    :param Matrix matrix_a: The matrix to dampen
    :param float damping_factor: The dampening factor
    :param reduce_result: True if the dampened matrix should be reduced (default: True)
    :return Matrix: The dampened matrix
    """
    build = Builder(matrix_a.diagram.pool)

    if damping_factor < 0 or damping_factor > 1:
        raise RuntimeError("Damping factor has to be in the interval [0, 1].")

    if damping_factor < 1:
        ones = build.exp(1.0 / matrix_a.height)
        for name, lb, ub in matrix_a.row_variables:
            ones = ones * build.limit(name, lb, ub)
        for name, lb, ub in matrix_a.column_variables:
            ones = ones * build.limit(name, lb, ub)
        matrix_ones = Matrix(ones, matrix_a.row_variables,
                             matrix_a.column_variables)
        updated_a = damping_factor * matrix_a + (1 -
                                                 damping_factor) * matrix_ones
        if reduce_result:
            assert isinstance(updated_a, Matrix)
            updated_a = updated_a.reduce()
        return updated_a
    else:
        return matrix_a
예제 #3
0
def build_diagram_2():
    b = Builder(Pool())
    b.ints("r", "c")
    lb, ub = 1, 10
    bounds = b.limit("r", lb, ub) & b.limit("c", lb, ub)
    t1 = b.ite(b.test("r", "<=", 0), 2, 4)
    t2 = b.ite(b.test("c", ">=", 11), 3, 5)
    return bounds * (t1 + t2)
예제 #4
0
    def test_inversion(self):
        pool = Pool()
        build = Builder(pool)
        build.vars("bool", "a", "b")
        build.vars("int", "x")

        test1 = build.test("a")
        test2 = build.test("b")
        test3 = build.test("x", "<=", 5)

        node3 = build.ite(test3, 1, 0)
        diagram = build.ite(test1, build.ite(test2, node3, 1), node3)

        self.assertTrue(is_ordered(diagram))

        def inversion1(root_id):
            minus_one = pool.terminal("-1")
            return pool.apply(Multiplication, pool.apply(Summation, root_id, minus_one), minus_one)

        def transform(terminal_node, d):
            if terminal_node.expression == 1:
                return d.pool.zero_id
            elif terminal_node.expression == 0:
                return d.pool.one_id
            else:
                raise RuntimeError("Could not invert value {}".format(terminal_node.expression))

        def inversion2(root_id):
            to_invert = pool.diagram(root_id)
            profile = WalkingProfile(diagram)
            return leaf_transform.transform_leaves(transform, to_invert)

        iterations = 1000
        timer = Timer(precision=6)
        timer.start("Legacy inversion")
        for _ in range(iterations):
            inversion1(diagram.root_id)
        time_legacy = timer.stop()

        inverted1 = pool.diagram(inversion1(diagram.root_id))

        timer.start("New inversion")
        for _ in range(iterations):
            inversion2(diagram.root_id)
        time_new = timer.stop()

        inverted2 = pool.diagram(inversion2(diagram.root_id))

        for a in [True, False]:
            for b in [True, False]:
                for x in range(10):
                    assignment = {"a": a, "b": b, "x": x}
                    self.assertNotEqual(diagram.evaluate(assignment), inverted1.evaluate(assignment))
                    self.assertNotEqual(diagram.evaluate(assignment), inverted2.evaluate(assignment))

        self.assertTrue(time_legacy > time_new, "New inversion ({}) not faster than legacy implementation ({})"
                        .format(time_new, time_legacy))
예제 #5
0
class TestTransform(unittest.TestCase):
    def setUp(self):
        pool = Pool()
        self.builder = Builder(pool)
        self.builder.ints("x", "y")

        b = self.builder
        limits = b.limit("x", 0, 20) & b.limit("y", 0, 20)
        rect1 = b.limit("x", 0, 10) & b.limit("y", 0, 10)
        rect2 = b.limit("x", 5, 20) & b.limit("y", 10, 20)
        self.diagram = rect1 * b.terminal("5") \
                  + rect2 * b.terminal("x + 2") \
                  + ~(rect1 | rect2) & limits * b.terminal("1")

        self.exporter = Exporter(
            os.path.join(os.path.dirname(os.path.realpath(__file__)),
                         "visual"), "transform")

    def test_constant(self):
        b = self.builder
        pool = b.pool

        test_d = b.limit("x", 1, 1) * b.exp(5) + b.limit("x", 2, 2) * b.exp(5)
        test_d = pool.diagram(
            LinearReduction(pool).reduce(test_d.root_node.node_id))
        self.exporter.export(test_d, "test_d")

        # Test with profile
        add_profile_cache(pool)
        get_profile(self.diagram)

        self.exporter.export(self.diagram, "to_constant")
        constant = to_constant(self.diagram)
        get_profile(constant)

        self.exporter.export(constant, "constant")

        def t(_, node):
            self.assertTrue(is_constant(node),
                            msg="{} still contains variables".format(
                                node.expression))

        walk_leaves(t, constant)

    def test_ground_tensor(self):
        bounds = [("x", 0, 20), ("y", 0, 20)]
        tensor = to_ground_tensor(self.diagram, bounds)
        size = 1
        for s in [ub - lb + 1 for _, lb, ub in bounds]:
            size *= s
        self.assertEqual(len(tensor.flatten()), size)
        for x in range(0, 21):
            for y in range(0, 21):
                self.assertEqual(self.diagram.evaluate({
                    "x": x,
                    "y": y
                }), tensor[(x, y)])
예제 #6
0
    def construct_diagram():
        pool = Pool()
        pool.int_var("x", "y")

        b = Builder(pool)
        bounds = b.test("x", ">=", 0) & b.test("x", "<=", 8) & b.test("y", ">=", 1) & b.test("y", "<=", 10)
        return bounds * b.ite(b.test("x", ">=", "y"), b.terminal("2*x + 3*y"), b.terminal("3*x + 2*y"))
예제 #7
0
    def setUp(self):
        pool = Pool()
        pool.int_var("x")
        pool.int_var("y")
        pool.int_var("z")
        b = Builder(pool)

        self.diagrams = []
        d = b.ite(b.test("x", "<=", "y"), b.terminal(1), b.test("x", "<=", 2))
        self.diagrams.append(({"x", "y"}, d))
        d = b.terminal("x * 2 * y + 5 * z")
        self.diagrams.append(({"x", "y", "z"}, d))
        d = b.ite(b.test("x", "<", "y"), b.terminal("z"), b.terminal("z * y"))
        self.diagrams.append(({"x", "y", "z"}, d))
예제 #8
0
 def test_summation_two_var(self):
     pool = Pool()
     pool.add_var("x", "int")
     pool.add_var("y", "int")
     b = Builder(pool)
     bounds = b.test("x", ">=", 0) & b.test("x", "<=", 10)
     bounds &= b.test("y", ">=", 0) & b.test("y", "<=", 1)
     d = b.ite(bounds, b.terminal("x"), b.terminal(0))
     d_const = Diagram(pool, SummationWalker(d, "x").walk())
     for y in range(2):
         self.assertEqual(55, d_const.evaluate({"y": y}))
예제 #9
0
class TestTransform(unittest.TestCase):
    def setUp(self):
        pool = Pool()
        self.builder = Builder(pool)
        self.builder.ints("x", "y")

        b = self.builder
        limits = b.limit("x", 0, 20) & b.limit("y", 0, 20)
        rect1 = b.limit("x", 0, 10) & b.limit("y", 0, 10)
        rect2 = b.limit("x", 5, 20) & b.limit("y", 10, 20)
        self.diagram = rect1 * b.terminal("5") \
                  + rect2 * b.terminal("x + 2") \
                  + ~(rect1 | rect2) & limits * b.terminal("1")

        self.exporter = Exporter(os.path.join(os.path.dirname(os.path.realpath(__file__)), "visual"), "transform")

    def test_constant(self):
        b = self.builder
        pool = b.pool

        test_d = b.limit("x", 1, 1) * b.exp(5) + b.limit("x", 2, 2) * b.exp(5)
        test_d = pool.diagram(LinearReduction(pool).reduce(test_d.root_node.node_id))
        self.exporter.export(test_d, "test_d")

        # Test with profile
        add_profile_cache(pool)
        get_profile(self.diagram)

        self.exporter.export(self.diagram, "to_constant")
        constant = to_constant(self.diagram)
        get_profile(constant)

        self.exporter.export(constant, "constant")

        def t(_, node):
            self.assertTrue(is_constant(node), msg="{} still contains variables".format(node.expression))

        walk_leaves(t, constant)

    def test_ground_tensor(self):
        bounds = [("x", 0, 20), ("y", 0, 20)]
        tensor = to_ground_tensor(self.diagram, bounds)
        size = 1
        for s in [ub - lb + 1 for _, lb, ub in bounds]:
            size *= s
        self.assertEqual(len(tensor.flatten()), size)
        for x in range(0, 21):
            for y in range(0, 21):
                self.assertEqual(self.diagram.evaluate({"x": x, "y": y}), tensor[(x, y)])
예제 #10
0
    def setUp(self):
        pool = Pool()
        self.builder = Builder(pool)
        self.builder.ints("x", "y")

        b = self.builder
        limits = b.limit("x", 0, 20) & b.limit("y", 0, 20)
        rect1 = b.limit("x", 0, 10) & b.limit("y", 0, 10)
        rect2 = b.limit("x", 5, 20) & b.limit("y", 10, 20)
        self.diagram = rect1 * b.terminal("5") \
                  + rect2 * b.terminal("x + 2") \
                  + ~(rect1 | rect2) & limits * b.terminal("1")

        self.exporter = Exporter(
            os.path.join(os.path.dirname(os.path.realpath(__file__)),
                         "visual"), "transform")
예제 #11
0
    def build_diagram2():
        build = Builder()
        build.vars("int", "x", "y")

        test1 = build.test("x", "<=", "y")
        test2 = build.test("y", "<=", 5)
        test3 = build.test("x", ">=", 5)

        exp1 = build.exp(5)
        exp2 = build.exp("2 * x")

        node3 = build.ite(test3, exp1, exp2)
        node2 = build.ite(test2, node3, exp1)
        node1 = build.ite(test1, node2, node3)

        if not is_ordered(node1):
            raise RuntimeError("Diagram not ordered")

        return node1
예제 #12
0
    def setUp(self):
        pool = Pool()
        self.builder = Builder(pool)
        self.builder.ints("x", "y")

        b = self.builder
        limits = b.limit("x", 0, 20) & b.limit("y", 0, 20)
        rect1 = b.limit("x", 0, 10) & b.limit("y", 0, 10)
        rect2 = b.limit("x", 5, 20) & b.limit("y", 10, 20)
        self.diagram = rect1 * b.terminal("5") \
                  + rect2 * b.terminal("x + 2") \
                  + ~(rect1 | rect2) & limits * b.terminal("1")

        self.exporter = Exporter(os.path.join(os.path.dirname(os.path.realpath(__file__)), "visual"), "transform")
예제 #13
0
def to_constant(diagram):
    b = Builder(diagram.pool)
    from pyxadd.variables import variables
    all_variables = variables(diagram)
    print("Variables: {}".format(all_variables))
    bounds = [(v, ) + get_bounds(v, diagram) for v in all_variables]

    # Approach 1:
    # Per path, find all included points, merge based on expression (constant all, one var => test only on that, ...)

    # Approach 2:
    # Ground to matrix, learn structure

    return ConstantWalker(diagram, all_variables).walk()
예제 #14
0
 def build_example1():
     # http://www.math.cornell.edu/~mec/Winter2009/RalucaRemus/Lecture3/lecture3.html
     pool = Pool()
     build = Builder(pool)
     variables = [("i", 1, 4)]
     for var in variables:
         name = var[0]
         build.ints("r_{}".format(name), "c_{}".format(name), name)
     limits = build.limit("r_i", 1, 4) & build.limit("c_i", 1, 4)
     column1 = (build.limit("c_i", 1, 1) & build.limit("r_i", 2, 4)) * build.exp("1/3")
     column2 = (build.limit("c_i", 2, 2) & build.limit("r_i", 3, 4)) * build.exp("1/2")
     column3 = (build.limit("c_i", 3, 3) & build.limit("r_i", 1, 1)) * build.exp("1")
     column4 = (build.limit("c_i", 4, 4) & (build.limit("r_i", 1, 1) | build.limit("r_i", 3, 3))) * build.exp("1/2")
     diagram = limits * (column1 + column2 + column3 + column4)
     return diagram, variables
예제 #15
0
def build_diagram1():
    pool = Pool()
    b = Builder(pool)
    b.ints("x")
    return b.ite(b.test("x", "<=", 3),
                 b.ite(b.test("x", "<=", 2), b.exp(1), b.exp("2*x")), b.exp(1))
예제 #16
0
    def test_inversion(self):
        pool = Pool()
        build = Builder(pool)
        build.vars("bool", "a", "b")
        build.vars("int", "x")

        test1 = build.test("a")
        test2 = build.test("b")
        test3 = build.test("x", "<=", 5)

        node3 = build.ite(test3, 1, 0)
        diagram = build.ite(test1, build.ite(test2, node3, 1), node3)

        self.assertTrue(is_ordered(diagram))

        def inversion1(root_id):
            minus_one = pool.terminal("-1")
            return pool.apply(Multiplication,
                              pool.apply(Summation, root_id, minus_one),
                              minus_one)

        def transform(terminal_node, d):
            if terminal_node.expression == 1:
                return d.pool.zero_id
            elif terminal_node.expression == 0:
                return d.pool.one_id
            else:
                raise RuntimeError("Could not invert value {}".format(
                    terminal_node.expression))

        def inversion2(root_id):
            to_invert = pool.diagram(root_id)
            profile = WalkingProfile(diagram)
            return leaf_transform.transform_leaves(transform, to_invert)

        iterations = 1000
        timer = Timer(precision=6)
        timer.start("Legacy inversion")
        for _ in range(iterations):
            inversion1(diagram.root_id)
        time_legacy = timer.stop()

        inverted1 = pool.diagram(inversion1(diagram.root_id))

        timer.start("New inversion")
        for _ in range(iterations):
            inversion2(diagram.root_id)
        time_new = timer.stop()

        inverted2 = pool.diagram(inversion2(diagram.root_id))

        for a in [True, False]:
            for b in [True, False]:
                for x in range(10):
                    assignment = {"a": a, "b": b, "x": x}
                    self.assertNotEqual(diagram.evaluate(assignment),
                                        inverted1.evaluate(assignment))
                    self.assertNotEqual(diagram.evaluate(assignment),
                                        inverted2.evaluate(assignment))

        self.assertTrue(
            time_legacy > time_new,
            "New inversion ({}) not faster than legacy implementation ({})".
            format(time_new, time_legacy))
예제 #17
0
import itertools
from png import Writer

from pyxadd.build import Builder
from pyxadd.diagram import Pool, Diagram
from pyxadd.matrix.matrix import assignments
from pyxadd.reduce import SmtReduce, LinearReduction
from pyxadd.view import export

pool = Pool()
pool.add_var("x", "int")
pool.add_var("xs", "int")
pool.add_var("y", "int")
pool.add_var("ys", "int")
b = Builder(pool)

# TODO needs control over interleaving


class RedGreenBlueDiagrams(object):
    def __init__(self, red, green, blue):
        self.diagrams = (red, green, blue)

    @staticmethod
    def all(diagram):
        return RedGreenBlueDiagrams(diagram, diagram, diagram)

    def _binary(self, op, other):
        if isinstance(other, RedGreenBlueDiagrams):
            return RedGreenBlueDiagrams(*(op(self.diagrams[i], other.diagrams[i]) for i in range(3)))
예제 #18
0
from pyxadd.build import Builder
from pyxadd.diagram import Pool
from pyxadd.view import export

pool = Pool()
b = Builder(pool)
b.ints("r", "c")

xadd_1 = b.terminal("5 + r * c")
export(xadd_1, "visual/examples/xadd_1.dot")

xadd_2 = b.limit("r", 1, 2) & b.limit("c", 1, 2) * xadd_1
export(xadd_2, "visual/examples/xadd_2.dot")
예제 #19
0
    def setUp(self):
        pool = Pool()
        pool.int_var("x")
        pool.int_var("y")
        pool.int_var("z")
        b = Builder(pool)

        self.diagrams = []
        d = b.ite(b.test("x", "<=", "y"), b.terminal(1), b.test("x", "<=", 2))
        self.diagrams.append(({"x", "y"}, d))
        d = b.terminal("x * 2 * y + 5 * z")
        self.diagrams.append(({"x", "y", "z"}, d))
        d = b.ite(b.test("x", "<", "y"), b.terminal("z"), b.terminal("z * y"))
        self.diagrams.append(({"x", "y", "z"}, d))
예제 #20
0
 def build_example1():
     # http://www.math.cornell.edu/~mec/Winter2009/RalucaRemus/Lecture3/lecture3.html
     pool = Pool()
     build = Builder(pool)
     variables = [("i", 1, 4)]
     for var in variables:
         name = var[0]
         build.ints("r_{}".format(name), "c_{}".format(name), name)
     limits = build.limit("r_i", 1, 4) & build.limit("c_i", 1, 4)
     column1 = (build.limit("c_i", 1, 1)
                & build.limit("r_i", 2, 4)) * build.exp("1/3")
     column2 = (build.limit("c_i", 2, 2)
                & build.limit("r_i", 3, 4)) * build.exp("1/2")
     column3 = (build.limit("c_i", 3, 3)
                & build.limit("r_i", 1, 1)) * build.exp("1")
     column4 = (build.limit("c_i", 4, 4) &
                (build.limit("r_i", 1, 1)
                 | build.limit("r_i", 3, 3))) * build.exp("1/2")
     diagram = limits * (column1 + column2 + column3 + column4)
     return diagram, variables
예제 #21
0
        diff_to_b = b - A_times_x
        norm_difference = diff_to_b.norm()
        residual = float(norm_difference) / norm_b
        print("Residual is {} / {} = {}".format(norm_difference, norm_b, residual))
        if residual < delta:
            print("Found solution: ", [x.evaluate({"r": i}) for i in range(lb, ub + 1)])
            return x

        x = x + w * diff_to_b
        x.export("visual/richardson/x_full{}.dot".format(i))
        x = x.reduce()
        x.export("visual/richardson/x{}.dot".format(i))
        print("X: ", [x.evaluate({"r": i}) for i in range(lb, ub + 1)])

pool = Pool()
build = Builder(pool)
build.ints("r", "c")

bounds = build.limit("r", lb, ub) & build.limit("c", lb, ub)

A_d = bounds \
      & build.test("r", "<=", "c") \
      & build.test("r", ">=", "c") * build.exp(2)

A_db = bounds & (build.test("r", ">", lb + (ub - lb) / 2) & build.test("c", "<=", lb + (ub - lb) / 2)
                 | (build.test("r", "<=", lb + (ub - lb) / 2) & build.test("c", ">", lb + (ub - lb) / 2)))

A = Matrix(A_d * build.terminal(3) + A_db, [("r", lb, ub)], [("c", lb, ub)], auto_reduce=False)
# TODO WHAT IS THAT 7
A.print_ground()
A = A.reduce()
예제 #22
0
    def test_summation_two_var_test(self):
        pool = Pool()
        pool.add_var("x", "int")
        pool.add_var("y", "int")
        b = Builder(pool)
        bounds = b.test("x", ">=", 0) & b.test("x", "<=", 1)
        bounds &= b.test("y", ">=", 1) & b.test("y", "<=", 3)
        two = b.test("x", ">=", "y")
        d = b.ite(bounds, b.ite(two, b.terminal("x"), b.terminal("10")), b.terminal(0))

        summed = Diagram(pool, SummationWalker(d, "x").walk())
        d_const = summed.reduce(["y"])
        for y in range(-20, 20):
            s = 0
            for x in range(-20, 20):
                s += d.evaluate({"x": x, "y": y})
            self.assertEqual(s, d_const.evaluate({"y": y}))
예제 #23
0
import itertools
from png import Writer

from pyxadd.build import Builder
from pyxadd.diagram import Pool, Diagram
from pyxadd.matrix.matrix import assignments
from pyxadd.reduce import SmtReduce, LinearReduction
from pyxadd.view import export

pool = Pool()
pool.add_var("x", "int")
pool.add_var("xs", "int")
pool.add_var("y", "int")
pool.add_var("ys", "int")
b = Builder(pool)

# TODO needs control over interleaving


class RedGreenBlueDiagrams(object):
    def __init__(self, red, green, blue):
        self.diagrams = (red, green, blue)

    @staticmethod
    def all(diagram):
        return RedGreenBlueDiagrams(diagram, diagram, diagram)

    def _binary(self, op, other):
        if isinstance(other, RedGreenBlueDiagrams):
            return RedGreenBlueDiagrams(
예제 #24
0
def build_diagram_1():
    b = Builder(Pool())
    b.ints("r", "c")
    lb = 1
    ub = 10
    bounds = b.limit("r", lb, ub) & b.limit("c", lb, ub)
    diagonal = b.test("r", "<=", "c") & b.test("r", ">=", "c")
    block_1 = b.test("r", ">", lb + (ub - lb) / 2) & b.test("c", "<=", lb + (ub - lb) / 2)
    block_2 = b.test("r", "<=", lb + (ub - lb) / 2) & b.test("c", ">", lb + (ub - lb) / 2)
    return bounds * (diagonal * b.exp(6) + (block_1 | block_2))
예제 #25
0
    def build_diagram2():
        build = Builder()
        build.vars("int", "x", "y")

        test1 = build.test("x", "<=", "y")
        test2 = build.test("y", "<=", 5)
        test3 = build.test("x", ">=", 5)

        exp1 = build.exp(5)
        exp2 = build.exp("2 * x")

        node3 = build.ite(test3, exp1, exp2)
        node2 = build.ite(test2, node3, exp1)
        node1 = build.ite(test1, node2, node3)

        if not is_ordered(node1):
            raise RuntimeError("Diagram not ordered")

        return node1