git init
Some checks failed
linter / quality (push) Failing after 6m40s
tests / ci (8.4) (push) Failing after 10s
tests / ci (8.5) (push) Failing after 11s

This commit is contained in:
2026-03-03 11:10:38 +01:00
commit 650cf56045
282 changed files with 27333 additions and 0 deletions

View File

@@ -0,0 +1,396 @@
<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,
ChartDataset,
ChartTypeRegistry,
BubbleDataPoint,
Point,
ChartData,
} from 'chart.js';
import { onMounted, ref } from 'vue';
import { Bar, Chart, Line } from 'vue-chartjs';
import LinkHeader from '@/components/LinkHeader.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: string;
dataset: number[][];
sessionId: string;
}>();
console.log('Session ID:', props.sessionId);
useEcho(
`${props.sessionId}-perceptron-training`,
'PerceptronTrainingIteration',
percpetronIteration,
[{}],
'public',
);
useEcho(
`${props.sessionId}-perceptron-training`,
'PerceptronTrainingEnded',
perceptronTrainingEnded,
[{}],
'public',
);
onMounted(() => {
// make a POST request to start the perceptron training
fetch('/api/perceptron/run', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'same-origin',
body: JSON.stringify({
type: 'simple',
min_error: 0.01,
session_id: props.sessionId,
}),
})
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((data) => {
console.log('Perceptron training started:', data);
})
.catch((error) => {
console.error('Error starting perceptron training:', error);
});
});
const iterations = ref<
{
iteration: number;
exampleIndex: number;
weights: number[];
error: number;
}[]
>([]);
function percpetronIteration(data: any) {
console.log('Received perceptron iteration data:', data);
iterations.value.push({
iteration: data.iteration,
exampleIndex: data.exampleIndex,
weights: data.synaptic_weights,
error: data.error,
});
}
const trainingEnded = ref(false);
const trainingEndReason = ref('');
function perceptronTrainingEnded(data: any) {
console.log('Perceptron training ended:', data);
trainingEnded.value = true;
trainingEndReason.value = data.reason;
}
// Separate data into each dataset based on value of the last column (label)
const cleanedDataset: { label: number; data: { x: number; y: number }[] }[] =
[];
props.dataset.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);
});
function getPerceptronDecisionBoundaryDataset(
weights: number[],
): ChartDataset<
keyof ChartTypeRegistry,
number | Point | [number, number] | BubbleDataPoint | null
> {
const label = 'Ligne de décision du Perceptron';
if (weights.length == 3) {
// Simple line
return {
type: 'line',
label: label,
data: [
{
x: -1,
y:
-(weights[1] / weights[2]) * -1 -
weights[0] / weights[2],
},
{
x: 2,
y: -(weights[1] / weights[2]) * 2 - weights[0] / weights[2],
},
],
borderColor: '#FFF',
borderWidth: 2,
pointRadius: 0,
};
} else {
return {
type: 'scatter',
label: label,
data: (() => {
const decisionBoundary = [];
const latestWeights =
iterations.value.length > 0
? iterations.value[iterations.value.length - 1].weights
: [0, 0, 0]; // default weights if no iterations yet
for (let x = -2; x <= 2; x += 0.05) {
for (let y = -2; y <= 2; y += 0.05) {
let value = 0;
for (let i = 0; i < latestWeights.length - 1; i++) {
value += latestWeights[i] * (i === 0 ? x : y); // TODO : Fix formula
}
value += latestWeights[0]; // bias
if (Math.abs(value) < 0.003) {
decisionBoundary.push({ x: x, y: y });
}
}
}
return decisionBoundary;
})(),
backgroundColor: '#FFF',
};
}
}
/**
* Return the datasets of the iterations with the form { label: `Exemple ${exampleIndex}`, data: [error for iteration 1, error for iteration 2, ...] }
*/
function getPerceptronErrorsPerIteration(): ChartData<
'bar',
(number | [number, number] | null)[]
>[] {
const datasets: ChartData<'bar', (number | [number, number] | null)[]>[] =
[];
const backgroundColors = [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#4BC0C0',
'#9D5C5C',
'#8B4513',
'#2E8B57',
'#800080',
];
iterations.value.forEach((iteration) => {
const exampleLabel = `Exemple ${iteration.exampleIndex}`;
let dataset = datasets.find((d) => d.label === exampleLabel);
if (!dataset) {
dataset = {
label: exampleLabel,
data: [],
backgroundColor:
backgroundColors[
iteration.exampleIndex % backgroundColors.length
],
};
datasets.push(dataset);
}
dataset.data.push(iteration.error);
});
// Sort dataset by label (Exemple 0, Exemple 1, ...)
datasets.sort((a, b) => {
const aIndex = parseInt(a.label.split(' ')[1]);
const bIndex = parseInt(b.label.split(' ')[1]);
return aIndex - bIndex;
});
return datasets;
}
</script>
<template>
<Head title="Perceptron Viewer"></Head>
<main class="space-y-6">
<LinkHeader class="w-full" />
<div
class="align-items-start justify-content-center flex h-full min-h-dvh max-w-dvw"
>
<div class="max-h-full w-full overflow-y-scroll">
<table
class="table w-full border-collapse border border-gray-300"
>
<tr class="text-left" v-if="iterations.length > 0">
<th>Itération</th>
<th>Exemple</th>
<th
v-for="(weight, index) in iterations[0].weights"
v-bind:key="index"
>
X<sub>{{ index }}</sub>
</th>
<th>Erreur</th>
</tr>
<tr
v-for="(iteration, index) in iterations"
v-bind:key="index"
:class="{
'bg-gray-900': iteration.iteration % 2 === 0,
}"
>
<td>{{ iteration.iteration }}</td>
<td>{{ iteration.exampleIndex }}</td>
<td
v-for="(weight, index) in iteration.weights"
v-bind:key="index"
>
{{ weight.toFixed(2) }}
</td>
<td>{{ iteration.error.toFixed(2) }}</td>
</tr>
<tr v-if="trainingEnded" class="bg-red-900 text-center">
<td colspan="100%">
<strong>Entraînement terminé :</strong>
{{ trainingEndReason }}
</td>
</tr>
</table>
</div>
<div class="h-full w-full">
<div>
<Chart
class="flex"
:options="{
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: 'Ligne de décision du Perceptron',
},
},
layout: {
padding: {
left: 10,
right: 10,
top: 10,
bottom: 10,
},
},
scales: {
x: {
type: 'linear',
position: 'bottom',
},
y: {
type: 'linear',
position: 'left',
},
},
}"
:data="{
datasets: [
// Points from the dataset
...cleanedDataset.map((dataset, index) => ({
type: 'scatter',
label: `Classe ${dataset.label}`,
data: dataset.data,
backgroundColor:
[
'#FF6384',
'#36A2EB',
'#FFCE56',
'#4BC0C0',
'#9D5C5C',
'#8B4513',
'#2E8B57',
'#800080',
][index] || '#AAA',
})),
// Perceptron decision boundary
getPerceptronDecisionBoundaryDataset(
iterations.length > 0
? iterations[iterations.length - 1]
.weights
: [0, 0, 0],
),
],
}"
/>
</div>
<div>
<Bar
class="flex"
:options="{
responsive: true,
maintainAspectRatio: true,
plugins: {
title: {
display: true,
text: 'Nombre d\'erreurs par itération',
},
},
scales: {
x: {
stacked: true,
min: 0,
},
y: {
stacked: true,
beginAtZero: true,
},
},
}"
:data="{
labels: iterations.reduce((labels, iteration) => {
if (
!labels.includes(
`Itération ${iteration.iteration}`,
)
) {
labels.push(
`Itération ${iteration.iteration}`,
);
}
return labels;
}, [] as string[]),
datasets: getPerceptronErrorsPerIteration(),
}"
/>
</div>
</div>
</div>
</main>
</template>