;
; opencl-vec-test.scm
;
; Basic OpenCL vector multiplication unit test.
;
(use-modules (srfi srfi-1))
(use-modules (opencog) (opencog exec))
(use-modules (opencog sensory) (opencog opencl))
(use-modules (opencog test-runner))
; (define clurl "opencl://Clover:AMD Radeon/tmp/vec-kernel.cl")
; (define clurl "opencl://CUDA:NVIDIA RTX 4000/tmp/vec-kernel.cl")
; Horrible hackery to extract location of the unit test.
; Currently works only with absolute paths; relative paths ar broken.
(format #t "Command line: ~A\n" (command-line))
(define last-arg (last (command-line)))
(define pathcomp (string-split last-arg #\/))
(define leader (take pathcomp (- (length pathcomp) 1)))
; leading char of path should be either slash, or dot.
; We want this to rebuild a relative path...
(define leading-char (list->string (list (string-ref last-arg 0))))
(define path (fold
(lambda (s t) (string-concatenate (list t "/" s)))
"" leader))
(format #t "Unit test location: ~A\n" path)
; If unit test is run from cmake, then pathcomp is a long list.
; Otherwise unit test is run by hand, and its just cwd.
(define curloc (if (< 1 (length pathcomp)) path (getcwd)))
(define clurl (string-concatenate (list
"opencl://:" curloc "/vec-kernel.cl")))
(format #t "Looking for kernel at ~A\n" clurl)
(opencog-test-runner)
(define tname "opencl-vec-test")
(test-begin tname)
; ---------------------------------------------------------------
(define clnode (OpenclNode clurl))
(format #t "testing ~A" clnode)
(cog-execute!
(SetValue clnode (Predicate "*-open-*") (Type 'FloatValue)))
; Test to see if stream is open.
(define cnct (cog-execute! (ValueOf clnode (Predicate "*-connected?-*"))))
(format #t "connected? ~A" cnct)
(test-assert "open stream" (cog-value-ref cnct 0))
; ---------------------------------------------------------------
(define kernel-runner
(SetValue clnode (Predicate "*-write-*")
(Section
(Item "vec_mult")
(ConnectorSeq
(Number 0 0 0 0 0)
(Number 1 2 3 4 5)
(Number 2 2 2 2 2 2 3 42 999)))))
(cog-execute! kernel-runner)
(define kern-m1
(cog-execute! (ValueOf clnode (Predicate "*-read-*"))))
(define args-m1 (cog-value-ref kern-m1 1))
(define out-m1 (cog-value-ref args-m1 0))
; Force out-m1 OpenclFloatValue to download from GPU by printing!!!
(format #t "Result out-m1=~A" out-m1)
(test-assert "mult one" (equal? (FloatValue 2 4 6 8 10) out-m1))
; ---------------------------------------------------------------
; Run it again, different data
(define krun-2
(SetValue clnode (Predicate "*-write-*")
(Section
(Item "vec_mult")
(ConnectorSeq
(Number 0 0 0 0 0 0 0 0 0 0 0)
(Number 1 2 3 4 5 6 7 8 9 10 11)
(Number 2 3 4 5 6 5 4 3 2 1 0)))))
(cog-execute! krun-2)
(define kern-m3
(cog-execute! (ValueOf clnode (Predicate "*-read-*"))))
(define args-m3 (cog-value-ref kern-m3 1))
(define out-m3 (cog-value-ref args-m3 0))
; Force out-m3 OpenclFloatValue to download from GPU by printing!!!
(format #t "Result out-m3=~A" out-m3)
(test-assert "mult three"
(equal? (FloatValue 2 6 12 20 30 30 28 24 18 10 0) out-m3))
; ---------------------------------------------------------------
; Run it again, with a different kernel
(define krun-3
(SetValue clnode (Predicate "*-write-*")
(Section
(Item "vec_add")
(ConnectorSeq
(Number 0 0 0 0 0 0 0 0 0 0 0)
(Number 1 2 3 4 5 6 7 8 9 10 11)
(Number 2 3 4 5 6 5 4 3 2 1 0)))))
(cog-execute! krun-3)
(define kern-m5
(cog-execute! (ValueOf clnode (Predicate "*-read-*"))))
(define args-m5 (cog-value-ref kern-m5 1))
(define out-m5 (cog-value-ref args-m5 0))
; Force out-m5 OpenclFloatValue to download from GPU by printing!!!
(format #t "Result out-m5=~A" out-m5)
(test-assert "mult five"
(equal? (FloatValue 3 5 7 9 11 11 11 11 11 11 11) out-m5))
; ---------------------------------------------------------------
; Initialize the accumulator
(define vec-size 130)
(cog-set-value!
(Anchor "some place") (Predicate "accumulator")
(OpenclFloatValue (make-list vec-size 0)))
(define accum-location
(ValueOf (Anchor "some place") (Predicate "accumulator")))
; Upload the accumulator to the GPU.
(cog-set-value! clnode (Predicate "*-write-*") accum-location)
(cog-execute! (ValueOf clnode (Predicate "*-read-*")))
; ---------------------------------------------------------------
; Set the data source.
(cog-set-value!
(Anchor "some place") (Predicate "data source")
(RandomStream vec-size))
(define source-location
(ValueOf (Anchor "some place") (Predicate "data source")))
; Define a feedback loop.
(define run-kernel
(SetValue clnode (Predicate "*-write-*")
(Section
(Item "vec_add")
(ConnectorSeq accum-location
accum-location source-location))))
(define get-status
(ValueOf clnode (Predicate "*-read-*")))
; Run it once ...
(cog-execute! run-kernel)
(define acc1 (cog-execute! get-status))
(test-assert "acc1 type" (cog-subtype? 'SectionValue (cog-type acc1)))
(define args1 (cog-value-ref acc1 1))
(test-assert "args1 type" (cog-subtype? 'LinkValue (cog-type args1)))
(define out1 (cog-value-ref args1 0))
(test-assert "out1 type" (cog-subtype? 'FloatValue (cog-type out1)))
(test-assert "out1 size" (equal? vec-size (length (cog-value->list out1))))
(flush-all-ports)
; Run it lots ...
(define (loopy N)
(cog-execute! run-kernel)
(cog-execute! get-status)
(if (< 0 N) (loopy (- N 1))))
(define run-len 5123)
(loopy run-len)
(cog-execute! run-kernel)
(define accn (cog-execute! get-status))
(test-assert "accn type" (cog-subtype? 'SectionValue (cog-type accn)))
(define argsn (cog-value-ref accn 1))
(test-assert "argsn type" (cog-subtype? 'LinkValue (cog-type argsn)))
(define outn (cog-value-ref argsn 0))
(test-assert "outn type" (cog-subtype? 'FloatValue (cog-type outn)))
(test-assert "outn size" (equal? vec-size (length (cog-value->list outn))))
; Result of repeated executation should be a large number,
; approx equal to 0.5 of vec-size * run-len by the central limit theorem
; and with stddev of sqrt of num samples.
(define vsum (fold + 0 (cog-value->list outn)))
(define vlen (* vec-size run-len))
(define vmean (/ vsum vlen))
(define vsigma (/ 1 (sqrt vlen)))
(format #t "sum: ~A len: ~A mean: ~A sigma: ~A\n" vsum vlen vmean vsigma)
; Acceptable deviation
(define accdev (* 5 vsigma))
(test-assert "accn lo bound" (< (- 0.5 accdev) vmean))
(test-assert "accn hi bound" (> (+ 0.5 accdev) vmean))
(test-end tname)
(opencog-test-end)