[SOLVED] turning working KNN code into a static library

New to FreeBASIC? Post your questions here.
Post Reply
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: [HELP] turning working KNN code into a static library

Post by paul doe »

fxm wrote:Even with composite type members, the implicit destructor (added at compilation time by the FB compiler) is sufficient to properly call the destructor (explicit, or implicit as for strings and arrays) of each composite type member.
Good to know, thanks. I seem to recall that this wasn't always so, or perhaps I just do it out of habit? In any case, that's one less burden to deal with which is always nice...
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: [SOLVED] turning working KNN code into a static library

Post by dodicat »

Here is an example skipping external files (except the .csv file of course, the code is short anyway)
The number of numerical columns can be retrieved without asking the user.
(don't need constructors or destructors here)

Code: Select all

#INCLUDE "file.bi"
Type pt
    As Double l(any)
    As String nm
    As Long done
End Type

Sub printout(p As pt)
    For n As Long=1 To ubound(p.l)
        Print p.l(n);",";
    Next
    Print p.nm
End Sub


Function Splitstring(s_in As String,chars As String,result() As String) As Long
    Dim As Long ctr,ctr2,k,n,LC=Len(chars)
    Dim As boolean tally(Len(s_in))
    #macro check_instring()
    n=0
    While n<Lc
        If chars[n]=s_in[k] Then
            tally(k)=true
            If (ctr2-1) Then ctr+=1
            ctr2=0
            Exit While
        End If
        n+=1
    Wend
    #endmacro
   
    #macro splice()
    If tally(k) Then
        If (ctr2-1) Then ctr+=1:result(ctr)=Mid(s_in,k+2-ctr2,ctr2-1)
        ctr2=0
    End If
    #endmacro
    '==================  LOOP TWICE =======================
    For k  =0 To Len(s_in)-1
        ctr2+=1:check_instring()
    Next k
    If ctr=0 Then
        If Len(s_in) Andalso Instr(chars,Chr(s_in[0])) Then ctr=1':beep
    End If
    If ctr Then Redim result(1 To ctr): ctr=0:ctr2=0 Else  Return 0
    For k  =0 To Len(s_in)-1
        ctr2+=1:splice()
    Next k
    '===================== Last one ========================
    If ctr2>0 Then
      Redim Preserve result(1 To ctr+1)
      result(ctr+1)=Mid(s_in,k+1-ctr2,ctr2)
   End If
   
    Return Ubound(result)
End Function

Function loadfiletostring(file As String) As String
    Var  f=Freefile
    Open file For Binary Access Read As #f
    Dim As String text
    If Lof(f) > 0 Then
        text = String(Lof(f), 0)
        Get #f, , text
    End If
    Close #f
    Return text
End Function

Function vdist(p1 As pt,p2 As pt) As Double
    Dim As Double acc
    For n As Long=Lbound(p1.l) To Ubound(p1.l)
        acc+=(p1.l(n)-p2.l(n))^2
    Next
    Return Sqr(acc)
End Function

Function GetClosest(a() As pt,ans() As Long,v As pt,num As Long) As Long
    Dim As Double d,i
    Dim As Long ctr
    Do
        d=2e8
        For n As Long=Lbound(a) To Ubound(a)
            Var dst=vdist(a(n),v)
            If d>dst And a(n).done=0 Then d=dst:i=n:a(n).done=1
        Next n
        ctr+=1
        Redim Preserve ans(1 To ctr)
        ans(ctr)=i
    Loop Until Ubound(ans)>=num
    Return Ubound(ans)
End Function

SUB knn_main(f AS STRING)
    dim as long col

Dim As String s=loadfiletostring(f)'("knn-dataset-plant.csv")
Redim As String g()
redim as string tmp()

splitstring(s,Chr(10),g()) 'load the file into a string array g()

'test to get col
splitstring(g(1),",",tmp())
col=ubound(tmp)-1  'get the dimension here (col)

Dim As pt array(Lbound(g) To UBOUND(g)) 

For n As Long=Lbound(array) To UBOUND(array)
    splitstring(g(n),",",tmp())''load into tmp() with the , as seperator.
    redim (array(n).l)(1 to col)' Must do!!
    For m As Long=1 To col
        array(n).l(m)=Val(tmp(m)) 'pick out the values to suit the udt, here, the doubles
    Next m
    array(n).nm=tmp(col+1) 'OK because tmp(col+1)=tmp(ubound(tmp)) which is the string field
    print n,
    printout(array(n))
Next

print

Redim As Long near()

GetClosest(array(),near(),array(10),3)'' <<--------  here, get 3 closest to array(10)
print "This is a fixed test for 3 (closest to 10 and including 10): using Getclosest()"

Print "index",,"values"
For n As Long=Lbound(near) To Ubound(near)
    Print near(n),
    printout array(near(n))
Next


'============= new predictions!!!=================
DIM answer AS STRING
DO
DIM predict AS pt
DIM AS DOUBLE x1
DIM neighbors AS LONG
print
dim as string s
do
    print "press 1 for closest to a flower"
    print "press 2 to enter predicted values"
    s=input(1)
loop until s="1" or s="2"
if s="1" then
do
print "Between ";lbound(array);" and ";ubound(array)
INPUT "insert index : "; x1
loop until x1>=lbound(array) and x1<=ubound(array)
end if
if s="2" then
    dim as double v
    redim predict.l(1 to col)
   for n as long=1 to col
       print n; " of ";col;",   enter a value ";
       input; v
       print
       predict.l(n)=v
       next n
end if


INPUT "how many neighbors TO search?: "; neighbors

FOR i AS INTEGER = LBOUND(array) TO UBOUND(array)'reset the array
   array(i).done = 0
NEXT

Redim As Long near()
if s="1" then GetClosest(array(),near(),array(x1),neighbors) 'using an existing array element
if s="2" then GetClosest(array(),near(),predict,neighbors)' <- new predictions for new values
print "using Getclosest()"

Print "index",,"values"
For n As Long=Lbound(near) To Ubound(near)
    Print near(n),
    printout array(near(n))
NEXT
print "another prediction? y/n: "
answer=input(1)

LOOP UNTIL answer = "n"
END SUB

'========== RUN ==========
KNN_MAIN("iris.csv")

SLEEP()
 
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: [HELP] turning working KNN code into a static library

Post by fxm »

paul doe wrote:
fxm wrote:Even with composite type members, the implicit destructor (added at compilation time by the FB compiler) is sufficient to properly call the destructor (explicit, or implicit as for strings and arrays) of each composite type member.
Good to know, thanks. I seem to recall that this wasn't always so, or perhaps I just do it out of habit? In any case, that's one less burden to deal with which is always nice...
Indeed, one can check with this example that the composite type member (ctm) is well destroyed without defining an explicit destructor in UDT1:

Code: Select all

Type UDT0
    Dim As Integer I
    Declare Destructor ()
End Type
Destructor UDT0 ()
    Print "UDT0.Destructor()"
End Destructor

Type UDT1
    Dim As UDT0 ctm
End Type

Scope
    Dim As UDT1 u1
End Scope

Sleep
The only case where explicit empty destructors are mandatory to destroy a composite type member is when one uses polymorphism with a derived type (Child) containing a composite type member:

Code: Select all

Type UDT0
    Dim As Integer I
    Declare Destructor ()
End Type
Destructor UDT0 ()
    Print "UDT0.Destructor()"
End Destructor

Type Parent Extends Object
    Declare Virtual Destructor ()
End Type
Destructor Parent ()
End destructor

Type Child Extends Parent
    Dim As UDT0 ctm
    Declare Virtual Destructor () Override
End Type
Destructor Child ()
End Destructor

Scope
    Dim As Parent Ptr pp = New Child
    Delete pp
End Scope

Sleep
The reason is that implicit destructors are always added by the compiler but currently they are not declared virtual.
paul doe
Moderator
Posts: 1733
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: [HELP] turning working KNN code into a static library

Post by paul doe »

fxm wrote:...
The reason is that implicit destructors are always added by the compiler but currently they are not declared virtual.
Ah, I see. May I suggest adding that little clarification to the Wiki?

https://www.freebasic.net/wiki/KeyPgDestructor

It isn't stated there, and it's useful to know. Thanks!
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: [SOLVED] turning working KNN code into a static library

Post by fxm »

This is already described in the Virtual documentation page, but I rewrote the sentence because not very clear:
.....
Destructors often must be virtual when deleting an object manipulated through a pointer to its base type, so that the destruction starts at the most derived type and works its way down to the base type. To do this, it may be necessary to add virtual destructors with an empty body anywhere an explicit destruction was not yet required, in order to supersede each non-virtual implicit destructor built by the compiler.
.....
Otherwise all of this is I think pretty well described in the Programmer's Guide:
Constructors, '=' Assignment-Operators, and Destructors (advanced, part #1)
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: [SOLVED] turning working KNN code into a static library

Post by dodicat »

In this thread I don't think a udt holding the .csv file represents anything OOP, so what is the use of empty constructors and destructors?
In fact the whole thing can be done without a udt, in fact an array of string is more suited than a udt.
For a static library just skip the end bit

'========== RUN ==========
KNN_MAIN("iris.csv")

Sleep()
compile -lib
and only

#inclib "mylib"
declare Sub knn_main(f As String)
(so no real need for a .bi file)

Code: Select all


#INCLUDE "file.bi"

Function Splitstring(s_in As String,chars As String,result() As String) As Long
    Dim As Long ctr,ctr2,k,n,LC=Len(chars)
    Dim As boolean tally(Len(s_in))
    #macro check_instring()
    n=0
    While n<Lc
        If chars[n]=s_in[k] Then
            tally(k)=true
            If (ctr2-1) Then ctr+=1
            ctr2=0
            Exit While
        End If
        n+=1
    Wend
    #endmacro
    
    #macro splice()
    If tally(k) Then
        If (ctr2-1) Then ctr+=1:result(ctr)=Mid(s_in,k+2-ctr2,ctr2-1)
        ctr2=0
    End If
    #endmacro
    '==================  LOOP TWICE =======================
    For k  =0 To Len(s_in)-1
        ctr2+=1:check_instring()
    Next k
    If ctr=0 Then
        If Len(s_in) Andalso Instr(chars,Chr(s_in[0])) Then ctr=1':beep
    End If
    If ctr Then Redim result(1 To ctr): ctr=0:ctr2=0 Else  Return 0
    For k  =0 To Len(s_in)-1
        ctr2+=1:splice()
    Next k
    '===================== Last one ========================
    If ctr2>0 Then
        Redim Preserve result(1 To ctr+1)
        result(ctr+1)=Mid(s_in,k+1-ctr2,ctr2)
    End If
    
    Return Ubound(result)
End Function

Function loadfiletostring(file As String) As String
    Var  f=Freefile
    Open file For Binary Access Read As #f
    Dim As String text
    If Lof(f) > 0 Then
        text = String(Lof(f), 0)
        Get #f, , text
    End If
    Close #f
    Return text
End Function

Function vdist(p1 As String,p2 As String,col As Long) As Double
    Dim As Double acc
    Redim As String t1(),t2()
    splitstring(p1,",",t1())
    splitstring(p2,",",t2())
    For n As Long=1 To col
        acc+=(Val(t1(n))-Val(t2(n)))^2
    Next
    Return Sqr(acc)
End Function

Function GetClosest(a() As String,ans() As Long,v As String,num As Long,col As Long) As Long
    Dim As Double d,i
    Dim As Long ctr
    Dim As Long done(Lbound(a) To Ubound(a))
    Do
        d=2e8
        For n As Long=Lbound(a) To Ubound(a)
            Var dst=vdist(a(n),v,col)
            If d>dst And done(n)=0 Then d=dst:i=n:done(n)=1
        Next n
        ctr+=1
        Redim Preserve ans(1 To ctr)
        ans(ctr)=i
    Loop Until Ubound(ans)>=num
    Return Ubound(ans)
End Function

Sub knn_main(f As String)
    Dim As Long col
    
    Dim As String s=loadfiletostring(f)'("knn-dataset-plant.csv")
    Redim As String g()
    Redim As String tmp()
    
    splitstring(s,Chr(10),g()) 'load the file into a string array g()
    
    'test to get col
    splitstring(g(1),",",tmp())
    col=Ubound(tmp)-1  'get the non strings parts of the string here (col)
    Print "The file:"
    For n As Long=Lbound(g) To Ubound(g)
        Print n,g(n)
    Next n
    
    Print
    
    Redim As Long near()
    
    GetClosest(g(),near(),g(10),5,col)'' <<--------  here, get 3 closest to array(10)
    Print "This is a fixed test for 5 (closest to 10 and including 10): using Getclosest()"
    
    Print "index",,"values"
    For n As Long=Lbound(near) To Ubound(near)
        Print near(n),
        Print g(near(n))
    Next
    
    
    '============= new predictions!!!=================
    Dim answer As String
    Do
        Dim predict As String
        Dim As Double x1
        Dim neighbors As Long
        Print
        Dim As String s
        Do
            Print "press 1 for closest to a flower"
            Print "press 2 to enter predicted values"
            s=Input(1)
        Loop Until s="1" Or s="2"
        If s="1" Then
            Do
                Print "Between ";Lbound(g);" and ";Ubound(g)
                Input "insert index : "; x1
            Loop Until x1>=Lbound(g) And x1<=Ubound(g)
        End If
        If s="2" Then
            Dim As Double v
            Dim As String s
            For n As Long=1 To col
                Print n; " of ";col;",   enter a value ";
                Input; v
                s+=Str(v)+","
                Print
            Next n
            s=Rtrim(s,",")
            predict=s
            print, predict
        End If
        
        Input "how many neighbors TO search?: "; neighbors
        
        Redim As Long near()
        If s="1" Then GetClosest(g(),near(),g(x1),neighbors,col)   'using an existing array element
        If s="2" Then GetClosest(g(),near(),predict,neighbors,col) ' <- new predictions for new values
        Print "using Getclosest()"
        
        Print "index",,"values"
        For n As Long=Lbound(near) To Ubound(near)
            Print near(n),
            Print g(near(n))
        Next
        Print "another prediction? y/n: "
        answer=Input(1)
        
    Loop Until answer = "n"
End Sub

'========== RUN ==========
KNN_MAIN("iris.csv")

Sleep()
 
ron77
Posts: 212
Joined: Feb 21, 2019 19:24

Re: [SOLVED] turning working KNN code into a static library

Post by ron77 »

hello dodicat :)

thank you so much for your code examples and for keeping it simple. i like your last two code examples and the fact that there is no need for manually inserting how many numeric columns (values) there are in the the csv... i will add one of the codes to the library in the source folder...

the iris flower is really beautiful - it's easy to forget that while we occupy ourselves with ML and machine learning algorithms.

i wish to deeply thank you for helping me with the ML KNN and linear regression code examples it was thanks to those code examples i was able to modify and create the libraries for FB...

ron77.
Post Reply