diff --git a/CP/optimization/100potions.dzn b/CP/optimization/100potions.dzn index 1c081e0..6eb9c27 100644 --- a/CP/optimization/100potions.dzn +++ b/CP/optimization/100potions.dzn @@ -1,10 +1,15 @@ -n = 100; %Water Number -w = 8; %Window Size -p = 140; %Leaves required in each Window -capacity= 380; %Total Energy we have for using holy Water -m= 4; %Mode numbers +n = 100; % number of potions, i.e. the number of segments we will grow on the tree +m = 4; % maximum number of segments emerging from a node (depends on the potion applied, see "nutrient") +w = 8; % window size, a segment count (evidently w <= n) +p = 140; % min number of leaves required in each window +capacity = 380; % total capacity of nutrients of a path through the tree -nutrient = +% For each potion (row) applied to a tree node, the nutrients +% required by each segment growing from that tree node (col). +% 381 is "beyond the capacity of nutrients" so essentially a +% stand-in for "infinity", that is, "that segment does not exist". + +nutrient = [|6,5,381,381 |4,1,5,381 |5,6,3,381 @@ -107,7 +112,11 @@ nutrient = |4,381,381,381 |]; -leave = +% For each potion (row) applied to a tree node, the number of +% leaves generated by each segment growing from that tree node (col). +% For unavailable segments, the number of leaves is 0. + +leave = [|68,36,0,0 |18,48,32,0 |83,90,18,0 @@ -208,4 +217,4 @@ leave = |93,35,0,0 |6,3,51,0 |19,0,0,0 -|]; \ No newline at end of file +|]; diff --git a/CP/optimization/4potions.dzn b/CP/optimization/4potions.dzn index 3aefa40..8c5ec22 100644 --- a/CP/optimization/4potions.dzn +++ b/CP/optimization/4potions.dzn @@ -1,19 +1,28 @@ -n = 4; -m = 4; -w = 2; -p = 8; -capacity= 19; +n = 4; % number of potions, i.e. the number of segments we will grow on the tree +m = 4; % maximum number of segments emerging from a node (depends on the potion applied, see "nutrient") +w = 2; % window size, a segment count (evidently w <= n) +p = 8; % min number of leaves required in each window +capacity = 19; % total capacity of nutrients of a path through the tree -nutrient = +% For each potion (row) applied to a tree node, the nutrients +% required by each segment growing from that tree node (col). +% 20 is "beyond the capacity of nutrients" so essentially a +% stand-in for "infinity", that is, "that segment does not exist". + +nutrient = [|4,2,3,7 |3,8,20,20 |5,4,6,20 |3,6,2,20 |]; -leave = +% For each potion (row) applied to a tree node, the number of +% leaves generated by each segment growing from that tree node (col). +% For unavailable segments, the number of leaves is 0. + +leave = [|7,6,4,8 |5,8,0,0 |5,4,7,0 |6,9,3,0 -|]; \ No newline at end of file +|]; diff --git a/CP/optimization/yaocao.mzn b/CP/optimization/yaocao.mzn index 9f2adf6..bd46e90 100644 --- a/CP/optimization/yaocao.mzn +++ b/CP/optimization/yaocao.mzn @@ -1,31 +1,74 @@ -int: n; % number of potions +% Solving the "Yao Cao" problem +% https://www.coursera.org/learn/solving-algorithms-discrete-optimization/lecture/t2J76/3-2-1-optimization-in-cp + +int: n; % number of potions, i.e. the number of segments we will grow on the tree set of int: POTIONS = 1..n; -int: w; % window size -int: p; % num of leaves in each window -int: capacity; % capacity of nutrients -int: m; % max number of grown segments +int: w; % window size, a segment count (evidently w <= n) +int: p; % min number of leaves required in each window +int: capacity; % total capacity of nutrients of a path through the tree +int: m; % maximum number of segments emerging from a node (depends on the potion applied, see "nutrient") set of int: SEGMENTS = 1..m; -array[POTIONS,SEGMENTS] of int: nutrient; % nutrient required -array[POTIONS,SEGMENTS] of int: leave; % num of leaves to grow +constraint assert(n>=w,"Window Size larger than number of potions (aka., number of segments)"); + +% For each potion (row) applied to a tree node, the nutrients +% required by each segment growing from that tree node (col). +% If the nutrient value is "beyond the capacity of nutrients", the +% meaning is "that segment does not exist" as we immediately +% would violate the nutrient limit if that segment were chosen-. + +array[POTIONS,SEGMENTS] of int: nutrient; + +% For each potion (row) applied to a tree node, the number of +% leaves generated by each segment growing from that tree node (col). +% For unavailable segments, the number of leaves is 0. + +array[POTIONS,SEGMENTS] of int: leave; + +% What we are looking for: for each potion applied to a +% tree node, we select the next segment to traverse. We do not +% have a choice in potion order, we have to use the potions +% in order of appearance. + +array[POTIONS] of var SEGMENTS: choice; -array[POTIONS] of var SEGMENTS: choice; %Choice of segment for each potion -array[POTIONS] of var int: nutrient_list = +% Derived: the number of nutrients required for each segment, +% which his identified by the potion applied to its parent tree node. + +array[POTIONS] of var int: nutrient_list = [nutrient[i,choice[i]] | i in POTIONS]; -array[POTIONS] of var int: leave_list = + +% Derived: the number of leaves generated by each segment, +% which his identified by the potion applied to its parent tree node. + +array[POTIONS] of var int: leave_list = [leave[i,choice[i]] | i in POTIONS]; var int: total_nutrient = sum(nutrient_list); var int: total_leaves = sum(leave_list); -constraint assert(n>=w,"Window Size larger than # of steps"); +% Can't exceed the total nutrient capacity -%Capacity constraint total_nutrient <= capacity; -%In each window, leave numbers should be larger than p + +% In each window, number of leaves must reach the minimum + constraint forall(tail in w..n) (sum(i in tail-w+1..tail)(leave_list[i]) >= p); +% Solving +% https://docs.minizinc.dev/en/stable/lib-stdlib-annotations.html#seq-search +% https://docs.minizinc.dev/en/stable/lib-stdlib-annotations.html#int-search +% On 100potions.dzn with "12th Gen Intel(R) Core(TM) i3-12100" + "Gecode 6.3.0": +% To find total_leaves = 6443; total_nutrient = 380: +% ssearch1 ~ 9 mins +% ssearch2+search1 ~ 27 s +% ssearch2+search2 ~ 27 s +% ssearch2+search3 ~ 15 s +% ssearch2+search4 ~ 11 s +% ssearch2+search5 ~ 9 s +% ssearch2+search6 ~ 19 s + solve :: ssearch2 maximize total_leaves; ann: search1 = int_search(leave_list, input_order, indomain_min, complete); @@ -37,8 +80,14 @@ ann: search6 = int_search(leave_list, smallest, indomain_max, complete); ann: ssearch1 = seq_search([search1, int_search([total_leaves], input_order, indomain_max, complete) ]); + ann: ssearch2 = seq_search([ int_search([total_leaves], input_order, indomain_max, complete), search1 ]); -output ["choice = ",show(choice),";\n","total_leaves = ",show(total_leaves),";\n","total_nutrient = ", show(total_nutrient)]; \ No newline at end of file +output ["choice = ",show(choice),";\n","total_leaves = ",show(total_leaves),";\n","total_nutrient = ", show(total_nutrient)]; +output ["\n"]; +output ["Apply potion \(i)"++ + ", then select segment \(fix(choice[i]))"++ + " which needs \(nutrient[i,fix(choice[i])]) nutrients"++ + " and gives \(leave[i,fix(choice[i])]) leaves\n" | i in POTIONS];