1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.

253 lines
9.9 KiB
MiniZinc

%
% Model for group splitting problem
%
% A group of people want to do activities (Cinema then Restaurant)
% in subgroups where the activities for subgroups are supposed to
% match better members' preferences.
% The aim of our model is to find the best activities and group
% combinations to recommend.
%
% @authors:
%
% Jacopo Mauro <mauro.jacopo@gmail.com>
% Tong Liu <t.liu@unibo.it>
%
include "count.mzn";
include "table.mzn";
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Variables and array definitions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% number of users
set of int: user_ids;
% activities for phase 1
set of int: activity1_ids;
% activities for phase 2
set of int: activity2_ids;
% number of cells
set of int: cell_ids;
% number of groups
set of int: group_ids;
% time domain
set of int: time_slot_ids;
% rating domains
set of int: pub_rating_domain = 0..5;
set of int: user_rating_domain = -2..2;
% global constraint Variables
int: min_group_size; % min cardinality of a subgroup
int: usersn; % number of users
int: max_wait; % wait time btw 2 activities
int: startAfter; % time after which schedule starts
int: eta; % balance user's rating and public rating
array[activity1_ids,1..5] of int: activities1;
array[activity2_ids,1..5] of int: activities2;
array[user_ids,activity1_ids] of user_rating_domain: preferences1;
array[user_ids,activity2_ids] of user_rating_domain: preferences2;
array[activity1_ids] of int: oid1;
array[activity2_ids] of int: oid2;
array[cell_ids,cell_ids] of int: distances;
% Create activities1_new for the calculation inserting index in the first position.
array[activity1_ids,1..6] of int: activities1_data = array2d(activity1_ids,1..6,
[ if i=1 then j else activities1[j,i-1] endif | j in activity1_ids, i in 1..6 ]);
array[activity2_ids,1..6] of int: activities2_data = array2d(activity2_ids,1..6,
[ if i=1 then j else activities2[j,i-1] endif | j in activity2_ids, i in 1..6 ]);
% Configure datalist for table constraint
array[1..max(user_ids)*max(activity1_ids),1..3] of int: preferences1_data = array2d(1..max(user_ids)*max(activity1_ids),1..3,
[ if k=1 then i else if k=2 then j else preferences1[i,j] endif endif | i in user_ids, j in activity1_ids, k in 1..3 ]);
array[1..max(user_ids)*max(activity2_ids),1..3] of int: preferences2_data = array2d(1..max(user_ids)*max(activity2_ids),1..3,
[ if k=1 then i else if k=2 then j else preferences2[i,j] endif endif | i in user_ids, j in activity2_ids, k in 1..3 ]);
array[1..max(cell_ids)*max(cell_ids),1..3] of int: distances_data = array2d(1..max(cell_ids)*max(cell_ids),1..3,
[ if k=1 then i else if k=2 then j else distances[i,j] endif endif | i in cell_ids, j in cell_ids, k in 1..3 ]);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Maps to define a solution
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% map user -> group
array[user_ids] of var group_ids: user_group_map1;
array[user_ids] of var group_ids: user_group_map2;
% map group -> activity
array[group_ids] of var activity1_ids: group_act_map1;
array[group_ids] of var activity2_ids: group_act_map2;
% map user -> activity
array[user_ids] of var activity1_ids: user_act_map1;
array[user_ids] of var activity2_ids: user_act_map2;
% map user -> start of activity
array[user_ids] of var time_slot_ids: user_start_time_map1;
array[user_ids] of var time_slot_ids: user_start_time_map2;
% map user -> duration
array[user_ids] of var time_slot_ids: user_duration_map1;
array[user_ids] of var time_slot_ids: user_duration_map2;
% map user -> begin
array[user_ids] of var time_slot_ids: activity_available_from1;
array[user_ids] of var time_slot_ids: activity_available_from2;
% map user -> end
array[user_ids] of var time_slot_ids: user_end_map1;
array[user_ids] of var time_slot_ids: user_end_map2;
% map user -> cell
array[user_ids] of var cell_ids: user_cell_map1;
array[user_ids] of var cell_ids: user_cell_map2;
% map user -> type
array[user_ids] of var pub_rating_domain: user_pub_rating_map1;
array[user_ids] of var pub_rating_domain: user_pub_rating_map2;
% map user -> weight
array[user_ids] of var user_rating_domain: user_weight_map1;
array[user_ids] of var user_rating_domain: user_weight_map2;
% map user -> distance
array[user_ids] of var time_slot_ids: user_distance_map;
array[int] of var int: check_all = user_group_map1 ++ user_group_map2 ++ group_act_map1 ++ group_act_map2 ++ user_act_map1 ++ user_act_map2 ++ user_start_time_map1 ++ user_start_time_map2 ++ user_duration_map1 ++ user_duration_map2 ++ activity_available_from1 ++ activity_available_from2 ++ user_end_map1 ++ user_end_map2 ++ user_cell_map1 ++ user_cell_map2 ++ user_pub_rating_map1 ++ user_pub_rating_map2 ++ user_weight_map1 ++ user_weight_map2 ++ user_distance_map;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Check that the group members satisfy minCardinality
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
constraint forall(i in group_ids) (
let { var min_group_size..usersn: c} in (
count(user_group_map1,i,c)) );
constraint forall(i in group_ids) (
let { var min_group_size..usersn: c} in (
count(user_group_map2,i,c)) );
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Channel constraints
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
constraint forall(i in user_ids) (
table( [ user_act_map1[i], activity_available_from1[i], user_end_map1[i], user_duration_map1[i], user_cell_map1[i], user_pub_rating_map1[i] ], activities1_data) );
constraint forall(i in user_ids) (
table( [ user_act_map2[i], activity_available_from2[i], user_end_map2[i], user_duration_map2[i], user_cell_map2[i], user_pub_rating_map2[i] ], activities2_data) );
constraint forall(i in user_ids) (
table( [ i, user_act_map1[i], user_weight_map1[i] ], preferences1_data));
constraint forall(i in user_ids) (
table( [ i, user_act_map2[i], user_weight_map2[i] ], preferences2_data));
constraint forall(i in user_ids) (
table( [ user_cell_map1[i], user_cell_map2[i], user_distance_map[i] ], distances_data));
% user's activity is also group's activity
constraint forall(i in user_ids) (
user_act_map1[i] = group_act_map1[user_group_map1[i]] );
constraint forall(i in user_ids) (
user_act_map2[i] = group_act_map2[user_group_map2[i]] );
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% % Symmetry breaking constraints
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% user 1 belongs always to the first group
constraint redundant_constraint(
user_group_map1[min(user_ids)] = min(group_ids) /\
user_group_map2[min(user_ids)] = min(group_ids));
% next user belongs to the group of the previous users or +1
constraint symmetry_breaking_constraint(
forall (i in 1..max(group_ids)-min(group_ids)+1) (
user_group_map1[min(user_ids)+i] in min(group_ids)..min(group_ids)+i /\
user_group_map2[min(user_ids)+i] in min(group_ids)..min(group_ids)+i
));
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% % Activity temporal constraints
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
constraint forall(i in user_ids) (
user_start_time_map1[i] >= activity_available_from1[i] /\
user_start_time_map1[i] <= user_end_map1[i] - user_duration_map1[i]);
constraint forall(i in user_ids) (
user_start_time_map2[i] >= activity_available_from2[i] /\
user_start_time_map2[i] <= user_end_map2[i] - user_duration_map2[i]);
constraint forall(i in user_ids) (
user_start_time_map2[i] >= user_start_time_map1[i] + user_duration_map1[i] +
user_distance_map[i] /\
user_start_time_map2[i] <= user_start_time_map1[i] + user_duration_map1[i] + max_wait );
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Start After Constraint
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
constraint forall(i in user_ids) (
user_start_time_map1[i] >= startAfter);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Compute objective function
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int: objub = eta*card(user_ids)*2 + (10-eta)*card(user_ids)*5 + (10-eta)*card(user_ids)*5 + eta*card(user_ids)*2;
int: objlb=eta*card(user_ids)*(-2)+ (10-eta)*card(user_ids)*0 + (10-eta)*card(user_ids)*0 + eta*card(user_ids)*(-2);
var objlb..objub: objective;
constraint objective = (
eta * sum (user_weight_map1) + (10-eta) * sum (user_pub_rating_map1) + (10-eta) * sum (user_pub_rating_map2) + eta * sum (user_weight_map2) );
solve :: seq_search([
int_search(user_group_map1,first_fail, indomain_min, complete),
int_search(user_group_map2,first_fail, indomain_min, complete),
int_search(user_weight_map1, first_fail, indomain_min, complete),
int_search(user_weight_map2, first_fail, indomain_min, complete),
int_search(user_act_map1, first_fail, indomain_min, complete),
int_search(user_act_map2, first_fail, indomain_min, complete),
int_search(user_start_time_map1, first_fail, indomain_min, complete),
int_search(user_start_time_map2, first_fail, indomain_min, complete),
int_search(user_duration_map1, first_fail, indomain_min, complete),
int_search(user_duration_map2, first_fail, indomain_min, complete),
int_search(user_end_map1, first_fail, indomain_min, complete),
int_search(user_end_map2, first_fail, indomain_min, complete),
])
maximize objective;
output [
"user_group_map1 = \(user_group_map1);\n",
"user_group_map2 = \(user_group_map2);\n",
"user_weight_map1 = \(user_weight_map1);\n",
"user_weight_map2 = \(user_weight_map2);\n",
"user_act_map1 = \(user_act_map1);\n",
"user_act_map2 = \(user_act_map2);\n",
"user_start_time_map1 = \(user_start_time_map1);\n",
"user_start_time_map2 = \(user_start_time_map2);\n",
"user_duration_map1 = \(user_duration_map1);\n",
"user_duration_map2 = \(user_duration_map2);\n",
"user_end_map1 = \(user_end_map1);\n",
"user_end_map2 = \(user_end_map2);\n",
"objective = \(objective);\n"
];