From 174acd4ad5bbba9397b8a8ecc3252ee5030c7020 Mon Sep 17 00:00:00 2001 From: jalmeida Date: Tue, 26 May 2026 14:13:53 +0100 Subject: [PATCH 1/3] chore: bump h2o version Bump h2o version to 3.46.0.11 and updates GLM parameter builder to account for missing defaults --- openml-h2o/pom.xml | 2 +- .../algos/H2OGeneralizedLinearModelUtils.java | 19 ++++++++------- .../h2o/params/ParametersBuilderUtil.java | 24 ++++++++++++++++++- .../openml/h2o/H2OAlgorithmTestParams.java | 2 +- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/openml-h2o/pom.xml b/openml-h2o/pom.xml index dc7a70f8..16eeeae7 100644 --- a/openml-h2o/pom.xml +++ b/openml-h2o/pom.xml @@ -30,7 +30,7 @@ Contains classes and logic related with the import of H2O models. - 3.36.0.3 + 3.46.0.11 diff --git a/openml-h2o/src/main/java/com/feedzai/openml/h2o/algos/H2OGeneralizedLinearModelUtils.java b/openml-h2o/src/main/java/com/feedzai/openml/h2o/algos/H2OGeneralizedLinearModelUtils.java index 94430ad8..fc6ea416 100644 --- a/openml-h2o/src/main/java/com/feedzai/openml/h2o/algos/H2OGeneralizedLinearModelUtils.java +++ b/openml-h2o/src/main/java/com/feedzai/openml/h2o/algos/H2OGeneralizedLinearModelUtils.java @@ -59,15 +59,16 @@ public final class H2OGeneralizedLinearModelUtils extends AbstractSupervisedH2OA */ public static final Set PARAMETERS = ParametersBuilderUtil.getParametersFor(GLMParametersV3.class, water.bindings.pojos.GLMParametersV3.class) - .stream() - // Excluded since we "hard-code" to classification. - .filter(modelParam -> !FAMILY.equals(modelParam.getName())) - // H2O UI excludes these in a hard-coded way...so do we. - .filter(modelParameter -> !"tweedie_link_power".equals(modelParameter.getName()) && - !"tweedie_variance_power".equals(modelParameter.getName()) && - !"nlambdas".equals(modelParameter.getName()) && !"early_stopping".equals(modelParameter.getName()) - ) - .collect(Collectors.toSet()); + .stream() + // Excluded since we "hard-code" to classification. + .filter(modelParam -> !FAMILY.equals(modelParam.getName())) + // H2O UI excludes these in a hard-coded way...so do we. + .filter(modelParameter -> !"tweedie_link_power".equals(modelParameter.getName()) && + !"tweedie_variance_power".equals(modelParameter.getName()) && + !"nlambdas".equals(modelParameter.getName()) && !"early_stopping".equals(modelParameter.getName()) && + !"influence".equals(modelParameter.getName()) + ) + .collect(Collectors.toSet()); /** * The complete collection of model parameter names of an H2O GLM model. diff --git a/openml-h2o/src/main/java/com/feedzai/openml/h2o/params/ParametersBuilderUtil.java b/openml-h2o/src/main/java/com/feedzai/openml/h2o/params/ParametersBuilderUtil.java index 0ac1d95d..ca900d05 100644 --- a/openml-h2o/src/main/java/com/feedzai/openml/h2o/params/ParametersBuilderUtil.java +++ b/openml-h2o/src/main/java/com/feedzai/openml/h2o/params/ParametersBuilderUtil.java @@ -243,7 +243,29 @@ private static ModelParameter getModelParameter(final Class 0) { final Set possibleValues = Arrays.stream(apiAnnot.values()).collect(Collectors.toSet()); final Enum defaultValue = getDefaultChoiceValue(paramsClass, fieldName); - paramType = new ChoiceFieldType(possibleValues, defaultValue.name()); + String defaultValueName = defaultValue != null ? defaultValue.name() : ""; + if (!possibleValues.contains(defaultValueName)) { + final String finalDefaultName = defaultValueName; + + // Check if there is a match + final Optional match = possibleValues.stream() + .filter(val -> val.equalsIgnoreCase(finalDefaultName)) + .findFirst(); + + if (match.isPresent()) { + defaultValueName = match.get(); + } else if (!possibleValues.isEmpty()) { + // Fallback to the first allowed choice if the default is missing or null + defaultValueName = possibleValues.iterator().next(); + logger.warn( + "Default value name `{}` is not present on possible values set `{}`. Falling back to `{}`", + finalDefaultName, + possibleValues, + defaultValueName + ); + } + } + paramType = new ChoiceFieldType(possibleValues, defaultValueName); } else if (fieldType.equals(String.class)) { final String defaultValue = getDefaultStringValue(paramsClass, fieldName); diff --git a/openml-h2o/src/test/java/com/feedzai/openml/h2o/H2OAlgorithmTestParams.java b/openml-h2o/src/test/java/com/feedzai/openml/h2o/H2OAlgorithmTestParams.java index b01ea281..732852cf 100644 --- a/openml-h2o/src/test/java/com/feedzai/openml/h2o/H2OAlgorithmTestParams.java +++ b/openml-h2o/src/test/java/com/feedzai/openml/h2o/H2OAlgorithmTestParams.java @@ -133,7 +133,7 @@ public static Map getGlm() { .put("lambda", "1") .put("lambda_search", "true") .put("standardize", "true") - .put("non_negative", "true") + .put("non_negative", "false") .put("obj_reg", "-1") .put("theta", "1e-10") // default value .put("HGLM", "false") // default value From 26b9d509f8dd186990fcb807de371c227b717931 Mon Sep 17 00:00:00 2001 From: jalmeida Date: Fri, 29 May 2026 08:43:58 +0100 Subject: [PATCH 2/3] chore: bump h2o version Update comments --- .../com/feedzai/openml/h2o/H2OAlgorithm.java | 2 -- .../algos/H2OGeneralizedLinearModelUtils.java | 20 +++++++++---------- .../openml/h2o/H2OAlgorithmTestParams.java | 2 ++ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/openml-h2o/src/main/java/com/feedzai/openml/h2o/H2OAlgorithm.java b/openml-h2o/src/main/java/com/feedzai/openml/h2o/H2OAlgorithm.java index 61e75053..897af1ec 100644 --- a/openml-h2o/src/main/java/com/feedzai/openml/h2o/H2OAlgorithm.java +++ b/openml-h2o/src/main/java/com/feedzai/openml/h2o/H2OAlgorithm.java @@ -95,8 +95,6 @@ public enum H2OAlgorithm implements MLAlgorithmEnum { /** * Generalized Linear Models (GLM) estimate regression models for outcomes following exponential distributions. - *

