/// <summary> /// Computes a single-mutation neighborhood (one param at a time) for a given configuration. For /// numeric parameters, samples K mutations (i.e., creates K neighbors based on that paramater). /// </summary> /// <param name="parent">Starting configuration.</param> /// <returns>A set of configurations that each differ from parent in exactly one parameter.</returns> private ParameterSet[] GetOneMutationNeighborhood(ParameterSet parent) { List <ParameterSet> neighbors = new List <ParameterSet>(); SweeperProbabilityUtils spu = new SweeperProbabilityUtils(_host); for (int i = 0; i < _sweepParameters.Length; i++) { // This allows us to query possible values of this parameter. IValueGenerator sweepParam = _sweepParameters[i]; // This holds the actual value for this parameter, chosen in this parameter set. IParameterValue pset = parent[sweepParam.Name]; _host.AssertValue(pset); DiscreteValueGenerator parameterDiscrete = sweepParam as DiscreteValueGenerator; if (parameterDiscrete != null) { // Create one neighbor for every discrete parameter. Float[] neighbor = SweeperProbabilityUtils.ParameterSetAsFloatArray(_host, _sweepParameters, parent, false); int hotIndex = -1; for (int j = 0; j < parameterDiscrete.Count; j++) { if (parameterDiscrete[j].Equals(pset)) { hotIndex = j; break; } } _host.Assert(hotIndex >= 0); Random r = new Random(); int randomIndex = r.Next(0, parameterDiscrete.Count - 1); randomIndex += randomIndex >= hotIndex ? 1 : 0; neighbor[i] = randomIndex; neighbors.Add(SweeperProbabilityUtils.FloatArrayAsParameterSet(_host, _sweepParameters, neighbor, false)); } else { INumericValueGenerator parameterNumeric = sweepParam as INumericValueGenerator; _host.Check(parameterNumeric != null, "SMAC sweeper can only sweep over discrete and numeric parameters"); // Create k neighbors (typically 4) for every numerical parameter. for (int j = 0; j < _args.NumNeighborsForNumericalParams; j++) { Float[] neigh = SweeperProbabilityUtils.ParameterSetAsFloatArray(_host, _sweepParameters, parent, false); double newVal = spu.NormalRVs(1, neigh[i], 0.2)[0]; while (newVal <= 0.0 || newVal >= 1.0) { newVal = spu.NormalRVs(1, neigh[i], 0.2)[0]; } neigh[i] = (Float)newVal; ParameterSet neighbor = SweeperProbabilityUtils.FloatArrayAsParameterSet(_host, _sweepParameters, neigh, false); neighbors.Add(neighbor); } } } return(neighbors.ToArray()); }
/// <summary> /// Generic parameter parser. Currently hand-hacked to auto-detect type. /// /// Generic form: Name:Values /// e.g.: lr:0.05-0.4 /// lambda:0.1-1000@log10 /// nl:2-64@log2 /// norm:-,+ /// </summary> /// REVIEW: allow overriding auto-detection to specify type /// and delegate to parameter type for actual parsing /// REVIEW: specifying ordinal discrete parameters public bool TryParseParameter(string paramValue, Type paramType, string paramName, out IValueGenerator sweepValues, out string error) { sweepValues = null; error = null; if (paramValue.Contains(',')) { var generatorArgs = new DiscreteParamArguments(); generatorArgs.Name = paramName; generatorArgs.Values = paramValue.Split(','); sweepValues = new DiscreteValueGenerator(generatorArgs); return(true); } // numeric parameter if (!CmdParser.IsNumericType(paramType)) { return(false); } // REVIEW: deal with negative bounds string scaleStr = null; int atIdx = paramValue.IndexOf('@'); if (atIdx < 0) { atIdx = paramValue.IndexOf(';'); } if (atIdx >= 0) { scaleStr = paramValue.Substring(atIdx + 1); paramValue = paramValue.Substring(0, atIdx); if (scaleStr.Length < 1) { error = $"Could not parse sweep range for parameter: {paramName}"; return(false); } } // Extract the minimum, and the maximum value of the list of suggested sweeps. // Positive lookahead splitting at the '-' character. // It is used for the Float and Long param types. // Example format: "0.02-0.1;steps:5". string[] minMaxRegex = Regex.Split(paramValue, "(?<=[^eE])-"); if (minMaxRegex.Length != 2) { if (minMaxRegex.Length > 2) { error = $"Could not parse sweep range for parameter: {paramName}"; } return(false); } string minStr = minMaxRegex[0]; string maxStr = minMaxRegex[1]; int numSteps = 100; Double stepSize = -1; bool logBase = false; if (scaleStr != null) { try { string[] options = scaleStr.Split(';'); bool[] optionsSpecified = new bool[3]; foreach (string option in options) { if (option.StartsWith("log") && !option.StartsWith("log-") && !option.StartsWith("log:-")) { logBase = true; optionsSpecified[0] = true; } if (option.StartsWith("steps")) { numSteps = int.Parse(option.Substring(option.IndexOf(':') + 1)); optionsSpecified[1] = true; } if (option.StartsWith("inc")) { stepSize = Double.Parse(option.Substring(option.IndexOf(':') + 1), CultureInfo.InvariantCulture); optionsSpecified[2] = true; } } if (options.Length != optionsSpecified.Count(b => b)) { error = $"Could not parse sweep range for parameter: {paramName}"; return(false); } } catch (Exception e) { error = $"Error creating sweep generator for parameter '{paramName}': {e.Message}"; return(false); } } if (paramType == typeof(UInt16) || paramType == typeof(UInt32) || paramType == typeof(UInt64) || paramType == typeof(short) || paramType == typeof(int) || paramType == typeof(long)) { long min; long max; if (!long.TryParse(minStr, out min) || !long.TryParse(maxStr, out max)) { return(false); } var generatorArgs = new Microsoft.ML.Runtime.Sweeper.LongParamArguments(); generatorArgs.Name = paramName; generatorArgs.Min = min; generatorArgs.Max = max; generatorArgs.NumSteps = numSteps; generatorArgs.StepSize = (stepSize > 0 ? stepSize : new Nullable <Double>()); generatorArgs.LogBase = logBase; try { sweepValues = new LongValueGenerator(generatorArgs); } catch (Exception e) { error = $"Error creating sweep generator for parameter '{paramName}': {e.Message}"; return(false); } } else { Float minF; Float maxF; if (!Float.TryParse(minStr, out minF) || !Float.TryParse(maxStr, out maxF)) { return(false); } var floatArgs = new FloatParamArguments(); floatArgs.Name = paramName; floatArgs.Min = minF; floatArgs.Max = maxF; floatArgs.NumSteps = numSteps; floatArgs.StepSize = (stepSize > 0 ? stepSize : new Nullable <Double>()); floatArgs.LogBase = logBase; try { sweepValues = new FloatValueGenerator(floatArgs); } catch (Exception e) { error = $"Error creating sweep generator for parameter '{paramName}': {e.Message}"; return(false); } } return(true); }