diff --git a/docs/samples/Microsoft.ML.Samples.GPU/Microsoft.ML.Samples.GPU.csproj b/docs/samples/Microsoft.ML.Samples.GPU/Microsoft.ML.Samples.GPU.csproj index c2cf24bf76..ebe0fde3b1 100644 --- a/docs/samples/Microsoft.ML.Samples.GPU/Microsoft.ML.Samples.GPU.csproj +++ b/docs/samples/Microsoft.ML.Samples.GPU/Microsoft.ML.Samples.GPU.csproj @@ -44,6 +44,7 @@ + diff --git a/docs/samples/Microsoft.ML.Samples/Microsoft.ML.Samples.csproj b/docs/samples/Microsoft.ML.Samples/Microsoft.ML.Samples.csproj index e343bdbfe5..caba26f5cc 100644 --- a/docs/samples/Microsoft.ML.Samples/Microsoft.ML.Samples.csproj +++ b/docs/samples/Microsoft.ML.Samples/Microsoft.ML.Samples.csproj @@ -977,6 +977,7 @@ + diff --git a/src/Microsoft.ML.Console/Microsoft.ML.Console.csproj b/src/Microsoft.ML.Console/Microsoft.ML.Console.csproj index f4331b79fb..78a1e768af 100644 --- a/src/Microsoft.ML.Console/Microsoft.ML.Console.csproj +++ b/src/Microsoft.ML.Console/Microsoft.ML.Console.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1 + net6.0 Exe MML Microsoft.ML.Tools.Console.Console @@ -27,6 +27,7 @@ + diff --git a/src/Microsoft.ML.SearchSpace/Converter/ChoiceOptionConverter.cs b/src/Microsoft.ML.SearchSpace/Converter/ChoiceOptionConverter.cs new file mode 100644 index 0000000000..0cf010475f --- /dev/null +++ b/src/Microsoft.ML.SearchSpace/Converter/ChoiceOptionConverter.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.ML.SearchSpace.Option; + +namespace Microsoft.ML.SearchSpace.Converter +{ + internal class ChoiceOptionConverter : JsonConverter + { + class Schema + { + /// + /// must be one of "int" | "float" | "double" + /// + [JsonPropertyName("default")] + public object Default { get; set; } + + [JsonPropertyName("choices")] + public object[] Choices { get; set; } + } + + public override ChoiceOption Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var schema = JsonSerializer.Deserialize(ref reader, options); + + return new ChoiceOption(schema.Choices, schema.Default); + } + + public override void Write(Utf8JsonWriter writer, ChoiceOption value, JsonSerializerOptions options) + { + var schema = new Schema + { + Choices = value.Choices, + Default = value.SampleFromFeatureSpace(value.Default), + }; + + JsonSerializer.Serialize(writer, schema, options); + } + } +} diff --git a/src/Microsoft.ML.SearchSpace/Converter/NumericOptionConverter.cs b/src/Microsoft.ML.SearchSpace/Converter/NumericOptionConverter.cs new file mode 100644 index 0000000000..709213c69b --- /dev/null +++ b/src/Microsoft.ML.SearchSpace/Converter/NumericOptionConverter.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.ML.SearchSpace.Option; + +namespace Microsoft.ML.SearchSpace.Converter +{ + internal class NumericOptionConverter : JsonConverter + { + class Schema + { + /// + /// must be one of "int" | "float" | "double" + /// + [JsonPropertyName("type")] + public string Type { get; set; } + + [JsonPropertyName("default")] + public object Default { get; set; } + + [JsonPropertyName("min")] + public object Min { get; set; } + + [JsonPropertyName("max")] + public object Max { get; set; } + + [JsonPropertyName("log_base")] + public bool LogBase { get; set; } + } + + public override UniformNumericOption Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var schema = JsonSerializer.Deserialize(ref reader, options); + + return schema.Type switch + { + "int" => new UniformIntOption(Convert.ToInt32(schema.Min), Convert.ToInt32(schema.Max), schema.LogBase, Convert.ToInt32(schema.Default)), + "float" => new UniformSingleOption(Convert.ToSingle(schema.Min), Convert.ToSingle(schema.Max), schema.LogBase, Convert.ToSingle(schema.Default)), + "double" => new UniformDoubleOption(Convert.ToDouble(schema.Min), Convert.ToDouble(schema.Max), schema.LogBase, Convert.ToDouble(schema.Default)), + _ => throw new ArgumentException($"unknown schema type: {schema.Type}"), + }; + } + + public override void Write(Utf8JsonWriter writer, UniformNumericOption value, JsonSerializerOptions options) + { + var schema = value switch + { + UniformIntOption intOption => new Schema + { + Type = "int", + Default = intOption.SampleFromFeatureSpace(intOption.Default).AsType(), + Min = Convert.ToInt32(intOption.Min), + Max = Convert.ToInt32(intOption.Max), + LogBase = intOption.LogBase, + }, + UniformDoubleOption doubleOption => new Schema + { + Type = "double", + Default = doubleOption.SampleFromFeatureSpace(doubleOption.Default).AsType(), + Min = doubleOption.Min, + Max = doubleOption.Max, + LogBase = doubleOption.LogBase, + }, + UniformSingleOption singleOption => new Schema + { + Type = "float", + Default = singleOption.SampleFromFeatureSpace(singleOption.Default).AsType(), + Min = Convert.ToSingle(singleOption.Min), + Max = Convert.ToSingle(singleOption.Max), + LogBase = singleOption.LogBase, + }, + _ => throw new ArgumentException("unknown type"), + }; + + JsonSerializer.Serialize(writer, schema, options); + } + } +} diff --git a/src/Microsoft.ML.SearchSpace/Converter/OptionConverter.cs b/src/Microsoft.ML.SearchSpace/Converter/OptionConverter.cs new file mode 100644 index 0000000000..ef2eb58e9a --- /dev/null +++ b/src/Microsoft.ML.SearchSpace/Converter/OptionConverter.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.ML.SearchSpace.Option; + +namespace Microsoft.ML.SearchSpace.Converter +{ + internal class OptionConverter : JsonConverter + { + public override OptionBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + try + { + return JsonSerializer.Deserialize(ref reader, options); + } + catch (Exception) + { + // try choice option + } + + try + { + return JsonSerializer.Deserialize(ref reader, options); + } + catch (Exception) + { + // try numeric option + } + + try + { + return JsonSerializer.Deserialize(ref reader, options); + } + catch (Exception) + { + throw new ArgumentException("unknown option type"); + } + } + + public override void Write(Utf8JsonWriter writer, OptionBase value, JsonSerializerOptions options) + { + if (value is SearchSpace ss) + { + JsonSerializer.Serialize(writer, ss, options); + } + else if (value is ChoiceOption choiceOption) + { + JsonSerializer.Serialize(writer, choiceOption, options); + } + else if (value is UniformNumericOption uniformNumericOption) + { + JsonSerializer.Serialize(writer, uniformNumericOption, options); + } + else + { + throw new ArgumentException("unknown option type"); + } + } + } +} diff --git a/src/Microsoft.ML.SearchSpace/Converter/SearchSpaceConverter.cs b/src/Microsoft.ML.SearchSpace/Converter/SearchSpaceConverter.cs new file mode 100644 index 0000000000..f0f9165c00 --- /dev/null +++ b/src/Microsoft.ML.SearchSpace/Converter/SearchSpaceConverter.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.ML.SearchSpace.Option; + +namespace Microsoft.ML.SearchSpace.Converter +{ + internal class SearchSpaceConverter : JsonConverter + { + public override SearchSpace Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var optionKVPairs = JsonSerializer.Deserialize>(ref reader, options); + + return new SearchSpace(optionKVPairs); + } + + public override void Write(Utf8JsonWriter writer, SearchSpace value, JsonSerializerOptions options) + { + JsonSerializer.Serialize>(value, options); + } + } +} diff --git a/src/Microsoft.ML.SearchSpace/Microsoft.ML.SearchSpace.csproj b/src/Microsoft.ML.SearchSpace/Microsoft.ML.SearchSpace.csproj index 85b50459f7..155feb6f84 100644 --- a/src/Microsoft.ML.SearchSpace/Microsoft.ML.SearchSpace.csproj +++ b/src/Microsoft.ML.SearchSpace/Microsoft.ML.SearchSpace.csproj @@ -2,7 +2,7 @@ netstandard2.0 - Microsoft.ML.AutoML + Microsoft.ML.Core true MSML_ContractsCheckMessageNotLiteralOrIdentifier 9.0 diff --git a/src/Microsoft.ML.SearchSpace/Option/ChoiceOption.cs b/src/Microsoft.ML.SearchSpace/Option/ChoiceOption.cs index 3a66ce1fff..8f14f679e8 100644 --- a/src/Microsoft.ML.SearchSpace/Option/ChoiceOption.cs +++ b/src/Microsoft.ML.SearchSpace/Option/ChoiceOption.cs @@ -5,6 +5,8 @@ using System; using System.Diagnostics.Contracts; using System.Linq; +using System.Text.Json.Serialization; +using Microsoft.ML.SearchSpace.Converter; #nullable enable @@ -13,6 +15,7 @@ namespace Microsoft.ML.SearchSpace.Option /// /// This class represent option for discrete value, such as string, enum, etc.. /// + [JsonConverter(typeof(ChoiceOptionConverter))] public sealed class ChoiceOption : OptionBase { private readonly UniformSingleOption _option; diff --git a/src/Microsoft.ML.SearchSpace/Option/NestOption.cs b/src/Microsoft.ML.SearchSpace/Option/NestOption.cs index a31aa74516..08c0847276 100644 --- a/src/Microsoft.ML.SearchSpace/Option/NestOption.cs +++ b/src/Microsoft.ML.SearchSpace/Option/NestOption.cs @@ -11,9 +11,9 @@ namespace Microsoft.ML.SearchSpace.Option { /// - /// This class represent nest option, which is an option that contains other options, like , or even itself. + /// This class represent nest option, which is an option that contains other options, like , or even itself. /// - public sealed class NestOption : OptionBase, IDictionary + public sealed class SearchSpace : OptionBase, IDictionary { private readonly Dictionary _options = new Dictionary(); diff --git a/src/Microsoft.ML.SearchSpace/Option/OptionBase.cs b/src/Microsoft.ML.SearchSpace/Option/OptionBase.cs index 6b218d242c..281e4137fc 100644 --- a/src/Microsoft.ML.SearchSpace/Option/OptionBase.cs +++ b/src/Microsoft.ML.SearchSpace/Option/OptionBase.cs @@ -5,11 +5,15 @@ #nullable enable +using System.Text.Json.Serialization; +using Microsoft.ML.SearchSpace.Converter; + namespace Microsoft.ML.SearchSpace.Option { /// /// abstrace class for Option. /// + [JsonConverter(typeof(OptionConverter))] public abstract class OptionBase { /// @@ -37,7 +41,7 @@ public abstract class OptionBase /// /// Gets the step of this option. The is used to determine the number of grid this option should be divided into. In , it's always the length of - /// . And in , it's always [null]. And in , it's a combination of all in its options. + /// . And in , it's always [null]. And in , it's a combination of all in its options. /// public abstract int?[] Step { get; } } diff --git a/src/Microsoft.ML.SearchSpace/Option/UniformNumericOption.cs b/src/Microsoft.ML.SearchSpace/Option/UniformNumericOption.cs index 7680538889..a03bcbab0e 100644 --- a/src/Microsoft.ML.SearchSpace/Option/UniformNumericOption.cs +++ b/src/Microsoft.ML.SearchSpace/Option/UniformNumericOption.cs @@ -5,12 +5,15 @@ using System; using System.Diagnostics.Contracts; using System.Linq; +using System.Text.Json.Serialization; +using Microsoft.ML.SearchSpace.Converter; namespace Microsoft.ML.SearchSpace.Option { /// /// abstract class for numeric option. /// + [JsonConverter(typeof(NumericOptionConverter))] public abstract class UniformNumericOption : OptionBase { /// diff --git a/src/Microsoft.ML.SearchSpace/Schema/SchemaBase.cs b/src/Microsoft.ML.SearchSpace/Schema/SchemaBase.cs deleted file mode 100644 index d0b52142d9..0000000000 --- a/src/Microsoft.ML.SearchSpace/Schema/SchemaBase.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text.Json.Serialization; - -namespace Microsoft.ML.SearchSpace.Schema -{ - [JsonConverter(typeof(JsonStringEnumConverter))] - internal enum SchemaType - { - UniformDoubleOption = 0, - IntegerOption = 1, - ChoiceOption = 2, - NestOption = 3, - } - - internal abstract class SchemaBase - { - [JsonPropertyName("schema_type")] - public abstract SchemaType SchemaType { get; } - - [JsonPropertyName("schema_type")] - public abstract int Version { get; } - } -} diff --git a/src/Microsoft.ML.SearchSpace/Schema/UniformDoubleOptionSchemaV0.cs b/src/Microsoft.ML.SearchSpace/Schema/UniformDoubleOptionSchemaV0.cs deleted file mode 100644 index 21402fc3a0..0000000000 --- a/src/Microsoft.ML.SearchSpace/Schema/UniformDoubleOptionSchemaV0.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Text.Json.Serialization; - -namespace Microsoft.ML.SearchSpace.Schema -{ - internal class UniformDoubleOptionSchemaV0 : SchemaBase - { - public override SchemaType SchemaType => SchemaType.UniformDoubleOption; - - public override int Version => 0; - - [JsonPropertyName("min")] - public double Min { get; set; } - - [JsonPropertyName("max")] - public double Max { get; set; } - - [JsonPropertyName("log_base")] - public bool? LogBase { get; set; } - } -} diff --git a/src/Microsoft.ML.SearchSpace/SearchSpace.cs b/src/Microsoft.ML.SearchSpace/SearchSpace.cs index b40651947c..84c2da890d 100644 --- a/src/Microsoft.ML.SearchSpace/SearchSpace.cs +++ b/src/Microsoft.ML.SearchSpace/SearchSpace.cs @@ -8,13 +8,15 @@ using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.ML.SearchSpace.Converter; using Microsoft.ML.SearchSpace.Option; namespace Microsoft.ML.SearchSpace { /// - /// This class is used to represent a set of , which can be either one of , or . + /// This class is used to represent a set of , which can be either one of , or another nested search space. /// + [JsonConverter(typeof(SearchSpaceConverter))] public class SearchSpace : OptionBase, IDictionary { private readonly Dictionary _options; @@ -168,11 +170,11 @@ private Dictionary GetOptionsFromType(Type typeInfo) } - private NestOption GetNestOptionFromType(Type typeInfo) + private SearchSpace GetSearchSpaceOptionFromType(Type typeInfo) { var propertyOptions = GetOptionsFromProperty(typeInfo); var fieldOptions = GetOptionsFromField(typeInfo); - var nestOption = new NestOption(); + var nestOption = new SearchSpace(); foreach (var kv in propertyOptions.Concat(fieldOptions)) { nestOption[kv.Key] = kv.Value; @@ -208,7 +210,7 @@ private Dictionary GetOptionsFromField(Type typeInfo) ChoiceAttribute choice => choice.Option, RangeAttribute range => range.Option, BooleanChoiceAttribute booleanChoice => booleanChoice.Option, - NestOptionAttribute nest => GetNestOptionFromType(field.FieldType), + NestOptionAttribute nest => GetSearchSpaceOptionFromType(field.FieldType), _ => throw new NotImplementedException(), }; @@ -246,7 +248,7 @@ private Dictionary GetOptionsFromProperty(Type typeInfo) ChoiceAttribute choice => choice.Option, RangeAttribute range => range.Option, BooleanChoiceAttribute booleanChoice => booleanChoice.Option, - NestOptionAttribute nest => GetNestOptionFromType(property.PropertyType), + NestOptionAttribute nest => GetSearchSpaceOptionFromType(property.PropertyType), _ => throw new NotImplementedException(), }; diff --git a/src/Microsoft.ML.StandardTrainers/LdSvm/LdSvmTrainer.cs b/src/Microsoft.ML.StandardTrainers/LdSvm/LdSvmTrainer.cs index 538fdce114..d749875d9d 100644 --- a/src/Microsoft.ML.StandardTrainers/LdSvm/LdSvmTrainer.cs +++ b/src/Microsoft.ML.StandardTrainers/LdSvm/LdSvmTrainer.cs @@ -13,6 +13,7 @@ using Microsoft.ML.Internal.Utilities; using Microsoft.ML.Numeric; using Microsoft.ML.Runtime; +using Microsoft.ML.SearchSpace; using Microsoft.ML.Trainers; [assembly: LoadableClass(LdSvmTrainer.Summary, typeof(LdSvmTrainer), typeof(LdSvmTrainer.Options), @@ -77,6 +78,7 @@ public sealed class Options : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Depth of Local Deep SVM tree", ShortName = "depth", SortOrder = 50)] [TGUI(SuggestedSweeps = "1,3,5,7")] [TlcModule.SweepableDiscreteParam("TreeDepth", new object[] { 1, 3, 5, 7 })] + [Range(1, 128, 1, true)] public int TreeDepth = Defaults.TreeDepth; /// @@ -85,6 +87,7 @@ public sealed class Options : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Regularizer for classifier parameter W", ShortName = "lw", SortOrder = 50)] [TGUI(SuggestedSweeps = "0.1,0.01,0.001")] [TlcModule.SweepableDiscreteParam("LambdaW", new object[] { 0.1f, 0.01f, 0.001f })] + [Range(1e-4f, 1f, 1e-4f, true)] public float LambdaW = Defaults.LambdaW; /// @@ -93,6 +96,7 @@ public sealed class Options : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Regularizer for kernel parameter Theta", ShortName = "lt", SortOrder = 50)] [TGUI(SuggestedSweeps = "0.1,0.01,0.001")] [TlcModule.SweepableDiscreteParam("LambdaTheta", new object[] { 0.1f, 0.01f, 0.001f })] + [Range(1e-4f, 1f, 1e-4f, true)] public float LambdaTheta = Defaults.LambdaTheta; /// @@ -101,6 +105,7 @@ public sealed class Options : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Regularizer for kernel parameter Thetaprime", ShortName = "lp", SortOrder = 50)] [TGUI(SuggestedSweeps = "0.1,0.01,0.001")] [TlcModule.SweepableDiscreteParam("LambdaThetaprime", new object[] { 0.1f, 0.01f, 0.001f })] + [Range(1e-4f, 1f, 1e-4f, true)] public float LambdaThetaprime = Defaults.LambdaThetaprime; /// @@ -109,6 +114,7 @@ public sealed class Options : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Parameter for sigmoid sharpness", ShortName = "s", SortOrder = 50)] [TGUI(SuggestedSweeps = "1.0,0.1,0.01")] [TlcModule.SweepableDiscreteParam("Sigma", new object[] { 1.0f, 0.1f, 0.01f })] + [Range(1e-4f, 1f, 1e-4f, true)] public float Sigma = Defaults.Sigma; /// @@ -116,6 +122,7 @@ public sealed class Options : TrainerInputBaseWithWeight /// [Argument(ArgumentType.AtMostOnce, HelpText = "No bias", ShortName = "bias")] [TlcModule.SweepableDiscreteParam("NoBias", null, isBool: true)] + [BooleanChoice(true)] public bool UseBias = Defaults.UseBias; /// @@ -125,6 +132,7 @@ public sealed class Options : TrainerInputBaseWithWeight HelpText = "Number of iterations", ShortName = "iter,NumIterations", SortOrder = 50)] [TGUI(SuggestedSweeps = "10000,15000")] [TlcModule.SweepableDiscreteParam("NumIterations", new object[] { 10000, 15000 })] + [Range(1, int.MaxValue, 1, true)] public int NumberOfIterations = Defaults.NumberOfIterations; [Argument(ArgumentType.AtMostOnce, HelpText = "The calibrator kind to apply to the predictor. Specify null for no calibration", Visibility = ArgumentAttribute.VisibilityType.EntryPointsOnly)] diff --git a/src/Microsoft.ML.StandardTrainers/Microsoft.ML.StandardTrainers.csproj b/src/Microsoft.ML.StandardTrainers/Microsoft.ML.StandardTrainers.csproj index f3b20954de..d3a7a06c3c 100644 --- a/src/Microsoft.ML.StandardTrainers/Microsoft.ML.StandardTrainers.csproj +++ b/src/Microsoft.ML.StandardTrainers/Microsoft.ML.StandardTrainers.csproj @@ -10,6 +10,10 @@ + + all + true + diff --git a/src/Microsoft.ML.StandardTrainers/Standard/LogisticRegression/LbfgsPredictorBase.cs b/src/Microsoft.ML.StandardTrainers/Standard/LogisticRegression/LbfgsPredictorBase.cs index 5de100fba6..c5008a76ca 100644 --- a/src/Microsoft.ML.StandardTrainers/Standard/LogisticRegression/LbfgsPredictorBase.cs +++ b/src/Microsoft.ML.StandardTrainers/Standard/LogisticRegression/LbfgsPredictorBase.cs @@ -13,6 +13,7 @@ using Microsoft.ML.Internal.Utilities; using Microsoft.ML.Numeric; using Microsoft.ML.Runtime; +using Microsoft.ML.SearchSpace; namespace Microsoft.ML.Trainers { @@ -44,6 +45,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "L2 regularization weight", ShortName = "l2, L2Weight", SortOrder = 50)] [TGUI(Label = "L2 Weight", Description = "Weight of L2 regularizer term", SuggestedSweeps = "0,0.1,1")] [TlcModule.SweepableFloatParamAttribute(0.0f, 1.0f, numSteps: 4)] + [Range(0.03125f, 32768f, 1, true)] public float L2Regularization = Defaults.L2Regularization; /// @@ -52,6 +54,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "L1 regularization weight", ShortName = "l1, L1Weight", SortOrder = 50)] [TGUI(Label = "L1 Weight", Description = "Weight of L1 regularizer term", SuggestedSweeps = "0,0.1,1")] [TlcModule.SweepableFloatParamAttribute(0.0f, 1.0f, numSteps: 4)] + [Range(0.03125f, 32768f, 1, true)] public float L1Regularization = Defaults.L1Regularization; /// @@ -61,6 +64,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight ShortName = "ot, OptTol", SortOrder = 50)] [TGUI(Label = "Optimization Tolerance", Description = "Threshold for optimizer convergence", SuggestedSweeps = "1e-4,1e-7")] [TlcModule.SweepableDiscreteParamAttribute(new object[] { 1e-4f, 1e-7f })] + [Range(1e-7f, 1e-1f, 1e-4f, true)] public float OptimizationTolerance = Defaults.OptimizationTolerance; /// @@ -69,6 +73,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Memory size for L-BFGS. Low=faster, less accurate", ShortName = "m, MemorySize", SortOrder = 50)] [TGUI(Description = "Memory size for L-BFGS", SuggestedSweeps = "5,20,50")] [TlcModule.SweepableDiscreteParamAttribute("MemorySize", new object[] { 5, 20, 50 })] + [Range(2, 512, 2, true)] public int HistorySize = Defaults.HistorySize; /// @@ -77,6 +82,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Maximum iterations.", ShortName = "maxiter, MaxIterations, NumberOfIterations")] [TGUI(Label = "Max Number of Iterations")] [TlcModule.SweepableLongParamAttribute("MaxIterations", 1, int.MaxValue)] + [Range(1, int.MaxValue, 1, true)] public int MaximumNumberOfIterations = Defaults.MaximumNumberOfIterations; /// @@ -106,6 +112,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.LastOccurrenceWins, HelpText = "Init weights diameter", ShortName = "initwts, InitWtsDiameter", SortOrder = 140)] [TGUI(Label = "Initial Weights Scale", SuggestedSweeps = "0,0.1,0.5,1")] [TlcModule.SweepableFloatParamAttribute("InitWtsDiameter", 0.0f, 1.0f, numSteps: 5)] + [Range(0f, 1f, 0f, false)] public float InitialWeightsDiameter = 0; // Deprecated @@ -124,12 +131,14 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight /// [Argument(ArgumentType.AtMostOnce, HelpText = "Force densification of the internal optimization vectors", ShortName = "do")] [TlcModule.SweepableDiscreteParamAttribute("DenseOptimizer", new object[] { false, true })] + [BooleanChoice] public bool DenseOptimizer = false; /// /// Enforce non-negative weights. Default is false. /// [Argument(ArgumentType.AtMostOnce, HelpText = "Enforce non-negative weights", ShortName = "nn", SortOrder = 90)] + [BooleanChoice] public bool EnforceNonNegativity = Defaults.EnforceNonNegativity; [BestFriend] diff --git a/src/Microsoft.ML.StandardTrainers/Standard/Online/AveragedLinear.cs b/src/Microsoft.ML.StandardTrainers/Standard/Online/AveragedLinear.cs index 6197e0b4c5..e666caca3e 100644 --- a/src/Microsoft.ML.StandardTrainers/Standard/Online/AveragedLinear.cs +++ b/src/Microsoft.ML.StandardTrainers/Standard/Online/AveragedLinear.cs @@ -10,6 +10,7 @@ using Microsoft.ML.Internal.Utilities; using Microsoft.ML.Numeric; using Microsoft.ML.Runtime; +using Microsoft.ML.SearchSpace; // TODO: Check if it works properly if Averaged is set to false @@ -26,6 +27,7 @@ public abstract class AveragedLinearOptions : OnlineLinearOptions [Argument(ArgumentType.AtMostOnce, HelpText = "Learning rate", ShortName = "lr", SortOrder = 50)] [TGUI(Label = "Learning rate", SuggestedSweeps = "0.01,0.1,0.5,1.0")] [TlcModule.SweepableDiscreteParam("LearningRate", new object[] { 0.01, 0.1, 0.5, 1.0 })] + [Range(1e-4f, 1f, 1f, true)] public float LearningRate = AveragedDefault.LearningRate; /// @@ -38,6 +40,7 @@ public abstract class AveragedLinearOptions : OnlineLinearOptions [Argument(ArgumentType.AtMostOnce, HelpText = "Decrease learning rate", ShortName = "decreaselr", SortOrder = 50)] [TGUI(Label = "Decrease Learning Rate", Description = "Decrease learning rate as iterations progress")] [TlcModule.SweepableDiscreteParam("DecreaseLearningRate", new object[] { false, true })] + [BooleanChoice] public bool DecreaseLearningRate = AveragedDefault.DecreaseLearningRate; /// @@ -66,6 +69,7 @@ public abstract class AveragedLinearOptions : OnlineLinearOptions [Argument(ArgumentType.AtMostOnce, HelpText = "L2 Regularization Weight", ShortName = "reg,L2RegularizerWeight", SortOrder = 50)] [TGUI(Label = "L2 Regularization Weight")] [TlcModule.SweepableFloatParam("L2RegularizerWeight", 0.0f, 0.4f)] + [Range(0f, 32768f, 0f, false)] public float L2Regularization = AveragedDefault.L2Regularization; /// diff --git a/src/Microsoft.ML.StandardTrainers/Standard/Online/LinearSvm.cs b/src/Microsoft.ML.StandardTrainers/Standard/Online/LinearSvm.cs index 1991df054a..2776eb94d3 100644 --- a/src/Microsoft.ML.StandardTrainers/Standard/Online/LinearSvm.cs +++ b/src/Microsoft.ML.StandardTrainers/Standard/Online/LinearSvm.cs @@ -13,6 +13,7 @@ using Microsoft.ML.Model; using Microsoft.ML.Numeric; using Microsoft.ML.Runtime; +using Microsoft.ML.SearchSpace; using Microsoft.ML.Trainers; [assembly: LoadableClass(LinearSvmTrainer.Summary, typeof(LinearSvmTrainer), typeof(LinearSvmTrainer.Options), @@ -82,18 +83,22 @@ public sealed class Options : OnlineLinearOptions [Argument(ArgumentType.AtMostOnce, HelpText = "Regularizer constant", ShortName = "lambda", SortOrder = 50)] [TGUI(SuggestedSweeps = "0.00001-0.1;log;inc:10")] [TlcModule.SweepableFloatParamAttribute("Lambda", 0.00001f, 0.1f, 10, isLogScale: true)] + [Range(1e-6f, 1f, 1e-4f, true)] public float Lambda = 0.001f; [Argument(ArgumentType.AtMostOnce, HelpText = "Batch size", ShortName = "batch", SortOrder = 190)] [TGUI(Label = "Batch Size")] + [Range(1, 128, 1, true)] public int BatchSize = 1; [Argument(ArgumentType.AtMostOnce, HelpText = "Perform projection to unit-ball? Typically used with batch size > 1.", ShortName = "project", SortOrder = 50)] [TlcModule.SweepableDiscreteParam("PerformProjection", null, isBool: true)] + [BooleanChoice(defaultValue: false)] public bool PerformProjection = false; [Argument(ArgumentType.AtMostOnce, HelpText = "No bias")] [TlcModule.SweepableDiscreteParam("NoBias", null, isBool: true)] + [BooleanChoice(defaultValue: false)] public bool NoBias = false; [Argument(ArgumentType.AtMostOnce, HelpText = "The calibrator kind to apply to the predictor. Specify null for no calibration", Visibility = ArgumentAttribute.VisibilityType.EntryPointsOnly)] diff --git a/src/Microsoft.ML.StandardTrainers/Standard/Online/OnlineLinear.cs b/src/Microsoft.ML.StandardTrainers/Standard/Online/OnlineLinear.cs index 9efe9d72f8..7bfd2e2afa 100644 --- a/src/Microsoft.ML.StandardTrainers/Standard/Online/OnlineLinear.cs +++ b/src/Microsoft.ML.StandardTrainers/Standard/Online/OnlineLinear.cs @@ -12,6 +12,7 @@ using Microsoft.ML.Model; using Microsoft.ML.Numeric; using Microsoft.ML.Runtime; +using Microsoft.ML.SearchSpace; namespace Microsoft.ML.Trainers { @@ -26,6 +27,7 @@ public abstract class OnlineLinearOptions : TrainerInputBaseWithLabel [Argument(ArgumentType.AtMostOnce, HelpText = "Number of iterations", ShortName = "iter,numIterations", SortOrder = 50)] [TGUI(Label = "Number of Iterations", Description = "Number of training iterations through data", SuggestedSweeps = "1,10,100")] [TlcModule.SweepableLongParamAttribute("NumIterations", 1, 100, stepSize: 10, isLogScale: true)] + [Range(1, 512, 1, true)] public int NumberOfIterations = OnlineDefault.NumberOfIterations; /// @@ -45,6 +47,7 @@ public abstract class OnlineLinearOptions : TrainerInputBaseWithLabel [Argument(ArgumentType.AtMostOnce, HelpText = "Init weights diameter", ShortName = "initwts,initWtsDiameter", SortOrder = 140)] [TGUI(Label = "Initial Weights Scale", SuggestedSweeps = "0,0.1,0.5,1")] [TlcModule.SweepableFloatParamAttribute("InitWtsDiameter", 0.0f, 1.0f, numSteps: 5)] + [Range(0f, 1f, 0f, false)] public float InitialWeightsDiameter = 0; /// diff --git a/src/Microsoft.ML.StandardTrainers/Standard/SdcaBinary.cs b/src/Microsoft.ML.StandardTrainers/Standard/SdcaBinary.cs index a1e16ad283..d19bfc8c42 100644 --- a/src/Microsoft.ML.StandardTrainers/Standard/SdcaBinary.cs +++ b/src/Microsoft.ML.StandardTrainers/Standard/SdcaBinary.cs @@ -19,6 +19,7 @@ using Microsoft.ML.Model; using Microsoft.ML.Numeric; using Microsoft.ML.Runtime; +using Microsoft.ML.SearchSpace; using Microsoft.ML.Trainers; using Microsoft.ML.Transforms; @@ -162,6 +163,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "L2 regularizer constant. By default the l2 constant is automatically inferred based on data set.", NullName = "", ShortName = "l2, L2Const", SortOrder = 1)] [TGUI(Label = "L2 Regularizer Constant", SuggestedSweeps = ",1e-7,1e-6,1e-5,1e-4,1e-3,1e-2")] [TlcModule.SweepableDiscreteParam("L2Const", new object[] { "", 1e-7f, 1e-6f, 1e-5f, 1e-4f, 1e-3f, 1e-2f })] + [Range(1e-7f, 32768f, 1e-7f, true)] public float? L2Regularization; // REVIEW: make the default positive when we know how to consume a sparse model @@ -172,6 +174,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight NullName = "", Name = "L1Threshold", ShortName = "l1", SortOrder = 2)] [TGUI(Label = "L1 Soft Threshold", SuggestedSweeps = ",0,0.25,0.5,0.75,1")] [TlcModule.SweepableDiscreteParam("L1Threshold", new object[] { "", 0f, 0.25f, 0.5f, 0.75f, 1f })] + [Range(1e-7f, 32768f, 1e-7f, true)] public float? L1Regularization; /// @@ -190,6 +193,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "The tolerance for the ratio between duality gap and primal loss for convergence checking.", ShortName = "tol")] [TGUI(SuggestedSweeps = "0.001, 0.01, 0.1, 0.2")] [TlcModule.SweepableDiscreteParam("ConvergenceTolerance", new object[] { 0.001f, 0.01f, 0.1f, 0.2f })] + [Range(1e-7f, 1f, 1e-7f, true)] public float ConvergenceTolerance = 0.1f; /// @@ -201,6 +205,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Maximum number of iterations; set to 1 to simulate online learning. Defaults to automatic.", NullName = "", ShortName = "iter, MaxIterations, NumberOfIterations")] [TGUI(Label = "Max number of iterations", SuggestedSweeps = ",10,20,100")] [TlcModule.SweepableDiscreteParam("MaxIterations", new object[] { "", 10, 20, 100 })] + [Range(10, 1000, 10, true)] public int? MaximumNumberOfIterations; /// @@ -229,6 +234,7 @@ public abstract class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "The learning rate for adjusting bias from being regularized.", ShortName = "blr")] [TGUI(SuggestedSweeps = "0, 0.01, 0.1, 1")] [TlcModule.SweepableDiscreteParam("BiasLearningRate", new object[] { 0.0f, 0.01f, 0.1f, 1f })] + [Range(1e-7f, 1f, 1e-7f, true)] public float BiasLearningRate = 0; internal virtual void Check(IHostEnvironment env) @@ -1834,6 +1840,7 @@ public class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "L2 Regularization constant", ShortName = "l2, L2Weight", SortOrder = 50)] [TGUI(Label = "L2 Regularization Constant", SuggestedSweeps = "1e-7,5e-7,1e-6,5e-6,1e-5")] [TlcModule.SweepableDiscreteParam("L2Const", new object[] { 1e-7f, 5e-7f, 1e-6f, 5e-6f, 1e-5f })] + [Range((float)0.03125, (float)32768, init: (float)1F, logBase: true)] public float L2Regularization = Defaults.L2Regularization; /// @@ -1853,6 +1860,7 @@ public class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Exponential moving averaged improvement tolerance for convergence", ShortName = "tol")] [TGUI(SuggestedSweeps = "1e-2,1e-3,1e-4,1e-5")] [TlcModule.SweepableDiscreteParam("ConvergenceTolerance", new object[] { 1e-2f, 1e-3f, 1e-4f, 1e-5f })] + [Range(1e-6, 1e-1, init: 1e-1, logBase: true)] public double ConvergenceTolerance = 1e-4; /// @@ -1864,6 +1872,7 @@ public class OptionsBase : TrainerInputBaseWithWeight [Argument(ArgumentType.AtMostOnce, HelpText = "Maximum number of iterations; set to 1 to simulate online learning.", ShortName = "iter, MaxIterations")] [TGUI(Label = "Max number of iterations", SuggestedSweeps = "1,5,10,20")] [TlcModule.SweepableDiscreteParam("MaxIterations", new object[] { 1, 5, 10, 20 })] + [Range((int)1, 20, init: 1, logBase: true)] public int NumberOfIterations = Defaults.NumberOfIterations; /// @@ -1871,6 +1880,7 @@ public class OptionsBase : TrainerInputBaseWithWeight /// [Argument(ArgumentType.AtMostOnce, HelpText = "Initial learning rate (only used by SGD)", Name = "InitialLearningRate", ShortName = "ilr,lr,InitLearningRate")] [TGUI(Label = "Initial Learning Rate (for SGD)")] + [Range(1e-3, 1, init: 1e-3, logBase: true)] public double LearningRate = Defaults.LearningRate; /// diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Sgd_default_search_space_test.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Sgd_default_search_space_test.approved.txt new file mode 100644 index 0000000000..3c0051da0a --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Sgd_default_search_space_test.approved.txt @@ -0,0 +1,26 @@ +{ + "L2Regularization": { + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "default": 0.1, + "min": 1E-06, + "max": 0.1, + "log_base": true + }, + "NumberOfIterations": { + "default": 1, + "min": 1, + "max": 20, + "log_base": true + }, + "LearningRate": { + "default": 0.001, + "min": 0.001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.AveragedPerceptronTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.AveragedPerceptronTrainer+Options.approved.txt new file mode 100644 index 0000000000..b803726231 --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.AveragedPerceptronTrainer+Options.approved.txt @@ -0,0 +1,37 @@ +{ + "LearningRate": { + "type": "float", + "default": 1, + "min": 0.00010, + "max": 1, + "log_base": true + }, + "DecreaseLearningRate": { + "default": true, + "choices": [ + true, + false + ] + }, + "L2Regularization": { + "type": "float", + "default": 0, + "min": 0, + "max": 32768, + "log_base": false + }, + "NumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 512, + "log_base": true + }, + "InitialWeightsDiameter": { + "type": "float", + "default": 0, + "min": 0, + "max": 1, + "log_base": false + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsLogisticRegressionBinaryTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsLogisticRegressionBinaryTrainer+Options.approved.txt new file mode 100644 index 0000000000..e3c66ec1e5 --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsLogisticRegressionBinaryTrainer+Options.approved.txt @@ -0,0 +1,58 @@ +{ + "L2Regularization": { + "type": "float", + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "L1Regularization": { + "type": "float", + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "OptimizationTolerance": { + "type": "float", + "default": 0.00010, + "min": 0.0000001, + "max": 0.1, + "log_base": true + }, + "HistorySize": { + "type": "int", + "default": 2, + "min": 2, + "max": 512, + "log_base": true + }, + "MaximumNumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 2147483647, + "log_base": true + }, + "InitialWeightsDiameter": { + "type": "float", + "default": 0, + "min": 0, + "max": 1, + "log_base": false + }, + "DenseOptimizer": { + "default": true, + "choices": [ + true, + false + ] + }, + "EnforceNonNegativity": { + "default": true, + "choices": [ + true, + false + ] + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsMaximumEntropyMulticlassTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsMaximumEntropyMulticlassTrainer+Options.approved.txt new file mode 100644 index 0000000000..e3c66ec1e5 --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsMaximumEntropyMulticlassTrainer+Options.approved.txt @@ -0,0 +1,58 @@ +{ + "L2Regularization": { + "type": "float", + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "L1Regularization": { + "type": "float", + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "OptimizationTolerance": { + "type": "float", + "default": 0.00010, + "min": 0.0000001, + "max": 0.1, + "log_base": true + }, + "HistorySize": { + "type": "int", + "default": 2, + "min": 2, + "max": 512, + "log_base": true + }, + "MaximumNumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 2147483647, + "log_base": true + }, + "InitialWeightsDiameter": { + "type": "float", + "default": 0, + "min": 0, + "max": 1, + "log_base": false + }, + "DenseOptimizer": { + "default": true, + "choices": [ + true, + false + ] + }, + "EnforceNonNegativity": { + "default": true, + "choices": [ + true, + false + ] + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsPoissonRegressionTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsPoissonRegressionTrainer+Options.approved.txt new file mode 100644 index 0000000000..e3c66ec1e5 --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LbfgsPoissonRegressionTrainer+Options.approved.txt @@ -0,0 +1,58 @@ +{ + "L2Regularization": { + "type": "float", + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "L1Regularization": { + "type": "float", + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "OptimizationTolerance": { + "type": "float", + "default": 0.00010, + "min": 0.0000001, + "max": 0.1, + "log_base": true + }, + "HistorySize": { + "type": "int", + "default": 2, + "min": 2, + "max": 512, + "log_base": true + }, + "MaximumNumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 2147483647, + "log_base": true + }, + "InitialWeightsDiameter": { + "type": "float", + "default": 0, + "min": 0, + "max": 1, + "log_base": false + }, + "DenseOptimizer": { + "default": true, + "choices": [ + true, + false + ] + }, + "EnforceNonNegativity": { + "default": true, + "choices": [ + true, + false + ] + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LdSvmTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LdSvmTrainer+Options.approved.txt new file mode 100644 index 0000000000..96a6f98401 --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LdSvmTrainer+Options.approved.txt @@ -0,0 +1,51 @@ +{ + "TreeDepth": { + "type": "int", + "default": 1, + "min": 1, + "max": 128, + "log_base": true + }, + "LambdaW": { + "type": "float", + "default": 0.00010, + "min": 0.00010, + "max": 1, + "log_base": true + }, + "LambdaTheta": { + "type": "float", + "default": 0.00010, + "min": 0.00010, + "max": 1, + "log_base": true + }, + "LambdaThetaprime": { + "type": "float", + "default": 0.00010, + "min": 0.00010, + "max": 1, + "log_base": true + }, + "Sigma": { + "type": "float", + "default": 0.00010, + "min": 0.00010, + "max": 1, + "log_base": true + }, + "UseBias": { + "default": true, + "choices": [ + true, + false + ] + }, + "NumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 2147483647, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LinearSvmTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LinearSvmTrainer+Options.approved.txt new file mode 100644 index 0000000000..5551bab2f3 --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.LinearSvmTrainer+Options.approved.txt @@ -0,0 +1,44 @@ +{ + "Lambda": { + "type": "float", + "default": 0.00010, + "min": 0.0000010, + "max": 1, + "log_base": true + }, + "BatchSize": { + "type": "int", + "default": 1, + "min": 1, + "max": 128, + "log_base": true + }, + "PerformProjection": { + "default": false, + "choices": [ + true, + false + ] + }, + "NoBias": { + "default": false, + "choices": [ + true, + false + ] + }, + "NumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 512, + "log_base": true + }, + "InitialWeightsDiameter": { + "type": "float", + "default": 0, + "min": 0, + "max": 1, + "log_base": false + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.OnlineGradientDescentTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.OnlineGradientDescentTrainer+Options.approved.txt new file mode 100644 index 0000000000..b803726231 --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.OnlineGradientDescentTrainer+Options.approved.txt @@ -0,0 +1,37 @@ +{ + "LearningRate": { + "type": "float", + "default": 1, + "min": 0.00010, + "max": 1, + "log_base": true + }, + "DecreaseLearningRate": { + "default": true, + "choices": [ + true, + false + ] + }, + "L2Regularization": { + "type": "float", + "default": 0, + "min": 0, + "max": 32768, + "log_base": false + }, + "NumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 512, + "log_base": true + }, + "InitialWeightsDiameter": { + "type": "float", + "default": 0, + "min": 0, + "max": 1, + "log_base": false + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaLogisticRegressionBinaryTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaLogisticRegressionBinaryTrainer+Options.approved.txt new file mode 100644 index 0000000000..4ecb6de5da --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaLogisticRegressionBinaryTrainer+Options.approved.txt @@ -0,0 +1,37 @@ +{ + "L2Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "L1Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + }, + "MaximumNumberOfIterations": { + "type": "int", + "default": 10, + "min": 10, + "max": 1000, + "log_base": true + }, + "BiasLearningRate": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaMaximumEntropyMulticlassTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaMaximumEntropyMulticlassTrainer+Options.approved.txt new file mode 100644 index 0000000000..4ecb6de5da --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaMaximumEntropyMulticlassTrainer+Options.approved.txt @@ -0,0 +1,37 @@ +{ + "L2Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "L1Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + }, + "MaximumNumberOfIterations": { + "type": "int", + "default": 10, + "min": 10, + "max": 1000, + "log_base": true + }, + "BiasLearningRate": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaNonCalibratedBinaryTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaNonCalibratedBinaryTrainer+Options.approved.txt new file mode 100644 index 0000000000..4ecb6de5da --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaNonCalibratedBinaryTrainer+Options.approved.txt @@ -0,0 +1,37 @@ +{ + "L2Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "L1Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + }, + "MaximumNumberOfIterations": { + "type": "int", + "default": 10, + "min": 10, + "max": 1000, + "log_base": true + }, + "BiasLearningRate": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaNonCalibratedMulticlassTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaNonCalibratedMulticlassTrainer+Options.approved.txt new file mode 100644 index 0000000000..4ecb6de5da --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaNonCalibratedMulticlassTrainer+Options.approved.txt @@ -0,0 +1,37 @@ +{ + "L2Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "L1Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + }, + "MaximumNumberOfIterations": { + "type": "int", + "default": 10, + "min": 10, + "max": 1000, + "log_base": true + }, + "BiasLearningRate": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaRegressionTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaRegressionTrainer+Options.approved.txt new file mode 100644 index 0000000000..4ecb6de5da --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SdcaRegressionTrainer+Options.approved.txt @@ -0,0 +1,37 @@ +{ + "L2Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "L1Regularization": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + }, + "MaximumNumberOfIterations": { + "type": "int", + "default": 10, + "min": 10, + "max": 1000, + "log_base": true + }, + "BiasLearningRate": { + "type": "float", + "default": 0.0000001, + "min": 0.0000001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SgdCalibratedTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SgdCalibratedTrainer+Options.approved.txt new file mode 100644 index 0000000000..6c097ef75c --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SgdCalibratedTrainer+Options.approved.txt @@ -0,0 +1,30 @@ +{ + "L2Regularization": { + "type": "float", + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "type": "double", + "default": 0.1, + "min": 0.000001, + "max": 0.1, + "log_base": true + }, + "NumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 20, + "log_base": true + }, + "LearningRate": { + "type": "double", + "default": 0.001, + "min": 0.001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SgdNonCalibratedTrainer+Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SgdNonCalibratedTrainer+Options.approved.txt new file mode 100644 index 0000000000..6c097ef75c --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Microsoft.ML.Trainers.SgdNonCalibratedTrainer+Options.approved.txt @@ -0,0 +1,30 @@ +{ + "L2Regularization": { + "type": "float", + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "type": "double", + "default": 0.1, + "min": 0.000001, + "max": 0.1, + "log_base": true + }, + "NumberOfIterations": { + "type": "int", + "default": 1, + "min": 1, + "max": 20, + "log_base": true + }, + "LearningRate": { + "type": "double", + "default": 0.001, + "min": 0.001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Options.approved.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Options.approved.txt new file mode 100644 index 0000000000..b1ea94398d --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Options.approved.txt @@ -0,0 +1,26 @@ +{ + "L2Regularization": { + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "default": 0.10000000000000001, + "min": 9.9999999999999995E-07, + "max": 0.10000000000000001, + "log_base": true + }, + "NumberOfIterations": { + "default": 1, + "min": 1, + "max": 20, + "log_base": true + }, + "LearningRate": { + "default": 0.001, + "min": 0.001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Options.received.txt b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Options.received.txt new file mode 100644 index 0000000000..30c5edc93b --- /dev/null +++ b/test/Microsoft.ML.SearchSpace.Tests/ApprovalTests/SearchSpaceTest.Trainer_default_search_space_test.Options.received.txt @@ -0,0 +1,26 @@ +{ + "L2Regularization": { + "default": 1, + "min": 0.03125, + "max": 32768, + "log_base": true + }, + "ConvergenceTolerance": { + "default": 0.10000000000000001, + "min": 9.9999999999999995E-07, + "max": 0.10000000000000001, + "log_base": true + }, + "NumberOfIterations": { + "default": 1, + "min": 1, + "max": 20, + "log_base": true + }, + "LearningRate": { + "default": 0.001, + "min": 0.001, + "max": 1, + "log_base": true + } +} \ No newline at end of file diff --git a/test/Microsoft.ML.SearchSpace.Tests/NestOptionTest.cs b/test/Microsoft.ML.SearchSpace.Tests/NestOptionTest.cs deleted file mode 100644 index 146369d051..0000000000 --- a/test/Microsoft.ML.SearchSpace.Tests/NestOptionTest.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Text; -using FluentAssertions; -using Microsoft.ML.SearchSpace.Option; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.ML.SearchSpace.Tests -{ - public class NestOptionTest : TestBase - { - public NestOptionTest(ITestOutputHelper output) - : base(output) - { - } - - [Fact] - public void NestOption_sampling_from_uniform_space_test() - { - var nestOption = new NestOption(); - nestOption.Add("choice", new ChoiceOption("a", "b", "c")); - nestOption.Add("int", new UniformIntOption(0, 1)); - var anotherNestOption = new NestOption(); - anotherNestOption["choice"] = new ChoiceOption("d", "e"); - anotherNestOption["int"] = new UniformIntOption(2, 3); - nestOption["nestOption"] = anotherNestOption; - - nestOption.FeatureSpaceDim.Should().Be(4); - var parameter = nestOption.SampleFromFeatureSpace(new double[] { 0, 0, 0, 0 }); - parameter["nestOption"]["choice"].AsType().Should().Be("d"); - parameter["nestOption"]["int"].AsType().Should().Be(2); - parameter["choice"].AsType().Should().Be("a"); - parameter["int"].AsType().Should().Be(0); - - parameter = nestOption.SampleFromFeatureSpace(new double[] { 1, 1, 1, 1 }); - parameter["nestOption"]["choice"].AsType().Should().Be("e"); - parameter["nestOption"]["int"].AsType().Should().Be(3); - parameter["choice"].AsType().Should().Be("c"); - parameter["int"].AsType().Should().Be(1); - } - - [Fact] - public void NestOption_mapping_to_uniform_space_test() - { - var nestOption = new NestOption(); - nestOption.Add("choice", new ChoiceOption("a", "b", "c")); - nestOption.Add("int", new UniformIntOption(0, 1)); - - var parameter = Parameter.CreateNestedParameter(); - parameter["choice"] = Parameter.FromString("a"); - parameter["int"] = Parameter.FromInt(0); - nestOption.MappingToFeatureSpace(parameter).Should().Equal(0, 0); - } - - [Fact] - public void NestOption_mapping_order_test() - { - // each dimension in uniform space should be mapping to the options under nest option in a certain (key ascending) order. - var nestOption = new NestOption(); - nestOption["a"] = new UniformIntOption(0, 1); - nestOption["b"] = new UniformIntOption(1, 2); - nestOption["c"] = new UniformIntOption(2, 3); - - // changing of the first dimension should be reflected in option "a" - var parameter = nestOption.SampleFromFeatureSpace(new double[] { 0, 0.5, 0.5 }); - parameter["a"].AsType().Should().Be(0); - parameter = nestOption.SampleFromFeatureSpace(new double[] { 1, 0.5, 0.5 }); - parameter["a"].AsType().Should().Be(1); - - nestOption.Remove("a"); - - // the first dimension should be option "b" - parameter = nestOption.SampleFromFeatureSpace(new double[] { 0, 0.5 }); - parameter["b"].AsType().Should().Be(1); - parameter = nestOption.SampleFromFeatureSpace(new double[] { 1, 0.5 }); - parameter["b"].AsType().Should().Be(2); - } - } -} diff --git a/test/Microsoft.ML.SearchSpace.Tests/SearchSpaceTest.cs b/test/Microsoft.ML.SearchSpace.Tests/SearchSpaceTest.cs index eed7e33318..b62eccb49d 100644 --- a/test/Microsoft.ML.SearchSpace.Tests/SearchSpaceTest.cs +++ b/test/Microsoft.ML.SearchSpace.Tests/SearchSpaceTest.cs @@ -3,10 +3,17 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers.Text; +using System.Buffers; using System.Text.Json; +using System.Text.Json.Serialization; +using ApprovalTests; +using ApprovalTests.Namers; +using ApprovalTests.Reporters; using FluentAssertions; using Microsoft.ML.SearchSpace.Option; using Microsoft.ML.SearchSpace.Tuner; +using Microsoft.ML.Trainers; using Xunit; using Xunit.Abstractions; @@ -14,9 +21,18 @@ namespace Microsoft.ML.SearchSpace.Tests { public class SearchSpaceTest : TestBase { + private readonly JsonSerializerOptions _settings = new JsonSerializerOptions() + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + NumberHandling = JsonNumberHandling.Strict, + }; + public SearchSpaceTest(ITestOutputHelper output) : base(output) { + _settings.Converters.Add(new DoubleConverter()); + _settings.Converters.Add(new SingleConverter()); } [Fact] @@ -197,6 +213,98 @@ public void Search_space_hash_code_test() ss.GetHashCode().Should().Be(125205970); } + [Fact] + public void SearchSpace_sampling_from_uniform_space_test() + { + var searchSpace = new Option.SearchSpace(); + searchSpace.Add("choice", new ChoiceOption("a", "b", "c")); + searchSpace.Add("int", new UniformIntOption(0, 1)); + var anotherNestOption = new Option.SearchSpace(); + anotherNestOption["choice"] = new ChoiceOption("d", "e"); + anotherNestOption["int"] = new UniformIntOption(2, 3); + searchSpace["nestOption"] = anotherNestOption; + + searchSpace.FeatureSpaceDim.Should().Be(4); + var parameter = searchSpace.SampleFromFeatureSpace(new double[] { 0, 0, 0, 0 }); + parameter["nestOption"]["choice"].AsType().Should().Be("d"); + parameter["nestOption"]["int"].AsType().Should().Be(2); + parameter["choice"].AsType().Should().Be("a"); + parameter["int"].AsType().Should().Be(0); + + parameter = searchSpace.SampleFromFeatureSpace(new double[] { 1, 1, 1, 1 }); + parameter["nestOption"]["choice"].AsType().Should().Be("e"); + parameter["nestOption"]["int"].AsType().Should().Be(3); + parameter["choice"].AsType().Should().Be("c"); + parameter["int"].AsType().Should().Be(1); + } + + [Fact] + public void SearchSpace_mapping_to_uniform_space_test() + { + var searchSpace = new SearchSpace(); + searchSpace.Add("choice", new ChoiceOption("a", "b", "c")); + searchSpace.Add("int", new UniformIntOption(0, 1)); + + var parameter = Parameter.CreateNestedParameter(); + parameter["choice"] = Parameter.FromString("a"); + parameter["int"] = Parameter.FromInt(0); + searchSpace.MappingToFeatureSpace(parameter).Should().Equal(0, 0); + } + + [Fact] + public void SearchSpace_mapping_order_test() + { + // each dimension in uniform space should be mapping to the options under nest option in a certain (key ascending) order. + var searchSpace = new SearchSpace(); + searchSpace["a"] = new UniformIntOption(0, 1); + searchSpace["b"] = new UniformIntOption(1, 2); + searchSpace["c"] = new UniformIntOption(2, 3); + + // changing of the first dimension should be reflected in option "a" + var parameter = searchSpace.SampleFromFeatureSpace(new double[] { 0, 0.5, 0.5 }); + parameter["a"].AsType().Should().Be(0); + parameter = searchSpace.SampleFromFeatureSpace(new double[] { 1, 0.5, 0.5 }); + parameter["a"].AsType().Should().Be(1); + + searchSpace.Remove("a"); + + // the first dimension should be option "b" + parameter = searchSpace.SampleFromFeatureSpace(new double[] { 0, 0.5 }); + parameter["b"].AsType().Should().Be(1); + parameter = searchSpace.SampleFromFeatureSpace(new double[] { 1, 0.5 }); + parameter["b"].AsType().Should().Be(2); + } + + [Fact] + [UseApprovalSubdirectory("ApprovalTests")] + [UseReporter(typeof(DiffReporter))] + public void Trainer_default_search_space_test() + { + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + CreateAndVerifyDefaultSearchSpace(); + } + + private void CreateAndVerifyDefaultSearchSpace() + where TOption : class, new() + { + var ss = new SearchSpace(); + var json = JsonSerializer.Serialize(ss, _settings); + NamerFactory.AdditionalInformation = typeof(TOption).FullName; + Approvals.Verify(json); + } + private class DefaultSearchSpace { public int Int { get; set; } @@ -253,5 +361,27 @@ private class NestSearchSpace [Range(-1000.0f, 1000, init: 0)] public float UniformFloat { get; set; } } + + class DoubleConverter : JsonConverter + { + public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => Convert.ToDouble(reader.GetDecimal()); + + public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) + { + writer.WriteNumberValue(Math.Round(Convert.ToDecimal(value), 6)); + } + } + + class SingleConverter : JsonConverter + { + public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => Convert.ToSingle(reader.GetDecimal()); + + public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options) + { + writer.WriteNumberValue(Convert.ToDecimal(value)); + } + } } }