| Server IP : 38.190.208.107 / Your IP : 216.73.216.152 Web Server : nginx/1.28.1 System : Linux ht2026040333114 6.1.0-10-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.37-1 (2023-07-03) x86_64 User : root ( 0) PHP Version : 8.2.28 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv,eval,assert,passthru,system,exec,shell_exec,popen,proc_open MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /www/wwwroot/wp.9/wp-content/themes/dt-the7/inc/mods/demo-content/ |
Upload File : |
<?php
/**
* Download time predictor for The7 demo content.
*
* @package The7\Mods\DemoContent
*/
namespace The7\Mods\Demo_Content;
defined( 'ABSPATH' ) || exit;
/**
* Download time forecaster for a single session.
*
* Assumptions:
* - Only current session history is available.
* - Features: file size (bytes) and observed time (seconds).
*
* Usage:
* $pred = new Download_Time_Predictor( array(
* 'min_samples_for_ols' => 5,
* 'ema_alpha' => 0.3,
* 'forget' => 0.98,
* 'outlier_low' => 0.5,
* 'outlier_high' => 2.0,
* 'init_p_scale' => 1e6,
* ) );
*
* // After each completed download:
* $pred->add_observation( $size_bytes, $time_seconds );
*
* // Predict time for the next file of size $size_bytes_next:
* $eta_seconds = $pred->predict_time( $size_bytes_next );
*/
class Download_Time_Predictor {
/** @var int Minimum samples to fit initial OLS before switching to RLS. */
private $min_samples_for_ols = 5;
/** @var float EMA alpha for speed (responsiveness). */
private $ema_alpha = 0.3;
/** @var float RLS forgetting factor (0.95–0.995 is typical). */
private $forget = 0.98;
/** @var float Outlier low threshold as ratio to EMA speed. */
private $outlier_low = 0.5;
/** @var float Outlier high threshold as ratio to EMA speed. */
private $outlier_high = 2.0;
/** @var float Initial covariance scale for RLS. */
private $init_p_scale = 1000000.0;
// ---- Running stats for tiny batch OLS ----
/** @var int Count of (non-outlier) observations. */
private $n = 0;
/** @var float Sum of sizes. */
private $sx = 0.0;
/** @var float Sum of times. */
private $sy = 0.0;
/** @var float Sum of size^2. */
private $sxx = 0.0;
/** @var float Sum of size * time. */
private $sxy = 0.0;
// ---- Model parameters (t = a*size + b) ----
/** @var float|null Seconds per byte (slope). */
private $a = null;
/** @var float|null Fixed overhead per file in seconds (intercept). */
private $b = null;
// ---- RLS state ----
/** @var bool Whether RLS updates are enabled. */
private $rls_enabled = false;
/** @var array 2x2 covariance matrix for RLS. */
private $p = [ [ 0.0, 0.0 ], [ 0.0, 0.0 ] ];
// ---- EMA of speed for outlier detection ----
/** @var float|null EMA of bytes per second. */
private $ema_speed = null;
/**
* Constructor.
*
* @param array $args Optional configuration overrides.
*/
public function __construct( $args = [] ) {
$defaults = [
'min_samples_for_ols' => 5,
'ema_alpha' => 0.3,
'forget' => 0.98,
'outlier_low' => 0.5,
'outlier_high' => 2.0,
'init_p_scale' => 1000000.0,
];
$args = wp_parse_args( $args, $defaults );
$this->min_samples_for_ols = max( 2, (int) $args['min_samples_for_ols'] );
$this->ema_alpha = (float) $args['ema_alpha'];
$this->forget = (float) $args['forget'];
$this->outlier_low = (float) $args['outlier_low'];
$this->outlier_high = (float) $args['outlier_high'];
$this->init_p_scale = (float) $args['init_p_scale'];
}
/**
* Add one observed download (size in bytes, time in seconds).
*
* @param float $size_bytes File size in bytes.
* @param float $time_sec Download time in seconds.
* @return bool True if the observation updated the model, false if treated as an outlier/invalid.
*/
public function add_observation( $size_bytes, $time_sec ) {
$size_bytes = (float) $size_bytes;
$time_sec = (float) $time_sec;
if ( $size_bytes <= 0.0 || $time_sec <= 0.0 ) {
return false;
}
$speed_bps = $size_bytes / $time_sec;
// Initialize EMA(speed) if needed.
if ( null === $this->ema_speed ) {
$this->ema_speed = $speed_bps;
} else {
$ratio = $speed_bps / max( $this->ema_speed, 1e-12 );
$is_outlier = ( $ratio < $this->outlier_low ) || ( $ratio > $this->outlier_high );
// Update EMA regardless; soften update if outlier.
$alpha = $is_outlier ? ( $this->ema_alpha * 0.25 ) : $this->ema_alpha;
$this->ema_speed = $alpha * $speed_bps + ( 1.0 - $alpha ) * $this->ema_speed;
if ( $is_outlier ) {
// Do not update parameter model with this sample.
return false;
}
}
// If RLS is enabled and we have parameters, do RLS update.
if ( $this->rls_enabled && null !== $this->a && null !== $this->b ) {
$this->rls_update( $size_bytes, $time_sec );
return true;
}
// Accumulate for tiny batch OLS.
$this->n += 1;
$this->sx += $size_bytes;
$this->sy += $time_sec;
$this->sxx += $size_bytes * $size_bytes;
$this->sxy += $size_bytes * $time_sec;
// Fit OLS once we have at least 2 points.
if ( $this->n >= 2 ) {
$this->fit_ols();
}
// Enable RLS after enough samples and a valid OLS fit.
if ( ! $this->rls_enabled && $this->n >= $this->min_samples_for_ols && null !== $this->a && null !== $this->b ) {
$this->enable_rls();
}
return true;
}
/**
* Predict time (seconds) for a given file size (bytes).
*
* @param float $size_bytes File size in bytes.
* @return float|null Predicted seconds, or null if insufficient information.
*/
public function predict_time( $size_bytes ) {
$size_bytes = (float) $size_bytes;
if ( $size_bytes <= 0.0 ) {
return null;
}
// Prefer linear model if available.
if ( null !== $this->a && null !== $this->b ) {
$t = $this->a * $size_bytes + $this->b;
return max( 0.0, $t );
}
// Fallback: EMA-based estimate if model not ready.
if ( null !== $this->ema_speed && $this->ema_speed > 0.0 ) {
return $size_bytes / $this->ema_speed;
}
return null;
}
/**
* Reset for a new session.
*/
public function reset() {
$this->n = 0;
$this->sx = 0.0;
$this->sy = 0.0;
$this->sxx = 0.0;
$this->sxy = 0.0;
$this->a = null;
$this->b = null;
$this->rls_enabled = false;
$this->p = [ [ 0.0, 0.0 ], [ 0.0, 0.0 ] ];
$this->ema_speed = null;
}
/**
* Get current parameters / telemetry (for logging or debugging).
*
* @return array {
* @type float|null 'a_sec_per_byte' Slope (seconds per byte).
* @type float|null 'b_overhead_sec' Intercept (seconds).
* @type float|null 'ema_speed_bps' EMA of speed (bytes/sec).
* @type bool 'rls' Whether RLS is enabled.
* }
*/
public function get_params() {
return [
'a_sec_per_byte' => $this->a,
'b_overhead_sec' => $this->b,
'ema_speed_bps' => $this->ema_speed,
'rls' => $this->rls_enabled,
];
}
// ===== Internals =====================================================
/**
* Fit OLS parameters for t = a*size + b using running sums.
*
* @return void
*/
private function fit_ols() {
$den = ( $this->n * $this->sxx - $this->sx * $this->sx );
if ( abs( $den ) < 1e-18 ) {
// Degenerate (e.g., all sizes equal). Fall back to inverse of average speed.
$avg_speed = ( $this->sy > 0.0 ) ? ( $this->sx / $this->sy ) : null; // bytes/s
if ( null !== $avg_speed && $avg_speed > 0.0 ) {
$this->a = 1.0 / $avg_speed;
$this->b = 0.0; // cannot infer overhead safely in this case.
}
return;
}
$a = ( $this->n * $this->sxy - $this->sx * $this->sy ) / $den;
$b = ( $this->sy * $this->sxx - $this->sx * $this->sxy ) / $den;
// Guard against nonsense; keep non-negative slope.
if ( is_finite( $a ) && is_finite( $b ) && $a >= 0.0 ) {
$this->a = $a;
$this->b = $b;
}
}
/**
* Enable RLS around the current OLS solution.
*
* @return void
*/
private function enable_rls() {
$s = $this->init_p_scale;
$this->p = [ [ $s, 0.0 ], [ 0.0, $s ] ];
$this->rls_enabled = true;
}
/**
* Perform one RLS update with forgetting.
*
* @param float $size_bytes File size in bytes.
* @param float $time_sec Observed time in seconds.
* @return void
*/
private function rls_update( $size_bytes, $time_sec ) {
$x1 = (float) $size_bytes; // size.
$x2 = 1.0; // bias term.
$y = (float) $time_sec; // time.
$lambda = $this->forget;
// P * x.
$p_x1 = $this->p[0][0] * $x1 + $this->p[0][1] * $x2;
$p_x2 = $this->p[1][0] * $x1 + $this->p[1][1] * $x2;
// denom = lambda + x^T * P * x.
$denom = $lambda + ( $x1 * $p_x1 + $x2 * $p_x2 );
if ( $denom <= 1e-18 ) {
return;
}
// Gain K = P*x / denom (2x1 vector).
$k1 = $p_x1 / $denom;
$k2 = $p_x2 / $denom;
// Prediction error e = y - x^T * theta.
$yhat = ( ( null !== $this->a ? $this->a : 0.0 ) * $x1 ) + ( null !== $this->b ? $this->b : 0.0 ) * $x2;
$e = $y - $yhat;
// Update theta.
$this->a = ( null !== $this->a ? $this->a : 0.0 ) + $k1 * $e;
$this->b = ( null !== $this->b ? $this->b : 0.0 ) + $k2 * $e;
// Update P: P = (1/lambda) * (P - K*x^T*P).
$k_x00 = $k1 * $x1;
$k_x01 = $k1 * $x2;
$k_x10 = $k2 * $x1;
$k_x11 = $k2 * $x2;
$k_xp00 = $k_x00 * $this->p[0][0] + $k_x01 * $this->p[1][0];
$k_xp01 = $k_x00 * $this->p[0][1] + $k_x01 * $this->p[1][1];
$k_xp10 = $k_x10 * $this->p[0][0] + $k_x11 * $this->p[1][0];
$k_xp11 = $k_x10 * $this->p[0][1] + $k_x11 * $this->p[1][1];
$p00 = ( $this->p[0][0] - $k_xp00 ) / $lambda;
$p01 = ( $this->p[0][1] - $k_xp01 ) / $lambda;
$p10 = ( $this->p[1][0] - $k_xp10 ) / $lambda;
$p11 = ( $this->p[1][1] - $k_xp11 ) / $lambda;
// Enforce symmetry (numerical hygiene).
$this->p = [
[ $p00, 0.5 * ( $p01 + $p10 ) ],
[ 0.5 * ( $p01 + $p10 ), $p11 ],
];
// Ensure non-negative slope.
if ( $this->a < 0.0 ) {
$this->a = 0.0;
}
}
}