A set of benchmarks to compare a new prototype MiniZinc implementation
1%
2% Model for group splitting problem
3%
4% A group of people want to do activities (Cinema then Restaurant)
5% in subgroups where the activities for subgroups are supposed to
6% match better members' preferences.
7% The aim of our model is to find the best activities and group
8% combinations to recommend.
9%
10% @authors:
11%
12% Jacopo Mauro <mauro.jacopo@gmail.com>
13% Tong Liu <t.liu@unibo.it>
14%
15
16include "count.mzn";
17include "table.mzn";
18
19%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
20% Variables and array definitions
21%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22
23% number of users
24set of int: user_ids;
25
26% activities for phase 1
27set of int: activity1_ids;
28
29% activities for phase 2
30set of int: activity2_ids;
31
32% number of cells
33set of int: cell_ids;
34
35% number of groups
36set of int: group_ids;
37
38% time domain
39set of int: time_slot_ids;
40
41% rating domains
42set of int: pub_rating_domain = 0..5;
43set of int: user_rating_domain = -2..2;
44
45% global constraint Variables
46int: min_group_size; % min cardinality of a subgroup
47int: usersn; % number of users
48int: max_wait; % wait time btw 2 activities
49int: startAfter; % time after which schedule starts
50int: eta; % balance user's rating and public rating
51
52array[activity1_ids,1..5] of int: activities1;
53array[activity2_ids,1..5] of int: activities2;
54
55array[user_ids,activity1_ids] of user_rating_domain: preferences1;
56array[user_ids,activity2_ids] of user_rating_domain: preferences2;
57
58array[activity1_ids] of int: oid1;
59array[activity2_ids] of int: oid2;
60
61array[cell_ids,cell_ids] of int: distances;
62
63% Create activities1_new for the calculation inserting index in the first position.
64array[activity1_ids,1..6] of int: activities1_data = array2d(activity1_ids,1..6,
65 [ if i=1 then j else activities1[j,i-1] endif | j in activity1_ids, i in 1..6 ]);
66array[activity2_ids,1..6] of int: activities2_data = array2d(activity2_ids,1..6,
67 [ if i=1 then j else activities2[j,i-1] endif | j in activity2_ids, i in 1..6 ]);
68
69% Configure datalist for table constraint
70array[1..max(user_ids)*max(activity1_ids),1..3] of int: preferences1_data = array2d(1..max(user_ids)*max(activity1_ids),1..3,
71 [ 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 ]);
72array[1..max(user_ids)*max(activity2_ids),1..3] of int: preferences2_data = array2d(1..max(user_ids)*max(activity2_ids),1..3,
73 [ 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 ]);
74array[1..max(cell_ids)*max(cell_ids),1..3] of int: distances_data = array2d(1..max(cell_ids)*max(cell_ids),1..3,
75 [ 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 ]);
76
77
78
79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80% Maps to define a solution
81%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82
83% map user -> group
84array[user_ids] of var group_ids: user_group_map1;
85array[user_ids] of var group_ids: user_group_map2;
86
87% map group -> activity
88array[group_ids] of var activity1_ids: group_act_map1;
89array[group_ids] of var activity2_ids: group_act_map2;
90
91% map user -> activity
92array[user_ids] of var activity1_ids: user_act_map1;
93array[user_ids] of var activity2_ids: user_act_map2;
94
95% map user -> start of activity
96array[user_ids] of var time_slot_ids: user_start_time_map1;
97array[user_ids] of var time_slot_ids: user_start_time_map2;
98
99% map user -> duration
100array[user_ids] of var time_slot_ids: user_duration_map1;
101array[user_ids] of var time_slot_ids: user_duration_map2;
102
103% map user -> begin
104array[user_ids] of var time_slot_ids: activity_available_from1;
105array[user_ids] of var time_slot_ids: activity_available_from2;
106
107% map user -> end
108array[user_ids] of var time_slot_ids: user_end_map1;
109array[user_ids] of var time_slot_ids: user_end_map2;
110
111% map user -> cell
112array[user_ids] of var cell_ids: user_cell_map1;
113array[user_ids] of var cell_ids: user_cell_map2;
114
115% map user -> type
116array[user_ids] of var pub_rating_domain: user_pub_rating_map1;
117array[user_ids] of var pub_rating_domain: user_pub_rating_map2;
118
119% map user -> weight
120array[user_ids] of var user_rating_domain: user_weight_map1;
121array[user_ids] of var user_rating_domain: user_weight_map2;
122
123% map user -> distance
124array[user_ids] of var time_slot_ids: user_distance_map;
125
126array[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;
127
128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129% Check that the group members satisfy minCardinality
130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
131
132constraint forall(i in group_ids) (
133 let { var min_group_size..usersn: c} in (
134 count(user_group_map1,i,c)) );
135
136constraint forall(i in group_ids) (
137 let { var min_group_size..usersn: c} in (
138 count(user_group_map2,i,c)) );
139
140
141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142% Channel constraints
143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144
145constraint forall(i in user_ids) (
146 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) );
147
148constraint forall(i in user_ids) (
149 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) );
150
151constraint forall(i in user_ids) (
152 table( [ i, user_act_map1[i], user_weight_map1[i] ], preferences1_data));
153
154constraint forall(i in user_ids) (
155 table( [ i, user_act_map2[i], user_weight_map2[i] ], preferences2_data));
156
157constraint forall(i in user_ids) (
158 table( [ user_cell_map1[i], user_cell_map2[i], user_distance_map[i] ], distances_data));
159
160% user's activity is also group's activity
161
162constraint forall(i in user_ids) (
163 user_act_map1[i] = group_act_map1[user_group_map1[i]] );
164
165constraint forall(i in user_ids) (
166 user_act_map2[i] = group_act_map2[user_group_map2[i]] );
167
168
169% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
170% % Symmetry breaking constraints
171% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172
173% user 1 belongs always to the first group
174constraint redundant_constraint(
175 user_group_map1[min(user_ids)] = min(group_ids) /\
176 user_group_map2[min(user_ids)] = min(group_ids));
177
178% next user belongs to the group of the previous users or +1
179constraint symmetry_breaking_constraint(
180 forall (i in 1..max(group_ids)-min(group_ids)+1) (
181 user_group_map1[min(user_ids)+i] in min(group_ids)..min(group_ids)+i /\
182 user_group_map2[min(user_ids)+i] in min(group_ids)..min(group_ids)+i
183));
184
185% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186% % Activity temporal constraints
187% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188
189constraint forall(i in user_ids) (
190 user_start_time_map1[i] >= activity_available_from1[i] /\
191 user_start_time_map1[i] <= user_end_map1[i] - user_duration_map1[i]);
192
193constraint forall(i in user_ids) (
194 user_start_time_map2[i] >= activity_available_from2[i] /\
195 user_start_time_map2[i] <= user_end_map2[i] - user_duration_map2[i]);
196
197constraint forall(i in user_ids) (
198 user_start_time_map2[i] >= user_start_time_map1[i] + user_duration_map1[i] +
199 user_distance_map[i] /\
200 user_start_time_map2[i] <= user_start_time_map1[i] + user_duration_map1[i] + max_wait );
201
202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
203% Start After Constraint
204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
205
206constraint forall(i in user_ids) (
207 user_start_time_map1[i] >= startAfter);
208
209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210% Compute objective function
211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212
213int: objub = eta*card(user_ids)*2 + (10-eta)*card(user_ids)*5 + (10-eta)*card(user_ids)*5 + eta*card(user_ids)*2;
214int: objlb=eta*card(user_ids)*(-2)+ (10-eta)*card(user_ids)*0 + (10-eta)*card(user_ids)*0 + eta*card(user_ids)*(-2);
215var objlb..objub: objective;
216
217constraint objective = (
218 eta * sum (user_weight_map1) + (10-eta) * sum (user_pub_rating_map1) + (10-eta) * sum (user_pub_rating_map2) + eta * sum (user_weight_map2) );
219
220solve :: seq_search([
221 int_search(user_group_map1,first_fail, indomain_min, complete),
222 int_search(user_group_map2,first_fail, indomain_min, complete),
223 int_search(user_weight_map1, first_fail, indomain_min, complete),
224 int_search(user_weight_map2, first_fail, indomain_min, complete),
225 int_search(user_act_map1, first_fail, indomain_min, complete),
226 int_search(user_act_map2, first_fail, indomain_min, complete),
227 int_search(user_start_time_map1, first_fail, indomain_min, complete),
228 int_search(user_start_time_map2, first_fail, indomain_min, complete),
229 int_search(user_duration_map1, first_fail, indomain_min, complete),
230 int_search(user_duration_map2, first_fail, indomain_min, complete),
231 int_search(user_end_map1, first_fail, indomain_min, complete),
232 int_search(user_end_map2, first_fail, indomain_min, complete),
233 ])
234 maximize objective;
235
236
237output [
238 "user_group_map1 = \(user_group_map1);\n",
239 "user_group_map2 = \(user_group_map2);\n",
240 "user_weight_map1 = \(user_weight_map1);\n",
241 "user_weight_map2 = \(user_weight_map2);\n",
242 "user_act_map1 = \(user_act_map1);\n",
243 "user_act_map2 = \(user_act_map2);\n",
244 "user_start_time_map1 = \(user_start_time_map1);\n",
245 "user_start_time_map2 = \(user_start_time_map2);\n",
246 "user_duration_map1 = \(user_duration_map1);\n",
247 "user_duration_map2 = \(user_duration_map2);\n",
248 "user_end_map1 = \(user_end_map1);\n",
249 "user_end_map2 = \(user_end_map2);\n",
250 "objective = \(objective);\n"
251];
252