Skip to content

Commit f7862b9

Browse files
committed
Implement 2023-07 time limit computation (but hard coded to use accepted for lower bound)
1 parent 69357b2 commit f7862b9

File tree

1 file changed

+38
-14
lines changed

1 file changed

+38
-14
lines changed

problemtools/verifyproblem.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class Context:
109109
def __init__(self, args: argparse.Namespace, executor: ThreadPoolExecutor | None) -> None:
110110
self.data_filter: Pattern[str] = args.data_filter
111111
self.submission_filter: Pattern[str] = args.submission_filter
112-
self.fixed_timelim: int | None = args.fixed_timelim
112+
self.fixed_timelim: float | None = args.fixed_timelim
113113
self.executor = executor
114114
self._background_work: list[concurrent.futures.Future[object]] = []
115115

@@ -990,7 +990,11 @@ def check(self, context: Context) -> bool:
990990

991991
if self._metadata.limits.time_limit is not None and not self._metadata.limits.time_limit.is_integer():
992992
self.warning(
993-
'Time limit configured to non-integer value. Problemtools does not yet support non-integer time limits, and will truncate'
993+
'Time limit configured to non-integer value. This can be fragile, and may not be supported by your CCS (Kattis does not).'
994+
)
995+
if not self._metadata.limits.time_resolution.is_integer():
996+
self.warning(
997+
'Time resolution is not an integer. This can be fragile, and may not be supported by your CCS (Kattis does not).'
994998
)
995999

9961000
return self._check_res
@@ -1832,15 +1836,19 @@ def check(self, context: Context) -> bool:
18321836
time_multiplier = limits.time_multipliers.ac_to_time_limit
18331837
safety_margin = limits.time_multipliers.time_limit_to_tle
18341838

1835-
timelim_margin_lo = 300 # 5 minutes
1836-
timelim_margin = 300
1837-
timelim = 300
1839+
timelim_margin_lo = 300.0 # 5 minutes
1840+
timelim_margin = 300.0
1841+
timelim = 300.0
18381842

18391843
if limits.time_limit is not None:
1840-
timelim = timelim_margin = int(limits.time_limit) # TODO: Support non-integer time limits
1841-
if context.fixed_timelim is not None:
1844+
timelim = limits.time_limit
1845+
timelim_margin = timelim * safety_margin
1846+
if context.fixed_timelim is not None: # It's weird to set this both in limits and in context, but if so, context wins
1847+
self.warning(
1848+
'There is a fixed time limit in problem.yaml, and you also provided one on the command line. Using command line.'
1849+
)
18421850
timelim = context.fixed_timelim
1843-
timelim_margin = int(round(timelim * safety_margin))
1851+
timelim_margin = timelim * safety_margin
18441852

18451853
for verdict in Submissions._VERDICTS:
18461854
acr = verdict[0]
@@ -1873,17 +1881,33 @@ def check(self, context: Context) -> bool:
18731881
max_runtime = max(runtimes)
18741882
exact_timelim = max_runtime * time_multiplier
18751883
max_runtime_str = f'{max_runtime:.3f}'
1876-
timelim = max(1, int(0.5 + exact_timelim)) # TODO: properly support 2023-07 time limit computation
1877-
timelim_margin_lo = max(1, min(int(0.5 + exact_timelim / safety_margin), timelim - 1))
1878-
timelim_margin = max(timelim + 1, int(0.5 + exact_timelim * safety_margin))
1884+
timelim = math.ceil(exact_timelim / limits.time_resolution) * limits.time_resolution
1885+
timelim_margin = timelim * safety_margin
1886+
# timelim_margin_lo is a bit weird. We use it for partially_accepted, which we want to be roughly as fast as accepted
1887+
# solutions. Setting it to the rounded timelim / time_multiplier means that we check that none of the submissions we
1888+
# apply this to would have affected the timelim if we based it on them.
1889+
timelim_margin_lo = timelim / time_multiplier
18791890
else:
18801891
max_runtime_str = None
1881-
if context.fixed_timelim is not None and context.fixed_timelim != timelim:
1892+
1893+
if limits.time_limit is not None:
1894+
if max_runtime > limits.time_limit / time_multiplier:
1895+
self.error(
1896+
f'Time limit set to {limits.time_limit}, but slowest AC runs in {max_runtime_str} which is within a factor {time_multiplier}.'
1897+
)
1898+
if not math.isclose(limits.time_limit, timelim):
1899+
self.msg(
1900+
f' Solutions give timelim of {timelim} seconds, but will use provided fixed limit of {limits.time_limit} seconds instead'
1901+
)
1902+
timelim = limits.time_limit
1903+
timelim_margin = timelim * safety_margin
1904+
1905+
if context.fixed_timelim is not None and not math.isclose(context.fixed_timelim, timelim):
18821906
self.msg(
18831907
f' Solutions give timelim of {timelim} seconds, but will use provided fixed limit of {context.fixed_timelim} seconds instead'
18841908
)
18851909
timelim = context.fixed_timelim
1886-
timelim_margin = round(timelim * safety_margin)
1910+
timelim_margin = timelim * safety_margin
18871911

18881912
self.msg(
18891913
f' Slowest AC runtime: {max_runtime_str}, setting timelim to {timelim} secs, safety margin to {timelim_margin} secs'
@@ -2150,7 +2174,7 @@ def argparser() -> argparse.ArgumentParser:
21502174
parser.add_argument(
21512175
'-t',
21522176
'--fixed_timelim',
2153-
type=int,
2177+
type=float,
21542178
help='use this fixed time limit (useful in combination with -d and/or -s when all AC submissions might not be run on all data)',
21552179
)
21562180
parser.add_argument(

0 commit comments

Comments
 (0)