Example #1
0
def golomb(n: int) -> facile.Solution:
    ticks = [facile.variable(range(2**n)) for i in range(n)]

    # First tick at the start of the ruler
    facile.constraint(ticks[0] == 0)

    # Ticks are ordered
    for i in range(n - 1):
        facile.constraint(ticks[i] < ticks[i + 1])

    # All distances
    distances = []
    for i in range(n - 1):
        for j in range(i + 1, n):
            distances.append(facile.variable(ticks[j] - ticks[i]))
    facile.constraint(facile.alldifferent(distances))

    for d in distances:
        facile.constraint(d > 0)

    # Breaking the symmetry
    size = len(distances)
    facile.constraint(distances[size - 1] > distances[0])

    return facile.minimize(ticks,
                           ticks[n - 1],
                           backtrack=True,
                           on_solution=print)
Example #2
0
def golomb(n):

    # On peut majorer la taille de la règle par 2 ** n. En effet, si
    # ticks[i] vaut 2**i alors tous les ticks[i] - ticks[j] = 2**i - 2**j
    # = 2**j * (2**(i-j) - 1) qui sont tous différents.
    # On a donc au moins cette solution.

    ticks = [facile.variable(range(2**n)) for i in range(n)]

    # First tick at the start of the ruler
    facile.constraint(ticks[0] == 0)

    # Ticks are ordered
    for i in range(n - 1):
        facile.constraint(ticks[i] < ticks[i + 1])

    # All distances
    distances = []
    for i in range(n - 1):
        for j in range(i + 1, n):
            distances.append(facile.variable(ticks[j] - ticks[i]))
    facile.constraint(facile.alldifferent(distances))

    for d in distances:
        facile.constraint(d > 0)

    # Breaking the symmetry
    facile.constraint(distances[-1] > distances[0])

    return (facile.minimize(ticks, ticks[n - 1], backtrack=True,
        on_solution=print))
Example #3
0
def tile(sizes, bigsize):
    n = len(sizes)
    xs = [facile.variable(0, bigsize - sizes[i]) for i in range(n)]
    ys = [facile.variable(0, bigsize - sizes[i]) for i in range(n)]

    for i in range(n-1):
        for j in range(i+1, n):
            c_left = xs[j] + sizes[j] <= xs[i] # j on the left of i
            c_right = xs[j] >= xs[i] + sizes[i] # j on the right of i
            c_below = ys[j] + sizes[j] <= ys[i] # etc.
            c_above = ys[j] >= ys[i] + sizes[i]
            facile.constraint(c_left | c_right | c_below | c_above)

    # Redundant capacity constraint
    def full_line(xy):
        for i in range(bigsize):
            # equivalent to (xy[j] >= i - sizes[j] + 1) & (xy[j] <= i)
            intersections = \
                    [xy[j].in_interval(i - sizes[j] + 1, i) for j in range(n)]

            scal_prod = sum([s * k for s, k in zip(sizes, intersections)])
            facile.constraint(scal_prod == bigsize)

    full_line(xs)
    full_line(ys)

    if facile.solve(xs + ys, heuristic=facile.Heuristic.Min_min):
        try:
            import matplotlib.pyplot as plt
            import matplotlib.cm as colormap
            from random import random as rand

            fig = plt.figure()
            ax = fig.gca()

            def fill_square(x, y, s):
                plt.fill([x, x, x+s, x+s], [y, y+s, y+s, y],
                         color=colormap.Pastel1(rand()))

            fill_square(0, 0, bigsize)
            for (x, y, s) in zip(xs, ys, sizes):
                fill_square(x.value(), y.value(), s)

            ax.set_xlim((0, bigsize))
            ax.set_ylim((0, bigsize))
            ax.set_aspect(1)
            ax.set_xticks(range(bigsize + 1))
            ax.set_yticks(range(bigsize + 1))
            fig.set_size_inches(7, 7)
            ax.set_frame_on(False)
            plt.pause(10)

        except Exception as e:
            # if matplotlib fails for an unknown reason
            print (e)
            for (x, y, s) in zip(xs, ys, sizes):
                print (x.value(), y.value(), s)
