@@ -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