;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This file is part of AIslash. ; ; AIslash is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; AIslash is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with AIslash. If not, see . ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;

A Trick for Sketching Distributions

;

The ``bald1`` says that software should pass the elbow ;test i.e. ;

;

The only way for software to pass this test is if ; users can rapidly comprehend what the software is saying. ; And the only way to do that is: ;

    ;
  1. Seperate the high-level knowledge of the code away from the ;low-level details ;
  2. Express that high-level knowledge in a human-readable form. ; ;

    For example, of software that is not rapidly comprehensible, ;consider the following glorious example from the ;The ;International Obfuscated C Code Contest: ;

    ;                                  #include\
    ;                                  
    ;
    ;                      #include                
    ;                      #include                
    ;
    ;                     #define w "Hk~HdA=Jk|Jk~LSyL[{M[wMcxNksNss:"
    ;                    #define r"Ht@H|@=HdJHtJHdYHtY:HtFHtF=JDBIl"\
    ;                   "DJTEJDFIlMIlM:HdMHdM=I|KIlMJTOJDOIlWITY:8Y"
    ;                  #define S"IT@I\\@=HdHHtGH|KILJJDIJDH:H|KID"\
    ;                 "K=HdQHtPH|TIDRJDRJDQ:JC?JK?=JDRJLRI|UItU:8T"
    ;                #define _(i,j)L[i=2*T[j,O[i=O[j-R[j,T[i=2*\
    ;               R[j-5*T[j+4*O[j-L[j,R[i=3*T[j-R[j-3*O[j+L[j,
    ;              #define t"IS?I\\@=HdGHtGIDJILIJDIItHJTFJDF:8J"
    ;
    ;     #define y                  yy(4),yy(5),                yy(6),yy(7)
    ;   #define yy(              i)R[i]=T[i],T[i ]            =O[i],O[i]=L [i]
    ; #define Y _(0          ], 4] )_ (1 ], 5] )_ (2      ], 6] )_ (3 ], 7] )_=1
    ; #define v(i)(      (( R[ i ] * _ + T [ i ]) * _ + O [ i ]) * _ + L [ i ]) *2
    ; double b = 32  ,l ,k ,o ,B ,_ ; int Q , s , V , R [8 ], T[ 8] ,O [8 ], L[ 8] ;
    ; #define q( Q,R ) R= *X ++ % 64 *8 ,R |= *X /8 &7 ,Q=*X++%8,Q=Q*64+*X++%64-256,
    ; # define  p      "G\\QG\\P=GLPGTPGdMGdNGtOGlOG"   "dSGdRGDPGLPG\\LG\\LHtGHtH:"
    ; #  define W         "Hs?H{?=HdGH|FI\\II\\GJlHJ"    "lFL\\DLTCMlAM\\@Ns}Nk|:8G"
    ; # define   U           "EDGEDH=EtCElDH{~H|AJk}"       "Jk?LSzL[|M[wMcxNksNst:"
    ; #  define u                  "Hs?H|@=HdFHtEI"             "\\HI\\FJLHJTD:8H"
    ; char  *   x                   ,*X , ( * i )[               640],z[3]="4_",
    ; *Z = "4,8O4.8O4G" r U "4M"u S"4R"u t"4S8CHdDH|E=HtAIDAIt@IlAJTCJDCIlKI\\K:8K"U
    ;  "4TDdWDdW=D\\UD\\VF\\FFdHGtCGtEIDBIDDIlBIdDJT@JLC:8D"t"4UGDNG\\L=GDJGLKHL\
    ; FHLGHtEHtE:"p"4ZFDTFLT=G|EGlHITBH|DIlDIdE:HtMH|M=JDBJLDKLAKDALDFKtFKdMK\
    ; \\LJTOJ\\NJTMJTM:8M4aGtFGlG=G|HG|H:G\\IG\\J=G|IG|I:GdKGlL=G|JG|J:4b"W
    ; S"4d"W t t"4g"r w"4iGlIGlK=G|JG|J:4kHl@Ht@=HdDHtCHdPH|P:HdDHdD=It\
    ; BIlDJTEJDFIdNI\\N:8N"w"4lID@IL@=HlIH|FHlPH|NHt^H|^:H|MH|N=J\\D\
    ; J\\GK\\OKTOKDXJtXItZI|YIlWI|V:8^4mHLGH\\G=HLVH\\V:4n" u t t
    ; "4p"W"IT@I\\@=HdHHtGIDKILIJLGJLG:JK?JK?=JDGJLGI|MJDL:8M4\
    ; rHt@H|@=HtDH|BJdLJTH:ITEI\\E=ILPILNNtCNlB:8N4t"W t"4u"
    ; p"4zI[?Il@=HlHH|HIDLILIJDII|HKDAJ|A:JtCJtC=JdLJtJL\
    ; THLdFNk|Nc|\
    ; :8K"; main (
    ; int C,char**        A) {for(x=A[1],i=calloc(strlen(x)+2,163840);
    ; C-1;C<3?Q=_=       0,(z[1]=*x++)?((*x++==104?z[1]^=32:--x), X =
    ; strstr(Z,z))      &&(X+=C++):(printf("P2 %d 320 4 ",V=b/2+32),
    ; V*=2,s=Q=0,C     =4):C<4?Q-->0?i[(int)((l+=o)+b)][(int)(k+=B)
    ; ]=1:_?_-=.5/    256,o=(v(2)-(l=v(0)))/(Q=16),B=(v(3)-(k=v(1)
    ; ))/Q:*X>60?y   ,q(L[4],L[5])q(L[6],L[7])*X-61||(++X,y,y,y),
    ; Y:*X>57?++X,  y,Y:*X >54?++X,b+=*X++%64*4:--C:printf("%d "
    ; ,i[Q][s]+i[Q ][s+1]+i[Q+1][s]+i[Q+1][s+1])&&(Q+=2)This code takes a single command line argument, transcribes it
    ;    argument text into Tolkien's Elvish letters, then writes the
    ;    transcription to standard output as a portable graymap (PGM) file
    ;   (you saw that, right?).
    ;

    Now compare that to the following code, written as a LISP macro, ; then ;lets a user sketch a belief distribution (in this case, a normal ; curve):

    ;(defun normal! ()
    ;  (buckets
    ;   ~
    ;   ~
    ;   ~~~
    ;   ~~~~~~
    ;   ~~~~~~~~~~
    ;   ~~~~~~~~~~
    ;   ~~~~~~
    ;   ~~~
    ;   ~
    ;   ~
    ;))

    (In the above, we use "~" to denote zero.) ;

    Note that, if the users want another belief distribution, then can ; sketch anything else they want using the following syntax.

    (defconstant ~ 0)
    (defconstant ~~ 1)
    (defconstant ~~~ 2)
    (defconstant ~~~~ 3)
    (defconstant ~~~~~ 4)
    (defconstant ~~~~~~ 5)
    (defconstant ~~~~~~~ 6)
    (defconstant ~~~~~~~~ 7)
    (defconstant ~~~~~~~~~ 8)
    (defconstant ~~~~~~~~~~ 9)
    (defconstant ~~~~~~~~~~~ 10)
    (defconstant ~~~~~~~~~~~~ 11)
    (defconstant ~~~~~~~~~~~~~ 12)
    (defconstant ~~~~~~~~~~~~~~ 13)
    (defconstant ~~~~~~~~~~~~~~~ 14)
    (defconstant ~~~~~~~~~~~~~~~~ 15)
    (defconstant ~~~~~~~~~~~~~~~~~ 16)
    (defconstant ~~~~~~~~~~~~~~~~~~ 18)
    (defconstant ~~~~~~~~~~~~~~~~~~~ 19)
    (defconstant ~~~~~~~~~~~~~~~~~~~~ 20)
    ;
    ;

    Now this page just shows a very simple example of elbow-able ;languages. ; For more details, see the hedges ;file or ;Google on ;Langauge-oriented ;programming. ;But for a description of the machinery behind the bucket ;example, read on. ;

    Code

    Bucket creates distributions:

    (defstruct dist
      (buckets     (list2buckets  ; defaults to a flat distribition
                    (make-sequence 'list (fixed-bins *f*)
                                   :initial-element (/ 1.0 (fixed-bins *f*))))) 
      (cardinality (fixed-bins    *f*)) ; distributions are sets of size 'cardinality'
      (min         (fixed-bin-min *f*)) ; distributions range min to max
      (max         (fixed-bin-max *f*)))
    ;
    ;

    A simple macro suffices:

    (defmacro buckets (&rest l)
      `(list2dist ',(mapcar #'isa-num l)))
    
    (defun isa-num (n)
      (if (numberp n) n (eval n)))
    
    (defun list2dist (l)
      (normalize
       (make-dist :buckets  (list2buckets l))))
    
    (defun list2buckets (l)
      (let ((want (fixed-bins *f*)))
        (if (eql want (length l))
            (let ((id 0))
              (mapcar #'(lambda (n) (cons (incf id) n)) l))
            (error "in defdist: ~a: not ~a items long" l want))))
    
    (defun normalize (d)
      "Buckets should sum to one."
      (let ((sum 0.0))
        (dolist (bucket (dist-buckets d))
          (incf sum (rest bucket)))
        (setf (dist-buckets d)
              (mapcar #'(lambda (bucket)
                          (cons (first bucket)
                                (/ (rest bucket) sum)))
                      (dist-buckets d)))
        d))
    ;
    ;

    Standard Operations on distrubutions

    ;

    (And-ing, or-ing, not-ing) = (max, min, compliment)

    (defun cons-max (x y) (if (> (rest x) (rest y)) x y))
    (defun cons-min (x y) (if (< (rest x) (rest y)) x y))
    (defun cons-neg (x  ) (cons (first x) (- 1 (rest x))))
    
    (defun dist-or   (d1 d2) (dist-combine d1 d2 #'cons-max))
    (defun dist-and  (d1 d2) (dist-combine d1 d2 #'cons-min))
    
    (defun dist-combine (d1 d2 fn)
        (normalize
         (make-dist 
          :min         (dist-min d1)
          :max         (dist-max d1)
          :cardinality (dist-cardinality d1)
          :buckets     (mapcar fn (dist-buckets d1) (dist-buckets d2)))))
      
    (defun dist-not (d)
      (make-dist 
       :min         (dist-min d)
       :max         (dist-max d)
       :cardinality (dist-cardinality d)
       :buckets     (mapcar  #'cons-neg (dist-buckets d))))
    
    (defun opposite (d)
      (let ((olds (dist-buckets d))
            (n -1)
            (cardinality (dist-cardinality d))
            news)
        (make-dist 
         :min         (dist-min d)
         :max         (dist-max d)
         :cardinality cardinality
         :buckets     (dolist (old olds news)
                        (push (cons (- cardinality (incf n)) (rest old)) news)))))
    ;
    ;

    Test cases

    ;

    Simple tests

    (deftest test-list2buckets ()
      (check
        '(equal '((1 . 1) (2 . 2) (3 . 3) (4 . 4) (5 . 5)
                  (6 . 5) (7 . 4) (8 . 3) (9 . 2) (10 . 1))
          (defbuckets '(1 2 3 4 5 45 4 3 2 1)))))
    
    (deftest test-freshdist ()
      (check
        (samep  "#S(DIST
                    :BUCKETS ((1 . 0.1) (2 . 0.1) (3 . 0.1) (4 . 0.1) (5  . 0.1)
                              (6 . 0.1) (7 . 0.1) (8 . 0.1) (9 . 0.1) (10 . 0.1))
                    :CARDINALITY 10
                    :MIN 0
                    :MAX 100)"
                (make-dist))))
      
    (deftest test-normal! ()
      (check
        (samep "#S(DIST
                   :BUCKETS ((1 . 0.0) (2 . 0.0) (3 . 0.0625) (4 . 0.15625) (5 . 0.28125)
                             (6 . 0.28125) (7 . 0.15625) (8 . 0.0625) (9 . 0.0) (10 . 0.0))
                   :CARDINALITY 10
                   :MIN 0
                   :MAX 100)"
               (normal!))))
    
    ;

    More Complex Tests

    (deftest test-opposite ()
      (check
        (samep (true-ish!)
               (opposite (opposite (true-ish!))))))
    
    (deftest test-dist-and ()
      (check
        (samep (dist-and (true-ish!) (false-ish!))
               "#S(DIST
                :BUCKETS ((1 . 0.0) (2 . 0.0) (3 . 0.0) (4 . 0.16666666) (5 . 0.3333333)
                          (6 . 0.3333333) (7 . 0.16666666) (8 . 0.0) (9 . 0.0) (10 . 0.0))
                :CARDINALITY 10
                :MIN 0
                :MAX 100)")))
    
    (deftest test-dist-or ()
      (check
        (samep (dist-or (true-ish!) (false-ish!))
               "#S(DIST
                 :BUCKETS ((1 . 0.11764707) (2 . 0.11764707) (3 . 0.11764707)
                           (4 . 0.08823529) (5 . 0.058823533) (6 . 0.058823533)
                           (7 . 0.08823529) (8 . 0.11764707) (9 . 0.11764707)
                           (10 . 0.11764707))
                 :CARDINALITY 10
                 :MIN 0
                 :MAX 100)")))
    
    (deftest test-dist-not ()
       (check
         (samep (dist-and (true-ish!) (false-ish!))
                "#S(DIST
                  :BUCKETS ((1 . 0.0) (2 . 0.0) (3 . 0.0) (4 . 0.16666666) (5 . 0.3333333)
                            (6 . 0.3333333) (7 . 0.16666666) (8 . 0.0) (9 . 0.0) (10 . 0.0))
                  :CARDINALITY 10
                  :MIN 0
                  :MAX 100)")))
    ;