Raw File
Collatz_circuits.py
import sys
import mawatam
from mawatam import C, CENTER, NORTH, EAST, SOUTH, WEST


def bridge_type_2_in_context(with_input=None):
    conf = bridge_type_2()

    for i in range(4):
        conf.add_glue((0, i)).west("ter.0")

    for i in range(3):
        conf.add_tile((0, -7 - i)).west("ter.0")

    for i in range(2):
        conf.add_glue((2 - i, -3)).north("bin.0")
        conf.add_glue((-7 - i, -6)).north("bin.0")

    if with_input is not None:
        conf.add_glue((-1, 4)).south(f"bin.{with_input[0]}")
        conf.add_glue((3, -2)).west(f"ter.{with_input[1]}")

    return conf

def circuit_from_spec(spec_file, with_input=None):
    conf = mawatam.Configuration(mawatam.CollatzTileset)
    with open(spec_file, "r") as f:
        spec_content = f.read()

    spec_split = spec_split.split("\n")

    nodes_index = {}
    nodes = []

    for i, line in enumerate(spec_split):
        space_split = line.split(" ")
        node_name = space_split[0]
        nodes_index[node_name] = i
        if len(space_split) == 1:
            # input node
            nodes.append((True, node_name))
        else:
            # computation node
            # <name> <gate> <node x> <node y>
            nodes.append(
                (False, node_name, space_split[1], space_split[2], space_split[3])
            )

    conf.add_tile((0, 0))
    return conf