Example #4
0
def tile(sizes, bigsize):
    n = len(sizes)
    xs = [facile.variable(0, bigsize - sizes[i]) for i in range(n)]
    ys = [facile.variable(0, bigsize - sizes[i]) for i in range(n)]

    for i in range(n - 1):
        for j in range(i + 1, n):
            c_left = xs[j] + sizes[j] <= xs[i]  # j on the left of i
            c_right = xs[j] >= xs[i] + sizes[i]  # j on the right of i
            c_below = ys[j] + sizes[j] <= ys[i]  # etc.
            c_above = ys[j] >= ys[i] + sizes[i]
            facile.constraint(c_left | c_right | c_below | c_above)

    # Redundant capacity constraint
    def full_line(xy):
        for i in range(bigsize):
            # equivalent to (xy[j] >= i - sizes[j] + 1) & (xy[j] <= i)
            intersections = \
                    [xy[j].in_interval(i - sizes[j] + 1, i) for j in range(n)]

            scal_prod = sum([s * k for s, k in zip(sizes, intersections)])
            facile.constraint(scal_prod == bigsize)

    full_line(xs)
    full_line(ys)

    if facile.solve(xs + ys, heuristic=facile.Heuristic.Min_min):
        try:
            import matplotlib.pyplot as plt
            import matplotlib.cm as colormap
            from random import random as rand

            fig = plt.figure()
            ax = fig.gca()

            def fill_square(x, y, s):
                plt.fill([x, x, x + s, x + s], [y, y + s, y + s, y],
                         color=colormap.Pastel1(rand()))

            fill_square(0, 0, bigsize)
            for (x, y, s) in zip(xs, ys, sizes):
                fill_square(x.value(), y.value(), s)

            ax.set_xlim((0, bigsize))
            ax.set_ylim((0, bigsize))
            ax.set_aspect(1)
            ax.set_xticks(range(bigsize + 1))
            ax.set_yticks(range(bigsize + 1))
            fig.set_size_inches(7, 7)
            ax.set_frame_on(False)
            plt.pause(10)

        except Exception as e:
            # if matplotlib fails for an unknown reason
            print(e)
            for (x, y, s) in zip(xs, ys, sizes):
                print(x.value(), y.value(), s)
Example #5
0
def arithmetic(puzzle="SEND+MORE=MONEY", base=10):

    problem = re.split("[\s+=]", puzzle)

    # remove spaces
    problem = list(filter(lambda w: len(w) > 0, problem))

    # letters
    letters = {l: fcl.variable(range(base)) for l in set("".join(problem))}

    # expressions
    expr_pb = [[letters[a] for a in word] for word in problem]
    words = [reduce(lambda a, b: 10 * a + b, word) for word in expr_pb]

    # constraints
    fcl.constraint(fcl.alldifferent(letters.values()))
    fcl.constraint(sum(words[:-1]) == words[-1])

    for word in expr_pb:
        fcl.constraint(word[0] > 0)

    assert fcl.solve(letters.values())

    # print solutions
    for word, numbers in zip(problem, expr_pb):
        strings = [str(n.value()) for n in numbers]
        print(word + " = " + "".join(strings))
Example #6
0
def test_basic_array() -> None:
    var_list = [facile.variable(0, 1) for _ in range(5)]
    array = facile.array(var_list)
    x = facile.variable(range(10))

    msg = "list indices must be integers or slices, not facile.core.Variable"
    with pytest.raises(TypeError, match=msg):
        facile.constraint(var_list[x] == 1)  # type: ignore

    facile.constraint(array[x] == 1)
    facile.constraint(array.sum() == 1)
    facile.constraint(x == 1)

    solution = facile.solve([*array, x])
    assert solution.solved
    assert array.value() == [0, 1, 0, 0, 0]
Example #7
0
def arithmetic(puzzle="SEND+MORE=MONEY", base=10) -> None:

    problem = re.split(r"[\s+=]", puzzle)

    # remove spaces
    problem = list(filter(lambda w: len(w) > 0, problem))

    letters = {
        letter: facile.variable(range(base))
        for letter in set("".join(problem))
    }

    # expressions
    expr_pb = [[letters[a] for a in word] for word in problem]

    def horner(a: Expression, b: Expression):
        return 10 * a + b

    words = [reduce(horner, word) for word in expr_pb]

    # constraints
    facile.constraint(facile.alldifferent(letters.values()))
    facile.constraint(facile.sum(words[:-1]) == words[-1])

    for word in expr_pb:
        facile.constraint(word[0] > 0)

    assert facile.solve(letters.values())

    # print solutions
    for word, numbers in zip(problem, expr_pb):
        strings = [str(n.value()) for n in numbers]
        print(f"{word} = {''.join(strings)}")
