File: Tuner\Flow2.cs
Web Access
Project: src\src\Microsoft.ML.AutoML\Microsoft.ML.AutoML.csproj (Microsoft.ML.AutoML)
// 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.Linq;
using Microsoft.ML.AutoMLService;
using Microsoft.ML.SearchSpace;
 
namespace Microsoft.ML.AutoML
{
    /// <summary>
    /// An implementation of Flow2 from https://www.aaai.org/AAAI21Papers/AAAI-10128.WuQ.pdf
    /// </summary>
    internal class Flow2
    {
        private const double _stepSize = 0.1;
        private const double _stepLowerBound = 0.0001;
 
        private readonly RandomNumberGenerator _rng = new RandomNumberGenerator();
 
        public double? BestObj = null;
        public double? CostIncumbent = null;
        private readonly Parameter _initConfig;
        private double _step;
 
        private readonly SearchSpace.SearchSpace _searchSpace;
        private readonly bool _minimize;
        private readonly double _convergeSpeed = 2;
        private Parameter _bestConfig;
        private double _costComplete4Incumbent = 0;
        private readonly int _dim;
        private double[] _directionTried = null;
        private double[] _incumbent;
        private int _numAllowed4Incumbent = 0;
        private readonly double _stepUpperBound;
        private int _trialCount = 1;
 
        public Flow2(SearchSpace.SearchSpace searchSpace, Parameter initValue = null, bool minimizeMode = true, double convergeSpeed = 1.5, RandomNumberGenerator rng = null)
        {
            _searchSpace = searchSpace;
            _minimize = minimizeMode;
 
            _initConfig = initValue;
            _bestConfig = _initConfig;
            _incumbent = _searchSpace.MappingToFeatureSpace(_bestConfig);
            _dim = _searchSpace.Count;
            _numAllowed4Incumbent = 2 * _dim;
            _step = _stepSize * Math.Sqrt(_dim);
            _stepUpperBound = Math.Sqrt(_dim);
            _convergeSpeed = convergeSpeed;
            if (_step > _stepUpperBound)
            {
                _step = _stepUpperBound;
            }
 
            _rng = rng;
        }
 
        public bool IsConverged
        {
            get => _step < _stepLowerBound;
        }
 
        public Parameter BestConfig
        {
            get => _bestConfig;
        }
 
        public SearchThread CreateSearchThread(Parameter config, double metric, double cost)
        {
            var flow2 = new Flow2(_searchSpace, config, _minimize, convergeSpeed: _convergeSpeed, rng: _rng);
            flow2.BestObj = metric;
            flow2.CostIncumbent = cost;
            return new SearchThread(flow2);
        }
 
        public Parameter Suggest(int trialId)
        {
            _numAllowed4Incumbent -= 1;
            double[] move;
            if (_directionTried != null)
            {
                move = ArrayMath.Sub(_incumbent, _directionTried);
 
                _directionTried = null;
            }
            else
            {
                _directionTried = RandVectorSphere();
                move = ArrayMath.Add(_incumbent, _directionTried);
            }
 
            move = Project(move);
            var config = _searchSpace.SampleFromFeatureSpace(move);
            return config;
        }
 
        public void ReceiveTrialResult(Parameter parameter, double metric, double cost)
        {
            _trialCount += 1;
            if (BestObj == null || metric < BestObj)
            {
                BestObj = metric;
                _bestConfig = parameter;
                _incumbent = _searchSpace.MappingToFeatureSpace(_bestConfig);
                CostIncumbent = cost;
                _costComplete4Incumbent = 0;
                _numAllowed4Incumbent = 2 * _dim;
                _step *= _convergeSpeed;
                _step = Math.Min(_step, _stepUpperBound);
                _directionTried = null;
                return;
            }
            else
            {
                _costComplete4Incumbent += cost;
                if (_numAllowed4Incumbent == 0)
                {
                    _numAllowed4Incumbent = 2;
                    if (!IsConverged)
                    {
                        _step /= _convergeSpeed;
                    }
                }
            }
        }
 
        private double[] RandVectorSphere()
        {
            double[] vec = _rng.Normal(0, 1, _searchSpace.FeatureSpaceDim);
            double mag = ArrayMath.Norm(vec);
            vec = ArrayMath.Mul(vec, _step / mag);
 
            return vec;
        }
 
        private double[] Project(double[] move)
        {
            return move.Select(x =>
            {
                if (x < 0)
                {
                    x = 0;
                }
                else if (x > 1)
                {
                    x = 0.99999999;
                }
 
                return x;
            }).ToArray();
        }
    }
}