- * We enforce it to always have a family parameter Binomial that causes it to be a classification algorithm. */ GENERALIZED_LINEAR_MODEL(createDescriptor( "Generalized Linear Modeling", diff --git a/openml-h2o/src/main/java/com/feedzai/openml/h2o/algos/H2OGeneralizedLinearModelUtils.java b/openml-h2o/src/main/java/com/feedzai/openml/h2o/algos/H2OGeneralizedLinearModelUtils.java index fc6ea416..a1cd1661 100644 --- a/openml-h2o/src/main/java/com/feedzai/openml/h2o/algos/H2OGeneralizedLinearModelUtils.java +++ b/openml-h2o/src/main/java/com/feedzai/openml/h2o/algos/H2OGeneralizedLinearModelUtils.java @@ -59,16 +59,16 @@ public final class H2OGeneralizedLinearModelUtils extends AbstractSupervisedH2OA */ public static final Set PARAMETERS = ParametersBuilderUtil.getParametersFor(GLMParametersV3.class, water.bindings.pojos.GLMParametersV3.class) - .stream() - // Excluded since we "hard-code" to classification. - .filter(modelParam -> !FAMILY.equals(modelParam.getName())) - // H2O UI excludes these in a hard-coded way...so do we. - .filter(modelParameter -> !"tweedie_link_power".equals(modelParameter.getName()) && - !"tweedie_variance_power".equals(modelParameter.getName()) && - !"nlambdas".equals(modelParameter.getName()) && !"early_stopping".equals(modelParameter.getName()) && - !"influence".equals(modelParameter.getName()) - ) - .collect(Collectors.toSet()); + .stream() + // Excluded since we "hard-code" to classification. + .filter(modelParam -> !FAMILY.equals(modelParam.getName())) + // H2O UI excludes these in a hard-coded way...so do we. + .filter(modelParameter -> !"tweedie_link_power".equals(modelParameter.getName()) && + !"tweedie_variance_power".equals(modelParameter.getName()) && + !"nlambdas".equals(modelParameter.getName()) && !"early_stopping".equals(modelParameter.getName()) && + !"influence".equals(modelParameter.getName()) + ) + .collect(Collectors.toSet()); /** * The complete collection of model parameter names of an H2O GLM model. diff --git a/openml-h2o/src/test/java/com/feedzai/openml/h2o/H2OAlgorithmTestParams.java b/openml-h2o/src/test/java/com/feedzai/openml/h2o/H2OAlgorithmTestParams.java index 732852cf..552f98e5 100644 --- a/openml-h2o/src/test/java/com/feedzai/openml/h2o/H2OAlgorithmTestParams.java +++ b/openml-h2o/src/test/java/com/feedzai/openml/h2o/H2OAlgorithmTestParams.java @@ -133,6 +133,8 @@ public static Map getGlm() { .put("lambda", "1") .put("lambda_search", "true") .put("standardize", "true") + // non_negative does not work with multinomial/ordinal families and GLM + // fails on init if set to true (see https://github.com/h2oai/h2o-3/issues/7055) .put("non_negative", "false") .put("obj_reg", "-1") .put("theta", "1e-10") // default value From badfef90623ba47230bb87bd7303d5e6baaab039 Mon Sep 17 00:00:00 2001 From: jalmeida Date: Fri, 29 May 2026 14:20:23 +0100 Subject: [PATCH 3/3] chore: bump h2o version Update ParametersTest.java --- .../openml/h2o/algos/ParametersTest.java | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/openml-h2o/src/test/java/com/feedzai/openml/h2o/algos/ParametersTest.java b/openml-h2o/src/test/java/com/feedzai/openml/h2o/algos/ParametersTest.java index 39a743d2..99f16fe4 100644 --- a/openml-h2o/src/test/java/com/feedzai/openml/h2o/algos/ParametersTest.java +++ b/openml-h2o/src/test/java/com/feedzai/openml/h2o/algos/ParametersTest.java @@ -32,6 +32,8 @@ import com.feedzai.openml.h2o.algos.mocks.RegularParameters; import com.feedzai.openml.h2o.params.ParametersBuilderUtil; import com.feedzai.openml.provider.descriptor.ModelParameter; +import com.feedzai.openml.provider.descriptor.fieldtype.ChoiceFieldType; + import org.junit.Test; import org.slf4j.LoggerFactory; @@ -177,4 +179,96 @@ private ListAppender appendLogger(final Class clazz) { classLogger.addAppender(listAppender); return listAppender; } + + /** + * Tests that a case-mismatched enum name is still resolved. + */ + @Test + public void choiceFieldCaseMismatchCorrection() { + final Set parameters = ParametersBuilderUtil.getParametersFor( + MockCaseMismatchSchema.class, + MockCaseMismatchBinding.class + ); + + assertThat(parameters).as("The choice parameter descriptor should be resolved successfully").hasSize(1); + + final ModelParameter parameter = parameters.iterator().next(); + + assertThat(parameter.getName()).as("The parameter name should match the target field").isEqualTo("dummyField"); + + final ChoiceFieldType fieldType = (ChoiceFieldType) parameter.getFieldType(); + + assertThat(fieldType.getDefaultValue()).as("The enum 'VALUE_ONE' should match the schema's 'value_one' choice") + .isEqualTo("value_one"); + } + + /** + * Tests that when an enum field evaluates to null or does not match any valid choices, we fall back to the first + * available choice. + */ + @Test + public void choiceFieldNullOrMissingFallback() { + final ListAppender listAppender = appendLogger(ParametersBuilderUtil.class); + + final Set parameters = ParametersBuilderUtil.getParametersFor( + MockFallbackSchema.class, + MockFallbackBinding.class + ); + + assertThat(parameters).as("The parameter descriptor should be built using a fallback strategy").hasSize(1); + + final ModelParameter parameter = parameters.iterator().next(); + + assertThat(parameter.getName()).as("The parameter name should match the target field").isEqualTo("dummyField"); + + final ChoiceFieldType fieldType = (ChoiceFieldType) parameter.getFieldType(); + + assertThat(fieldType.getDefaultValue()).as( + "A null value should fall back safely to the first choice in the schema").isEqualTo("value_one"); + + final List loggingList = listAppender.list; + assertThat(loggingList).as("A fallback must log an explicit warning").isNotEmpty(); + + final ILoggingEvent warningEvent = loggingList.get(0); + assertThat(warningEvent.getLevel()).isEqualTo(Level.WARN); + assertThat(warningEvent.getMessage()).as("The warning message should detail the fallback replacement") + .containsIgnoringCase("is not present on possible values set"); + } + + /** + * Dummy enum to simulate parameter choices. + */ + public enum DummyEnum { + VALUE_ONE, VALUE_TWO; + } + + /** + * Schema where the annotation choices are lowercase. + */ + public static class MockCaseMismatchSchema extends water.api.schemas3.ModelParametersSchemaV3 { + @water.api.API(help = "Case mismatch", values = {"value_one", "value_two"}) + public DummyEnum dummyField; + } + + /** + * Fallback schema to be used when client binding POJO is null. + */ + public static class MockFallbackSchema extends water.api.schemas3.ModelParametersSchemaV3 { + @water.api.API(help = "Fallback", values = {"value_one"}) + public DummyEnum dummyField; + } + + /** + * Client binding POJO where the runtime field resolves to uppercase. + */ + public static class MockCaseMismatchBinding extends water.bindings.pojos.ModelParametersSchemaV3 { + public DummyEnum dummyField = DummyEnum.VALUE_ONE; + } + + /** + * Client binding POJO where the enum initializes as null. + */ + public static class MockFallbackBinding extends water.bindings.pojos.ModelParametersSchemaV3 { + public DummyEnum dummyField = null; + } }