Example #8
0
def golomb(n):

    # On peut majorer la taille de la règle par 2 ** n. En effet, si
    # ticks[i] vaut 2**i alors tous les ticks[i] - ticks[j] = 2**i - 2**j
    # = 2**j * (2**(i-j) - 1) qui sont tous différents.
    # On a donc au moins cette solution.

    n2 = 2 ** n
    
    ticks = [facile.variable(0, n2) for i in range(n)]

    # First tick at the start of the ruler
    facile.constraint(ticks[0] == 0)

    # Ticks are ordered
    for i in range(n-1):
        facile.constraint(ticks[i] < ticks[i+1])

    # All distances
    distances = []
    for i in range(n-1):
        for j in range(i + 1, n):
            distances.append(ticks[j] - ticks[i])
    facile.constraint(facile.alldifferent(distances))

    for d in distances:
        facile.constraint(d > 0)

    # Breaking the symmetry
    size = len(distances)
    facile.constraint(distances[size - 1] > distances[0])

    return (facile.minimize(ticks, ticks[n-1])[1])
Example #9
0
def test_nosolution() -> None:
    a, b, c = [facile.variable(0, 1) for _ in range(3)]
    facile.constraint(a != b)
    facile.constraint(b != c)
    facile.constraint(c != a)
    sol = facile.solve([a, b, c])
    assert not sol.solved
Example #10
0
def coins(values, maxval):
    """
    Which coins do you need to give back change for any amount between 0 and
    maxval, using coins from values?
    """

    # How many coin types
    nb_vals = len(values)
    nb_min_coins = [variable(0, maxval/values[i]) for i in range(nb_vals)]

    for val in range(maxval):
        # How many coins per type
        nb_coins = [variable(0, maxval/values[i]) for i in range(nb_vals)]
        mysum = sum([x[0] * x[1] for x in zip(values, nb_coins)])
        constraint(mysum == val)
        for j in range(len(nb_coins)):
            constraint(nb_coins[j] <= nb_min_coins[j])

    return minimize(nb_min_coins, sum(nb_min_coins))
Example #11
0
def n_queens(n: int, *args, **kwargs) -> facile.Solution:
    queens = [facile.variable(range(n)) for i in range(n)]
    diag1 = [queens[i] + i for i in range(n)]
    diag2 = [queens[i] - i for i in range(n)]

    facile.constraint(facile.alldifferent(queens))
    facile.constraint(facile.alldifferent(diag1))
    facile.constraint(facile.alldifferent(diag2))

    return facile.solve(queens, *args, **kwargs)
Example #12
0
def test_invalid_array() -> None:

    n = 5
    var_array = [[facile.variable(0, 1) for _ in range(n)] for _ in range(n)]
    np_array = np.array([var_array[0], var_array[1][1:]])
    msg = (
        "Only numpy arrays of variables, expressions, constraints and integers "
        "are accepted")
    with pytest.raises(TypeError, match=msg):
        _ = facile.array(np_array)
Example #13
0
def coins(values, maxval):
    """
    Which coins do you need to give back change for any amount between 0 and
    maxval, using coins from values?
    """

    # How many coin types
    nb_vals = len(values)
    nb_min_coins = [variable(0, maxval / values[i]) for i in range(nb_vals)]

    for val in range(maxval):
        # How many coins per type
        nb_coins = [variable(0, maxval / values[i]) for i in range(nb_vals)]
        mysum = sum([x[0] * x[1] for x in zip(values, nb_coins)])
        constraint(mysum == val)
        for j in range(len(nb_coins)):
            constraint(nb_coins[j] <= nb_min_coins[j])

    return minimize(nb_min_coins, sum(nb_min_coins))
Example #14
0
def test_magical() -> None:
    array = [facile.variable(range(10)) for i in range(10)]

    for i in range(10):
        sum_ = facile.sum(x == i for x in array)
        facile.constraint(sum_ == array[i])

    solution = facile.solve(array)

    assert solution.solved
    assert solution.solution == [6, 2, 1, 0, 0, 0, 1, 0, 0, 0]
