query('type', 'simple'); $dataSet = $request->input('data_set', 'logic_and'); $dataSetReader = $this->getDataSetReader($dataSet); return inertia('PerceptronViewer', [ 'type' => $perceptronType, 'sessionId' => session()->getId(), 'dataset' => $dataSetReader->lines, 'csrf_token' => csrf_token(), ]); } private function getDataSetReader(string $dataSet): DataSetReader { $dataSetFileName = "data_sets/{$dataSet}.csv"; return new DataSetReader($dataSetFileName); } public function run(Request $request, ISynapticWeights $synapticWeightsProvider) { $perceptronType = $request->query('type', 'simple'); $minError = $request->query('min_error', 0.01); $dataSet = $request->input('data_set', 'logic_and'); $learningRate = $request->input('learning_rate', 0.1); $sessionId = $request->input('session_id', session()->getId()); $dataSetReader = $this->getDataSetReader($dataSet); $MAX_ITERATIONS = 100; $stopCondition; $trainFunction; $trainFunctionState = []; switch ($perceptronType) { case 'simple': $stopCondition = function($iteration, $iterationErrorCounter) use ($sessionId) { $condition = $iterationErrorCounter == 0; if ($condition === true) { Log::info("Perceptron training ended after {$iteration} iterations with no errors."); event(new \App\Events\PerceptronTrainingEnded('Le perceptron ne commet plus d\'erreurs sur aucune des données', $sessionId)); } return $iterationErrorCounter == 0; }; $iterationFunction = function(&$state) use ($synapticWeightsProvider, $learningRate, $minError) { if (!isset($state['perceptron'])) { $state['perceptron'] = new SimplePerceptron($synapticWeightsProvider->generate(2)); } $perceptron = $state['perceptron']; $inputs = $state['inputs']; $correctOutput = $state['correctOutput']; $iterationErrorCounter = $state['iterationErrorCounter'] ?? 0; $output = $perceptron->test($inputs); $error = $correctOutput - $output; if (abs($error) > $minError) { $iterationErrorCounter++; } if ($error !== 0) { // Update synaptic weights if needed $synaptic_weights = $perceptron->getSynapticWeights(); $inputs_with_bias = array_merge([1], $inputs); // Add bias input $new_weights = array_map(fn($weight, $input) => $weight + $learningRate * $error * $input, $synaptic_weights, $inputs_with_bias); $perceptron->setSynapticWeights($new_weights); } return [$error, $perceptron->getSynapticWeights(), $iterationErrorCounter, $state]; }; break; default: return response()->json(['error' => 'Invalid perceptron type'], 400); } $iteration = 0; $error = 1.0; // Initial error do { $iterationErrorCounter = 0; $iteration++; while ($nextRow = $dataSetReader->getRandomLine()) { $inputs = array_slice($nextRow, 0, -1); $correctOutput = end($nextRow); $trainFunctionState['inputs'] = $inputs; $trainFunctionState['correctOutput'] = $correctOutput; $trainFunctionState['iterationErrorCounter'] = $iterationErrorCounter; [$error, $synaptic_weights, $iterationErrorCounter, $trainFunctionState] = $iterationFunction($trainFunctionState); $error = abs($error); // Use absolute error // Broadcast the training iteration event event(new \App\Events\PerceptronTrainingIteration($iteration, $dataSetReader->getLastReadLineIndex(), $error, $synaptic_weights, $sessionId)); } $dataSetReader->reset(); // Reset the dataset for the next iteration } while ($iteration < $MAX_ITERATIONS && !$stopCondition($iteration, $iterationErrorCounter)); if ($iteration >= $MAX_ITERATIONS) { event(new \App\Events\PerceptronTrainingEnded('Le nombre maximal d\'itérations a été atteint', $sessionId)); } return response()->json([ 'message' => 'Training completed', 'iterations' => $iteration, 'final_error' => $error, 'final_synaptic_weights' => isset($trainFunctionState['perceptron']) ? $trainFunctionState['perceptron']->getSynapticWeights() : [0], ]); } }