1
- import time
1
+
2
+ import json
2
3
3
4
import nextmv
4
- from visuals import draw_sol
5
5
import numpy as np
6
- import json
6
+ from visuals import draw_sol
7
7
8
8
try :
9
9
import xpress as xp
@@ -25,18 +25,29 @@ def main() -> None:
25
25
options = nextmv .Options (
26
26
nextmv .Option ("input" , str , "" , "Path to input file. Default is stdin." , False ),
27
27
nextmv .Option ("output" , str , "" , "Path to output file. Default is stdout." , False ),
28
- nextmv .Option ("duration" , int , 30 , "Max runtime duration (in seconds)." , False ),
28
+ nextmv .Option ("objective" , str , "average_distance" , "minimizes for average_distance, total_distance, or max_distance" , False ),
29
+ nextmv .Option ("parks_override" , int , 4 , "number of parks to build" , False ),
29
30
)
30
31
31
32
input = nextmv .load (options = options , path = options .input )
33
+ if options .objective not in ["average_distance" , "total_distance" , "max_distance" ]:
34
+ raise ValueError ("Invalid objective. Must be either 'average_distance', 'total_distance', or 'max_distance'." )
32
35
33
36
nextmv .log ("Solving facility location problem:" )
34
- nextmv .log (f" - schools: { input .data .get ('num_schools' , [])} " )
35
- nextmv .log (f" - sites: { input .data .get ('num_sites' , 0 )} " )
36
- nextmv .log (f" - parks: { input .data .get ('num_parks' , 0 )} " )
37
+ nextmv .log (f" - objective: { options .objective } " )
38
+ nextmv .log (f" - parks_override: { options .parks_override } " )
39
+ nextmv .log (f" - schools: { input .data .get ('num_schools' )} " )
40
+ nextmv .log (f" - sites: { input .data .get ('num_sites' )} " )
41
+ nextmv .log (f" - parks: { input .data .get ('num_parks' )} " )
42
+
43
+ np .random .seed (input .data .get ('seed' ))
37
44
38
45
SCHOOLS = range (input .data .get ('num_schools' )) # set of schools
39
46
SITES = range (input .data .get ('num_sites' )) # set of candidate sites
47
+ if options .parks_override is not None :
48
+ num_parks = options .parks_override
49
+ else :
50
+ num_parks = input .data .get ('num_parks' )
40
51
41
52
coord_schools = 10 * np .random .random ((input .data .get ('num_schools' ), 2 )) # x-y coordinates between 0 and 10 (in km)
42
53
coord_sites = 10 * np .random .random ((input .data .get ('num_sites' ), 2 ))
@@ -51,13 +62,20 @@ def main() -> None:
51
62
build = prob .addVariables (SITES , vartype = xp .binary )
52
63
53
64
# Objective function and constraints
54
- prob .setObjective (xp .Sum (dist [i ,j ] * serves [i ,j ] for i in SCHOOLS for j in SITES ))
65
+ if options .objective == "average_distance" :
66
+ prob .setObjective (xp .Sum (dist [i ,j ] * serves [i ,j ] for i in SCHOOLS for j in SITES ) / input .data .get ('num_schools' ))
67
+ elif options .objective == "total_distance" :
68
+ prob .setObjective (xp .Sum (dist [i ,j ] * serves [i ,j ] for i in SCHOOLS for j in SITES ))
69
+ elif options .objective == "max_distance" :
70
+ z = prob .addVariable () # add auxiliary variable to the problem
71
+ prob .addConstraint (z >= xp .Sum (dist [i ,j ] * serves [i ,j ] for j in SITES ) for i in SCHOOLS )
72
+ prob .setObjective (z ) # replaces the old objective function
55
73
56
74
# Every school must be served by one park
57
75
prob .addConstraint (xp .Sum (serves [i ,j ] for j in SITES ) == 1 for i in SCHOOLS )
58
76
59
77
# Exactly n parks are built:
60
- prob .addConstraint (xp .Sum (build [j ] for j in SITES ) == input . data . get ( ' num_parks' ) )
78
+ prob .addConstraint (xp .Sum (build [j ] for j in SITES ) == num_parks )
61
79
62
80
# Only parks that are built can serve schools
63
81
prob .addConstraint (xp .Sum (serves [i ,j ] for i in SCHOOLS ) <= input .data .get ('num_schools' ) * build [j ] for j in SITES )
@@ -71,15 +89,30 @@ def main() -> None:
71
89
72
90
input .options .provider = "xpress"
73
91
74
- input_charts = draw_sol (n = input .data .get ('num_schools' ),m = input .data .get ('num_sites' ), label = "Input Chart" , coord_schools = coord_schools , coord_sites = coord_sites , SCHOOLS = SCHOOLS , SITES = SITES )
75
- output_charts = draw_sol (input .data .get ('num_schools' ),input .data .get ('num_sites' ),prob ,serves ,build , "Output Chart" , coord_schools = coord_schools , coord_sites = coord_sites , SCHOOLS = SCHOOLS , SITES = SITES )
92
+ input_charts = draw_sol (n = input .data .get ('num_schools' ),
93
+ m = input .data .get ('num_sites' ),
94
+ label = "Input Chart" ,
95
+ coord_schools = coord_schools ,
96
+ coord_sites = coord_sites ,
97
+ SCHOOLS = SCHOOLS ,
98
+ SITES = SITES )
99
+ output_charts = draw_sol (input .data .get ('num_schools' ),
100
+ input .data .get ('num_sites' ),
101
+ prob ,
102
+ serves ,
103
+ build ,
104
+ label = "Output Chart" ,
105
+ coord_schools = coord_schools ,
106
+ coord_sites = coord_sites ,
107
+ SCHOOLS = SCHOOLS ,
108
+ SITES = SITES )
76
109
77
110
output = nextmv .Output (
78
111
solution = {"solution" : solution },
79
112
statistics = {"result" : {"value" : value }, "schema" : "v1" },
80
113
assets = [input_charts , output_charts ]
81
114
)
82
-
115
+
83
116
nextmv .write (output , path = options .output )
84
117
85
118
0 commit comments