Example #15
0
def test_domains() -> None:
    a = facile.variable(range(320))
    b = facile.variable(range(160))
    c = facile.variable(range(130))
    d = facile.variable(range(130))

    facile.constraint(a + b + c + d == 711)
    assert len(a.domain()) == 26
    assert len(b.domain()) == 26
    assert len(c.domain()) == 26
    assert len(d.domain()) == 26

    facile.constraint(a * b * c * d == 711000000)
    assert len(a.domain()) == 17
    assert len(b.domain()) == 23
    assert len(c.domain()) == 20
    assert len(d.domain()) == 20

    sol = facile.solve([a, b, c, d], backtrack=True)
    assert sol.solved
    assert sol.backtrack == 2
Example #16
0
def test_2d_array() -> None:
    n = 5
    # array = facile.Array.binary((n, n))
    var_array = [[facile.variable(0, 1) for _ in range(n)] for _ in range(n)]
    array = facile.array(np.array(var_array))

    for i in range(n):
        facile.constraint(array[:, i].sum() == 1)
        facile.constraint(array[i, :].sum() == 1)

    x, y = facile.variable(range(n)), facile.variable(range(n))
    facile.constraint(array[x, y] == 1)
    # TODO redundant but necessary to test one of the arguments as a variable
    # facile.constraint(array[:, x].sum() == 1)

    sol = facile.solve([*array])
    assert sol.solved

    sol = facile.solve([*array, x, y])
    assert sol.solved
    *_, x_, y_ = sol.solution
    assert array[x_, y_].value() == 1
Example #17
0
def n_queen(n):
    """Solves the n-queen problem. """
    queens = [variable(0, n-1) for i in range(n)]
    diag1 = [queens[i] + i for i in range(n)]
    diag2 = [queens[i] - i for i in range(n)]

    constraint(alldifferent(queens))
    constraint(alldifferent(diag1))
    constraint(alldifferent(diag2))

    if solve(queens):
        return [x.value() for x in queens]
    else:
        return None
Example #18
0
def n_queen(n):
    """Solves the n-queen problem. """
    queens = [variable(0, n - 1) for i in range(n)]
    diag1 = [queens[i] + i for i in range(n)]
    diag2 = [queens[i] - i for i in range(n)]

    constraint(alldifferent(queens))
    constraint(alldifferent(diag1))
    constraint(alldifferent(diag2))

    if solve(queens):
        return [x.value() for x in queens]
    else:
        return None
def solver(cost, OWA):
    assert OWA in ['utilitarism', 'egalitarism', 'gini']

    n_task = cost.shape[1]
    n_agent = cost.shape[0]

    boolean_variable = np.array([
        facile.array([facile.variable([0, 1]) for i in range(n_task)])
        for j in range(n_agent)
    ])

    # Every task must be complete, by only one agent !
    for j in range(n_task):
        facile.constraint(sum(boolean_variable[:, j]) == 1)

    how_does_it_cost = facile.array([
        np.matmul(cost[i, :], facile.array(boolean_variable[i, :]))
        for i in range(n_agent)
    ])

    if OWA == 'utilitarism':
        weights = np.array([1 for i in range(n_agent)])
    elif OWA == 'egalitarism':
        # as we only have the .sort() method for desutilities, weights must be in reverse order because the 'desutilities' should be sorted in descending order
        weights = np.array([0 for i in range(n_agent)])
        weights[-1] = 1
    else:
        # as we only have the .sort() method for desutilities, weights must be in reverse order because the 'desutilities' should be sorted in descending order
        weights = np.flip(
            np.array([2 * (n_agent - i) + 1 for i in range(1, n_agent + 1)]))

    print(weights)
    to_minimize = np.matmul(weights, how_does_it_cost.sort())

    vars = []

    for i in range(n_agent):
        for j in range(n_task):
            vars.append(boolean_variable[i, j])

    res = np.array(facile.minimize(vars, to_minimize).solution)
    boolean_res = res > 0

    tasks = np.array([list(string.ascii_lowercase)[0:n_task]])

    for i in range(n_agent):
        boolean_res[i:i + n_task]
        print(i + 1, ' does : ',
              tasks[:, boolean_res[n_task * i:n_task * (i + 1)]][0])
