From 775a06459cb88628a692e71b42ed745888ebe33e Mon Sep 17 00:00:00 2001 From: lbh930 Date: Mon, 1 Dec 2025 09:43:42 -0600 Subject: [PATCH 1/2] Fix flaky ReproUtilTest by adding fallback lookup for trainers --- .../org/tribuo/reproducibility/ReproUtil.java | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Reproducibility/src/main/java/org/tribuo/reproducibility/ReproUtil.java b/Reproducibility/src/main/java/org/tribuo/reproducibility/ReproUtil.java index 41b9ec556..4a8408c61 100644 --- a/Reproducibility/src/main/java/org/tribuo/reproducibility/ReproUtil.java +++ b/Reproducibility/src/main/java/org/tribuo/reproducibility/ReproUtil.java @@ -24,6 +24,7 @@ import com.oracle.labs.mlrg.olcut.config.Configurable; import com.oracle.labs.mlrg.olcut.config.ConfigurationData; import com.oracle.labs.mlrg.olcut.config.ConfigurationManager; +import com.oracle.labs.mlrg.olcut.config.PropertyException; import com.oracle.labs.mlrg.olcut.provenance.ConfiguredObjectProvenance; import com.oracle.labs.mlrg.olcut.provenance.ListProvenance; import com.oracle.labs.mlrg.olcut.provenance.MapProvenance; @@ -180,7 +181,30 @@ public Trainer recoverTrainer() { for (int i = 0; i < ordering.traversalOrder.size(); i++){ if(ordering.traversalOrder.get(i) instanceof TrainerProvenance trainerProvenance){ String componentName = ProvenanceUtil.computeName(trainerProvenance, i); - Configurable configurableObject = cm.lookup(componentName); + Configurable configurableObject; + try { + configurableObject = cm.lookup(componentName); + } catch (PropertyException e) { + try { + Class trainerClass = Class.forName(trainerProvenance.getClassName()); + if (Configurable.class.isAssignableFrom(trainerClass)) { + @SuppressWarnings("unchecked") + Class configurableClass = (Class) trainerClass; + List names = cm.listAll(configurableClass); + if (names.size() == 1) { + logger.log(Level.WARNING, "Fallback to class-based lookup for component " + componentName + ", found " + names.get(0)); + configurableObject = cm.lookup(names.get(0)); + } else { + throw e; + } + } else { + throw e; + } + } catch (ClassNotFoundException ex) { + throw new IllegalStateException("Failed to load trainer class " + trainerProvenance.getClassName(), ex); + } + } + // Once a Trainer is identified we need to set the invocationCount as identified // in the provenance. Invocation count is not configurable since it is a provenance value, // it is an immutable value mapping one-to-one to a single execution. From b0b7001ba114e60045732a042890019f10d0320c Mon Sep 17 00:00:00 2001 From: lbh930 Date: Mon, 1 Dec 2025 16:53:36 -0600 Subject: [PATCH 2/2] Fix flaky ReproUtilTest by deterministic trainer recovery fallbacks --- .../org/tribuo/reproducibility/ReproUtil.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/Reproducibility/src/main/java/org/tribuo/reproducibility/ReproUtil.java b/Reproducibility/src/main/java/org/tribuo/reproducibility/ReproUtil.java index 4a8408c61..c004a4ede 100644 --- a/Reproducibility/src/main/java/org/tribuo/reproducibility/ReproUtil.java +++ b/Reproducibility/src/main/java/org/tribuo/reproducibility/ReproUtil.java @@ -177,6 +177,7 @@ public Trainer recoverTrainer() { // construct the correct component name using ProvenanceUtil.computeName, and // link a provObject to its corresponding configuration in the cm. ProvenanceUtil.ProvenanceOrdering ordering = ProvenanceUtil.orderProvenances(this.modelProvenance); + Set usedComponentNames = new java.util.HashSet<>(); for (int i = 0; i < ordering.traversalOrder.size(); i++){ if(ordering.traversalOrder.get(i) instanceof TrainerProvenance trainerProvenance){ @@ -184,6 +185,7 @@ public Trainer recoverTrainer() { Configurable configurableObject; try { configurableObject = cm.lookup(componentName); + usedComponentNames.add(componentName); } catch (PropertyException e) { try { Class trainerClass = Class.forName(trainerProvenance.getClassName()); @@ -191,9 +193,33 @@ public Trainer recoverTrainer() { @SuppressWarnings("unchecked") Class configurableClass = (Class) trainerClass; List names = cm.listAll(configurableClass); - if (names.size() == 1) { - logger.log(Level.WARNING, "Fallback to class-based lookup for component " + componentName + ", found " + names.get(0)); - configurableObject = cm.lookup(names.get(0)); + // Sort names to ensure deterministic mapping, preferring numeric suffix sort + Collections.sort(names, (a, b) -> { + try { + int lastDashA = a.lastIndexOf('-'); + int lastDashB = b.lastIndexOf('-'); + if (lastDashA != -1 && lastDashB != -1) { + int valA = Integer.parseInt(a.substring(lastDashA + 1)); + int valB = Integer.parseInt(b.substring(lastDashB + 1)); + return Integer.compare(valA, valB); + } + } catch (NumberFormatException nfe) { + // ignore + } + return a.compareTo(b); + }); + + String foundName = null; + for (String name : names) { + if (!usedComponentNames.contains(name)) { + foundName = name; + break; + } + } + if (foundName != null) { + logger.log(Level.WARNING, "Fallback to class-based lookup for component " + componentName + ", found " + foundName); + configurableObject = cm.lookup(foundName); + usedComponentNames.add(foundName); } else { throw e; }