Files
Siro/backend/bot/ai_formula_solver.php

177 lines
5.7 KiB
PHP

<?php
/**
* ai_formula_solver.php
* مكتشف خوارزميات المنافسين (AI Competitor Formula Solver)
* يستخدم الانحدار الخطي المتعدد (Multiple Linear Regression) لاكتشاف
* أجرة فتح العداد، وسعر الكيلومتر، وسعر الدقيقة لكل تطبيق منافس.
*/
require_once __DIR__ . '/../core/bootstrap.php';
require_once __DIR__ . '/../functions.php';
try {
$con = Database::get('main');
} catch (Exception $e) {
die("Database connection failed: " . $e->getMessage() . "\n");
}
echo "Starting AI Formula Discovery Engine...\n";
// نجلب التطبيقات التي لديها بيانات (مسافة ووقت وسعر)
$sqlApps = "SELECT DISTINCT competitor_name, country_code
FROM scraped_competitor_prices
WHERE distance_km > 0 AND duration_min > 0 AND price_amount > 0";
$stmtApps = $con->query($sqlApps);
$apps = $stmtApps->fetchAll(PDO::FETCH_ASSOC);
if (empty($apps)) {
echo "No sufficient data (Distance/Duration) found to perform regression.\n";
exit;
}
// دالة لحل نظام معادلات خطية (Gaussian Elimination)
function solveLinearSystem($A, $B) {
$n = count($A);
for ($i = 0; $i < $n; $i++) {
// Search for maximum in this column
$maxEl = abs($A[$i][$i]);
$maxRow = $i;
for ($k = $i + 1; $k < $n; $k++) {
if (abs($A[$k][$i]) > $maxEl) {
$maxEl = abs($A[$k][$i]);
$maxRow = $k;
}
}
// Swap maximum row with current row
for ($k = $i; $k < $n; $k++) {
$tmp = $A[$maxRow][$k];
$A[$maxRow][$k] = $A[$i][$k];
$A[$i][$k] = $tmp;
}
$tmp = $B[$maxRow];
$B[$maxRow] = $B[$i];
$B[$i] = $tmp;
// Make all rows below this one 0 in current column
for ($k = $i + 1; $k < $n; $k++) {
if ($A[$i][$i] == 0) continue;
$c = -$A[$k][$i] / $A[$i][$i];
for ($j = $i; $j < $n; $j++) {
if ($i == $j) {
$A[$k][$j] = 0;
} else {
$A[$k][$j] += $c * $A[$i][$j];
}
}
$B[$k] += $c * $B[$i];
}
}
// Solve equation Ax=b for an upper triangular matrix A
$x = array_fill(0, $n, 0);
for ($i = $n - 1; $i >= 0; $i--) {
if ($A[$i][$i] == 0) continue;
$x[$i] = $B[$i] / $A[$i][$i];
for ($k = $i - 1; $k >= 0; $k--) {
$B[$k] -= $A[$k][$i] * $x[$i];
}
}
return $x;
}
foreach ($apps as $app) {
$competitor = $app['competitor_name'];
$countryCode = $app['country_code'];
echo "Analyzing: $competitor ($countryCode)...\n";
// سحب أحدث 5000 رحلة لتكوين نموذج رياضي دقيق
$sqlData = "SELECT distance_km, duration_min, price_amount
FROM scraped_competitor_prices
WHERE competitor_name = :comp
AND country_code = :country
AND distance_km > 0 AND duration_min > 0 AND price_amount > 0
ORDER BY id DESC LIMIT 5000";
$stmtData = $con->prepare($sqlData);
$stmtData->execute([':comp' => $competitor, ':country' => $countryCode]);
$samples = $stmtData->fetchAll(PDO::FETCH_ASSOC);
$N = count($samples);
if ($N < 10) {
echo " -> Not enough samples ($N). Skipping.\n";
continue;
}
// بناء مصفوفات Least Squares (X^T X) * Beta = (X^T Y)
// Beta = [Base_Fare, KM_Price, Min_Price]
$sum_x1 = 0; $sum_x2 = 0; $sum_y = 0;
$sum_x1_sq = 0; $sum_x2_sq = 0; $sum_x1_x2 = 0;
$sum_x1_y = 0; $sum_x2_y = 0;
foreach ($samples as $s) {
$x1 = (float)$s['distance_km'];
$x2 = (float)$s['duration_min'];
$y = (float)$s['price_amount'];
$sum_x1 += $x1;
$sum_x2 += $x2;
$sum_y += $y;
$sum_x1_sq += ($x1 * $x1);
$sum_x2_sq += ($x2 * $x2);
$sum_x1_x2 += ($x1 * $x2);
$sum_x1_y += ($x1 * $y);
$sum_x2_y += ($x2 * $y);
}
$matrixA = [
[$N, $sum_x1, $sum_x2],
[$sum_x1, $sum_x1_sq, $sum_x1_x2],
[$sum_x2, $sum_x1_x2, $sum_x2_sq]
];
$matrixB = [
$sum_y,
$sum_x1_y,
$sum_x2_y
];
// حل المصفوفة
try {
$beta = solveLinearSystem($matrixA, $matrixB);
$baseFare = round(max(0, $beta[0]), 3); // Base fare cannot be negative
$kmPrice = round(max(0, $beta[1]), 3);
$minPrice = round(max(0, $beta[2]), 3);
echo " -> [DISCOVERED] Base Fare: $baseFare, KM: $kmPrice, Min: $minPrice\n";
// حفظ في جدول المعادلات السرية
$sqlUpsert = "INSERT INTO competitor_secret_formulas
(competitor_name, country_code, base_fare, price_per_km, price_per_min, sample_size)
VALUES (:comp, :country, :base, :km, :min, :size)
ON DUPLICATE KEY UPDATE
base_fare = :base, price_per_km = :km, price_per_min = :min, sample_size = :size, last_updated = NOW()";
$stmtUp = $con->prepare($sqlUpsert);
$stmtUp->execute([
':comp' => $competitor,
':country' => $countryCode,
':base' => $baseFare,
':km' => $kmPrice,
':min' => $minPrice,
':size' => $N
]);
echo " -> Saved successfully.\n";
} catch (Exception $e) {
echo " -> Error solving matrix: " . $e->getMessage() . "\n";
}
}
echo "Done.\n";
?>