Example #20
0
def n_queen(n):
    """Solves the n-queen problem. """

    queens = [variable(range(n)) for i in range(n)]

    # prepare for animation
    def on_bt(nb_bt):
        for i, q in enumerate(queens):
            print(i, q.domain())

    constraint(alldifferent(queens))
    constraint(alldifferent(queens[i] - i for i in range(n)))
    constraint(alldifferent(queens[i] + i for i in range(n)))

    return solve(queens, strategy=queen_strategy, backtrack=True)
Example #21
0
def lazy_n_queens(n: int, *args, **kwargs) -> facile.Solution:
    queens = [facile.variable(range(n)) for i in range(n)]
    diag1 = [queens[i] + i for i in range(n)]
    diag2 = [queens[i] - i for i in range(n)]

    # facile.constraint(facile.alldifferent(queens))
    for i, q1 in enumerate(queens):
        for q2 in queens[i + 1:]:
            facile.constraint(q1 != q2)

    # facile.constraint(facile.alldifferent(diag1))
    for i, q1 in enumerate(diag1):
        for q2 in diag1[i + 1:]:
            facile.constraint(q1 != q2)

    # facile.constraint(facile.alldifferent(diag2))
    for i, q1 in enumerate(diag2):
        for q2 in diag2[i + 1:]:
            facile.constraint(q1 != q2)

    return facile.solve(queens, *args, **kwargs)
Example #22
0
# Charles live in Dreadsbury Mansion, and are the only ones to live there. A
# killer always hates, and is no richer than his victim. Charles hates noone
# that Agatha hates. Agatha hates everybody except the butler. The butler hates
# everyone not richer than Aunt Agatha. The butler hates everyone whom Agatha
# hates. Noone hates everyone. Who killed Agatha?
#
# Originally from F. J. Pelletier:
#   Seventy-five problems for testing automatic theorem provers.
#   Journal of Automated Reasoning, 2: 216, 1986.

from facile import Array, constraint, solve, sum, variable

n = 3
agatha, butler, charles = 0, 1, 2

killer = variable(range(3))
victim = variable(range(3))

hates = Array.binary((n, n))
richer = Array.binary((n, n))

# Agatha, the butler, and Charles live in Dreadsbury Mansion, and
# are the only ones to live there.

# A killer always hates, and is no richer than his victim.
constraint(hates[killer, victim] == 1)
constraint(richer[killer, victim] == 0)

# No one is richer than him-/herself
for i in range(n):
    constraint(richer[i, i] == 0)
Example #23
0
import facile

# Magical sequence!
# The value inside array[i] is equal to the number of i in array

array = [facile.variable(0,10) for i in range(10)]

for i in range(10):
    facile.constraint(sum([x == i for x in array]) == array[i])

if facile.solve(array):
    print ([v.value() for v in array])

Example #24
0
# flake8: noqa: E226

from facile import constraint, solve, sum, variable

# Number of buckets
nb = 3
# Number of steps (let's say we know... :p)
steps = 8
# The capacity of each bucket
capacity = [8, 5, 3]

buckets = [
    [variable(range(capacity[b] + 1)) for b in range(nb)] for i in range(steps)
]

constraint(buckets[0][0] == 8)
constraint(buckets[0][1] == 0)
constraint(buckets[0][2] == 0)

constraint(buckets[steps - 1][0] == 4)
constraint(buckets[steps - 1][1] == 4)
constraint(buckets[steps - 1][2] == 0)

for i in range(steps - 1):
    # we change the contents of two buckets at a time
    sum_buckets = sum([buckets[i][b] != buckets[i + 1][b] for b in range(nb)])
    constraint(sum_buckets == 2)
    # we play with a constant amount of water
    sum_water = sum([buckets[i][b] for b in range(nb)])
    constraint(sum_water == 8)
    for b1 in range(nb):
Example #25
0
# -*- coding: utf-8 -*-
"""
Basic examples of CSP problems:
    - a ≠ b
    - alldifferent(a, b, c) and a + b <= 2c
"""

from facile import variable, constraint, solve, alldifferent

a = variable(0, 1)
b = variable(0, 1)

constraint(a != b)