def circuit_prime_3_bits(with_input=None):
    # (not x and y) or (x and z)
    conf = mawatam.Configuration(mawatam.CollatzTileset)

    if with_input is not None:
        # x
        conf.add_tile(C(0, 0) + EAST).west(f"ter.{with_input[0]}")
        # top horizontal wire for x
        conf.add_tile(C(0, 0) + SOUTH).north("bin.1")
        conf.add_tile(C(0, 0) + WEST + SOUTH).north("bin.1")
        conf.add_tile(C(0, 0) + WEST * 2 + SOUTH).north("bin.1")

        # y
        conf.add_tile(C(0, 0) + SOUTH * 3 + EAST).west(f"ter.{with_input[1]}")

        # horz wire for y
        now = C(0, 0) + SOUTH * 3
        conf.add_glue(now + SOUTH).north("bin.1")
        conf.add_glue(now + WEST + SOUTH).north("bin.1")

        # z
        conf.add_tile(C(0, 0) + SOUTH * 9 + EAST).west(f"ter.{with_input[2]}")

        # horz wire for z
        now = C(0, 0) + SOUTH * 9
        conf.add_glue(now + SOUTH).north("bin.1")
        conf.add_glue(now + WEST + SOUTH).north("bin.1")

    # turn south for x
    conf.add_tile(C(0, 0) + WEST * 3 + NORTH).south("bin.1")
    conf.add_tile(C(0, 0) + WEST * 4 + NORTH).south("bin.0")
    conf.add_glue(C(0, 0) + WEST * 5 + SOUTH).north("bin.1")
    conf.add_glue(C(0, 0) + WEST * 2 + SOUTH).west("ter.0")

    # growth blocker
    conf.add_tile(C(0, 0) + WEST * 4 + SOUTH)

    # bridge x y
    now = C(0, 0) + WEST * 3 + SOUTH
    conf.add_sub_conf(bridge_type_2(minimal=True).translate(C(-2, -1)))

    # bridge x z
    now = C(0, 0) + WEST * 2 + SOUTH * 7
    conf.add_glue(now).west("ter.0")
    conf.add_sub_conf(bridge_type_2(minimal=True).translate(now))

    # brige y z
    now = C(0, 0) + WEST * 7 + SOUTH * 13
    conf.add_tile(now).north("bin.1")
    conf.add_tile(now + WEST).north("bin.1")
    now += WEST * 2 + NORTH * 3
    conf.add_sub_conf(bridge_type_2(minimal=True).translate(now))

    # turn for y
    now = now = C(0, 0) + WEST * 7 + SOUTH * 7
    conf.add_glue(now).north("bin.1")
    conf.add_glue(now + WEST).north("bin.1")
    conf.add_glue(now + WEST * 2).north("bin.1")
    conf.add_glue(now + WEST * 3 + NORTH * 2).south("bin.1")
    # conf.add_glue(now + WEST * 4 + NORTH * 2).south("bin.0")
    # conf.add_glue(now + WEST * 5).north("bin.1")

    # y goes south to bridge
    for i in range(4):
        conf.add_glue(now + WEST * 2 + SOUTH * i).west("ter.0")

    now = C(0, 0) + SOUTH * 15 + WEST
    # bring x to gate
    for i in range(4):
        conf.add_glue(now + NORTH * 2 + WEST + SOUTH * i).west("ter.0")
        conf.add_glue(now + NORTH * 2 + WEST * 3 + SOUTH * 4 + WEST * i).north("bin.1")
    conf.add_tile(now + NORTH * 2 + WEST * 3 + SOUTH * 4 + NORTH * 2)
    conf.add_glue(now + NORTH * 2 + WEST * 3 + SOUTH * 4 + NORTH * 2 + WEST * 4).south(
        "bin.1"
    )

    conf.add_sub_conf(
        input_x_y_on_top_canonical_gate("AND").translate(now + WEST * 7 + SOUTH)
    )
    conf.add_glue(now + WEST * 7 + SOUTH + WEST).west("ter.0")

    # bridge (x,z)
    conf.add_glue(C(-16, -13) + EAST + SOUTH * 3).north("bin.1")
    conf.add_glue(C(-16, -13) + EAST * 2 + SOUTH * 3).north("bin.1")
    conf.add_sub_conf(bridge_type_2(minimal=True).translate((-16, -13)))

    # bridge (x, AND x y)
    conf.add_glue((-16, -19)).west("ter.0")
    conf.add_sub_conf(bridge_type_2(minimal=True).translate((-16, -19)))

    # x coming to bridges (x,z) (x, AND x y)
    for i in range(6, 17):
        conf.add_glue((-1 * i, -1)).north("bin.1")
    conf.add_glue((-17, 1)).south("bin.1")

    for i in range(1, 14):
        conf.add_glue((-16, -1 * i)).west("ter.0")

    # (AND x y) coming to x bridge we need a not to correct parity
    conf.add_glue((-12, -16)).south("bin.1")
    for i in range(18, 22):
        conf.add_glue((-11, -1 * i)).west("ter.0")

    for i in range(13, 16):
        conf.add_glue((-1 * i, -22)).north("bin.1")

    conf.add_tile((-13, -20))

    # bridge z, (AND x y)
    conf.add_glue((-21, -25)).north("bin.1")
    conf.add_glue((-22, -25)).north("bin.1")
    conf.add_sub_conf(bridge_type_2(minimal=True).translate((-23, -22)))

    # z coming to brige z, (AND x y)
    for i in range(21, 24):
        conf.add_glue((-1 * i, -19)).north("bin.1")

    conf.add_glue((-24, -17)).south("bin.1")

    for i in range(19, 23):
        conf.add_glue((-23, -1 * i)).west("ter.0")

    # (AND x z)
    conf.add_sub_conf(input_x_y_on_top_canonical_gate("AND").translate((-22, -28)))

    # bringing x to (AND x z)
    conf.add_glue((-23, -28)).west("ter.0")
    for i in range(25, 29):
        conf.add_glue((-16, -1 * i)).west("ter.0")

    for i in range(18, 22):
        conf.add_glue((-1 * i, -29)).north("bin.1")
    conf.add_tile((-18, -27))

    # OR
    conf.add_glue((-22, -27)).south("bin.0")
    conf.add_glue((-26, -28)).south("bin.0")
    conf.add_sub_conf(input_x_y_on_top_canonical_gate("OR").translate((-26, -29)))

    # bring AND(x,y) to OR gate
    conf.add_glue((-28, -26)).south("bin.0")
    conf.add_glue((-27, -28)).west("ter.0")
    conf.add_glue((-27, -29)).west("ter.0")

    # light bulb
    for i in range(30, 36):
        conf.add_glue((-1 * i, -33)).south("bin.0")
    for i in range(35, 40):
        conf.add_glue((-29, -1 * i)).west("ter.0")

    return conf


