Files
perceptron-viewer/resources/js/pages/PerceptronViewer.vue
T
2026-09-08 19:06:02 +02:00

166 lines
5.2 KiB
Vue

<script setup lang="ts">
import { Head } from '@inertiajs/vue3';
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale,
PointElement,
LineElement,
} from 'chart.js';
import { computed, ref } from 'vue';
import LinkHeader from '@/components/LinkHeader.vue';
import { usePerceptronTraining } from '@/composables/usePerceptronTraining';
import type { Dataset, DatasetPoint, InitializationMethod, 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;
maxDisplayedWeights: number;
}>();
const selectedDatasetName = ref<string>('');
const dataset = computed<DatasetPoint[]>(() => {
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) => {
let dataset = cleanedDataset.find((d) => d.label === row.label);
if (!dataset) {
dataset = { label: row.label, data: [] };
cleanedDataset.push(dataset);
}
dataset.data.push({ x: row.x, y: row.y });
});
return cleanedDataset;
});
const hiddenLayers = ref(3);
const hiddenLayersNeurons = ref(3);
const initializationMethod = ref<InitializationMethod>(props.type === 'multilayer' ? 'random' : 'zeros');
const {
activationFunction,
iterations,
setTrainingId,
trainingEnded,
trainingEndReason,
} = usePerceptronTraining(props.sessionId);
const isRegression = computed(
() => (props.type === 'multilayer' || props.type === 'monolayer') && activationFunction.value === 'linear',
);
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);
case 'linear':
return (x) => x;
default:
return (x) => x; // Identity function as fallback
}
}
</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"
:hidden-layers="hiddenLayers"
:hidden-layers-neurons="hiddenLayersNeurons"
:minError="props.minError"
:sessionId="props.sessionId"
:defaultLearningRate="props.learningRate"
:defaultMaxIterations="props.maxIterations"
@update:selected-dataset="
(newValue) => {
selectedDatasetName = newValue;
}
"
@update:training-id="setTrainingId"
/>
<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"
:maxDisplayedWeights="props.maxDisplayedWeights"
/>
</div>
<div class="sticky top-0 h-full w-full">
<div>
<PerceptronDecisionGraph
:cleanedDataset="cleanedDataset"
:iterations="iterations"
:is-regression="isRegression"
:activation-function="
getActivationFunction(activationFunction)
"
/>
</div>
<div>
<PerceptronIterationsErrorsGraph
:iterations="iterations"
:is-regression="isRegression"
v-if="iterations.length > 0"
/>
</div>
</div>
</div>
</main>
</template>