;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; LUSH Lisp Universal Shell
;;;   Copyright (C) 2002 Leon Bottou, Yann Le Cun, AT&T Corp, NECI.
;;; Includes parts of TL3:
;;;   Copyright (C) 1987-1999 Leon Bottou and Neuristique.
;;; Includes selected parts of SN3.2:
;;;   Copyright (C) 1991-2001 AT&T Corp.
;;;
;;; This program 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 2 of the License, or
;;; (at your option) any later version.
;;;
;;; This program 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 this program; if not, write to the Free Software
;;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; $Id: gb-conx.lsh,v 1.2 2002/12/19 04:27:25 profshadoko Exp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; GB-CONX architecture
;;
;; and SUM-ARCH architecture
;;
;; THIS IS MISSING THE SCATTER/GATHER FUNCTIONS

(libload "gblearn2/gb-modules")
(libload "gblearn2/gb-states")

#? *** gb-conx
;;.AUTHOR Yann LeCun
;; A module architecture that connects inputs to outputs (with exactly one
;; input per output but maybe more than one output per input,
;; i.e. a one to many map). The inputs and outputs are 
;; bidimensional (i.e. sequences of vectors).
;; It has no adjustable parameters. Instead it has internal 
;; non-adjustable parameters
;; specifying the input/output connectivity.
(defclass gb-conx gb-module
  n-outputs
  n-inputs
  indices
  )

(defmethod gb-conx undim () ())

#? (new gb-conx <n-outputs> <n-inputs> <indices>)
;; Construct a gb-conx architecture. The parameters are given
;; when constructing the corresponding machine.
;;.SEE machine
;;.SEE gb-module
;;.SEE gb-conx
(defmethod gb-conx gb-conx (n-outp n-inp ind)
   (setq n-outputs n-outp n-inputs n-inp indices ind)
)

(defmethod gb-conx save-structure (fdesc &optional option)
  (writing  fdesc
    (bwrite (classname (classof this)))
    (bwrite n-outputs)
    (bwrite n-inputs)
    (bwrite indices)))

(defmethod gb-conx load-structure (fdesc)
  (reading fdesc
    (setq n-outputs (bread))
    (setq n-inputs (bread))
    (setq indices (bread))))

;#? (==> <gb-conx> output-dim <inputx>)
;(defmethod gb-conx output-dim (inputx)
;   (let*((seq-len (idx-dim inputx 1)))
;      (list n-outputs seq-len)
;   )
;)


#? (==> <gb-conx> fprop <parameters> <input> <output>)
;; Fprop method for a gb-conx architecture. Just gather the
;; inputs into the output vectors. Both the input and output
;; are assumed to be sequences of vectors [i,t] (2D matrices).
;;.SEE gb-conx
(defmethod gb-conx fprop (input output)
	(gb-conx-fprop indices :input:x :output:x)
)

(de gb-conx-fprop (parametersx inputx outputx)
	(-idx2- inputx outputx)
	(-idx1- parametersx)
   (let*((p parametersx))
      (idx-eloop ((x inputx)(y outputx))
         (gather x p y))))

#? (==> <gb-conx> bprop <input> <output>)
;; Bprop method for a gb-conx architecture. Just scatter with accumulate the
;; derivatives of the outputs into the derivative of the inputs. 
;; Both the input and output
;; are assumed to be sequences of vectors [i,t] (2D matrices).
;;.SEE gb-conx
(defmethod gb-conx bprop (input output)
	(idx-redim :input:dx (list n-inputs (idx-dim :output:dx 1)))
	(gb-conx-bprop indices :input:dx :output:dx)
)
(de gb-conx-bprop (parameters=x input=dx output=dx)
	(-idx2- input=dx output=dx)
	(-idx1- parameters=x)
   (let*((p parameters=x))
		(idx-m2clear input=dx)
      (idx-eloop ((dx input=dx)(dy output=dx))
         (scatter-acc dx p dy))))

#? (==> <gb-conx> generate <input> <output>)
;; :output:x is a matrix whose columns have zeros and
;; a single one. According to the permutation matrix <parameters>,
;; a similar matrix :input:x is produced.
(defmethod gb-conx generate (input output)
	(let*((inputx :input:x)
			(outputx :output:x)
			(seq-len (idx-dim outputx 1)))
		(idx-redim inputx (list n-inputs seq-len))
		(gb-conx-generate indices inputx outputx)
	)
)
(de gb-conx-generate (parametersx inputx outputx)
	(-idx1- parametersx)
	(-idx2- inputx outputx)
   (let*((p parametersx))
		(idx-m2clear inputx)
      (idx-eloop ((x inputx)(y outputx))
         (scatter-acc x p y)
      )
   )
	()
)

#? sum-arch
;; An architecture that takes the sum of its inputs.
;; The input is a 2D matrix whose first index is <n-inputs>
;; and second index is variable (e.g. time).
;; The output is zero-dimensional (an idx0).
;; There are no parameters.
;;
;;.SEE gb-module
;;.SEE (new sum-arch)
;;.SEE (==> <sum-arch> fprop <parameters> <input> <output>)
;;.SEE (==> <sum-arch> bprop <parameters> <input> <output>)
;;.SEE (==> <sum-arch> output-dim <inputx>)
;;.SEE (==> <sum-arch> generate <parameters> <input> <output>)

(defclass sum-arch gb-module
	n-inputs
)

#? (new sum-arch)
;; Construct a sum-arch gb-module. 
;;.SEE machine
;;.SEE gb-module
;;.SEE sum-arch
(defmethod sum-arch sum-arch (n-inp)
	(setq n-inputs n-inp)
)

#? (==> <sum-arch> output-dim <inputx>)
(defmethod sum-arch output-dim (inputx)
   ()
)


#? (==> <sum-arch> fprop <parameters> <input> <output>)
;; Fprop method for a sum-arch architecture. Just add all the
;; inputs. The input is assumed to be any matrix (except an idx0).
;; The output is an idx0.
;;.SEE sum-arch
(defmethod sum-arch fprop (parameters input output)
   (let*((x (idx-clone :input:x))
			(x-dim (idx-dim x))
			(n (apply '* x-dim)))
		(idx-redim x (list n))
		(idx-m1sum x :output:x)
   )
)

#? (==> <sum-arch> bprop <parameters> <input> <output>)
;; Bprop method for a sum-arch architecture. 
;;.SEE prod
(defmethod sum-arch bprop (parameters input output)
   (let*((dx (idx-clone :input:dx))
			(dims (idx-dim dx))
			(n (apply '* dims)))
		(idx-redim dx (list n))
		(idx-m1dotm0 (ones-matrix n) :output:x dx)
   )
)
#? (==> <sum-arch> generate <parameters> <input> <output>)
;; Generate a 2-d sequence of inputs. A prior distribution
;; on sequence length is chosen here to be uniform in [5,6,..10].
;; Ignore the output. For each time step, the input vector
;; is a vector of 0's and a single 1, chosen uniformly.
(defmethod sum-arch generate (parameters input output)
	(let*((len (int (rand 5 11)))
			(inputx (float-matrix n-inputs len)))
		(idx-eloop ((inp inputx))
			(inp (int (rand 0 (- n-inputs (-very-small-positive-number-)))) 1)
		)
		(setq :input:x inputx)
	)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;

(dhc-make () gb-conx-fprop gb-conx-bprop gb-conx-generate)