def fanout_two_even_pos(with_input=None):
    conf = mawatam.Configuration(mawatam.CollatzTileset)
    if with_input is not None:
        conf.add_glue((0, 0)).west(f"ter.{with_input[0]}")
    conf.add_glue((-1, -1)).north("bin.1")
    conf.add_glue((-2, -1)).north("bin.1").west("ter.1")
    conf.add_glue(C(-2, -1) + NORTH * 2 + WEST).south("bin.1")
    return conf


def bridge_type_2_inverter(with_input=None, variant=0, minimal=False):
    bridge_variant = [
        ("000", "021"),
    ]
    conf = mawatam.Configuration(mawatam.CollatzTileset)

    # TOP
    conf.add_glue((0, 0)).south("bin.0")

    for i in range(3):
        conf.add_glue((-1 * (i + 2), 0)).south("bin.0")

    # EAST
    conf.add_glue((1, -1)).west("ter.0")

    for i in range(3):
        conf.add_glue((1, -(i + 3))).west(
            f"ter.{bridge_variant[variant%len(bridge_variant)][1][i]}",
        )

    # Output wiring
    bottom_left = conf.bottom_left()
    if not minimal:
        conf.add_glue(bottom_left + SOUTH + WEST).north("bin.0")
        conf.add_glue(bottom_left + SOUTH + WEST * 2).north("bin.0")
        conf.add_glue(bottom_left + SOUTH + EAST * 4).west("ter.0")

    conf.add_glue(bottom_left + NORTH + WEST)
    conf.add_glue(bottom_left + SOUTH + EAST * 2)

    # input
    if with_input is not None:
        if with_input[0] is not None:
            conf.add_glue((-1, 0)).south(f"bin.{with_input[0]}").north(
                f"bin.{with_input[0]}"
            )
        if with_input[1] is not None:
            conf.add_glue(C(1, -2)).east(f"ter.{with_input[1]}").west(
                f"ter.{with_input[1]}"
            )

    return conf


def bridge_type_2(with_input=None, variant=0, minimal=False):
    # This bridge takes one input from north and one input from east
    # and carries them along: north is to be read on same column than input north
    # and east is to be read on the bottom left most tile

    # if minimal=False a short 2 base wire will be added for west output

    # the encoding is: on north: `top0 top1 top2 x 0` with top one of the words below (first component)
    #                   on east: `0 y east0 east1 east2` from top to bottom with east one the words below (second component)

    # Found by computer search, ((top, east))
    bridge_variant = [
        ("000", "012"),
        ("000", "100"),
        ("000", "111"),
        ("000", "122"),
        ("000", "210"),
        ("000", "221"),
        ("001", "001"),
        ("001", "012"),
        ("001", "100"),
        ("001", "111"),
        ("010", "001"),
        ("111", "122"),
        ("111", "210"),
        ("111", "221"),
    ]
    conf = mawatam.Configuration(mawatam.CollatzTileset)

    # TOP
    conf.add_glue((0, 0)).south("bin.0")

    for i in range(3):
        conf.add_glue((-1 * (i + 2), 0)).south("bin.0")

    # EAST
    conf.add_glue((1, -1)).west("ter.0")

    for i in range(3):
        conf.add_glue((1, -(i + 3))).west(
            f"ter.{bridge_variant[variant%len(bridge_variant)][1][i]}",
        )

    # Output wiring
    bottom_left = conf.bottom_left()
    if not minimal:
        conf.add_glue(bottom_left + SOUTH + WEST).north("bin.0")
        conf.add_glue(bottom_left + SOUTH + WEST * 2).north("bin.0")
        conf.add_glue(bottom_left + SOUTH + EAST * 4).west("ter.0")

    conf.add_glue(bottom_left + NORTH + WEST)
    conf.add_glue(bottom_left + SOUTH + EAST * 2)

    # input
    if with_input is not None:
        if with_input[0] is not None:
            conf.add_glue((-1, 0)).south(f"bin.{with_input[0]}").north(
                f"bin.{with_input[0]}"
            )
        if with_input[1] is not None:
            conf.add_glue(C(1, -2)).east(f"ter.{with_input[1]}").west(
                f"ter.{with_input[1]}"
            )

    return conf


