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])
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)
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]
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
# # 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
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): bb = [] for i in tasks: c1 = s[i] <= t c2 = t < s[i] + d[i] bb.append(c1 * c2 * r[i]) facile.constraint(sum(bb) <= b)
n = 4 duration = [30, 10, 15, 15] demand = [3, 1, 3, 2] upper_limit = 160 start_times = [fcl.variable(range(upper_limit)) for i in range(n)] end_times = [fcl.variable(range(2 * upper_limit)) for i in range(n)] end_time = fcl.variable(range(2 * upper_limit)) n_resources = fcl.variable(range(11)) for i in range(n): fcl.constraint(end_times[i] == start_times[i] + duration[i]) fcl.constraint(end_time == fcl.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): bb = [] for i in tasks: c1 = s[i] <= t c2 = t < s[i] + d[i] bb.append(c1 * c2 * r[i]) fcl.constraint(sum(bb) <= b)
# # 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)
[1, 2, 4, 3, 5], [3, 5, 1, 2, 4], [5, 4, 2, 1, 3], [1, 3, 5, 4, 2], [4, 2, 3, 5, 1], ] rank_men = [ [5, 1, 2, 4, 3], [4, 1, 3, 2, 5], [5, 3, 2, 4, 1], [1, 5, 4, 3, 2], [4, 3, 2, 1, 5], ] wife = array([variable(range(n)) for i in range(n)]) husband = array([variable(range(n)) for i in range(n)]) # You are your wife's husband, and conversely for m in range(n): constraint(husband[wife[m]] == m) for w in range(n): constraint(wife[husband[w]] == w) for m in range(n): for w in range(n): # m prefers this woman to his wife c1 = rank_men[m][w] < array(rank_men[m])[wife[m]] # w prefers her husband to this man c2 = array(rank_women[w])[husband[w]] < rank_women[w][m] # alias for c1 => c2