diff --git a/src/constraints/conversion_2.mzn b/src/constraints/conversion_2.mzn index 41a329b1769a99a1df40e21330db40c61d406e07..feca09a5b641637a7e8d436af1d814d2f1ff93af 100644 --- a/src/constraints/conversion_2.mzn +++ b/src/constraints/conversion_2.mzn @@ -81,7 +81,7 @@ array [0..n_moving_obj, 0..n_operations] of bool : moving_op_of_obj; ); array [1..n_locations] of 0..n_locations : twin_location = [i - 1 + (i mod 2) * 2 | i in 1..n_locations]; -% TODO нумерация с нуля +array [1..n_locations] of 1..n_locations : main_location = [i - ((i + 1) mod 2)| i in 1..n_locations]; array [0..n_operations] of 0..n_locations : operations_destination; % Локация в которой окажется объект после завершения операции. @@ -212,9 +212,10 @@ array [0..n_operations] of 0..n_locations : operations_destination; % Локац ); % Участие объекта в операциях грузообработки. - array [0..n_moving_obj, 1..n_intervals] of var bool : is_involved_in_cargo_op; + array [0..n_moving_obj, 0..n_intervals] of var bool : is_involved_in_cargo_op; constraint forall (t in 1..n_intervals) (is_involved_in_cargo_op[0, t] = false); % Фиктивный объект. + constraint forall (obj in 1..n_moving_obj) (is_involved_in_cargo_op[obj, 0] = false); % Операции погрузки, которые используют этот объект в качестве главного или в качестве бункеровщика. array [0..n_moving_obj] of set of 0..n_operations : related_cargo_op; @@ -222,11 +223,9 @@ array [0..n_operations] of 0..n_locations : operations_destination; % Локац % Операции отшвартовки, которые используют объект в качестве главного. array [0..n_moving_obj] of set of 1..n_operations : related_unmooring_op; - % TODO узнать про where % Определение is_involved_in_cargo_op. constraint forall (obj in 1..n_moving_obj, t in 1..n_intervals) ( is_involved_in_cargo_op[obj, t] = ( - % (exists (op in 1..n_operations where (not is_moving_operation[op]) /\ (main_obj_of_operation[op] = obj)) ( (exists (op in related_cargo_op[obj]) (op_status[op, t])) \/ ((participation_as_resource[obj, t] != 0) /\ (not is_moving_operation[participation_as_resource[obj, t]])) @@ -351,6 +350,8 @@ array [0..n_operations] of 0..n_locations : operations_destination; % Локац array [1..n_loading_op] of 1..n_operations : loading_op_n; % Номера среди общего списка операций. array [1..n_all_storage_sections] of set of 1..n_loading_op : involved_operations; + array [1..n_moving_obj] of set of 1..n_all_storage_sections : sections_of_moving_obj; + array [1..n_moving_obj] of bool : is_sections_of_moving_obj_empty; constraint forall (storage in 1..n_all_storage_sections, t in 1..(n_intervals + 1)) ( storage_used_volume[storage, t] = ( @@ -509,6 +510,51 @@ array [0..n_operations] of 0..n_locations : operations_destination; % Локац ) -> is_attended_useful_op_in_cur_loc[obj, t - 1] ); +% Оптимизация - если объект не может совершить в данной локации операции без выхода за границы хранилища, а так же если он +% может уйти только своим ходом, то он либо уходит немедленно, либо остаётся на месте навсегда. + + array [1..n_all_storage_sections, 1..n_locations] of int : min_positive_cargo_val; % Объём максимальной положительной операции, которая может быть произведена с этой секцией в этом хранилище. Если в локации таковой нет, то это значение объёма хранилища + 1. + array [1..n_all_storage_sections, 1..n_locations] of int : max_negative_cargo_val; + + array [1..n_moving_obj, 1..n_locations] of bool : can_obj_leave_loc_only_alone; + array [1..n_moving_obj, 1..n_locations, 1..n_intervals] of bool : is_fixed_op_planned_in_future; + + array [0..n_moving_obj, 0..(n_intervals + 1)] of var bool : can_obj_theoretically_use_cargo_op; + % Определение can_obj_theoretically_use_cargo_op. + constraint forall (obj in 1..n_moving_obj, t in 1..(n_intervals + 1)) ( + can_obj_theoretically_use_cargo_op[obj, t] = ( + is_sections_of_moving_obj_empty[obj] + \/ + exists (section in sections_of_moving_obj[obj]) ( + (storage_used_volume[section, t - 1] + min_positive_cargo_val[section, main_location[m_obj_loc[obj, t]]] <= max_storage_vol[section]) + \/ (storage_used_volume[section, t - 1] + max_negative_cargo_val[section, main_location[m_obj_loc[obj, t]]] >= 0) + ) + ) + ); + constraint forall (t in 0..(n_intervals + 1)) (can_obj_theoretically_use_cargo_op[0, t] = false); + constraint forall (obj in 1..n_moving_obj) (can_obj_theoretically_use_cargo_op[obj, 0] = false); + + % Остановился ли объект окончательно, начиная с данного интервала. + array [0..n_moving_obj, 0..(n_intervals + 1)] of var bool : is_obj_totally_terminated; + constraint forall (obj in 1..n_moving_obj) (is_obj_totally_terminated[obj, n_intervals + 1] = true); + constraint forall (t in 0..(n_intervals + 1)) (is_obj_totally_terminated[0, t] = true); + + constraint forall (obj in 1..n_moving_obj, t in 0..n_intervals) ( + is_obj_totally_terminated[obj, t] = ( + (current_moving_operation[obj, t] = 0) + /\ (not is_involved_in_cargo_op[obj, t]) + /\ (is_obj_totally_terminated[obj, t + 1]) + ) + ); + + % Само ограничение. + constraint forall (obj in 1..n_moving_obj, t in 1..n_intervals) ( + ( (can_obj_leave_loc_only_alone[obj, m_obj_loc[obj, t]]) + /\ (not can_obj_theoretically_use_cargo_op[obj, t]) + /\ (not is_fixed_op_planned_in_future[obj, m_obj_loc[obj, t], t]) + ) -> + (is_obj_totally_terminated[obj, t] \/ (current_moving_operation[obj, t] != 0)) + ); % Критерий оптимизации array [1..(n_intervals + 1)] of var bool : is_not_terminated; @@ -541,16 +587,25 @@ output [show(sum(is_not_terminated)), "\n", "is_involved_in_cargo_op = {", show(n_intervals), "} ", show(is_involved_in_cargo_op), "\n\n", "m_obj_loc = ", show(m_obj_loc), "\n\n", - +/* "prev_m_obj_loc = {", show(n_intervals + 2), "} ", show(prev_m_obj_loc), "\n\n", "is_obj_involved_in_useful_operation = {", show(n_intervals + 2), "} ", show(is_obj_involved_in_useful_operation), "\n\n", "is_attended_useful_op_in_cur_loc = {", show(n_intervals + 2), "} ", show(is_attended_useful_op_in_cur_loc), "\n\n", +*/ + + "can_obj_theoretically_use_cargo_op = {", show(n_intervals + 2), "} ", show(can_obj_theoretically_use_cargo_op), "\n\n", + "is_obj_totally_terminated = {", show(n_intervals + 2), "} ", show(is_obj_totally_terminated), "\n\n", + "main_location = {", show(n_locations), "} ", show(main_location), "\n\n", + + "is_fixed_op_planned_in_future = {", show(n_locations), " ", show(n_intervals), "} ", show(is_fixed_op_planned_in_future), "\n\n", + "can_obj_leave_loc_only_alone {", show(n_locations), "} = ", show(can_obj_leave_loc_only_alone), "\n\n", /* "is_op_possible {", show(n_intervals), "} = ", show(is_op_possible), "\n\n", "debug_1 {", show(n_intervals), "} = ", show(debug_1), "\n\n", "is_enough_free_resources {", show(n_intervals), "} = ", show(is_enough_free_resources), "\n\n", "operation_of_counter {", show(n_resources_counters), "} = ", show(operation_of_counter), "\n\n", + "possible_resources_counter {", show(n_intervals), "} = ", show(possible_resources_counter), "\n\n", */ ""]; diff --git a/src/inport/ConversionUtil.java b/src/inport/ConversionUtil.java index 6ad99238bc107f5ddba245be8800c9e0b7b5a182..7619e95313c5f41ccd1840951d931261fff03deb 100644 --- a/src/inport/ConversionUtil.java +++ b/src/inport/ConversionUtil.java @@ -116,6 +116,26 @@ public class ConversionUtil { writer.write("]);\n"); } + static private void write3dArray(FileWriter writer, + String name, + ArrayList>> array, + Function toString) throws IOException { + int dim1 = array.size(); + int dim2 = (dim1 > 0) ? array.get(0).size() : 0; + int dim3 = (dim2 > 0) ? array.get(0).get(0).size() : 0; + + writer.write(name + " = array3d(1.." + dim1 + ", 1.." + dim2 + ", 1.." + dim3 + ", "); + ArrayList values = new ArrayList<>(); + for (ArrayList> a2 : array) { + for (ArrayList a1 : a2) { + for (T val : a1) { + values.add(toString.apply(val)); + } + } + } + writer.write("[" + String.join(", ", values) + "]);\n"); + } + static private void writeArrayOfSetAs2DArray(FileWriter writer, String name, ArrayList> operations) throws IOException { @@ -896,6 +916,21 @@ public class ConversionUtil { bunker_of_cargo_op.add(0); } } + + ArrayList> sections_of_moving_obj = arrayOfIntegerArrays(movingObjects.size()); + for (int i = 0; i < movingObjects.size(); i++) { + if (movingObjects.get(i) instanceof TransportShip) { + TransportShip ship = ((TransportShip) movingObjects.get(i)); + for (Pair p : ship.getStorageSections()) { + sections_of_moving_obj.get(i).add(sectionIdToN(ship, p.getKey()) + 1); + } + } + } + ArrayList is_sections_of_moving_obj_empty = new ArrayList<>(); + for (ArrayList sections : sections_of_moving_obj) { + is_sections_of_moving_obj_empty.add(sections.isEmpty()); + } + writer.write("n_loading_op = " + loadingOpDelta.size() + ";\n"); writeArray(writer, "involved_operations", involvedOperations, ConversionUtil::arrayToStringAsSet); @@ -913,6 +948,9 @@ public class ConversionUtil { writeArray(writer, "operations_cargo_t", operations_cargo_t, Optional.of(1)); writeArray(writer, "bunker_of_cargo_op", bunker_of_cargo_op, Optional.of(0)); + writeArray(writer, "sections_of_moving_obj", sections_of_moving_obj, ConversionUtil::arrayToStringAsSet); + writeArray(writer, "is_sections_of_moving_obj_empty", is_sections_of_moving_obj_empty); + writer.write("\n"); } @@ -1319,6 +1357,17 @@ public class ConversionUtil { } } } + { // Взаимоисключение всех возможных пар операций перемещения с одним деятелем и пунктом отправления. + if ((operationTemplates.get(i) instanceof MovingTemplate) && + (operationTemplates.get(j) instanceof MovingTemplate)) { + MovingTemplate op1 = (MovingTemplate) operationTemplates.get(i); + MovingTemplate op2 = (MovingTemplate) operationTemplates.get(j); + + if ((op1.getMover() == op2.getMover()) && (op1.getStartLocation() == op2.getStartLocation())) { + conflictingOperationsG.get(i).add(j + 1); + } + } + } } } writeArray(writer, "conflicting_operations", conflictingOperationsG, ConversionUtil::setToString, @@ -1358,6 +1407,8 @@ public class ConversionUtil { ArrayList fixedOpEnd = new ArrayList<>(); ArrayList fixedOpIntensity = new ArrayList<>(); + ArrayList>> is_fixed_op_planned_in_future = new ArrayList<>(); + class OperationData implements Comparable { private int opId, executorId; private OptionalInt bunkerId; @@ -1404,6 +1455,16 @@ public class ConversionUtil { } } + for (int i = 0; i < movingObjects.size(); i++) { + is_fixed_op_planned_in_future.add(new ArrayList<>()); + for (int j = 0; j < locationNumberById.size(); j++) { + is_fixed_op_planned_in_future.get(i).add(new ArrayList<>()); + for (int k = 0; k < n_intervals; k++) { + is_fixed_op_planned_in_future.get(i).get(j).add(false); + } + } + } + for (Operation op : task.getFixedOperations()) { if (op.getFixation()) { OptionalInt bunkerId = op.getBunker().map(b -> OptionalInt.of(b.getId())).orElse(OptionalInt.empty()); @@ -1427,6 +1488,12 @@ public class ConversionUtil { } } + int loc = getLocNById(op.getTemplate().getStartLocation().getId(), false); + int executor = mObjToN(op.getExecutor()); + for (int i = 0; i < end; i++) { + is_fixed_op_planned_in_future.get(executor).get(loc).set(i, true); + } + fixedOp.add(opNo + 1); TreeSet resources = new TreeSet<>(); for (MovingObject obj : op.getResources()) { @@ -1447,6 +1514,8 @@ public class ConversionUtil { writeArray(writer, "fixed_op_intensity", fixedOpIntensity); locWrite2DArray(writer, "is_fixed", getIsFixedArray(), Objects::toString, true); locWrite2DArray(writer, "is_obj_involved_in_fixed_op", is_obj_involved_in_fixed_op, Objects::toString, true); + + write3dArray(writer, "is_fixed_op_planned_in_future", is_fixed_op_planned_in_future, Objects::toString); } void defDataForCurMovingOp() throws IOException { @@ -1548,6 +1617,62 @@ public class ConversionUtil { writeArray(writer, "bunker_start_loc", bunkerStartLoc, (Integer val) -> val + 1, Optional.of(-1)); } + void dataForOptimization3() throws IOException { + ArrayList> min_positive_cargo_val = arrayOfIntegerArrays(sectionNById.size()); + ArrayList> max_negative_cargo_val = arrayOfIntegerArrays(sectionNById.size()); + ArrayList> can_obj_leave_loc_only_alone = new ArrayList<>(); + + for (int i = 0; i < sectionNById.size(); i++) { + for (int j = 0; j < locationNumberById.size(); j++) { + min_positive_cargo_val.get(i).add(maxStorageVol.get(i) + 1); + max_negative_cargo_val.get(i).add(-maxStorageVol.get(i) - 1); + } + } + for (int i = 0; i < movingObjects.size(); i++) { + can_obj_leave_loc_only_alone.add(new ArrayList<>()); + for (int j = 0; j < locationNumberById.size(); j++) { + can_obj_leave_loc_only_alone.get(i).add((j + 1) % 2 == 1); + } + } + for (OperationTemplate t : operationTemplates) { + if (t instanceof LoadingTemplate) { + LoadingTemplate op = (LoadingTemplate) t; + int val = (int) op.getIntensity(); + int loc = getLocNById(op.getStartLocation().getId(), false) ; + + ArrayList> sections = new ArrayList<>(); + + sections.add(new Pair<>(sectionIdToN(op.getLoader(), op.getCargo()), val)); + if (op.getBunker().isPresent()) { + sections.add(new Pair<>(sectionIdToN(op.getBunker().get(), op.getCargo()), -val)); + } else { + sections.add(new Pair<>(sectionIdToN(op.getStorage(), op.getCargo()), -val)); + } + for (Pair p : sections) { + if (p.getValue() > 0) { + min_positive_cargo_val.get(p.getKey()).set(loc, Math.min(p.getValue(), min_positive_cargo_val.get(p.getKey()).get(loc))); + } else { + max_negative_cargo_val.get(p.getKey()).set(loc, Math.max(p.getValue(), max_negative_cargo_val.get(p.getKey()).get(loc))); + } + } + } else if (t instanceof MovingTemplate) { + int loc = getLocNById(t.getStartLocation().getId(), false); + int obj = mObjToN(getExecutor(t)); + + if (! ((MovingTemplate) t).getResourcesTypes().isEmpty()) { + can_obj_leave_loc_only_alone.get(obj).set(loc, false); + } + } + } + + + + write2DArrayOfInt(writer, "min_positive_cargo_val", min_positive_cargo_val, false); + write2DArrayOfInt(writer, "max_negative_cargo_val", max_negative_cargo_val, false); + locWrite2DArray(writer, "can_obj_leave_loc_only_alone", can_obj_leave_loc_only_alone, Objects::toString); + writer.write("\n"); + } + /* С типизацией. */ void portToMiniZinc_2() throws IOException { if (!task.isTypified()) { @@ -1588,6 +1713,7 @@ public class ConversionUtil { boundaryStorageStates(); cargoFlows(); cargoOperations(); + dataForOptimization3(); fixedOperations(); } finally {