# python3 mawatam-tools/Collatz_circuits.py input_two_bridges_type_2 110 0 0 | ./mawatam -i
def two_bridges_type_2(with_input=None, variant1=0, variant2=0):

    if len(with_input) != 3:
        print("Need three input for the two bridges configuration", file=sys.stderr)
        exit(-1)

    conf = mawatam.Configuration(mawatam.CollatzTileset)
    conf.add_sub_conf(bridge_type_2(with_input[:2], variant1))
    bottom_left = conf.bottom_left()
    conf.add_sub_conf(
        bridge_type_2((with_input[2], None), variant2).translate(
            bottom_left + NORTH * 3 + WEST * 1
        )
    )
    conf.add_glue(bottom_left).north("bin.0")
    return conf


# python3 mawatam-tools/Collatz_circuits.py input_two_bridges_type_2_tighter_gap 110 0 0 | ./mawatam -i
def two_bridges_type_2_tighter_gap(with_input=None, variant1=0, variant2=0):

    if len(with_input) != 3:
        print("Need three input for the two bridges configuration", file=sys.stderr)
        exit(-1)

    conf = mawatam.Configuration(mawatam.CollatzTileset)
    conf.add_sub_conf(bridge_type_2(with_input[:2], variant1))
    bottom_left = conf.bottom_left()

    conf.add_sub_conf(
        bridge_type_2((with_input[2], None), variant2).translate(
            bottom_left + NORTH * 3
        )
    )
    conf.delete_tile(bottom_left)
    conf.add_glue(bottom_left + EAST).north("bin.1")
    conf.delete_tile(conf.bottom_left())
    conf.add_glue(conf.bottom_left()).north("bin.1")
    return conf


def bridge_type_1(with_input=None, variant=0):
    # This bridge takes one input from north and one input from east
    # and carries them along on bottom left most tile

    # the encoding is: on north: `top0 top1 top2 x 0` with top one of the words below (first component)
    #                   on east: `0 y east0 east1 east2` from top to bottom with east one the words below (second component)

    # Found by computer search, ((top, east))
    bridge_variant = [
        ("000", "120"),
        ("001", "010"),
    ]
    conf = mawatam.Configuration(mawatam.CollatzTileset)

    # TOP
    conf.add_tile((0, 0)).south("bin.0")

    for i in range(3):
        conf.add_tile((-1 * (i + 2), 0)).south(
            f"bin.{bridge_variant[variant%len(bridge_variant)][0][-(i+1)]}"
        )

    # EAST
    conf.add_tile((1, -1)).west("ter.0")

    for i in range(3):
        conf.add_tile((1, -(i + 3))).west(
            f"ter.{bridge_variant[variant%len(bridge_variant)][1][i]}",
        )

    # Output wiring
    bottom_left = conf.bottom_left()
    conf.add_tile(bottom_left + SOUTH + WEST).north("bin.0")
    conf.add_tile(bottom_left + SOUTH + WEST * 2).north("bin.0")
    conf.add_tile(bottom_left + NORTH + WEST)
    conf.add_tile(bottom_left + SOUTH + EAST * 1).west("ter.0")

    # input
    if with_input is not None:
        conf.add_tile((-1, 0)).south(f"bin.{with_input[0]}").north(
            f"bin.{with_input[0]}"
        )
        conf.add_tile(C(1, -2)).east(f"ter.{with_input[1]}").west(
            f"ter.{with_input[1]}"
        )

    return conf


# Any gate of the form <prefix_top> x <middle_top> y <right_word> going down
def x_y_on_top_gate(with_input=None, right_word="", middle_top="", prefix_top=""):
    conf = mawatam.Configuration(mawatam.CollatzTileset)

    total_top_word = prefix_top + " " + middle_top + " "

    curr_in = 1
    for i in range(len(total_top_word)):
        c = total_top_word[::-1][i]
        if c == " ":
            if with_input is None:
                continue

            c = with_input[curr_in]
            curr_in -= 1
            # conf.add_glue((-1 * i, 0)).north(f"bin.{c}")

        conf.add_glue((-1 * i, 0)).south(f"bin.{c}")

    for i in range(len(right_word)):
        conf.add_glue(C(0, -1 * i) + SOUTH + EAST).west(f"ter.{right_word[i]}")

    return conf


def input_x_y_on_top_gate(in_, right_word, middle_top="", prefix_top=""):
    return x_y_on_top_gate(in_, right_word, middle_top, prefix_top)


