From aec182dd5da7f41084c2c8d7dea3b1d8d81f574c 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 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/visualizer.py b/scripts/visualizer.py index ed520d974..715be2949 100644 --- a/scripts/visualizer.py +++ b/scripts/visualizer.py @@ -1,9 +1,12 @@ +import math import os import json import glob import shutil import logging import re as _re +from numbers import Number +from typing import Optional, Any from flask import Flask, render_template, jsonify @@ -63,6 +66,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) @@ -84,6 +88,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():