Skip to content

Commit ebad3b8

Browse files
feat: prepare the scripts for single and batch run of the auto search
1 parent a67bf94 commit ebad3b8

2 files changed

Lines changed: 475 additions & 0 deletions

File tree

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
import argparse
2+
import asyncio
3+
import json
4+
import logging
5+
import os
6+
from typing import Any, Tuple
7+
8+
from auto_search.run_search import run_search, setup_logging
9+
10+
logger = logging.getLogger(__name__)
11+
12+
13+
async def process_problem(
14+
problem_dir: str,
15+
algorithm: str,
16+
sem: asyncio.Semaphore,
17+
**kwargs: Any,
18+
) -> Tuple[str, str]:
19+
"""Executes the search algorithm for a single benchmark problem in the batch."""
20+
async with sem:
21+
try:
22+
return await run_search(
23+
problem_dir=problem_dir,
24+
algorithm=algorithm,
25+
**kwargs,
26+
)
27+
except Exception as e:
28+
problem_id = os.path.basename(os.path.normpath(problem_dir))
29+
logger.error(
30+
f"Error executing search on {problem_id}: {e}", exc_info=True
31+
)
32+
return problem_id, f"Failed with exception: {e}"
33+
34+
35+
async def run_batch_search(
36+
data_dir: str,
37+
algorithm: str = "parallel",
38+
num_problem_concurrency: int = 1,
39+
**kwargs: Any,
40+
):
41+
"""Coordinates concurrent problem execution across the dataset."""
42+
if not os.path.isdir(data_dir):
43+
logger.error(
44+
f"Dataset directory not found or not a directory: {data_dir}"
45+
)
46+
return
47+
48+
data_dir_valid = [
49+
os.path.join(data_dir, d)
50+
for d in os.listdir(data_dir)
51+
if os.path.isfile(os.path.join(data_dir, d, "reference.py"))
52+
]
53+
data_dir_valid.sort()
54+
55+
if not data_dir_valid:
56+
logger.warning(
57+
f"No valid benchmark problems (directories containing reference.py) found in {data_dir}"
58+
)
59+
return
60+
61+
logger.info(f"Found {len(data_dir_valid)} problems to process.")
62+
max_concurrency = kwargs.get("max_concurrency", 2)
63+
logger.info(
64+
f"Algorithm: {algorithm}, Problem Concurrency:"
65+
f" {num_problem_concurrency}, Worker Concurrency: {max_concurrency}"
66+
)
67+
68+
sem = asyncio.Semaphore(num_problem_concurrency)
69+
tasks = [
70+
process_problem(
71+
problem_dir=problem_dir,
72+
algorithm=algorithm,
73+
sem=sem,
74+
**kwargs,
75+
)
76+
for problem_dir in data_dir_valid
77+
]
78+
79+
completed = 0
80+
results = []
81+
for future in asyncio.as_completed(tasks):
82+
try:
83+
prob_id, status = await future
84+
completed += 1
85+
results.append((prob_id, status))
86+
logger.info(
87+
f"[{completed}/{len(data_dir_valid)}] Problem {prob_id}: {status}"
88+
)
89+
except Exception as e:
90+
logger.error(f"A task raised an exception: {e}")
91+
92+
logger.info("\n--- Search Execution Summary ---")
93+
for prob_id, status in sorted(results):
94+
logger.info(f"{prob_id}: {status}")
95+
96+
97+
def parse_args() -> argparse.Namespace:
98+
parser = argparse.ArgumentParser(
99+
description="Run Auto-Search algorithms against batch dataset."
100+
)
101+
102+
# General & Orchestration Arguments
103+
orch_group = parser.add_argument_group(
104+
"General & Orchestration Arguments",
105+
"Arguments shared across all algorithms and orchestrators.",
106+
)
107+
orch_group.add_argument(
108+
"--data_dir",
109+
type=str,
110+
required=True,
111+
help="Path to benchmark dataset directory",
112+
)
113+
orch_group.add_argument(
114+
"--algorithm",
115+
type=str,
116+
choices=["parallel", "beam", "agentic"],
117+
default="parallel",
118+
help="Search algorithm to execute",
119+
)
120+
orch_group.add_argument(
121+
"--max_concurrency",
122+
type=int,
123+
default=2,
124+
help="Max concurrent worker expansions",
125+
)
126+
orch_group.add_argument(
127+
"--num_problem_concurrency",
128+
type=int,
129+
default=1,
130+
help="Number of dataset problems to run concurrently",
131+
)
132+
orch_group.add_argument(
133+
"--log_file",
134+
type=str,
135+
default=None,
136+
help="File to save logs to",
137+
)
138+
# Parallel Search Arguments
139+
parallel_group = parser.add_argument_group(
140+
"Parallel Search Arguments",
141+
"Parameters specific to the 'parallel' search algorithm.",
142+
)
143+
parallel_group.add_argument(
144+
"--num_parallel_runs",
145+
type=int,
146+
default=2,
147+
help="Number of parallel runs",
148+
)
149+
parallel_group.add_argument(
150+
"--max_retries",
151+
type=int,
152+
default=1,
153+
help="Max worker retries per expansion task",
154+
)
155+
parallel_group.add_argument(
156+
"--strategies",
157+
nargs="+",
158+
type=str,
159+
default=None,
160+
help="List of strategy strings to explore",
161+
)
162+
parallel_group.add_argument(
163+
"--agent_config",
164+
type=str,
165+
default=None,
166+
help="JSON string of agent config parameters (e.g. '{\"max_iterations\": 5}')",
167+
)
168+
return parser.parse_args()
169+
170+
171+
def main():
172+
args = parse_args()
173+
setup_logging(args.log_file)
174+
175+
parsed_agent_config = None
176+
if args.agent_config:
177+
try:
178+
parsed_agent_config = json.loads(args.agent_config)
179+
except json.JSONDecodeError as e:
180+
logger.error(f"Invalid JSON string for --agent_config: {e}")
181+
return
182+
183+
kwargs = {
184+
"max_concurrency": args.max_concurrency,
185+
"num_parallel_runs": args.num_parallel_runs,
186+
"max_worker_retries": args.max_retries,
187+
"strategies": args.strategies,
188+
"agent_config": parsed_agent_config,
189+
}
190+
191+
asyncio.run(
192+
run_batch_search(
193+
data_dir=args.data_dir,
194+
algorithm=args.algorithm,
195+
num_problem_concurrency=args.num_problem_concurrency,
196+
**kwargs,
197+
)
198+
)
199+
200+
201+
if __name__ == "__main__":
202+
main()

0 commit comments

Comments
 (0)