def input_x_y_on_top_canonical_gate(gate_name, in_=None):

    nicknames = {
        "OR": "0111",
        "AND": "0001",
        "NOR": "1000",
        "NAND": "1110",
        "XOR": "0110",
        "NXOR": "1001",
    }

    # All 2x1 boolean gates in a '0'*g[1] + x + "0" + y with g[0] as right word
    # and g in:
    canonical_gates = {
        "0011": ("0", 0),
        "0110": ("10", 0),
        "1100": ("110", 0),
        "1001": ("0120", 0),
        "0000": ("0", 1),
        "0001": ("1", 1),
        "1101": ("000201", 1),  # can do better with ('1','01',1)
        "1000": ("1000", 1),
        "1010": ("1001", 1),
        "1110": ("1011", 1),
        "0111": ("00111", 1),
        "1111": ("00121", 1),
        "0100": ("000110", 1),
        "0101": ("012", 2),
        "0010": ("0000210", 2),
        "1011": ("0001112", 2),
    }

    if gate_name in nicknames:
        gate_name = nicknames[gate_name]

    g = canonical_gates[gate_name]

    return x_y_on_top_gate(in_, g[0], "0", "0" * g[1])


def x_y_on_east_gate(with_input, top, east, middle_east):

    conf = mawatam.Configuration(mawatam.CollatzTileset)

    total_east_word = " " + middle_east + " " + east

    curr_in = 0
    for i in range(len(total_east_word)):
        c = total_east_word[i]
        if c == " ":
            if with_input is None:
                continue

            c = with_input[curr_in]
            curr_in += 1

        conf.add_glue((0, -1 * i)).west(f"ter.{c}")

    for i in range(len(top)):
        conf.add_glue(C(-1 * i, 0) + NORTH + WEST).south(f"bin.{top[::-1][i]}")

    return conf


def input_x_y_on_east_canonical_gate(gate_name, in_=None):
    """size_dict, int(frep), frep, top, east, middle_east, score
    1 4 0100 01  0 12
    2 13 1101 10  0 12
    3 3 0011 000  0 16
    4 0 0000 0000  0 20
    5 1 0001 0010  0 20
    6 15 1111 0110  0 20
    7 7 0111 1101  0 20
    8 6 0110 0 0 0 10
    9 9 1001 1 0 0 10
    9 1 0001 00 0 0 15
    10 12 1100 0101 0 0 25
    10 7 0111 00 1 0 15
    11 8 1000 10 00 0 18
    12 14 1110 10 01 0 18
    13 5 0101 0000 000 0 35
    14 10 1010 0011 000 0 35
    15 2 0010 1101 0000 0 40
    16 11 1011 1111 0001 0 40
    """
    nicknames = {
        "OR": "0111",
        "AND": "0001",
        "NOR": "1000",
        "NAND": "1110",
        "XOR": "0110",
        "NXOR": "1001",
    }

    # All 2x1 boolean gates in a '0'*g[1] + x + "0" + y with g[0] as right word
    # and g in:
    canonical_gates = {
        "0000": ("0000", "", "0"),
        "0001": ("00", "0", "0"),
        "0010": ("1101", "0000", "0"),
        "0011": ("000", "", "0"),
        "0100": ("01", "", "0"),
        "0101": ("0000", "000", "0"),
        "0110": ("0", "0", "0"),
        "0111": ("00", "1", "0"),
        "1000": ("10", "00", "0"),
        "1001": ("1", "0", "0"),
        "1010": ("0011", "000", "0"),
        "1011": ("1111", "0001", "0"),
        "1100": ("0101", "0", "0"),
        "1101": ("10", "", "0"),
        "1110": ("10", "01", "0"),
        "1111": ("0110", "", "0"),
    }

    if gate_name in nicknames:
        gate_name = nicknames[gate_name]

    g = canonical_gates[gate_name]

    return x_y_on_east_gate(in_, g[0], g[1], g[2])


def fanout(even=True):
    conf = mawatam.Configuration(mawatam.CollatzTileset)
    conf.add_glue((0, -1)).west(f"ter.{int(even)}")
    conf.add_glue((-1, 1)).south("bin.1")
    conf.add_glue((-2, 1)).south("bin.0")
    conf.add_glue((-2, -1))
    return conf


