Imports System.IO
Imports System.Globalization

Public Class frmSOM

    Private outputs As Neuron(,)
    ' Collection of weights.
    Private iteration As Integer
    ' Current iteration.
    Private length As Integer
    ' Side length of output grid.
    Private dimensions As Integer
    ' Number of input dimensions.
    Private rnd As New Random()
    '
    Private winX, winY As Integer
    Private header As String()
    Private bStop As Boolean

    Private labels As New List(Of String)()
    Private patterns As New List(Of Double())()

    Private Sub temp()

        Dim x As New SOM(3, 10, "c:\temp\Food.csv")


    End Sub

    Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress

        If Asc(e.KeyChar) = 27 Then
            bStop = True
        End If

    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        '
        cboMap.SelectedIndex = 2
        length = 4

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

        length = 4
        dimensions = 3
        Initialise()
        LoadData("c:\temp\Food.csv")
        ShowData()
        NormalisePatterns()
        ShowDataNorm()

    End Sub


    Private Sub Initialise()

        'Declare
        Dim w As String

        '
        iteration = 0
        lblError.Text = "0.0"
        lblIteration.Text = "0"

        '
        outputs = New Neuron(length - 1, length - 1) {}

        '
        For i As Integer = 0 To length - 1
            For j As Integer = 0 To length - 1
                outputs(i, j) = New Neuron(i, j, length)
                outputs(i, j).Weights = New Double(dimensions - 1) {}
                '
                w = ""
                For k As Integer = 0 To dimensions - 1
                    outputs(i, j).Weights(k) = rnd.NextDouble()
                    w += Format(outputs(i, j).Weights(k), "0.00000") & vbNewLine
                Next
                SetNodeText(i, j, w)
            Next
        Next

        '
        SetNodeBackcolor(winX, winY, Color.White)
        Application.DoEvents()

    End Sub

    Private Sub LoadData(ByVal FileName As String)

        'Declare
        Dim reader As StreamReader = File.OpenText(FileName)

        'Header
        header = reader.ReadLine().Split(","c)
        dimensions = header.Length - 1

        '
        patterns.Clear()
        labels.Clear()

        ' Ignore first line.
        While Not reader.EndOfStream
            Dim line As String() = reader.ReadLine().Split(","c)
            labels.Add(line(0))
            Dim inputs As Double() = New Double(dimensions - 1) {}

            For i As Integer = 0 To dimensions - 1
                inputs(i) = Double.Parse(line(i + 1), CultureInfo.GetCultureInfo("en-US"))
            Next
            patterns.Add(inputs)
        End While

        '
        reader.Close()

    End Sub

    Private Sub NormalisePatterns()

        For j As Integer = 0 To dimensions - 1
            Dim sum As Double = 0
            For i As Integer = 0 To patterns.Count - 1
                sum += patterns(i)(j)
            Next

            Dim average As Double = sum / patterns.Count

            For i As Integer = 0 To patterns.Count - 1
                patterns(i)(j) = patterns(i)(j) / average
            Next
        Next

    End Sub

    Private Sub ShowData()

        'Declare
        Dim i, j As Integer

        '
        lvwData.Clear()

        'Header
        With lvwData
            For j = 0 To dimensions
                .Columns.Add(header(j), 70, HorizontalAlignment.Right)
            Next
        End With

        'Data
        Dim s(dimensions) As String

        '
        With lvwData
            For i = 0 To patterns.Count - 1
                s(0) = labels(i)
                For j = 1 To dimensions
                    s(j) = patterns(i)(j - 1).ToString
                Next
                Dim itm = New ListViewItem(s)
                .Items.Add(itm)
            Next
        End With

        '
        lvwData.Refresh()

    End Sub


    Private Sub ShowDataNorm()

        'Declare
        Dim i, j As Integer

        '
        lvwNorm.Clear()

        'Header
        With lvwNorm
            For j = 0 To dimensions
                .Columns.Add(header(j), 70, HorizontalAlignment.Right)
            Next
        End With

        'Normalized Data
        Dim s(dimensions) As String

        '
        With lvwNorm
            For i = 0 To patterns.Count - 1
                s(0) = labels(i)
                For j = 1 To dimensions
                    s(j) = Format(patterns(i)(j - 1), "0.00000")
                Next
                Dim itm = New ListViewItem(s)
                .Items.Add(itm)
            Next
        End With

        '
        lvwNorm.Refresh()

    End Sub

    Private Sub btnLoadData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadData.Click

        Try
            ofdMain.ShowDialog()
            LoadData(ofdMain.FileName.ToString)
            ShowData()
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub

    Private Sub btnNorm_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNorm.Click

        Try
            NormalisePatterns()
            ShowDataNorm()
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub

    Private Sub btnInitialize_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnInitialize.Click

        Try
            ClearNodes()
            Initialise()
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    
    End Sub



    Private Sub btnTrain_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnTrain.Click

        Try
            Train(Val(txtError.Text))
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub

    Private Sub Train(ByVal maxError As Double)
        Dim currentError As Double = Double.MaxValue

        While currentError > maxError
            currentError = 0
            Dim TrainingSet As New List(Of Double())()
            For Each pattern As Double() In patterns
                TrainingSet.Add(pattern)
            Next

            For i As Integer = 0 To patterns.Count - 1
                Dim pattern As Double() = TrainingSet(rnd.[Next](patterns.Count - i)) 'select a random pattern
                ShowPattern(pattern)
                currentError += TrainPattern(pattern)
                TrainingSet.Remove(pattern)
                '
                If bStop = True Then
                    bStop = False
                    Exit While
                End If
            Next
            ShowWeights()
            lblError.Text = currentError.ToString("0.0000000")
            Application.DoEvents()
        End While
    End Sub

    Private Function TrainPattern(ByVal pattern As Double()) As Double
        Dim [error] As Double = 0
        Dim winner__1 As Neuron = Winner(pattern)
        For i As Integer = 0 To length - 1
            For j As Integer = 0 To length - 1
                [error] += outputs(i, j).UpdateWeights(pattern, winner__1, iteration)
            Next
        Next
        iteration += 1
        lblIteration.Text = iteration.ToString
        Return Math.Abs([error] / (length * length))
    End Function

    Private Function Winner(ByVal pattern As Double()) As Neuron

        'Declare
        Dim owinner As Neuron = Nothing
        Dim min As Double = Double.MaxValue

        For i As Integer = 0 To length - 1
            For j As Integer = 0 To length - 1
                Dim d As Double = Distance(pattern, outputs(i, j).Weights)
                If d < min Then
                    min = d
                    owinner = outputs(i, j)
                End If
            Next
        Next

        '
        If chkWinner.Checked = True Then
            SetNodeBackcolor(winX, winY, Color.White)
            SetNodeBackcolor(owinner.X, owinner.Y, Color.Red)
            winX = owinner.X
            winY = owinner.Y
            Application.DoEvents()
        End If

        '
        Return owinner

    End Function

    Private Function Distance(ByVal vector1 As Double(), ByVal vector2 As Double()) As Double
        Dim value As Double = 0
        For i As Integer = 0 To vector1.Length - 1
            value += Math.Pow((vector1(i) - vector2(i)), 2)
        Next
        Return Math.Sqrt(value)
    End Function


    Private Sub ShowWeights()

        'Declare
        Dim w As String

        '
        For i As Integer = 0 To length - 1
            For j As Integer = 0 To length - 1
                '
                w = ""
                For k As Integer = 0 To dimensions - 1
                    w += Format(outputs(i, j).Weights(k), "0.00000") & vbNewLine
                Next
                SetNodeText(i, j, w)
            Next
        Next
    End Sub


    Private Sub ShowPattern(ByVal pattern As Double())

        'Declare
        Dim s As String

        '
        s = ""
        For i As Integer = 0 To pattern.length - 2
            s += Format(pattern(i), "0.00000") & ","
        Next
        s += pattern(pattern.length - 1).ToString

    End Sub


    Private Sub TrainIteration()

        'Declare
        Dim currentError As Double = Double.MaxValue

        '
        currentError = 0
        Dim TrainingSet As New List(Of Double())()
        For Each pattern As Double() In patterns
            TrainingSet.Add(pattern)
        Next

        '
        For i As Integer = 0 To patterns.Count - 1
            Dim pattern As Double() = TrainingSet(rnd.[Next](patterns.Count - i)) 'select a random pattern
            ShowPattern(pattern)
            currentError += TrainPattern(pattern)
            TrainingSet.Remove(pattern)
        Next

        '
        ShowWeights()
        lblError.Text = currentError.ToString("0.0000000")

    End Sub


    Private Sub btnPredict_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPredict.Click

        Try
            Predict()
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

      
    End Sub

    Private Sub Predict()

        'Declare
        Dim pattern As Double()
        Dim owinner As Neuron
        Dim i, j, r, c As Integer
        Dim s(3, 3) As String

        '
        For i = 0 To patterns.Count - 1
            pattern = patterns(i)
            owinner = Winner(pattern)
            r = owinner.X
            c = owinner.Y
            s(r, c) += labels(i).Replace(" ", "") & vbNewLine
        Next

        '
        For i = 0 To 3
            For j = 0 To 3
                SetNodeText(i, j, s(i, j))
            Next
        Next

    End Sub


    Private Function GetWinner(ByVal pattern As Double()) As Neuron

        'Declare
        Dim owinner As Neuron = Nothing
        Dim min As Double = Double.MaxValue

        '
        For i As Integer = 0 To length - 1
            For j As Integer = 0 To length - 1
                Dim d As Double = Distance(pattern, outputs(i, j).Weights)
                If d < min Then
                    min = d
                    owinner = outputs(i, j)
                End If
            Next
        Next

        '
        Return owinner

    End Function


    Private Sub cboMap_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cboMap.SelectedIndexChanged

        Select Case cboMap.SelectedIndex
            Case 0
                length = 2
            Case 1
                length = 3
            Case Else
                length = 4
        End Select


        Try
            ClearNodes()
            Initialise()
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

    End Sub


    Private Sub chkWinner_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkWinner.CheckedChanged

        SetNodeBackcolor(winX, winY, Color.White)
        Application.DoEvents()

    End Sub


    Private Sub SetNodeText(ByVal i As Integer, ByVal j As Integer, ByVal NodeText As String)

        If i = 0 And j = 0 Then lblN00.Text = NodeText
        If i = 0 And j = 1 Then lblN01.Text = NodeText
        If i = 0 And j = 2 Then lblN02.Text = NodeText
        If i = 0 And j = 3 Then lblN03.Text = NodeText

        If i = 1 And j = 0 Then lblN10.Text = NodeText
        If i = 1 And j = 1 Then lblN11.Text = NodeText
        If i = 1 And j = 2 Then lblN12.Text = NodeText
        If i = 1 And j = 3 Then lblN13.Text = NodeText

        If i = 2 And j = 0 Then lblN20.Text = NodeText
        If i = 2 And j = 1 Then lblN21.Text = NodeText
        If i = 2 And j = 2 Then lblN22.Text = NodeText
        If i = 2 And j = 3 Then lblN23.Text = NodeText

        If i = 3 And j = 0 Then lblN30.Text = NodeText
        If i = 3 And j = 1 Then lblN31.Text = NodeText
        If i = 3 And j = 2 Then lblN32.Text = NodeText
        If i = 3 And j = 3 Then lblN33.Text = NodeText

    End Sub

    Private Sub SetNodeBackcolor(ByVal i As Integer, ByVal j As Integer, ByVal bColor As Color)

        If i = 0 And j = 0 Then lblN00.BackColor = bColor
        If i = 0 And j = 1 Then lblN01.BackColor = bColor
        If i = 0 And j = 2 Then lblN02.BackColor = bColor
        If i = 0 And j = 3 Then lblN03.BackColor = bColor

        If i = 1 And j = 0 Then lblN10.BackColor = bColor
        If i = 1 And j = 1 Then lblN11.BackColor = bColor
        If i = 1 And j = 2 Then lblN12.BackColor = bColor
        If i = 1 And j = 3 Then lblN13.BackColor = bColor

        If i = 2 And j = 0 Then lblN20.BackColor = bColor
        If i = 2 And j = 1 Then lblN21.BackColor = bColor
        If i = 2 And j = 2 Then lblN22.BackColor = bColor
        If i = 2 And j = 3 Then lblN23.BackColor = bColor

        If i = 3 And j = 0 Then lblN30.BackColor = bColor
        If i = 3 And j = 1 Then lblN31.BackColor = bColor
        If i = 3 And j = 2 Then lblN32.BackColor = bColor
        If i = 3 And j = 3 Then lblN33.BackColor = bColor

    End Sub

    Private Sub lblN00_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN00.Click
        txtNode.Text = lblN00.Text
    End Sub

    Private Sub ClearNodes()

        Dim i, j As Integer

        For i = 0 To 3
            For j = 0 To 3
                SetNodeText(i, j, "")
            Next
        Next

    End Sub

    Private Sub lblN01_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN01.Click
        txtNode.Text = lblN01.Text
    End Sub

    Private Sub lblN02_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN02.Click
        txtNode.Text = lblN02.Text
    End Sub

    Private Sub lblN03_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN03.Click
        txtNode.Text = lblN03.Text
    End Sub

    Private Sub lblN10_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN10.Click
        txtNode.Text = lblN10.Text
    End Sub

    Private Sub lblN11_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN11.Click
        txtNode.Text = lblN11.Text
    End Sub

    Private Sub lblN12_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN12.Click
        txtNode.Text = lblN12.Text
    End Sub

    Private Sub lblN13_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN13.Click
        txtNode.Text = lblN13.Text
    End Sub

    Private Sub lblN20_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN20.Click
        txtNode.Text = lblN20.Text
    End Sub

    Private Sub lblN21_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN21.Click
        txtNode.Text = lblN21.Text
    End Sub

    Private Sub lblN22_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN22.Click
        txtNode.Text = lblN22.Text
    End Sub

    Private Sub lblN23_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN23.Click
        txtNode.Text = lblN23.Text
    End Sub

    Private Sub lblN30_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN30.Click
        txtNode.Text = lblN30.Text
    End Sub

    Private Sub lblN31_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN31.Click
        txtNode.Text = lblN31.Text
    End Sub

    Private Sub lblN32_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN32.Click
        txtNode.Text = lblN32.Text
    End Sub

    Private Sub lblN33_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblN33.Click
        txtNode.Text = lblN33.Text
    End Sub
End Class
