Pyffi and infix version of Conway's Game of Life

From outdoor to screen i wrote today an infix version of the program Conway's Game of Life that was in pyffi-test :

the 2 versions are here: test-numpy-game-of-life+.rkt

#lang reader SRFI-105 ; you can use SRFI-105 or SRFI-110


(module numpy-game-of-life racket/base

(require (rename-in Scheme+
                    (for for+)
                    (:= :=s)
                    (-> s->)))

(require pyffi pyffi/numpy)

(require "pyffi-infix+.rkt")
  

;;; Conway's Game of Life

(initialize)                 ; handles `main` and `builtins`
(import-numpy)
;                            ; load the `numpy` module
;                            ; import and initialize numpy before
;                            ; running the delayed initializers
(finish-initialization)      ; run delayed setters



(define Z (numpy.array '[[0 0 0 0 0 0]
                         [0 0 0 1 0 0]
                         [0 1 0 1 0 0]
                         [0 0 1 1 0 0]
                         [0 0 0 0 0 0]
                         [0 0 0 0 0 0]]))


;; The matrix has a border consisting of zeros.
;; This makes it easier to apply the rules without worrying about special case.
;; We do not want to show the border however, so we use `show` to display the
;; the inner parts of our matrix.

(define (show A)
  ; remove outer border
  ; for each row, display it
  (for ([i (len A)])
    (displayln (numpy.ndarray.tolist {A[i]} #;(ref A i) )))
  (newline))


'Z
(show Z)
'Z.T
(show (.T Z))
'Z
(show Z)


;; def compute_neigbours(Z):
;;     shape = len(Z), len(Z[0])
;;     N  = [[0,]*(shape[0])  for i in range(shape[1])]
;;     for x in range(1,shape[0]-1):
;;         for y in range(1,shape[1]-1):
;;             N[x][y] = Z[x-1][y-1]+Z[x][y-1]+Z[x+1][y-1] \
;;                     + Z[x-1][y]            +Z[x+1][y]   \
;;                     + Z[x-1][y+1]+Z[x][y+1]+Z[x+1][y+1]
;;     return N

;; (define (compute-neighbours Z)
;;   ;; (displayln (list (list 'len len)
;;   ;;                  (list 'numpy.array numpy.array)))
;;              
;;   (define shape (vector (len Z) (len (ref Z 0))))
;;   (define N (numpy.array
;;              (for/list ([i (ref shape 1)])
;;                (mul '(0) (ref shape 0)))))
;;   (for*/list ([x (in-range 1 (- (ref shape 0) 1))]
;;               [y (in-range 1 (- (ref shape 1) 1))])
;;     (define-values (x- x+) (values (- x 1) (+ x 1)))
;;     (define-values (y- y+) (values (- y 1) (+ y 1)))
;;     (:= N [x y] (+ (ref Z x- y-) (ref Z x  y-) (ref Z x+ y-)
;;                    (ref Z x- y )               (ref Z x+ y)
;;                    (ref Z x- y+) (ref Z x  y+) (ref Z x+ y+))))
;;   N)

(define (compute-neighbours Z)
  ;; (displayln (list (list 'len len)
  ;;                  (list 'numpy.array numpy.array)))
             
  (define shape (vector (len Z) (len {Z[0]})))
  (define N (numpy.array
             (for/list ([i {shape[1]}])
               {'(0) * shape[0]})))
  (for*/list ([x (in-range 1 {shape[0] - 1})]
              [y (in-range 1 {shape[1] - 1})])
    {(x- x+) <- (values (x - 1) (x + 1))}
    {(y- y+) <- (values (y - 1) (y + 1))}
 ;;   {N[x y]  <-  (+ Z[x- y-] Z[x y-] Z[x+ y-]
 ;;                   Z[x-  y]         Z[x+  y]
 ;;                   Z[x- y+] Z[x y+] Z[x+ y+])})
;; ; this syntax also works:
  {N[x y] <-  Z[x- y-] + Z[x y-] + Z[x+ y-] 
            + Z[x-  y] +           Z[x+  y]
            + Z[x- y+] + Z[x y+] + Z[x+ y+]})
  N)


(show (compute-neighbours Z))

;; ;; def iterate(Z):
;; ;;     N = compute_neighbours(Z)
;; ;;     for x in range(1,shape[0]-1):
;; ;;         for y in range(1,shape[1]-1):
;; ;;              if Z[x][y] == 1 and (N[x][y] < 2 or N[x][y] > 3):
;; ;;                  Z[x][y] = 0
;; ;;              elif Z[x][y] == 0 and N[x][y] == 3:
;; ;;                  Z[x][y] = 1
;; ;;     return Z

;; (define (iterate Z)
;;   (define N (compute-neighbours Z))
;;   (define shape (vector (len Z) (len (ref Z 0))))
;;   (for* ([x (in-range 1 (- (ref shape 0) 1))]
;;          [y (in-range 1 (- (ref shape 1) 1))])
;;     (cond [(and (= (ref Z x y) 1) (or (< (ref N x y) 2) (> (ref N x y) 3)))
;;            (:= Z [x y] 0)]
;;           [(and (= (ref Z x y) 0) (= (ref N x y) 0))
;;            (:= Z [x y] 1)]))
;;   Z)


(define (iterate Z)
  (define N (compute-neighbours Z))
  (define shape (vector (len Z) (len {Z[0]})))
  (for* ([x (in-range 1 {shape[0] - 1})]
         [y (in-range 1 {shape[1] - 1})])
    (cond [{Z[x y] = 1 and (N[x y] < 2 or N[x y] > 3)}
           {Z[x y] <- 0}]
          [{Z[x y] = 0 and N[x y] = 0}
           {Z[x y] <- 1}]))
  Z)

(show Z)
(void (for ([i 4]) (iterate Z)))
(show Z)

)

it uses also this file: pyffi/pyffi-tests/tests/pyffi-infix+.rkt at main · damien-mattei/pyffi · GitHub

#lang reader SRFI-105

(module pyffi-infix racket/base

  (provide *
           numpy-array?
           ndarray-line-ref
           ndarray-line-set!
           set)
  
  (require (rename-in Scheme+
                      (:= :=s) ; for compatibility with pyffi
                      (-> s->)))


  (require pyffi
           pyffi/numpy
           ;pyffi/numpy-core
           )

  ;(set-environment-variables) ; do not know what it does?
  (initialize)
  (display "initialize passed") (newline)

  ;(display "initialize passed") (newline)
  (import-numpy)
  (display "import-numpy passed") (newline)

  ;; 2 methods below seems to work
  ;(post-initialize)
  ;(display "post-initialize passed") (newline)
  (finish-initialization)
  (display "finish-initialization passed") (newline)

  ;(declare-special-prefix numpy)

  (import numpy)

  ; getter for ndarray line
  (define (ndarray-line-ref A lin)
    (numpy.take A (numpy.array (list lin)) #:axis 0))

  ; setter for ndarray line
  (define (ndarray-line-set! A lin vect-line)
    {shp <- (numpy.shape A)}
    {size-col <- shp[0]}
    (numpy.put_along_axis A
                          (numpy.full (pytuple size-col 1)
                                      (numpy.array (list lin)))
                          vect-line
                          0))

  ; predicate for ndarray
  (define (numpy-array? A)
    (builtins.isinstance A (builtins.type (numpy.array '(0)))))

  
  ;; > {Z[2]}
  ;; ($bracket-apply$ Z 2)
  ;; (obj "ndarray" : array([[0, 0, 0, 1, 0, 0]]))

  ;; {Z[2] <- (numpy.array '[[0 -7 0 0 0 0]])}
  ;;(overload-square-brackets ndarray-line-ref ndarray-line-set! (numpy-array? number?))

  
  ;; to be compatible with game of life original program i will use  instead of above overloading this below:
  ;; the difference is only returning [x y z ...] instead of [[x y z ...]]
  (overload-square-brackets ref ndarray-line-set! (numpy-array? number?))

  (define-overload-existing-operator *)
  (overload-existing-operator * mul (list? number?))

  (define (set A x y val)
    (:= A [x y] val))

  ;; example:
  ;; {N[x y] <-  Z[x- y-] + Z[x y-] + Z[x+ y-] 
  ;;           + Z[x-  y] +           Z[x+  y]
  ;;           + Z[x- y+] + Z[x y+] + Z[x+ y+]}
  (overload-square-brackets ref set (numpy-array? number? number?))
  )

I wish you all a sunny week-end.