def not_gate(in_=None):
    conf = mawatam.Configuration(mawatam.CollatzTileset)
    if in_ is not None:
        conf.add_glue((0, 0)).west(f"ter.{in_[0]}")
    conf.add_glue((-1, 1)).south("bin.1")
    conf.add_glue((0, -1)).west("ter.0")
    return conf


def prime_circuit_better(in_=None):
    conf = mawatam.Configuration(mawatam.CollatzTileset)

    if in_ is not None:
        conf.add_glue((1, 0)).west(f"ter.{in_[0]}")
        conf.add_glue((1, -3)).west(f"ter.{in_[1]}")
        conf.add_glue((1, -11)).west(f"ter.{in_[2]}")

    conf.add_glue((0, -1)).north(f"bin.1")
    conf.add_sub_conf(fanout(even=False))  # even true because we want not

    conf.add_sub_conf(bridge_type_2(minimal=True).translate(SOUTH))

    # not x and y
    conf.add_sub_conf(input_x_y_on_east_canonical_gate("AND").translate((-6, -4)))

    # not x to and
    conf.add_glue((-3, -1)).north("bin.1")
    conf.add_glue((-4, -1)).north("bin.1")
    conf.add_glue((-5, -1)).north("bin.1")

    for i in range(1, 5):
        conf.add_glue((-5, -1 * i)).west("ter.0")

    conf.add_glue((-6, 1)).south("bin.1")

    # y to and
    conf.add_glue((-5, -7)).north("bin.1")
    conf.add_glue((-6, -7)).north("bin.1")

    # x and z
    conf.add_sub_conf(input_x_y_on_east_canonical_gate("AND").translate((-6, -10)))

    # x to and
    conf.add_glue((0, -7)).west("ter.0")
    conf.add_glue((0, -8)).west("ter.0")
    conf.add_glue((0, -9)).west("ter.0")
    conf.add_glue((-2, -8))

    for i in range(2, 6):
        conf.add_glue((-1 * i, -10)).north("bin.1")

    conf.add_sub_conf(not_gate().translate((-4, -11)))
    conf.add_sub_conf(not_gate().translate((-5, -9)))
    # z to and
    for i in range(0, 5):
        conf.add_glue((-1 * i, -12)).north("bin.1")
    conf.add_glue((-1, -10))
    conf.add_glue((-6, -13)).north("bin.1")
    conf.add_glue((-5, -10)).south("bin.0")
    conf.add_glue((-6, -8)).south("bin.0")
    conf.add_glue((-9, -6))
    conf.add_sub_conf(input_x_y_on_east_canonical_gate("OR").translate((-10, -11)))
    conf.add_glue((-9, -14)).north("bin.1")
    conf.add_glue((-10, -14)).north("bin.1")
    conf.add_glue((-9, -12))

    conf.add_glue((-9, -8)).north("bin.1")

    conf.add_glue((-10, -6)).south("bin.1")
    for i in range(8, 12):
        conf.add_glue((-9, -1 * i)).west("ter.0")
    return conf


def Collatz_tileset():
    conf = mawatam.Configuration(mawatam.CollatzTileset)
    conf.add_tile((0, 0), "0")
    conf.add_tile((2, 0), "1")
    conf.add_tile((4, 0), "2")
    conf.add_tile((0, -2), "3")
    conf.add_tile((2, -2), "4")
    conf.add_tile((4, -2), "5")
    return conf


def input_bridge_type_1(in_, variant=0):
    variant = int(variant)
    return bridge_type_1(in_, variant)


def input_bridge_type_2(in_, variant=0):
    variant = int(variant)
    return bridge_type_2(in_, variant)


def input_two_bridges_type_2(in_, variant1=0, variant2=0):
    variant1 = int(variant1)
    variant2 = int(variant2)
    return two_bridges_type_2(in_, int(variant1), int(variant2))


def input_two_bridges_type_2_tighter_gap(in_, variant1=0, variant2=0):
    variant1 = int(variant1)
    variant2 = int(variant2)
    return two_bridges_type_2_tighter_gap(in_, int(variant1), int(variant2))


if __name__ == "__main__":
    conf = locals()[sys.argv[1]](*sys.argv[2:])
    print(conf)
back to top