if solve([a, b]):
    print("Solution found a=%d and b=%d" % (a.value(), b.value()))

a = variable(0, 2)
b = variable(0, 2)
c = variable(0, 2)

constraint(alldifferent([a, b, c]))
constraint(a + b <= 2 * c)
if solve([a, b, c]):
    print("Solution found a=%d, b=%d and c=%d" %
          (a.value(), b.value(), c.value()))
Example #26
0
# Variables
# There is a risk of integer overflow when computing a*b*c*d
# We need small domains...
import facile
a = facile.variable(range(0, 321))
b = facile.variable(range(0, 161))
c = facile.variable(range(0, 131))
d = facile.variable(range(0, 131))

# Constraints

# The problem
facile.constraint(a + b + c + d == 711)

print("Domains after posting the sum constraint")
for x in [a, b, c, d]:
    domain = x.domain()
    print("  {!r} (size {})".format(domain, len(domain)))

facile.constraint(a * b * c * d == 711000000)

print("\nDomains after posting the mul constraint")
for x in [a, b, c, d]:
    domain = x.domain()
    print("  {!r} (size {})".format(domain, len(domain)))

print()

# Resolution
sol = facile.solve([a, b, c, d], backtrack=True)
Example #27
0
from facile import alldifferent, constraint, solve, variable

colors = [variable(range(1, 6)) for i in range(5)]
red, green, yellow, blue, ivory = colors

people = [variable(range(1, 6)) for i in range(5)]
englishman, spaniard, japanese, ukrainian, norwegian = people

names = ["Englishman", "Spaniard", "Japanese", "Ukrainian", "Norwegian"]

animals = [variable(range(1, 6)) for i in range(5)]
dog, snails, fox, zebra, horse = animals

drinks = [variable(range(1, 6)) for i in range(5)]
tea, coffee, water, milk, fruit_juice = drinks

cigarettes = [variable(range(1, 6)) for i in range(5)]
old_gold, kools, chesterfields, lucky_strike, parliaments = cigarettes

constraint(alldifferent(colors))
constraint(alldifferent(people))
constraint(alldifferent(animals))
constraint(alldifferent(drinks))
constraint(alldifferent(cigarettes))

constraint(englishman == red)
constraint(spaniard == dog)
constraint(coffee == green)
constraint(ukrainian == tea)
constraint(green == ivory + 1)
constraint(old_gold == snails)
Example #28
0
def tiles(sizes, bigsize):
    n = len(sizes)
    xs = [variable(range(bigsize - sizes[i] + 1)) for i in range(n)]
    ys = [variable(range(bigsize - sizes[i] + 1)) for i in range(n)]

    for i in range(n - 1):
        for j in range(i + 1, n):
            c_left = xs[j] + sizes[j] <= xs[i]  # j on the left of i
            c_right = xs[j] >= xs[i] + sizes[i]  # j on the right of i
            c_below = ys[j] + sizes[j] <= ys[i]  # etc.
            c_above = ys[j] >= ys[i] + sizes[i]
            constraint(c_left | c_right | c_below | c_above)

    # Redundant capacity constraint
    def full_line(xy):
        for i in range(bigsize):
            # equivalent to (xy[j] >= i - sizes[j] + 1) & (xy[j] <= i)
            intersections = [
                xy[j].in_interval(i - sizes[j] + 1, i) for j in range(n)
            ]

            scal_prod = sum([s * k for s, k in zip(sizes, intersections)])
            constraint(scal_prod == bigsize)

    full_line(xs)
    full_line(ys)

    gx = Goal.forall(xs, assign="assign", strategy="min_min")
    gy = Goal.forall(ys, assign="assign", strategy="min_min")

    # Now the proper resolution process
    solution = solve(gx & gy, backtrack=True)
    print(solution)
    try:
        import matplotlib.pyplot as plt  # type: ignore

        fig, ax = plt.subplots(figsize=(7, 7))

        def fill_square(x, y, s):
            plt.fill(
                [x, x, x + s, x + s],
                [y, y + s, y + s, y],
                color=plt.get_cmap("tab20")(random()),
            )

        fill_square(0, 0, bigsize)
        for (x, y, s) in zip(xs, ys, sizes):
            fill_square(x.value(), y.value(), s)

        ax.set_xlim((0, bigsize))
        ax.set_ylim((0, bigsize))
        ax.set_aspect(1)
        ax.set_xticks(range(bigsize + 1))
        ax.set_yticks(range(bigsize + 1))
        ax.set_frame_on(False)
        plt.pause(10)

    except ImportError as e:
        # if matplotlib fails for an unknown reason
        print(e)
        for (x, y, s) in zip(xs, ys, sizes):
            print(x.value(), y.value(), s)
