Files
Reseaux-de-neurones-artific…/resources/js/pages/PerceptronViewer.vue
Matthias Guillitte 83b7aa3f3a
Some checks failed
linter / quality (push) Failing after 7s
tests / ci (8.4) (push) Failing after 6s
tests / ci (8.5) (push) Failing after 5s
Added configuration panel datasets, back-end refactor and others
2026-03-12 16:38:50 +01:00

228 lines
6.7 KiB
Vue

<script setup lang="ts">
import { Head } from '@inertiajs/vue3';
import { useEcho } from '@laravel/echo-vue';
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale,
PointElement,
LineElement,
} from 'chart.js';
import { computed, onMounted, ref, watch } from 'vue';
import LinkHeader from '@/components/LinkHeader.vue';
import type {
Dataset,
InitializationMethod,
Iteration,
PerceptronType,
} from '@/types/perceptron';
import IterationTable from '../components/IterationTable.vue';
import PerceptronDecisionGraph from '../components/PerceptronDecisionGraph.vue';
import PerceptronIterationsErrorsGraph from '../components/PerceptronIterationsErrorsGraph.vue';
import PerceptronSetup from '../components/PerceptronSetup.vue';
ChartJS.register(
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale,
PointElement,
LineElement,
);
ChartJS.defaults.font.size = 16;
ChartJS.defaults.color = '#FFF';
ChartJS.defaults.backgroundColor = '#AAA';
const props = defineProps<{
type: PerceptronType;
sessionId: string;
datasets: Dataset[];
minError: number;
learningRate: number;
maxIterations: number;
}>();
const selectedDatasetName = ref<string>('');
const dataset = computed<number[][]>(() => {
const selected = props.datasets.find(
(d) => d.label === selectedDatasetName.value,
);
return selected ? selected.data : [];
});
const cleanedDataset = computed<
{
label: number;
data: { x: number; y: number }[];
}[]
>(() => {
if (!dataset.value) {
return [];
}
const cleanedDataset: {
label: number;
data: { x: number; y: number }[];
}[] = [];
// Separate data into each dataset based on value of the last column (label)
dataset.value.forEach((row) => {
const label = row[row.length - 1];
const dataPoint = { x: row[0], y: row[1] };
let dataset = cleanedDataset.find((d) => d.label === label);
if (!dataset) {
dataset = { label, data: [] };
cleanedDataset.push(dataset);
}
dataset.data.push(dataPoint);
});
return cleanedDataset;
});
const initializationMethod = ref<InitializationMethod>('zeros');
console.log('Session ID:', props.sessionId);
useEcho(
`${props.sessionId}-perceptron-training`,
'PerceptronTrainingIteration',
percpetronIteration,
[{}],
'public',
);
useEcho(
`${props.sessionId}-perceptron-training`,
'PerceptronTrainingEnded',
perceptronTrainingEnded,
[{}],
'public',
);
useEcho(
`${props.sessionId}-perceptron-training`,
'PerceptronInitialization',
perceptroninitialization,
[{}],
'public',
);
const iterations = ref<Iteration[]>([]);
const trainingId = ref<string>('');
function percpetronIteration(data: any) {
console.log('Received perceptron iteration data:', data);
if (data.trainingId !== trainingId.value) {
console.warn(
`Received iteration for training ID ${data.trainingId}, but current training ID is ${trainingId.value}. Ignoring this iteration.`
);
return;
}
iterations.value.push(...data.iterations);
}
const trainingEnded = ref(false);
const trainingEndReason = ref('');
function perceptronTrainingEnded(data: any) {
console.log('Perceptron training ended:', data);
if (data.trainingId !== trainingId.value) {
console.warn(
`Received training ended event for training ID ${data.trainingId}, but current training ID is ${trainingId.value}. Ignoring this event.`
);
return;
}
trainingEnded.value = true;
trainingEndReason.value = data.reason;
}
const activationFunction = ref<string>('');
function perceptroninitialization(data: any) {
console.log('Perceptron training initialized:', data);
if (data.trainingId !== trainingId.value) {
console.warn(
`Received initialization event for training ID ${data.trainingId}, but current training ID is ${trainingId.value}. Ignoring this event.`
);
return;
}
activationFunction.value = data.activation_function;
}
function getActivationFunction(type: string): (x: number) => number {
switch (type) {
case 'step':
return (x) => (x >= 0 ? 1 : 0);
case 'sigmoid':
return (x) => 1 / (1 + Math.exp(-x));
case 'tanh':
return (x) => Math.tanh(x);
default:
return (x) => x; // Identity function as fallback
}
}
function resetTraining() {
iterations.value = [];
trainingEnded.value = false;
trainingEndReason.value = '';
activationFunction.value = '';
}
</script>
<template>
<Head title="Perceptron Viewer"></Head>
<main class="space-y-6">
<LinkHeader class="w-full" />
<PerceptronSetup
:type="props.type"
:datasets="props.datasets"
:selectedDataset="selectedDatasetName"
:initializationMethod="initializationMethod"
:minError="props.minError"
:sessionId="props.sessionId"
:defaultLearningRate="props.learningRate"
:defaultMaxIterations="props.maxIterations"
@update:selected-dataset="
(newValue) => {
selectedDatasetName = newValue;
}
"
@update:training-id="
(newValue) => {
trainingId = newValue;
resetTraining();
}"
/>
<div
class="align-items-start justify-content-center flex h-full min-h-dvh max-w-dvw"
v-if="selectedDatasetName || iterations.length > 0"
>
<div class="max-h-full w-full overflow-y-scroll">
<IterationTable
:iterations="iterations"
:trainingEnded="trainingEnded"
:trainingEndReason="trainingEndReason"
/>
</div>
<div class="sticky top-0 h-full w-full">
<div>
<PerceptronDecisionGraph
:cleanedDataset="cleanedDataset"
:iterations="iterations"
:activation-function="
getActivationFunction(activationFunction)
"
/>
</div>
<div>
<PerceptronIterationsErrorsGraph
:iterations="iterations"
v-if="iterations.length > 0"
/>
</div>
</div>
</div>
</main>
</template>