Coverage for sparkle/solver/solver_cli.py: 0%
77 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-07 15:22 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-07 15:22 +0000
1#!/usr/bin/env python3
2# -*- coding: UTF-8 -*-
3"""Run a solver, read/write to performance dataframe."""
4from filelock import FileLock
5import argparse
6from pathlib import Path
7import random
8import ast
9import time
11from runrunner import Runner
13from sparkle.solver import Solver
14from sparkle.instance import Instance_Set
15from sparkle.types import resolve_objective
16from sparkle.structures import PerformanceDataFrame
19if __name__ == "__main__":
20 # Define command line arguments
21 parser = argparse.ArgumentParser()
22 parser.add_argument("--performance-dataframe", required=True, type=Path,
23 help="path to the performance dataframe")
24 parser.add_argument("--solver", required=True, type=Path, help="path to solver")
25 parser.add_argument("--instance", required=True, type=str,
26 help="path to instance to run on")
27 parser.add_argument("--run-index", required=True, type=int,
28 help="run index in the dataframe.")
29 parser.add_argument("--log-dir", type=Path, required=True,
30 help="path to the log directory")
31 parser.add_argument("--configuration", type=dict, required=False,
32 help="configuration for the solver. If not provided, read from "
33 "the PerformanceDataFrame.")
34 parser.add_argument("--seed", type=str, required=False,
35 help="seed to use for the solver. If not provided, read from "
36 "the PerformanceDataFrame or generate one.")
37 parser.add_argument("--cutoff-time", type=int, required=False,
38 help="the cutoff time for the solver.")
39 parser.add_argument("--target-objective", required=False, type=str,
40 help="The objective to use to determine the best configuration.")
41 parser.add_argument("--best-configuration-instances", required=False, type=str,
42 help="If given, will ignore any given configurations, and try to"
43 " determine the best found configurations over the given "
44 "instances. Defaults to the first objective given in the "
45 " objectives argument or the one given by the dataframe "
46 " to use to determine the best configuration.")
47 args = parser.parse_args()
48 # Process command line arguments
49 log_dir = args.log_dir
50 print(f"Running Solver and read/writing results with {args.performance_dataframe}")
51 # Resolve possible multi-file instance
52 instance_path = Path(args.instance)
53 instance_name = instance_path.name
54 instance_key = instance_path
55 run_index = args.run_index
56 if not instance_path.exists():
57 # If its an instance name (Multi-file instance), retrieve path list
58 data_set = Instance_Set(instance_path.parent)
59 instance_path = data_set.get_path_by_name(instance_name)
60 instance_key = instance_name
62 solver = Solver(args.solver)
64 if not args.configuration or not args.seed: # Read
65 # Desyncronize from other possible jobs writing to the same file
66 time.sleep(random.random() * 10)
67 lock = FileLock(f"{args.performance_dataframe}.lock") # Lock the file
68 with lock.acquire(timeout=600):
69 performance_dataframe = PerformanceDataFrame(args.performance_dataframe)
71 objectives = performance_dataframe.objectives
72 # Filter out possible errors, shouldn't occur
73 objectives = [o for o in objectives if o is not None]
74 if args.best_configuration_instances: # Determine best configuration
75 best_configuration_instances = args.best_configuration_instances.split(",")
76 target_objective = resolve_objective(args.target_objective)
77 configuration, value = performance_dataframe.best_configuration(
78 solver=str(args.solver),
79 objective=target_objective,
80 instances=best_configuration_instances,
81 )
82 # Read the seed from the dataframe
83 seed = performance_dataframe.get_value(
84 str(args.solver),
85 str(args.instance),
86 objective=target_objective.name,
87 run=run_index,
88 solver_fields=[PerformanceDataFrame.column_seed])
89 else:
90 target = performance_dataframe.get_value(
91 str(args.solver),
92 str(args.instance),
93 objective=None,
94 run=run_index,
95 solver_fields=[PerformanceDataFrame.column_seed,
96 PerformanceDataFrame.column_configuration])
97 if isinstance(target[0], list): # We take the first value we find
98 target = target[0]
99 seed, df_configuration = target
100 configuration = args.configuration
101 if configuration is None: # Try to read from the dataframe
102 if not isinstance(df_configuration, dict):
103 try:
104 configuration = ast.literal_eval(df_configuration)
105 except Exception:
106 print("Failed to read configuration from dataframe: "
107 f"{df_configuration}")
108 else:
109 configuration = df_configuration
110 seed = args.seed or seed
111 # If no seed is provided and no seed can be read, generate one
112 if not isinstance(seed, int):
113 seed = random.randint(0, 2**32 - 1)
115 print(f"Running Solver {solver} on instance {instance_path.name} with seed {seed}..")
116 solver_output = solver.run(
117 instance_path.absolute(),
118 objectives=objectives,
119 seed=seed,
120 configuration=configuration.copy() if configuration else None,
121 cutoff_time=args.cutoff_time,
122 log_dir=log_dir,
123 run_on=Runner.LOCAL)
125 # Prepare the results for the DataFrame for each objective
126 result = [[solver_output[objective.name] for objective in objectives],
127 [seed] * len(objectives)]
128 solver_fields = [PerformanceDataFrame.column_value, PerformanceDataFrame.column_seed]
129 if args.best_configuration_instances: # Need to specify the configuration
130 result.append([configuration] * len(objectives))
131 solver_fields.append(PerformanceDataFrame.column_configuration)
132 objective_values = [f"{objective.name}: {solver_output[objective.name]}"
133 for objective in objectives]
134 print(f"Appending value objective values: {', '.join(objective_values)}")
135 print(f"For index: Instance {args.instance}, Run {args.run_index}")
137 # Desyncronize from other possible jobs writing to the same file
138 time.sleep(random.random() * 10)
140 # Now that we have all the results, we can add them to the performance dataframe
141 lock = FileLock(f"{args.performance_dataframe}.lock") # Lock the file
142 with lock.acquire(timeout=600):
143 performance_dataframe = PerformanceDataFrame(args.performance_dataframe)
144 performance_dataframe.set_value(
145 result,
146 solver=str(args.solver),
147 instance=str(args.instance),
148 objective=[o.name for o in objectives],
149 run=run_index,
150 solver_fields=solver_fields,
151 append_write_csv=True)