Example #29
0
# -*- coding: utf-8 -*-

"""
Find four numbers such that their sum is 711 and their product is 711000000
"""

from facile import variable, constraint, solve

a = variable(0, 330)
b = variable(0, 160)
c = variable(0, 140)
d = variable(0, 140)

constraint(a + b + c + d == 711)
constraint(a * b * c * d == 711000000)

if solve([a, b, c, d]):
    [va, vb, vc, vd] = [x.value() for x in [a, b, c, d]]
    print ("Solution found a=%d, b=%d, c=%d, d=%d" % (va, vb, vc, vd))
else:
    print ("No solution found")
Example #30
0
# -*- coding: utf-8 -*-

"""
Basic examples of CSP problems:
    - a ≠ b
    - alldifferent(a, b, c) and a + b <= 2c
"""

from facile import variable, constraint, solve, alldifferent

a = variable(0, 1)
b = variable(0, 1)

constraint(a != b)

if solve([a, b]):
    print ("Solution found a=%d and b=%d" % (a.value(), b.value()))

a = variable(0, 2)
b = variable(0, 2)
c = variable(0, 2)

constraint(alldifferent([a, b, c]))
constraint(a + b <= 2 * c)
if solve([a, b, c]):
    print ("Solution found a=%d, b=%d and c=%d" %
           (a.value(), b.value(), c.value()))
Example #31
0
# -*- coding: utf-8 -*-
"""
Find four numbers such that their sum is 711 and their product is 711000000
"""

from facile import variable, constraint, solve

a = variable(range(0, 330))
b = variable(range(0, 160))
c = variable(range(0, 140))
d = variable(range(0, 140))

constraint(a + b + c + d == 711)
constraint(a * b * c * d == 711000000)

sol = solve([a, b, c, d])
print("Solution found a={}, b={}, c={}, d={}".format(*sol.solution))
Example #32
0
# https://github.com/google/or-tools/.../examples/python/furniture_moving.py
#  Moving furnitures (scheduling) problem in Google CP Solver.
#  Marriott & Stukey: 'Programming with constraints', page  112f
#  The model implements an experimental decomposition of the
#  global constraint cumulative.

import facile

n = 4
duration = [30, 10, 15, 15]
demand = [3, 1, 3, 2]
upper_limit = 160

start_times = [facile.variable(range(upper_limit)) for i in range(n)]
end_times = [facile.variable(range(2 * upper_limit)) for i in range(n)]

end_time = facile.variable(range(2 * upper_limit))
n_resources = facile.variable(range(11))

for i in range(n):
    facile.constraint(end_times[i] == start_times[i] + duration[i])

facile.constraint(end_time == facile.array(end_times).max())


# detail here!
def cumulative(s, d, r, b):
    tasks = [i for i in range(len(s)) if r[i] > 0 and d[i] > 0]
    times_min = min([s[i].domain()[0] for i in tasks])
    times_max = max([s[i].domain()[-1] + max(d) for i in tasks])
    for t in range(times_min, times_max + 1):
import facile

# The list comprehension mechanism is always helpful!
[s, e, n, d, m, o, r, y] = [facile.variable(range(10)) for i in range(8)]

# A shortcut
letters = [s, e, n, d, m, o, r, y]

# Retenues
[c0, c1, c2] = [facile.variable([0, 1]) for i in range(3)]

# Constraints
# facile.constraint(s > 0)
# facile.constraint(m > 0)
facile.constraint(facile.alldifferent(letters))
facile.constraint(d + e == y + 10 * c0)
facile.constraint(c0 + n + r == e + 10 * c1)
facile.constraint(c1 + e + o == n + 10 * c2)
facile.constraint(c2 + s + m == o + 10 * m)

