From 6d0840013842245b353238f2735e6d4e6881792f Mon Sep 17 00:00:00 2001 From: Nolwen Date: Mon, 19 Jan 2026 09:41:35 +0100 Subject: [PATCH] Replace -inf, +inf, NaN in programs metrics by None before visualizing When the visualizer import data from a checkpoint, this is sent to the javascript via a response object decoded with `resp.json()` in `fetchAndRender()` from "main.js". This is crashing if it does not respect fully json specs (and NaN, Infinity are not json valid even though js objects). This is useful for evolutions based on positive metrics to minimize (like a cost). In that case, we want to put -metric in combined_score (which will then be negative). Thus an evolved program not working should be given a worse score during evaluation. An easy way to do it is to put -inf (instead of not outputing any metric, which will be replaced by a 0 by default by the database when requesting a fitness). Doing so works well during evolution (ranking the top programs as expected), but during visualization, it was raising an error when fetching data. --- scripts/visualizer.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/scripts/visualizer.py b/scripts/visualizer.py index 98f16a974..f2652dada 100644 --- a/scripts/visualizer.py +++ b/scripts/visualizer.py @@ -1,9 +1,13 @@ +import math import os import json import glob import logging import shutil import re as _re +from numbers import Number +from typing import Optional, Any + from flask import Flask, render_template, render_template_string, jsonify @@ -60,6 +64,7 @@ def load_evolution_data(checkpoint_folder): if os.path.exists(prog_path): with open(prog_path) as pf: prog = json.load(pf) + sanitize_program_for_visualization(prog) prog["id"] = pid prog["island"] = island_idx nodes.append(prog) @@ -81,6 +86,18 @@ def load_evolution_data(checkpoint_folder): "checkpoint_dir": checkpoint_folder, } +def sanitize_program_for_visualization(program: dict[str, Any]) -> None: + for k, v in program["metrics"].items(): + if not check_json_float(v): + program["metrics"][k] = None + if "parent_metrics" in program["metadata"]: + for k, v in program["metadata"]["parent_metrics"].items(): + if not check_json_float(v): + program["metadata"]["parent_metrics"][k] = None + +def check_json_float(v: Optional[float]) -> bool: + return isinstance(v, Number) and not (math.isinf(v) or math.isnan(v)) + @app.route("/") def index():