@@ -183,20 +183,38 @@ def get_filter_layout(register: Callable) -> List[Any]:
183183 The layouts for the filter block.
184184 """
185185 return [
186- html . Div (
186+ dbc . Row (
187187 [
188- dbc .Label ("Show all configurations" ),
189- help_button (
190- "Additionally to the pareto front, also the other configurations "
191- "are displayed. This makes it easier to see the performance "
192- "differences."
188+ dbc .Col (
189+ [
190+ dbc .Label ("Show all configurations" ),
191+ help_button (
192+ "Additionally to the pareto front, also the other configurations "
193+ "are displayed. This makes it easier to see the performance "
194+ "differences."
195+ ),
196+ dbc .Select (
197+ id = register ("show_all" , ["value" , "options" ]),
198+ placeholder = "Select ..." ,
199+ ),
200+ ],
201+ md = 6 ,
193202 ),
194- dbc .Select (
195- id = register ("show_all" , ["value" , "options" ]),
196- placeholder = "Select ..." ,
203+ dbc .Col (
204+ [
205+ dbc .Label ("Show error bars" ),
206+ help_button (
207+ "Show error bars In the case of non-deterministic runs with "
208+ "multiple seeds evaluated per configuration."
209+ ),
210+ dbc .Select (
211+ id = register ("show_error" , ["value" , "options" ]),
212+ placeholder = "Select ..." ,
213+ ),
214+ ],
215+ md = 6 ,
197216 ),
198217 ],
199- className = "mb-3" ,
200218 ),
201219 dbc .Row (
202220 [
@@ -251,6 +269,7 @@ def load_inputs(self) -> Dict[str, Dict[str, Any]]:
251269 "value" : self .budget_options [- 1 ]["value" ],
252270 },
253271 "show_all" : {"options" : get_select_options (binary = True ), "value" : "false" },
272+ "show_error" : {"options" : get_select_options (binary = True ), "value" : "false" },
254273 "show_runs" : {"options" : get_select_options (binary = True ), "value" : "true" },
255274 "show_groups" : {"options" : get_select_options (binary = True ), "value" : "true" },
256275 }
@@ -291,22 +310,29 @@ def process(run, inputs) -> Dict[str, Any]: # type: ignore
291310 objective_id_2 = inputs ["objective_id_2" ]
292311 objective_2 = run .get_objective (objective_id_2 )
293312
294- points : Union [List , np .ndarray ] = []
295- config_ids : Union [List , np .ndarray ] = []
296- for config_id , costs in run .get_all_costs (budget , statuses = [Status .SUCCESS ]).items ():
297- points += [[costs [objective_id_1 ], costs [objective_id_2 ]]]
298- config_ids += [config_id ]
313+ points_avg : Union [List , np .ndarray ] = []
314+ points_std : Union [List , np .ndarray ] = []
315+ config_ids : Union [List , np .ndarray ] = list (
316+ run .get_configs (budget , statuses = [Status .SUCCESS ]).keys ()
317+ )
318+
319+ for config_id in config_ids :
320+ avg_costs , std_costs = run .get_avg_costs (config_id , budget , statuses = [Status .SUCCESS ])
321+ points_avg += [[avg_costs [objective_id_1 ], avg_costs [objective_id_2 ]]]
322+ points_std += [[std_costs [objective_id_1 ], std_costs [objective_id_2 ]]]
299323
300- points = np .array (points )
324+ points_avg = np .array (points_avg )
325+ points_std = np .array (points_std )
301326 config_ids = np .array (config_ids )
302327
303328 # Sort the points s.t. x axis is monotonically increasing
304- sorted_idx = np .argsort (points [:, 0 ])
305- points = points [sorted_idx ]
329+ sorted_idx = np .argsort (points_avg [:, 0 ])
330+ points_avg = points_avg [sorted_idx ]
331+ points_std = points_std [sorted_idx ]
306332 config_ids = config_ids [sorted_idx ]
307333
308- is_front : np .ndarray = np .ones (points .shape [0 ], dtype = bool )
309- for point_idx , costs in enumerate (points ):
334+ is_front : np .ndarray = np .ones (points_avg .shape [0 ], dtype = bool )
335+ for point_idx , costs in enumerate (points_avg ):
310336 if is_front [point_idx ]:
311337 # Keep any point with a lower/upper cost
312338 # This loop is a little bit complicated than
@@ -316,9 +342,9 @@ def process(run, inputs) -> Dict[str, Any]: # type: ignore
316342 select = None
317343 for idx , (objective , cost ) in enumerate (zip ([objective_1 , objective_2 ], costs )):
318344 if objective .optimize == "upper" :
319- select2 = np .any (points [is_front ][:, idx , np .newaxis ] > [cost ], axis = 1 )
345+ select2 = np .any (points_avg [is_front ][:, idx , np .newaxis ] > [cost ], axis = 1 )
320346 else :
321- select2 = np .any (points [is_front ][:, idx , np .newaxis ] < [cost ], axis = 1 )
347+ select2 = np .any (points_avg [is_front ][:, idx , np .newaxis ] < [cost ], axis = 1 )
322348
323349 if select is None :
324350 select = select2
@@ -331,7 +357,8 @@ def process(run, inputs) -> Dict[str, Any]: # type: ignore
331357 is_front [point_idx ] = True
332358
333359 return {
334- "points" : points .tolist (),
360+ "points_avg" : points_avg .tolist (),
361+ "points_std" : points_std .tolist (),
335362 "pareto_points" : is_front .tolist (),
336363 "config_ids" : config_ids .tolist (),
337364 }
@@ -384,6 +411,7 @@ def load_outputs(runs, inputs, outputs) -> go.Figure: # type: ignore
384411 The output figure.
385412 """
386413 show_all = inputs ["show_all" ]
414+ show_error = inputs ["show_error" ]
387415
388416 traces = []
389417 for idx , run in enumerate (runs ):
@@ -396,31 +424,50 @@ def load_outputs(runs, inputs, outputs) -> go.Figure: # type: ignore
396424 if run .prefix != "group" and not show_runs :
397425 continue
398426
399- points = np .array (outputs [run .id ]["points" ])
427+ points_avg = np .array (outputs [run .id ]["points_avg" ])
428+ points_std = np .array (outputs [run .id ]["points_std" ])
400429 config_ids = outputs [run .id ]["config_ids" ]
430+ budget = run .get_budget (inputs ["budget_id" ])
401431 pareto_config_ids = []
402432
403- x , y = [], []
404- x_pareto , y_pareto = [], []
433+ x , y , x_std , y_std = [], [], [], []
434+ x_pareto , y_pareto , x_pareto_std , y_pareto_std = [], [], [], []
405435
406436 pareto_points = outputs [run .id ]["pareto_points" ]
407437 for point_idx , pareto in enumerate (pareto_points ):
408438 if pareto :
409- x_pareto += [points [point_idx ][0 ]]
410- y_pareto += [points [point_idx ][1 ]]
439+ x_pareto += [points_avg [point_idx ][0 ]]
440+ y_pareto += [points_avg [point_idx ][1 ]]
441+ x_pareto_std += [points_std [point_idx ][0 ]]
442+ y_pareto_std += [points_std [point_idx ][1 ]]
411443 pareto_config_ids += [config_ids [point_idx ]]
412444 else :
413- x += [points [point_idx ][0 ]]
414- y += [points [point_idx ][1 ]]
445+ x += [points_avg [point_idx ][0 ]]
446+ y += [points_avg [point_idx ][1 ]]
447+ x_std += [points_std [point_idx ][0 ]]
448+ y_std += [points_std [point_idx ][1 ]]
415449
416450 color = get_color (idx , alpha = 0.1 )
417451 color_pareto = get_color (idx )
418452
419453 if show_all :
454+ error_x = (
455+ dict (array = x_std , color = "rgba(0, 0, 0, 0.3)" )
456+ if show_error and not all (value == 0.0 for value in x_std )
457+ else None
458+ )
459+ error_y = (
460+ dict (array = y_std , color = "rgba(0, 0, 0, 0.3)" )
461+ if show_error and not all (value == 0.0 for value in y_std )
462+ else None
463+ )
464+
420465 traces .append (
421466 go .Scatter (
422467 x = x ,
423468 y = y ,
469+ error_x = error_x ,
470+ error_y = error_y ,
424471 name = run .name ,
425472 mode = "markers" ,
426473 showlegend = False ,
@@ -444,13 +491,26 @@ def load_outputs(runs, inputs, outputs) -> go.Figure: # type: ignore
444491 line_shape = "hv"
445492
446493 hovertext = [
447- get_hovertext_from_config (run , config_id ) for config_id in pareto_config_ids
494+ get_hovertext_from_config (run , config_id , budget ) for config_id in pareto_config_ids
448495 ]
449496
497+ error_pareto_x = (
498+ dict (array = x_pareto_std , color = "rgba(0, 0, 0, 0.3)" )
499+ if show_error and not all (value == 0.0 for value in x_pareto_std )
500+ else None
501+ )
502+ error_pareto_y = (
503+ dict (array = y_pareto_std , color = "rgba(0, 0, 0, 0.3)" )
504+ if show_error and not all (value == 0.0 for value in y_pareto_std )
505+ else None
506+ )
507+
450508 traces .append (
451509 go .Scatter (
452510 x = x_pareto ,
453511 y = y_pareto ,
512+ error_x = error_pareto_x ,
513+ error_y = error_pareto_y ,
454514 name = run .name ,
455515 line_shape = line_shape ,
456516 showlegend = True ,
0 commit comments