1# Copyright 2010-2021 Google LLC
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6#     http://www.apache.org/licenses/LICENSE-2.0
7#
8# Unless required by applicable law or agreed to in writing, software
9# distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
13"""Reallocate production to smooth it over years."""
14
15
16import collections
17
18from ortools.sat.python import cp_model
19
20
21def main():
22
23    # Data
24    data_0 = [
25        [107, 107, 107, 0, 0],  # pr1
26        [0, 47, 47, 47, 0],  # pr2
27        [10, 10, 10, 0, 0],  # pr3
28        [0, 55, 55, 55, 55],  # pr4
29    ]
30
31    data_1 = [
32        [119444030, 0, 0, 0],
33        [34585586, 38358559, 31860661, 0],
34        [19654655, 21798799, 18106106, 0],
35        [298836792, 0, 0, 0],
36        [3713428, 4118530, 4107277, 3072018],
37        [6477273, 7183884, 5358471, 0],
38        [1485371, 1647412, 1642911, 1228807]
39    ]
40
41    data_2 = [
42        [1194440, 0, 0, 0],
43        [345855, 383585, 318606, 0],
44        [196546, 217987, 181061, 0],
45        [2988367, 0, 0, 0],
46        [37134, 41185, 41072, 30720],
47        [64772, 71838, 53584, 0],
48        [14853, 16474, 16429, 12288]
49    ]
50
51    pr = data_0
52
53    num_pr = len(pr)
54    num_years = len(pr[1])
55    total = sum(pr[p][y] for p in range(num_pr) for y in range(num_years))
56    avg = total // num_years
57
58    # Model
59    model = cp_model.CpModel()
60
61    # Variables
62    delta = model.NewIntVar(0, total, 'delta')
63
64    contributions_per_years = collections.defaultdict(list)
65    contributions_per_prs = collections.defaultdict(list)
66    all_contribs = {}
67
68    for p, inner_l in enumerate(pr):
69        for y, item in enumerate(inner_l):
70            if item != 0:
71                contrib = model.NewIntVar(0, total, 'r%d c%d' % (p, y))
72                contributions_per_years[y].append(contrib)
73                contributions_per_prs[p].append(contrib)
74                all_contribs[p, y] = contrib
75
76    year_var = [
77        model.NewIntVar(0, total, 'y[%i]' % i) for i in range(num_years)
78    ]
79
80    # Constraints
81
82    # Maintain year_var.
83    for y in range(num_years):
84        model.Add(year_var[y] == sum(contributions_per_years[y]))
85
86    # Fixed contributions per pr.
87    for p in range(num_pr):
88        model.Add(sum(pr[p]) == sum(contributions_per_prs[p]))
89
90    # Link delta with variables.
91    for y in range(num_years):
92        model.Add(year_var[y] >= avg - delta)
93
94    for y in range(num_years):
95        model.Add(year_var[y] <= avg + delta)
96
97    # Solve and output
98    model.Minimize(delta)
99
100    # Solve model.
101    solver = cp_model.CpSolver()
102    status = solver.Solve(model)
103
104    # Output solution.
105    if status == cp_model.OPTIMAL:
106        print('Data')
107        print('  - total = ', total)
108        print('  - year_average = ', avg)
109        print('  - number of projects = ', num_pr)
110        print('  - number of years = ', num_years)
111
112        print('  - input production')
113        for p in range(num_pr):
114            for y in range(num_years):
115                if pr[p][y] == 0:
116                    print('        ', end='')
117                else:
118                    print('%10i' % pr[p][y], end='')
119            print()
120
121        print('Solution')
122        for p in range(num_pr):
123            for y in range(num_years):
124                if pr[p][y] == 0:
125                    print('        ', end='')
126                else:
127                    print('%10i' % solver.Value(all_contribs[p, y]), end='')
128            print()
129
130        for y in range(num_years):
131            print('%10i' % solver.Value(year_var[y]), end='')
132        print()
133
134
135if __name__ == '__main__':
136    main()
137