pipstools 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pipstools/__init__.py +5 -0
- pipstools/annotation.py +204 -0
- pipstools/cli.py +258 -0
- pipstools/conversion.py +75 -0
- pipstools/io/__init__.py +5 -0
- pipstools/io/gdx.py +461 -0
- pipstools/io/mps.py +232 -0
- pipstools/io/parquet.py +51 -0
- pipstools/partitioning.py +609 -0
- pipstools/scoring.py +143 -0
- pipstools/utils.py +174 -0
- pipstools/validation.py +57 -0
- pipstools/visualisation.py +226 -0
- pipstools-0.2.1.dist-info/METADATA +115 -0
- pipstools-0.2.1.dist-info/RECORD +17 -0
- pipstools-0.2.1.dist-info/WHEEL +4 -0
- pipstools-0.2.1.dist-info/entry_points.txt +3 -0
pipstools/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from .annotation import annotate_problem as annotate_problem
|
|
2
|
+
from .conversion import convert_problem as convert_problem
|
|
3
|
+
from .scoring import score_problem as score_problem
|
|
4
|
+
from .validation import validate_problem as validate_problem
|
|
5
|
+
from .visualisation import visualize_problem as visualize_problem
|
pipstools/annotation.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import gzip
|
|
2
|
+
import importlib.util
|
|
3
|
+
import shutil
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from time import perf_counter
|
|
6
|
+
|
|
7
|
+
from pipstools.io import read_gdx, read_mps, read_parquet, write_gdx, write_parquet
|
|
8
|
+
from pipstools.partitioning import get_partitions
|
|
9
|
+
from pipstools.scoring import get_score
|
|
10
|
+
from pipstools.utils import get_stem
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def annotate_problem(
|
|
14
|
+
f_input,
|
|
15
|
+
k,
|
|
16
|
+
f_output=None,
|
|
17
|
+
fileformat="gdx",
|
|
18
|
+
var_dense=None,
|
|
19
|
+
equ_dense=None,
|
|
20
|
+
presolve=False,
|
|
21
|
+
mpsreader=None,
|
|
22
|
+
method="hypergraph",
|
|
23
|
+
hypergraph="col",
|
|
24
|
+
hg_objective="soed",
|
|
25
|
+
regex_pattern=None,
|
|
26
|
+
regex_order=None,
|
|
27
|
+
regex_mapfile=None,
|
|
28
|
+
dec=None,
|
|
29
|
+
write_names=True,
|
|
30
|
+
write_uels=True,
|
|
31
|
+
distributed=False,
|
|
32
|
+
vcycles=0,
|
|
33
|
+
):
|
|
34
|
+
start = perf_counter()
|
|
35
|
+
if isinstance(f_input, str):
|
|
36
|
+
f_input = Path(f_input)
|
|
37
|
+
|
|
38
|
+
if var_dense is not None and var_dense < 0:
|
|
39
|
+
var_dense = None
|
|
40
|
+
|
|
41
|
+
if equ_dense is not None and equ_dense < 0:
|
|
42
|
+
var_dense = None
|
|
43
|
+
|
|
44
|
+
if mpsreader is None:
|
|
45
|
+
lib_gurobi = importlib.util.find_spec("gurobipy")
|
|
46
|
+
if lib_gurobi is not None:
|
|
47
|
+
mpsreader = "gurobi"
|
|
48
|
+
else:
|
|
49
|
+
mpsreader = "highs"
|
|
50
|
+
print(f"Selected {mpsreader} as default mps reader.")
|
|
51
|
+
|
|
52
|
+
if k == 1 and method.lower() != "oneblock":
|
|
53
|
+
print('Specificed partitioning into a single block, switching method to "oneblock"')
|
|
54
|
+
method = "oneblock"
|
|
55
|
+
|
|
56
|
+
if regex_pattern is not None:
|
|
57
|
+
print('Specificed regex pattern, switching method to "regex"')
|
|
58
|
+
method = "regex"
|
|
59
|
+
|
|
60
|
+
if dec is not None:
|
|
61
|
+
print('Specificed .dec file, switching method to "dec"')
|
|
62
|
+
method = "dec"
|
|
63
|
+
|
|
64
|
+
# Check if valid hypergraph arguments are provided
|
|
65
|
+
if method == "hypergraph":
|
|
66
|
+
if hypergraph.lower() not in ["col", "row", "colrow", "rowcol"]:
|
|
67
|
+
raise IOError(
|
|
68
|
+
f"Hypergraph type {hypergraph} is not available. "
|
|
69
|
+
"Use either 'col', 'row', 'colrow', or 'rowcol'"
|
|
70
|
+
)
|
|
71
|
+
if hg_objective.lower() not in ["cut", "km1", "soed"]:
|
|
72
|
+
raise IOError(
|
|
73
|
+
f"Hypergraph objective {hg_objective} is not available. "
|
|
74
|
+
"Use either 'cut', 'km1', or 'soed'"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Read in file and extract A, vars, cols
|
|
78
|
+
match f_input.suffix.lower():
|
|
79
|
+
case ".gdx":
|
|
80
|
+
A, cols, rows, objcol, objrow, objcoef, objjacval = read_gdx(f_input=f_input)
|
|
81
|
+
case ".parquet":
|
|
82
|
+
A, cols, rows, objcol, objrow, objcoef, objjacval = read_parquet(f_input=f_input)
|
|
83
|
+
case ".lp" | ".mps" | ".gz" | ".bz2":
|
|
84
|
+
compressed_input = f_input.suffix.lower() in [".gz", ".bz2"]
|
|
85
|
+
|
|
86
|
+
# File needs to be compresses lp or mps
|
|
87
|
+
if compressed_input:
|
|
88
|
+
suffix_uncomp = f_input.stem.lower()
|
|
89
|
+
if not suffix_uncomp.endswith(".lp") and not suffix_uncomp.endswith(".mps"):
|
|
90
|
+
raise (
|
|
91
|
+
IOError(
|
|
92
|
+
"Can only read compressed files of type .lp and .mps, "
|
|
93
|
+
f"not {f_input.suffix}"
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# For highspy manually decompress file and delete later
|
|
98
|
+
remove_decompressed = False
|
|
99
|
+
if compressed_input and mpsreader == "highs":
|
|
100
|
+
print(f"Automatically decompressing problem file {f_input.as_posix()} for highs")
|
|
101
|
+
if f_input.with_suffix("").exists():
|
|
102
|
+
raise (
|
|
103
|
+
IOError(
|
|
104
|
+
f"Cannot automatically decompress file {f_input.as_posix()}, since "
|
|
105
|
+
"a file with the target name already exists!"
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
with gzip.open(f_input, "rb") as f_in:
|
|
109
|
+
with open(f_input.with_suffix(""), "wb") as f_out:
|
|
110
|
+
shutil.copyfileobj(f_in, f_out)
|
|
111
|
+
f_input = f_input.with_suffix("")
|
|
112
|
+
remove_decompressed = True
|
|
113
|
+
|
|
114
|
+
# Read in mps content
|
|
115
|
+
A, cols, rows, objcol, objrow, objcoef = read_mps(
|
|
116
|
+
f_input=f_input,
|
|
117
|
+
presolve=presolve,
|
|
118
|
+
read_names=write_names or method == "regex",
|
|
119
|
+
mpsreader=mpsreader,
|
|
120
|
+
)
|
|
121
|
+
objjacval = 1.0
|
|
122
|
+
|
|
123
|
+
# Remove temporary decompressed file again
|
|
124
|
+
if remove_decompressed:
|
|
125
|
+
print(f"Automatically removing decompressed file {f_input.as_posix()}")
|
|
126
|
+
f_input.unlink()
|
|
127
|
+
case _:
|
|
128
|
+
raise (IOError(f"No reader implemented for filetype {f_input.suffix}"))
|
|
129
|
+
|
|
130
|
+
# Check if kahypar is available
|
|
131
|
+
if method == "hypergraph":
|
|
132
|
+
lib_mkkahypar = importlib.util.find_spec("mtkahypar")
|
|
133
|
+
if lib_mkkahypar is None:
|
|
134
|
+
raise ImportError(
|
|
135
|
+
"Hypergraph partitioning specified, but library mtkahypar not available."
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Partition by regex, hypergraph or dense elements
|
|
139
|
+
cols, rows = get_partitions(
|
|
140
|
+
A=A,
|
|
141
|
+
cols=cols,
|
|
142
|
+
rows=rows,
|
|
143
|
+
method=method,
|
|
144
|
+
hypergraph=hypergraph,
|
|
145
|
+
hg_objective=hg_objective,
|
|
146
|
+
k=k,
|
|
147
|
+
var_dense=var_dense,
|
|
148
|
+
equ_dense=equ_dense,
|
|
149
|
+
regex_pattern=regex_pattern,
|
|
150
|
+
regex_order=regex_order,
|
|
151
|
+
regex_mapfile=regex_mapfile,
|
|
152
|
+
dec=dec,
|
|
153
|
+
vcycles=vcycles,
|
|
154
|
+
objcol=objcol,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Derive stem for output
|
|
158
|
+
if f_output is None:
|
|
159
|
+
stem, suffix = get_stem(f_input)
|
|
160
|
+
else:
|
|
161
|
+
stem, suffix = get_stem(f_output)
|
|
162
|
+
if suffix is not None:
|
|
163
|
+
fileformat = suffix.lstrip(".")
|
|
164
|
+
|
|
165
|
+
f_output = Path(f"{stem}_{len(set(cols['partition'].unique()) - {1})}b")
|
|
166
|
+
f_output.parent.mkdir(parents=True, exist_ok=True)
|
|
167
|
+
|
|
168
|
+
# Get scoring for annotation
|
|
169
|
+
get_score(A, cols, rows)
|
|
170
|
+
|
|
171
|
+
# Write out annotated gdx file
|
|
172
|
+
match fileformat.lower():
|
|
173
|
+
case "gdx":
|
|
174
|
+
write_gdx(
|
|
175
|
+
f_input=f_input,
|
|
176
|
+
f_output=f_output,
|
|
177
|
+
A=A,
|
|
178
|
+
cols=cols,
|
|
179
|
+
rows=rows,
|
|
180
|
+
objcol=objcol,
|
|
181
|
+
objrow=objrow,
|
|
182
|
+
objcoef=objcoef,
|
|
183
|
+
objjacval=objjacval,
|
|
184
|
+
write_names=write_names,
|
|
185
|
+
write_uels=write_uels,
|
|
186
|
+
distributed=distributed,
|
|
187
|
+
)
|
|
188
|
+
case "parquet":
|
|
189
|
+
write_parquet(
|
|
190
|
+
f_input=f_input,
|
|
191
|
+
f_output=f_output,
|
|
192
|
+
A=A,
|
|
193
|
+
cols=cols,
|
|
194
|
+
rows=rows,
|
|
195
|
+
objcol=objcol,
|
|
196
|
+
objrow=objrow,
|
|
197
|
+
objcoef=objcoef,
|
|
198
|
+
objjacval=objjacval,
|
|
199
|
+
)
|
|
200
|
+
case _:
|
|
201
|
+
raise (IOError(f"No method implemented for output format {fileformat}"))
|
|
202
|
+
|
|
203
|
+
stop = perf_counter()
|
|
204
|
+
print(f"Annotated problem in {(stop - start):.2f} seconds")
|
pipstools/cli.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from typing_extensions import Annotated
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def version_callback(value: bool):
|
|
8
|
+
if value:
|
|
9
|
+
import importlib.metadata
|
|
10
|
+
|
|
11
|
+
version = importlib.metadata.version("pipstools")
|
|
12
|
+
typer.echo(f"{version}")
|
|
13
|
+
raise typer.Exit()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
app = typer.Typer(
|
|
17
|
+
add_completion=False,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@app.callback()
|
|
22
|
+
def common(
|
|
23
|
+
ctx: typer.Context,
|
|
24
|
+
version: bool = typer.Option(None, "--version", callback=version_callback),
|
|
25
|
+
):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.command(
|
|
30
|
+
help="Annotate a lp/mps/gdx file using hypergraph partitioning or regular expressions."
|
|
31
|
+
)
|
|
32
|
+
def annotate(
|
|
33
|
+
# Options for file IO
|
|
34
|
+
inputfile: Annotated[
|
|
35
|
+
str,
|
|
36
|
+
typer.Argument(help="Path to the input file (.lp, .mps, .gdx, or .parquet)"),
|
|
37
|
+
],
|
|
38
|
+
outputfile: Annotated[
|
|
39
|
+
str | None,
|
|
40
|
+
typer.Option(help="Path to the output file"),
|
|
41
|
+
] = None,
|
|
42
|
+
fileformat: Annotated[
|
|
43
|
+
str,
|
|
44
|
+
typer.Option(help="File format the output file"),
|
|
45
|
+
] = "gdx",
|
|
46
|
+
mpsreader: Annotated[
|
|
47
|
+
str | None,
|
|
48
|
+
typer.Option(help='Package to use as mps reader ("gurobi", "highs")'),
|
|
49
|
+
] = None,
|
|
50
|
+
# Options for matrix structure
|
|
51
|
+
blocks: Annotated[
|
|
52
|
+
int,
|
|
53
|
+
typer.Option(help="Number of blocks to annotate"),
|
|
54
|
+
] = 30,
|
|
55
|
+
densecol: Annotated[
|
|
56
|
+
int | None,
|
|
57
|
+
typer.Option(
|
|
58
|
+
help="Number of non-zero entries to consider a column as dense, "
|
|
59
|
+
"defaults to heuristic detection of dense columns"
|
|
60
|
+
),
|
|
61
|
+
] = None,
|
|
62
|
+
denserow: Annotated[
|
|
63
|
+
int | None,
|
|
64
|
+
typer.Option(
|
|
65
|
+
help="Number of non-zero entries to consider a row as dense, "
|
|
66
|
+
"defaults to no detection of dense rows"
|
|
67
|
+
),
|
|
68
|
+
] = None,
|
|
69
|
+
presolve: Annotated[
|
|
70
|
+
bool,
|
|
71
|
+
typer.Option(help="Presolve the problem before partitioning"),
|
|
72
|
+
] = False,
|
|
73
|
+
# Options for methods
|
|
74
|
+
method: Annotated[
|
|
75
|
+
str,
|
|
76
|
+
typer.Option(help='Method to use for partitioning ("hypergraph", "oneblock", "regex")'),
|
|
77
|
+
] = "hypergraph",
|
|
78
|
+
hypergraph: Annotated[
|
|
79
|
+
str,
|
|
80
|
+
typer.Option(help='Type of hypergraph to use for partitioning ("col", "row", "colrow")'),
|
|
81
|
+
] = "col",
|
|
82
|
+
hg_objective: Annotated[
|
|
83
|
+
str,
|
|
84
|
+
typer.Option(
|
|
85
|
+
help='Objective metric to use for hypergraph partitioning ("cut", "km1", "soed")'
|
|
86
|
+
),
|
|
87
|
+
] = "soed",
|
|
88
|
+
regex_pattern: Annotated[
|
|
89
|
+
str | None,
|
|
90
|
+
typer.Option(help="Pattern to use for variable partitioning via regular expressions"),
|
|
91
|
+
] = None,
|
|
92
|
+
regex_order: Annotated[
|
|
93
|
+
str | None,
|
|
94
|
+
typer.Option(help="Ordering of the capture groups for partitioning (e.g. [2,1])"),
|
|
95
|
+
] = None,
|
|
96
|
+
regex_mapfile: Annotated[
|
|
97
|
+
str | None,
|
|
98
|
+
typer.Option(help="Mapping file from regex capture groups to blocks in csv format."),
|
|
99
|
+
] = None,
|
|
100
|
+
dec: Annotated[
|
|
101
|
+
str | None,
|
|
102
|
+
typer.Option(help="Additional .dec file containing a predefined decomposition"),
|
|
103
|
+
] = None,
|
|
104
|
+
# Options for performance
|
|
105
|
+
distributed: Annotated[
|
|
106
|
+
bool,
|
|
107
|
+
typer.Option(help="Write out distributed gdx files"),
|
|
108
|
+
] = False,
|
|
109
|
+
names: Annotated[
|
|
110
|
+
bool,
|
|
111
|
+
typer.Option(help="Write variable and constraint names to output files"),
|
|
112
|
+
] = True,
|
|
113
|
+
uels: Annotated[
|
|
114
|
+
bool,
|
|
115
|
+
typer.Option(help="Write unique element list to output files"),
|
|
116
|
+
] = True,
|
|
117
|
+
) -> None:
|
|
118
|
+
from pipstools import annotate_problem
|
|
119
|
+
|
|
120
|
+
f_input = Path(inputfile)
|
|
121
|
+
if not f_input.exists():
|
|
122
|
+
raise IOError(f"File {f_input.as_posix()} not found.")
|
|
123
|
+
|
|
124
|
+
annotate_problem(
|
|
125
|
+
f_input,
|
|
126
|
+
f_output=outputfile,
|
|
127
|
+
fileformat=fileformat,
|
|
128
|
+
k=int(blocks),
|
|
129
|
+
var_dense=densecol,
|
|
130
|
+
equ_dense=denserow,
|
|
131
|
+
presolve=presolve,
|
|
132
|
+
mpsreader=mpsreader,
|
|
133
|
+
method=method,
|
|
134
|
+
hypergraph=hypergraph,
|
|
135
|
+
hg_objective=hg_objective,
|
|
136
|
+
regex_pattern=regex_pattern,
|
|
137
|
+
regex_order=regex_order,
|
|
138
|
+
regex_mapfile=regex_mapfile,
|
|
139
|
+
dec=dec,
|
|
140
|
+
write_names=names,
|
|
141
|
+
write_uels=uels,
|
|
142
|
+
distributed=distributed,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@app.command(help="Convert the problem structure between different file formats.")
|
|
147
|
+
def convert(
|
|
148
|
+
# Options for file IO
|
|
149
|
+
inputfile: Annotated[
|
|
150
|
+
str,
|
|
151
|
+
typer.Argument(help="Path to the problem file (.gdx or .parquet)"),
|
|
152
|
+
],
|
|
153
|
+
outputfile: Annotated[
|
|
154
|
+
str | None,
|
|
155
|
+
typer.Option(help="Path to the output file"),
|
|
156
|
+
] = None,
|
|
157
|
+
fileformat: Annotated[
|
|
158
|
+
str,
|
|
159
|
+
typer.Option(help="File format the output file"),
|
|
160
|
+
] = "gdx",
|
|
161
|
+
# Options for performance
|
|
162
|
+
distributed: Annotated[
|
|
163
|
+
bool,
|
|
164
|
+
typer.Option(help="Write out distributed gdx files"),
|
|
165
|
+
] = False,
|
|
166
|
+
names: Annotated[
|
|
167
|
+
bool,
|
|
168
|
+
typer.Option(help="Write variable and constraint names to output files"),
|
|
169
|
+
] = True,
|
|
170
|
+
uels: Annotated[
|
|
171
|
+
bool,
|
|
172
|
+
typer.Option(help="Write unique element list to output files"),
|
|
173
|
+
] = True,
|
|
174
|
+
) -> None:
|
|
175
|
+
from pipstools import convert_problem
|
|
176
|
+
|
|
177
|
+
f_input = Path(inputfile)
|
|
178
|
+
if not f_input.exists():
|
|
179
|
+
raise IOError(f"File {f_input.as_posix()} not found.")
|
|
180
|
+
|
|
181
|
+
convert_problem(
|
|
182
|
+
f_input,
|
|
183
|
+
f_output=outputfile,
|
|
184
|
+
fileformat=fileformat,
|
|
185
|
+
write_names=names,
|
|
186
|
+
write_uels=uels,
|
|
187
|
+
distributed=distributed,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@app.command(help="Visualize the blocks structure of an annotated .gdx file.")
|
|
192
|
+
def visualize(
|
|
193
|
+
inputfile: Annotated[
|
|
194
|
+
str,
|
|
195
|
+
typer.Argument(help="Path to the problem file (.gdx or .parquet)"),
|
|
196
|
+
],
|
|
197
|
+
outputfile: Annotated[
|
|
198
|
+
str | None,
|
|
199
|
+
typer.Option(help="Redirect the figure to a file."),
|
|
200
|
+
] = None,
|
|
201
|
+
annotation: Annotated[
|
|
202
|
+
bool,
|
|
203
|
+
typer.Option(help="Visualize the annotation"),
|
|
204
|
+
] = True,
|
|
205
|
+
names: Annotated[
|
|
206
|
+
bool,
|
|
207
|
+
typer.Option(help="Try to use variable and constraint names"),
|
|
208
|
+
] = True,
|
|
209
|
+
force: Annotated[
|
|
210
|
+
bool,
|
|
211
|
+
typer.Option(help="Try to use variable and constraint names"),
|
|
212
|
+
] = False,
|
|
213
|
+
) -> None:
|
|
214
|
+
from pipstools import visualize_problem
|
|
215
|
+
|
|
216
|
+
f_input = Path(inputfile)
|
|
217
|
+
if not f_input.exists():
|
|
218
|
+
raise IOError(f"File {f_input.as_posix()} not found.")
|
|
219
|
+
|
|
220
|
+
visualize_problem(
|
|
221
|
+
f_input,
|
|
222
|
+
output=outputfile,
|
|
223
|
+
read_names=names,
|
|
224
|
+
use_annotation=annotation,
|
|
225
|
+
force=force,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@app.command(help="Score the blocks structure of an annotated problem.")
|
|
230
|
+
def score(
|
|
231
|
+
inputfile: Annotated[
|
|
232
|
+
str,
|
|
233
|
+
typer.Argument(help="Path to the problem file (.gdx or .parquet)"),
|
|
234
|
+
],
|
|
235
|
+
) -> None:
|
|
236
|
+
from pipstools import score_problem
|
|
237
|
+
|
|
238
|
+
f_input = Path(inputfile)
|
|
239
|
+
if not f_input.exists():
|
|
240
|
+
raise IOError(f"File {f_input.as_posix()} not found.")
|
|
241
|
+
|
|
242
|
+
score_problem(f_input)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@app.command(help="Validate the blocks structure of an annotated problem.")
|
|
246
|
+
def validate(
|
|
247
|
+
inputfile: Annotated[
|
|
248
|
+
str,
|
|
249
|
+
typer.Argument(help="Path to the problem file (.gdx or .parquet)"),
|
|
250
|
+
],
|
|
251
|
+
) -> None:
|
|
252
|
+
from pipstools import validate_problem
|
|
253
|
+
|
|
254
|
+
f_input = Path(inputfile)
|
|
255
|
+
if not f_input.exists():
|
|
256
|
+
raise IOError(f"File {f_input.as_posix()} not found.")
|
|
257
|
+
|
|
258
|
+
validate_problem(f_input)
|
pipstools/conversion.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from time import perf_counter
|
|
3
|
+
|
|
4
|
+
from pipstools.io import read_gdx, read_parquet, write_gdx, write_parquet
|
|
5
|
+
from pipstools.utils import get_stem
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def convert_problem(
|
|
9
|
+
f_input,
|
|
10
|
+
f_output=None,
|
|
11
|
+
fileformat="gdx",
|
|
12
|
+
write_names=True,
|
|
13
|
+
write_uels=True,
|
|
14
|
+
distributed=False,
|
|
15
|
+
):
|
|
16
|
+
if isinstance(f_input, str):
|
|
17
|
+
f_input = Path(f_input)
|
|
18
|
+
|
|
19
|
+
match f_input.suffix.lower():
|
|
20
|
+
case ".gdx":
|
|
21
|
+
A, cols, rows, objcol, objrow, objcoef, objjacval = read_gdx(f_input=f_input)
|
|
22
|
+
case ".parquet":
|
|
23
|
+
A, cols, rows, objcol, objrow, objcoef, objjacval = read_parquet(f_input=f_input)
|
|
24
|
+
case _:
|
|
25
|
+
raise IOError("Converting is only supported for .gdx files and .parquet collections.")
|
|
26
|
+
|
|
27
|
+
print(f"Converting a model with {len(rows)} rows, {len(cols)} columns, {len(A)} nonzeros")
|
|
28
|
+
start = perf_counter()
|
|
29
|
+
|
|
30
|
+
# Derive stem for output
|
|
31
|
+
if f_output is None:
|
|
32
|
+
stem, suffix = get_stem(f_input)
|
|
33
|
+
else:
|
|
34
|
+
stem, suffix = get_stem(f_output)
|
|
35
|
+
if suffix is not None:
|
|
36
|
+
print(f"Automatically derived fileformat {suffix} from output file")
|
|
37
|
+
fileformat = suffix.lstrip(".")
|
|
38
|
+
|
|
39
|
+
f_output = Path(f"{stem}_{len(set(cols['partition'].unique()) - {1})}b")
|
|
40
|
+
f_output.parent.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
|
|
42
|
+
# Write out annotated gdx file
|
|
43
|
+
match fileformat.lower():
|
|
44
|
+
case "gdx":
|
|
45
|
+
write_gdx(
|
|
46
|
+
f_input=f_input,
|
|
47
|
+
f_output=f_output,
|
|
48
|
+
A=A,
|
|
49
|
+
cols=cols,
|
|
50
|
+
rows=rows,
|
|
51
|
+
objcol=objcol,
|
|
52
|
+
objrow=objrow,
|
|
53
|
+
objcoef=objcoef,
|
|
54
|
+
objjacval=objjacval,
|
|
55
|
+
write_names=write_names,
|
|
56
|
+
write_uels=write_uels,
|
|
57
|
+
distributed=distributed,
|
|
58
|
+
)
|
|
59
|
+
case "parquet":
|
|
60
|
+
write_parquet(
|
|
61
|
+
f_input=f_input,
|
|
62
|
+
f_output=f_output,
|
|
63
|
+
A=A,
|
|
64
|
+
cols=cols,
|
|
65
|
+
rows=rows,
|
|
66
|
+
objcol=objcol,
|
|
67
|
+
objrow=objrow,
|
|
68
|
+
objcoef=objcoef,
|
|
69
|
+
objjacval=objjacval,
|
|
70
|
+
)
|
|
71
|
+
case _:
|
|
72
|
+
raise (IOError(f"No method implemented for output format {fileformat}"))
|
|
73
|
+
|
|
74
|
+
stop = perf_counter()
|
|
75
|
+
print(f"Converted problem in {(stop - start):.2f} seconds")
|