11from __future__ import annotations
22
33import argparse
4+ import json
5+ import sys
46from dataclasses import dataclass
57from statistics import mean
68from time import perf_counter
@@ -141,42 +143,80 @@ def evaluate(problem_name: str, solver_name: str, config: SolverConfig, args):
141143 nifty_runtimes = []
142144
143145 for _ in range (args .n_repeats ):
144- bic_objective = bic .graph .MulticutObjective (bic_graph , costs )
145- start = perf_counter ()
146- bic_labels = config .make_bic_solver (args .threads ).optimize (bic_objective )
147- bic_runtimes .append (perf_counter () - start )
148- bic_energies .append (bic_energy (bic_graph , costs , bic_labels ))
149-
150- nifty_objective = nmc .multicutObjective (nifty_graph , costs )
151- start = perf_counter ()
152- nifty_labels = (
153- config .make_nifty_factory (nifty_objective , args .threads )
154- .create (nifty_objective )
155- .optimize ()
156- )
157- nifty_runtimes .append (perf_counter () - start )
158- nifty_energies .append (nifty_energy (nifty_graph , costs , nifty_labels ))
159-
160- bic_runtime = mean (bic_runtimes )
161- nifty_runtime = mean (nifty_runtimes )
146+ if args .backend in ("both" , "bic" ):
147+ bic_objective = bic .graph .MulticutObjective (bic_graph , costs )
148+ start = perf_counter ()
149+ bic_labels = config .make_bic_solver (args .threads ).optimize (bic_objective )
150+ bic_runtimes .append (perf_counter () - start )
151+ bic_energies .append (bic_energy (bic_graph , costs , bic_labels ))
152+
153+ if args .backend in ("both" , "nifty" ):
154+ nifty_objective = nmc .multicutObjective (nifty_graph , costs )
155+ start = perf_counter ()
156+ nifty_labels = (
157+ config .make_nifty_factory (nifty_objective , args .threads )
158+ .create (nifty_objective )
159+ .optimize ()
160+ )
161+ nifty_runtimes .append (perf_counter () - start )
162+ nifty_energies .append (nifty_energy (nifty_graph , costs , nifty_labels ))
163+
164+ bic_runtime = mean (bic_runtimes ) if bic_runtimes else None
165+ nifty_runtime = mean (nifty_runtimes ) if nifty_runtimes else None
166+ bic_energy_value = mean (bic_energies ) if bic_energies else None
167+ nifty_energy_value = mean (nifty_energies ) if nifty_energies else None
168+ energy_diff = (
169+ bic_energy_value - nifty_energy_value
170+ if bic_energy_value is not None and nifty_energy_value is not None
171+ else None
172+ )
162173 return {
163174 "problem" : problem_name ,
164175 "solver" : solver_name ,
165176 "nodes" : int (bic_graph .number_of_nodes ),
166177 "edges" : int (bic_graph .number_of_edges ),
167- "bic_energy" : mean ( bic_energies ) ,
168- "nifty_energy" : mean ( nifty_energies ) ,
169- "energy_diff" : mean ( bic_energies ) - mean ( nifty_energies ) ,
178+ "bic_energy" : bic_energy_value ,
179+ "nifty_energy" : nifty_energy_value ,
180+ "energy_diff" : energy_diff ,
170181 "bic_runtime_s" : bic_runtime ,
171182 "nifty_runtime_s" : nifty_runtime ,
172- "runtime_ratio" : nifty_runtime / bic_runtime if bic_runtime > 0 else float ("inf" ),
183+ "runtime_ratio" : (
184+ nifty_runtime / bic_runtime
185+ if bic_runtime is not None and nifty_runtime is not None and bic_runtime > 0
186+ else None
187+ ),
173188 }
174189
175190
176191def format_float (value : float ) -> str :
192+ if value is None :
193+ return ""
177194 return f"{ value :.6g} "
178195
179196
197+ def print_progress (row : dict ) -> None :
198+ print (
199+ (
200+ f"[done] { row ['problem' ]} / { row ['solver' ]} : "
201+ f"bic_energy={ format_float (row ['bic_energy' ])} , "
202+ f"nifty_energy={ format_float (row ['nifty_energy' ])} , "
203+ f"delta={ format_float (row ['energy_diff' ])} , "
204+ f"bic_runtime={ format_float (row ['bic_runtime_s' ])} s, "
205+ f"nifty_runtime={ format_float (row ['nifty_runtime_s' ])} s, "
206+ f"ratio={ format_float (row ['runtime_ratio' ])} "
207+ ),
208+ file = sys .stderr ,
209+ flush = True ,
210+ )
211+
212+
213+ def append_jsonl (path : str , row : dict ) -> None :
214+ with open (path , "a" , encoding = "utf-8" ) as f :
215+ json .dump (row , f , sort_keys = True )
216+ f .write ("\n " )
217+ f .flush ()
218+
219+
180220def print_markdown_table (rows : list [dict ]) -> None :
181221 headers = [
182222 "problem" ,
@@ -233,6 +273,16 @@ def main() -> None:
233273 parser .add_argument ("--n-repeats" , type = int , default = 1 )
234274 parser .add_argument ("--threads" , type = int , default = 1 )
235275 parser .add_argument ("--timeout" , type = float , default = 30.0 )
276+ parser .add_argument (
277+ "--backend" ,
278+ choices = ("both" , "bic" , "nifty" ),
279+ default = "both" ,
280+ help = "Which implementation to run. Defaults to both." ,
281+ )
282+ parser .add_argument (
283+ "--results-jsonl" ,
284+ help = "Append each completed row to this JSONL file." ,
285+ )
236286 args = parser .parse_args ()
237287
238288 if args .n_repeats < 1 :
@@ -243,7 +293,11 @@ def main() -> None:
243293 rows = []
244294 for problem_name in args .problems :
245295 for solver_name in args .solvers :
246- rows .append (evaluate (problem_name , solver_name , configs [solver_name ], args ))
296+ row = evaluate (problem_name , solver_name , configs [solver_name ], args )
297+ rows .append (row )
298+ print_progress (row )
299+ if args .results_jsonl is not None :
300+ append_jsonl (args .results_jsonl , row )
247301 print_markdown_table (rows )
248302
249303
0 commit comments