Skip to content

Commit f4b48f5

Browse files
maurerlelkstrp
andauthored
io: allow setting progress=False in solver to disable stdout even for larger models (#375)
* allow setting progress=False in solver to disable stdout even for larger models * docs: add release notes * fix: tests --------- Co-authored-by: lkstrp <lkstrp@pm.me>
1 parent 1c5dd0a commit f4b48f5

File tree

3 files changed

+70
-41
lines changed

3 files changed

+70
-41
lines changed

doc/release_notes.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
Release Notes
22
=============
33

4-
.. Upcoming Version
5-
.. ----------------
4+
Upcoming Version
5+
----------------
6+
7+
* The method :meth:`model.to_file <linopy.model.Model.to_file>` now includes a progress argument to enable or disable the progress bar while writing.
8+
69

710
Version 0.4.2
811
--------------

linopy/io.py

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ def objective_write_quad_terms(
8787

8888

8989
def objective_to_file(
90-
m: Model, f: TextIOWrapper, log: bool = False, batch_size: int = 10000
90+
m: Model, f: TextIOWrapper, progress: bool = False, batch_size: int = 10000
9191
) -> None:
9292
"""
9393
Write out the objective of a model to a lp file.
9494
"""
95-
if log:
95+
if progress:
9696
logger.info("Writing objective.")
9797

9898
sense = m.objective.sense
@@ -129,7 +129,7 @@ def objective_to_file(
129129
def constraints_to_file(
130130
m: Model,
131131
f: TextIOWrapper,
132-
log: bool = False,
132+
progress: bool = False,
133133
batch_size: int = 50_000,
134134
slice_size: int = 100_000,
135135
) -> None:
@@ -138,7 +138,7 @@ def constraints_to_file(
138138

139139
f.write("\n\ns.t.\n\n")
140140
names: Iterable = m.constraints
141-
if log:
141+
if progress:
142142
names = tqdm(
143143
list(names),
144144
desc="Writing constraints.",
@@ -198,7 +198,7 @@ def constraints_to_file(
198198
def bounds_to_file(
199199
m: Model,
200200
f: TextIOWrapper,
201-
log: bool = False,
201+
progress: bool = False,
202202
batch_size: int = 10000,
203203
slice_size: int = 100_000,
204204
) -> None:
@@ -210,7 +210,7 @@ def bounds_to_file(
210210
return
211211

212212
f.write("\n\nbounds\n\n")
213-
if log:
213+
if progress:
214214
names = tqdm(
215215
list(names),
216216
desc="Writing continuous variables.",
@@ -241,7 +241,7 @@ def bounds_to_file(
241241
def binaries_to_file(
242242
m: Model,
243243
f: TextIOWrapper,
244-
log: bool = False,
244+
progress: bool = False,
245245
batch_size: int = 1000,
246246
slice_size: int = 100_000,
247247
) -> None:
@@ -253,7 +253,7 @@ def binaries_to_file(
253253
return
254254

255255
f.write("\n\nbinary\n\n")
256-
if log:
256+
if progress:
257257
names = tqdm(
258258
list(names),
259259
desc="Writing binary variables.",
@@ -277,7 +277,7 @@ def binaries_to_file(
277277
def integers_to_file(
278278
m: Model,
279279
f: TextIOWrapper,
280-
log: bool = False,
280+
progress: bool = False,
281281
batch_size: int = 1000,
282282
slice_size: int = 100_000,
283283
integer_label: str = "general",
@@ -290,7 +290,7 @@ def integers_to_file(
290290
return
291291

292292
f.write(f"\n\n{integer_label}\n\n")
293-
if log:
293+
if progress:
294294
names = tqdm(
295295
list(names),
296296
desc="Writing integer variables.",
@@ -311,9 +311,13 @@ def integers_to_file(
311311
f.writelines(batch)
312312

313313

314-
def to_lp_file(m: Model, fn: Path, integer_label: str, slice_size: int = 10_000_000):
315-
log = m._xCounter > 10_000
316-
314+
def to_lp_file(
315+
m: Model,
316+
fn: Path,
317+
integer_label: str,
318+
slice_size: int = 10_000_000,
319+
progress: bool = True,
320+
) -> None:
317321
batch_size = 5000
318322

319323
with open(fn, mode="w") as f:
@@ -322,17 +326,21 @@ def to_lp_file(m: Model, fn: Path, integer_label: str, slice_size: int = 10_000_
322326
if isinstance(f, int):
323327
raise ValueError("File not found.")
324328

325-
objective_to_file(m, f, log=log)
329+
objective_to_file(m, f, progress=progress)
326330
constraints_to_file(
327-
m, f=f, log=log, batch_size=batch_size, slice_size=slice_size
331+
m, f=f, progress=progress, batch_size=batch_size, slice_size=slice_size
332+
)
333+
bounds_to_file(
334+
m, f=f, progress=progress, batch_size=batch_size, slice_size=slice_size
335+
)
336+
binaries_to_file(
337+
m, f=f, progress=progress, batch_size=batch_size, slice_size=slice_size
328338
)
329-
bounds_to_file(m, f=f, log=log, batch_size=batch_size, slice_size=slice_size)
330-
binaries_to_file(m, f=f, log=log, batch_size=batch_size, slice_size=slice_size)
331339
integers_to_file(
332340
m,
333341
integer_label=integer_label,
334342
f=f,
335-
log=log,
343+
progress=progress,
336344
batch_size=batch_size,
337345
slice_size=slice_size,
338346
)
@@ -371,11 +379,11 @@ def objective_write_quadratic_terms_polars(f, df):
371379
f.write(b"] / 2\n")
372380

373381

374-
def objective_to_file_polars(m, f, log=False):
382+
def objective_to_file_polars(m, f, progress=False):
375383
"""
376384
Write out the objective of a model to a lp file.
377385
"""
378-
if log:
386+
if progress:
379387
logger.info("Writing objective.")
380388

381389
sense = m.objective.sense
@@ -399,7 +407,7 @@ def objective_to_file_polars(m, f, log=False):
399407
objective_write_quadratic_terms_polars(f, quads)
400408

401409

402-
def bounds_to_file_polars(m, f, log=False, slice_size=2_000_000):
410+
def bounds_to_file_polars(m, f, progress=False, slice_size=2_000_000):
403411
"""
404412
Write out variables of a model to a lp file.
405413
"""
@@ -408,7 +416,7 @@ def bounds_to_file_polars(m, f, log=False, slice_size=2_000_000):
408416
return
409417

410418
f.write(b"\n\nbounds\n\n")
411-
if log:
419+
if progress:
412420
names = tqdm(
413421
list(names),
414422
desc="Writing continuous variables.",
@@ -437,7 +445,7 @@ def bounds_to_file_polars(m, f, log=False, slice_size=2_000_000):
437445
formatted.write_csv(f, **kwargs)
438446

439447

440-
def binaries_to_file_polars(m, f, log=False, slice_size=2_000_000):
448+
def binaries_to_file_polars(m, f, progress=False, slice_size=2_000_000):
441449
"""
442450
Write out binaries of a model to a lp file.
443451
"""
@@ -446,7 +454,7 @@ def binaries_to_file_polars(m, f, log=False, slice_size=2_000_000):
446454
return
447455

448456
f.write(b"\n\nbinary\n\n")
449-
if log:
457+
if progress:
450458
names = tqdm(
451459
list(names),
452460
desc="Writing binary variables.",
@@ -471,7 +479,7 @@ def binaries_to_file_polars(m, f, log=False, slice_size=2_000_000):
471479

472480

473481
def integers_to_file_polars(
474-
m, f, log=False, integer_label="general", slice_size=2_000_000
482+
m, f, progress=False, integer_label="general", slice_size=2_000_000
475483
):
476484
"""
477485
Write out integers of a model to a lp file.
@@ -481,7 +489,7 @@ def integers_to_file_polars(
481489
return
482490

483491
f.write(f"\n\n{integer_label}\n\n".encode())
484-
if log:
492+
if progress:
485493
names = tqdm(
486494
list(names),
487495
desc="Writing integer variables.",
@@ -505,13 +513,13 @@ def integers_to_file_polars(
505513
formatted.write_csv(f, **kwargs)
506514

507515

508-
def constraints_to_file_polars(m, f, log=False, lazy=False, slice_size=2_000_000):
516+
def constraints_to_file_polars(m, f, progress=False, lazy=False, slice_size=2_000_000):
509517
if not len(m.constraints):
510518
return
511519

512520
f.write(b"\n\ns.t.\n\n")
513521
names = m.constraints
514-
if log:
522+
if progress:
515523
names = tqdm(
516524
list(names),
517525
desc="Writing constraints.",
@@ -559,18 +567,22 @@ def constraints_to_file_polars(m, f, log=False, lazy=False, slice_size=2_000_000
559567
# formatted.sink_csv(f, **kwargs)
560568

561569

562-
def to_lp_file_polars(m, fn, integer_label="general", slice_size=2_000_000):
563-
log = m._xCounter > 10_000
564-
570+
def to_lp_file_polars(
571+
m, fn, integer_label="general", slice_size=2_000_000, progress: bool = True
572+
):
565573
with open(fn, mode="wb") as f:
566574
start = time.time()
567575

568-
objective_to_file_polars(m, f, log=log)
569-
constraints_to_file_polars(m, f=f, log=log, slice_size=slice_size)
570-
bounds_to_file_polars(m, f=f, log=log, slice_size=slice_size)
571-
binaries_to_file_polars(m, f=f, log=log, slice_size=slice_size)
576+
objective_to_file_polars(m, f, progress=progress)
577+
constraints_to_file_polars(m, f=f, progress=progress, slice_size=slice_size)
578+
bounds_to_file_polars(m, f=f, progress=progress, slice_size=slice_size)
579+
binaries_to_file_polars(m, f=f, progress=progress, slice_size=slice_size)
572580
integers_to_file_polars(
573-
m, integer_label=integer_label, f=f, log=log, slice_size=slice_size
581+
m,
582+
integer_label=integer_label,
583+
f=f,
584+
progress=progress,
585+
slice_size=slice_size,
574586
)
575587
f.write(b"end\n")
576588

@@ -583,6 +595,7 @@ def to_file(
583595
io_api: str | None = None,
584596
integer_label: str = "general",
585597
slice_size: int = 2_000_000,
598+
progress: bool | None = None,
586599
) -> Path:
587600
"""
588601
Write out a model to a lp or mps file.
@@ -597,10 +610,15 @@ def to_file(
597610
if io_api is None:
598611
io_api = fn.suffix[1:]
599612

613+
if progress is None:
614+
progress = m._xCounter > 10_000
615+
600616
if io_api == "lp":
601-
to_lp_file(m, fn, integer_label, slice_size=slice_size)
617+
to_lp_file(m, fn, integer_label, slice_size=slice_size, progress=progress)
602618
elif io_api == "lp-polars":
603-
to_lp_file_polars(m, fn, integer_label, slice_size=slice_size)
619+
to_lp_file_polars(
620+
m, fn, integer_label, slice_size=slice_size, progress=progress
621+
)
604622

605623
elif io_api == "mps":
606624
if "highs" not in solvers.available_solvers:

linopy/model.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,7 @@ def solve(
969969
sanitize_infinities: bool = True,
970970
slice_size: int = 2_000_000,
971971
remote: None = None,
972+
progress: bool | None = None,
972973
**solver_options,
973974
) -> tuple[str, str]:
974975
"""
@@ -1027,6 +1028,10 @@ def solve(
10271028
Remote handler to use for solving model on a server. Note that when
10281029
solving on a rSee
10291030
linopy.remote.RemoteHandler for more details.
1031+
progress : bool, optional
1032+
Whether to show a progress bar of writing the lp file. The default is
1033+
None, which means that the progress bar is shown if the model has more
1034+
than 10000 variables and constraints.
10301035
**solver_options : kwargs
10311036
Options passed to the solver.
10321037
@@ -1125,7 +1130,10 @@ def solve(
11251130
)
11261131
else:
11271132
problem_fn = self.to_file(
1128-
to_path(problem_fn), io_api, slice_size=slice_size
1133+
to_path(problem_fn),
1134+
io_api,
1135+
slice_size=slice_size,
1136+
progress=progress,
11291137
)
11301138
result = solver.solve_problem_from_file(
11311139
problem_fn=to_path(problem_fn),

0 commit comments

Comments
 (0)