if facile.solve(letters):
    [vs, ve, vn, vd, vm, vo, vr, vy] = [x.value() for x in letters]
    print("Solution found :")
    print
    print("  %d%d%d%d" % (vs, ve, vn, vd))
    print("+ %d%d%d%d" % (vm, vo, vr, ve))
    print("------")
    print(" %d%d%d%d%d" % (vm, vo, vn, ve, vy))
else:
    print("No solution found")
Example #34
0
#
# The formulation is generalized to any number of golfers, groups and weeks.

nb_groups = 5
nb_golfers = 15
size_group = 3

assert (size_group * nb_groups == nb_golfers)

nb_weeks = 5

# An array of nb_weeks * nb_golfers decision variables to choose the group of
# every golfer every week

groups = [ facile.array(
    [facile.variable(0, nb_groups-1) for i in range(nb_golfers)]
    ) for j in range(nb_weeks)]

# For each week, exactly size_group golfers in each group:
for i in range(nb_weeks):

    # [1] Use a Sorting Constraint (redundant with [2])
    s = groups[i].sort()
    for j in range(nb_golfers):
        facile.constraint(s[j] == j//size_group)

    # [2] Use a Global Cardinality Constraint (redundant with [1])
    gcc = groups[i].gcc([(size_group, i) for i in range(nb_groups)])
    facile.constraint(gcc)

# Two golfers do not play in the same group more than once
Example #35
0
def test_overconstrained() -> None:
    a, b, c = [facile.variable(0, 1) for _ in range(3)]
    with pytest.raises(ValueError, match="The problem is overconstrained"):
        facile.constraint(facile.alldifferent([a, b, c]))
Example #36
0
import facile

# Magical sequence!
# The value inside array[i] is equal to the number of i in array

array = [facile.variable(0, 10) for i in range(10)]

for i in range(10):
    facile.constraint(sum([x == i for x in array]) == array[i])

if facile.solve(array):
    print([v.value() for v in array])
Example #37
0
def test_solution() -> None:
    a = facile.variable([0, 1])
    b = facile.variable([0, 1])
    facile.constraint(a != b)
    sol = facile.solve([a, b])
    assert sol.solved
Example #38
0
import facile
import functools

# The list comprehension mechanism is always helpful!
[s, e, n, d, m, o, r, y] = [facile.variable(range(10)) for i in range(8)]

# A shortcut
letters = [s, e, n, d, m, o, r, y]

# Constraints
facile.constraint(s > 0)
facile.constraint(m > 0)
facile.constraint(facile.alldifferent(letters))

send = functools.reduce(lambda x, y: 10 * x + y, [s, e, n, d])
more = functools.reduce(lambda x, y: 10 * x + y, [m, o, r, e])
money = functools.reduce(lambda x, y: 10 * x + y, [m, o, n, e, y])

facile.constraint(send + more == money)

if facile.solve(letters):
    [vs, ve, vn, vd, vm, vo, vr, vy] = [x.value() for x in letters]
    print("Solution found :")
    print
    print("  %d%d%d%d" % (vs, ve, vn, vd))
    print("+ %d%d%d%d" % (vm, vo, vr, ve))
    print("------")
    print(" %d%d%d%d%d" % (vm, vo, vn, ve, vy))
else:
    print("No solution found")
Example #39
0
from facile import variable, constraint, solve

# Number of buckets
nb = 3
# Number of steps (let's say we know... :p)
steps = 8
# The capacity of each bucket
capacity = [8, 5, 3]

buckets = [ [variable(0, capacity[b]) for b in range(nb)] for i in range(steps)]

constraint(buckets[0][0] == 8)
constraint(buckets[0][1] == 0)
constraint(buckets[0][2] == 0)

constraint(buckets[steps - 1][0] == 4)
constraint(buckets[steps - 1][1] == 4)
constraint(buckets[steps - 1][2] == 0)

for i in range(steps - 1):
    # we change the contents of two buckets at a time
    constraint( sum([buckets[i][b] != buckets[i+1][b] for b in range(nb)]) == 2)
    # we play with a constant amount of water
    constraint(sum([buckets[i][b] for b in range(nb)]) == 8)
    for b1 in range(nb):
        for b2 in range(b1):
            constraint(
                # either the content of the bucket does not change
                (buckets[i][b1] == buckets[i+1][b1]) |
                (buckets[i][b2] == buckets[i+1][b2]) |
                # or the bucket ends up empty or full