chromium/chrome/browser/ash/power/auto_screen_brightness/gaussian_trainer.h

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_POWER_AUTO_SCREEN_BRIGHTNESS_GAUSSIAN_TRAINER_H_
#define CHROME_BROWSER_ASH_POWER_AUTO_SCREEN_BRIGHTNESS_GAUSSIAN_TRAINER_H_

#include <memory>

#include "chrome/browser/ash/power/auto_screen_brightness/trainer.h"

namespace ash {
namespace power {
namespace auto_screen_brightness {

struct TrainingResult {
  TrainingResult();
  TrainingResult(const std::optional<MonotoneCubicSpline>& new_curve,
                 double error);
  TrainingResult(const TrainingResult& result);
  ~TrainingResult();
  // |new_curve| will be nullopt if trainer's curve stays unchanged after
  // training.
  std::optional<MonotoneCubicSpline> new_curve;
  // Evaluation error of the latest curve (possibly updated) using the training
  // data points.
  double error;
};

// GaussianTrainer updates an existing brightness curve (a mapping from
// ambient light to screen brightness) using training data points that represent
// how user changes brightness following an ambient value change. The update
// procedure is Gaussian hence the name. It uses a global curve to check for
// outliers that may exist in training data. It also ensures new curves are
// monotone and also satisfy requirements on the slope.
class GaussianTrainer : public Trainer {
 public:
  // TODO(jiameng): revise default values.
  struct Params {
    Params();
    // |brightness_bound_scale| and |brightness_bound_offset| are used to define
    // training example outliers.
    double brightness_bound_scale = 1.5;
    double brightness_bound_offset = 40;

    // |brightness_step_size| defines reasonable brightness change scale: a
    // reasonable change would be between
    // brightness_old/(1+|brightness_step_size|) and
    // brightness_old*(1+|brightness_step_size|)
    double brightness_step_size = 1.5;

    // Similar to |brightness_step_size| except it defines reasonable brightness
    // change scale between target brightness and model predicted brightness.
    double model_brightness_step_size = 2.0;

    // One training data point could modify all the points on the curve, but its
    // effect is greatest on the point nearest to it (as measured by difference
    // in ambient value). The effect on the other points decay with a Gaussian
    // distribution with standard deviation |sigma|.
    double sigma = 1;

    // If log lux is below |low_log_lux_threshold| then we'll use
    // |min_grad_low_lux| as gradient constraint.
    double low_log_lux_threshold = 0.1;
    double min_grad_low_lux = 0;

    // If log lux is above |high_log_lux_threshold| then we'll use
    // |min_grad_high_lux| as gradient constraint.
    double high_log_lux_threshold = 7.5;
    double min_grad_high_lux = 0;

    // Min and max grad as a power of brightness ratios.
    double min_grad = 0.25;
    double max_grad = 1;

    double min_brightness = 0;
  };

  GaussianTrainer();

  GaussianTrainer(const GaussianTrainer&) = delete;
  GaussianTrainer& operator=(const GaussianTrainer&) = delete;

  ~GaussianTrainer() override;

  // Trainer overrides:
  bool HasValidConfiguration() const override;
  bool SetInitialCurves(const MonotoneCubicSpline& global_curve,
                        const MonotoneCubicSpline& current_curve) override;
  MonotoneCubicSpline GetGlobalCurve() const override;
  MonotoneCubicSpline GetCurrentCurve() const override;
  TrainingResult Train(const std::vector<TrainingDataPoint>& data) override;

 private:
  // Returns whether initial personal curve (passed in by |SetInitialCurves|) is
  // valid, i.e. satisfying min/max ratio constraints.
  bool IsInitialPersonalCurveValid() const;

  // Updates |brightness_| using |data|. It also sets |need_to_update_curve_|
  // to true if |brightness_| is actually changed.
  void AdjustCurveWithSingleDataPoint(const TrainingDataPoint& data);

  // Called each time |AdjustCurveWithSingleDataPoint| changes |brightness_|.
  // It ensures the curve is still monotone and also satisfies min/max grad
  // constraints. It does this by changing points to the left and to the right
  // of |center_index|.
  void EnforceMonotonicity(size_t center_index);

  // Calculates (possibly) updated curve's MAE error w.r.t. |data|. The error
  // will be in the range of [0, 100].
  double CalculateCurveError(const std::vector<TrainingDataPoint>& data) const;

  // Default params_ are valid.
  bool valid_params_ = true;

  Params params_;
  // |global_curve| does not change after |SetInitialCurves| is called.
  std::optional<MonotoneCubicSpline> global_curve_;
  // |current_curve_| initially is set by |SetInitialCurves| and then gets
  // updated during training.
  std::optional<MonotoneCubicSpline> current_curve_;

  // Whether the |brightness_| has been updated since last time |Train| updated
  // the curve.
  bool need_to_update_curve_ = false;

  // (|ambient_log_lux_|, |brightness_|) are the control points of
  // |current_curve_|. |ambient_log_lux_| doesn't change, but |brightness_| may
  // be updated during training.
  std::vector<double> ambient_log_lux_;
  std::vector<double> brightness_;

  // Minimum and max brightness ratios of two adjacent control points. They are
  // calculated from the global curve's brightness values.
  std::vector<double> min_ratios_;
  std::vector<double> max_ratios_;
};

}  // namespace auto_screen_brightness
}  // namespace power
}  // namespace ash

#endif  // CHROME_BROWSER_ASH_POWER_AUTO_SCREEN_BRIGHTNESS_GAUSSIAN_TRAINER_H_