Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

gnn_serial.c

Go to the documentation of this file.
00001 /***************************************************************************
00002  *  @file gnn_serial.c
00003  *  @brief gnn_serial Function Stacking Layer.
00004  *
00005  *  @date   : 06-08-03 21:51
00006  *  @author : Pedro Ortega C. <peortega@dcc.uchile.cl>
00007  *  Copyright  2003  Pedro Ortega C.
00008  ****************************************************************************/
00009 /*
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU General Public License
00021  *  along with this program; if not, write to the Free Software
00022  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00023  */
00024 
00025 /**
00026  * @brief A constructor node for node stacking.
00027  * @defgroup gnn_serial_doc gnn_serial : Serial Constructor for Function Composition.
00028  * @ingroup  gnn_constructor_doc
00029  *
00030  * This node allows to stack nodes one on another. The resulting function
00031  * is the composition of the node's subfunctions. That is, given \f$N\f$
00032  * functions \f$g_1, g_2, g_3, \ldots, g_N\f$ in this order, the stack node
00033  * forms the function
00034  * \f[ Y = F(X) = g_N \circ g_{N-1} \circ \ldots \circ g_1 (X) \f]
00035  *
00036  * A new stack node can be built using the \ref gnn_serial_new function.
00037  */
00038 
00039 /******************************************/
00040 /* Include Files                          */
00041 /******************************************/
00042 
00043 #include <stdarg.h>
00044 #include "gnn_serial.h"
00045 
00046 
00047 
00048 /******************************************/
00049 /* Static Declaration                     */
00050 /******************************************/
00051 
00052 /**
00053  * @brief Internal buffer structure.
00054  * @ingroup gnn_serial_doc
00055  *
00056  * This structure stores the buffers needed for saving the intermediate results
00057  * between the subfunction's evaluations.
00058  */
00059 typedef struct _stack_buf
00060 {
00061     gsl_vector *xy;    /**< Stores intermediate function evaluation       */
00062     gsl_vector *dydx;  /**< Stores intermediate
00063                             \f$\frac{\partial E}{\partial x}\f$ gradients */
00064 } stack_buffer;
00065 
00066 /**
00067  * @brief \ref gnn_serial structure.
00068  * @ingroup gnn_serial_doc
00069  *
00070  * This is the real structure for a \ref gnn_serial node. It needs to store
00071  * additional information for managing the interfunction activity.
00072  */
00073 typedef struct _gnn_serial
00074 {
00075     gnn_node      node;    /**< The subyacent \ref gnn_node         */
00076     stack_buffer *buffers; /**< The buffers for intermediate values */
00077 } gnn_serial;
00078 
00079 
00080 
00081 static int
00082 stack_buffer_init (stack_buffer *sb, size_t size);
00083 
00084 static int
00085 stack_buffer_clear (stack_buffer *sb);
00086 
00087 static int
00088 stack_buffer_finalize (stack_buffer *sb);
00089 
00090 static int
00091 gnn_serial_make_buffers (gnn_node *node);
00092 
00093 static int
00094 gnn_serial_destroy_buffers (gnn_node *node);
00095 
00096 
00097 
00098 static int
00099 gnn_serial_f (gnn_node *node,
00100               const gsl_vector *x,
00101               const gsl_vector *w,
00102               gsl_vector *y);
00103 
00104 static int
00105 gnn_serial_dx (gnn_node *node,
00106                const gsl_vector *x,
00107                const gsl_vector *w,
00108                const gsl_vector *dy,
00109                gsl_vector *dx);
00110 
00111 static int
00112 gnn_serial_dw (gnn_node *node,
00113                const gsl_vector *x,
00114                const gsl_vector *w,
00115                const gsl_vector *dy,
00116                gsl_vector *dw);
00117 
00118 static void
00119 gnn_serial_destroy (gnn_node *node);
00120 
00121 
00122 /******************************************/
00123 /* Static Implementation                  */
00124 /******************************************/
00125 
00126 /**
00127  * @brief Initializes a stack buffer.
00128  * @ingroup gnn_serial_doc
00129  *
00130  * This function initializes a buffer of the given size.
00131  *
00132  * @param  sb   A pointer to a \ref stack_buffer structure.
00133  * @param  size The size of the buffers.
00134  * @return 0 if suceeded.
00135  */
00136 static int
00137 stack_buffer_init (stack_buffer *sb, size_t size)
00138 {
00139     assert (sb != NULL);
00140     assert (size >= 0);
00141     
00142     /* set fields */
00143     if (size > 0)
00144     {
00145         sb->xy   = gsl_vector_alloc (size);
00146         sb->dydx = gsl_vector_alloc (size);
00147         if (sb->xy == NULL || sb->dydx == NULL)
00148         {
00149             stack_buffer_finalize (sb);
00150             GSL_ERROR ("couldn't initialize stack buffer", GSL_ENOMEM);
00151         }
00152     }
00153     return 0;
00154 }
00155 
00156 /**
00157  * @brief Clears a stack buffer.
00158  * @ingroup gnn_serial_doc
00159  *
00160  * This function resets the buffers to zero.
00161  *
00162  * @param  sb   A pointer to a \ref stack_buffer structure.
00163  * @return 0 if suceeded.
00164  */
00165 static int
00166 stack_buffer_clear (stack_buffer *sb)
00167 {
00168     assert (sb != NULL);
00169 
00170     /* clear buffers */
00171     if (sb->xy != NULL && sb->dydx != NULL)
00172     {
00173         gsl_vector_set_zero (sb->xy);
00174         gsl_vector_set_zero (sb->dydx);
00175     }
00176     return 0;
00177 }
00178 
00179 /**
00180  * @brief Finalizes a stack buffer.
00181  * @ingroup gnn_serial_doc
00182  *
00183  * This function finalizes a \ref stack_buffer, by freeing the memory
00184  * associated to its buffer.
00185  *
00186  * @param  sb   A pointer to a \ref stack_buffer structure.
00187  * @return 0 if suceeded.
00188  */
00189 static int
00190 stack_buffer_finalize (stack_buffer *sb)
00191 {
00192     assert (sb != NULL);
00193     
00194     /* deallocate vector buffer if any */
00195     if (sb->xy != NULL)
00196         gsl_vector_free (sb->xy);
00197     if (sb->dydx != NULL)
00198         gsl_vector_free (sb->dydx);
00199     
00200     return 0;
00201 }
00202 
00203 /**
00204  * @brief Builds the buffers for a given \ref gnn_serial node.
00205  * @ingroup gnn_serial_doc
00206  *
00207  * This function takes a \ref gnn_serial node as argument, and builds the
00208  * corresponding buffers for the installed subnodes.
00209  *
00210  * @param  node A pointer to a \ref gnn_serial structure.
00211  * @return 0 if suceeded.
00212  */
00213 static int
00214 gnn_serial_make_buffers (gnn_node *node)
00215 {
00216     int i;
00217     int size;
00218     gnn_serial *stack;
00219     gnn_node  *pre;
00220     gnn_node  *post;
00221     
00222     assert (node != NULL);
00223     
00224     /* get number of subnodes */
00225     size = gnn_node_sub_get_number (node);
00226     if (size < 2)
00227         return 0;
00228     
00229     /* cast to stack */
00230     stack = (gnn_serial *) node;
00231     
00232     /* allocate space for stack buffer */
00233     stack->buffers = (stack_buffer *) malloc (sizeof (stack_buffer) * (size-1));
00234     if (stack->buffers == NULL)
00235         GSL_ERROR ("couldn't allocate stack buffers", GSL_ENOMEM);
00236 
00237     /* fill buffers */
00238     post = gnn_node_sub_get_node_at (node, 0);
00239     for (i=0; i<size-1; ++i)
00240     {
00241         int m, n, status;
00242         
00243         pre = post;
00244         post = gnn_node_sub_get_node_at (node, i + 1);
00245         
00246         /* check sizes */
00247         m = gnn_node_output_get_size (pre);
00248         n = gnn_node_input_get_size (post);
00249         if (m != n)
00250         {
00251             gnn_serial_destroy_buffers (node);
00252             GSL_ERROR ("the size of two consecutive nodes should match",
00253                        GSL_EINVAL);
00254         }
00255         
00256         /* make buffer */
00257         status = stack_buffer_init (&(stack->buffers[i]), n);
00258         if (status)
00259         {
00260             gnn_serial_destroy_buffers (node);
00261             GSL_ERROR ("couldn't create stack buffers", GSL_ENOMEM);
00262         }
00263     }
00264     
00265     return 0;
00266 }
00267 
00268 /**
00269  * @brief Destroys a \ref gnn_serial node's stack buffers.
00270  * @ingroup gnn_serial_doc
00271  *
00272  * This function destroys the buffers for the current \ref gnn_serial node.
00273  *
00274  * @param  node A pointer to a \ref gnn_serial structure.
00275  * @return 0 if suceeded.
00276  */
00277 static int
00278 gnn_serial_destroy_buffers (gnn_node *node)
00279 {
00280     int i;
00281     int size;
00282     gnn_serial *stack;
00283 
00284     assert (node != NULL);
00285 
00286     /* get number of subnodes */
00287     size = gnn_node_sub_get_number (node);
00288     if (size < 2)
00289         return 0;
00290 
00291     /* cast to stack */
00292     stack = (gnn_serial *) node;
00293 
00294     /* destroy buffers */
00295     for (i=0; i<size-1; ++i)
00296     {
00297         stack_buffer_finalize (&(stack->buffers[i]));
00298     }
00299     
00300     return 0;
00301 }
00302 
00303 /**
00304  * @brief Evaluate a stack node.
00305  * @ingroup gnn_serial_doc
00306  *
00307  * This function computes the result from the stack node. If its \f$N\f$
00308  * subnodes implement the functions \f$g_1, g_2, \ldots, g_N\f$ then
00309  * the result is given by
00310  * \f[ Y = g_N \circ \ldots \circ g_2 \circ g_1 (X) \f]
00311  *
00312  * @param node A stack node.
00313  * @param x    The input vector \f$x\f$.
00314  * @param w    The current parameter vector \f$w\f$.
00315  * @param y    An output vector \f$y\f$ where the result should be stored.
00316  * @return 0 if succeeded.
00317  */
00318 static int
00319 gnn_serial_f (gnn_node *node,
00320              const gsl_vector *x,
00321              const gsl_vector *w,
00322              gsl_vector *y)
00323 {
00324     int i;
00325     int size;
00326     gnn_serial  *stack;
00327     
00328     const gsl_vector *xptr;
00329     gnn_node         *subnode;
00330     gsl_vector       *yptr;
00331     
00332     assert (node != NULL);
00333 
00334     /* initialize stack evaluation */
00335     stack   = (gnn_serial *) node;
00336     size    = gnn_node_sub_get_number (node);
00337     xptr    = x;
00338     subnode = gnn_node_sub_get_node_at (node, 0);
00339 
00340     /* traverse stack evaluation */
00341     for (i=0; i<size-1; ++i)
00342     {
00343         /* clear buffer */
00344         stack_buffer_clear (&(stack->buffers[i]));
00345 
00346         /* set output buffer */
00347         yptr = stack->buffers[i].xy;
00348 
00349         /* evaluate */
00350         gnn_node_eval_f (subnode, xptr, yptr);
00351 
00352         /* move pointers for next iteration */
00353         xptr = yptr;
00354         subnode = gnn_node_sub_get_node_at (node, i+1);
00355     }
00356 
00357     /* last evaluation */
00358     yptr = y;
00359     gnn_node_eval_f (subnode, xptr, yptr);
00360 
00361     return 0;
00362 }
00363 
00364 /**
00365  * @brief Computes \f$\frac{\partial E}{\partial x}\f$ for the doc.
00366  * @ingroup gnn_serial_doc
00367  *
00368  * This function computes the gradient \f$\frac{\partial E}{\partial x}\f$
00369  * as:
00370  * \f[ \frac{\partial E}{\partial x}
00371  *     = \frac{\partial E}{\partial x_N}
00372  *       \circ \frac{\partial E}{\partial x_{N-1}} \ldots
00373  *       \circ \frac{\partial E}{\partial x_1}
00374  *       (\frac{\partial E}{\partial y})
00375  * \f]
00376  * where \f$x_1, \ldots, x_N\f$ where the \f$N\f$ input vectors involved
00377  * in the subnode's functions \f$g_1, \ldots, g_N\f$.
00378  *
00379  * @param node A stack node.
00380  * @param x    The input vector \f$x\f$.
00381  * @param w    The current parameter vector \f$w\f$.
00382  * @param dy   The error-backpropagation vector
00383  *             \f$\frac{\partial E}{\partial y}\f$
00384  * @param dx   An output vector where the result
00385  *             \f$\frac{\partial E}{\partial x}\f$ should be stored.
00386  * @return 0 if suceeded.
00387  */
00388 static int
00389 gnn_serial_dx (gnn_node *node,
00390                const gsl_vector *x,
00391                const gsl_vector *w,
00392                const gsl_vector *dy,
00393                gsl_vector *dx)
00394 {
00395     int i;
00396     int size;
00397     gnn_serial  *stack;
00398 
00399     const gsl_vector *dyptr;
00400     gnn_node         *subnode;
00401     gsl_vector       *dxptr;
00402 
00403     assert (node != NULL);
00404 
00405     /* initialize stack evaluation */
00406     stack   = (gnn_serial *) node;
00407     size    = gnn_node_sub_get_number (node);
00408     dyptr   = dy;
00409     subnode = gnn_node_sub_get_node_at (node, size-1);
00410 
00411     /* traverse stack evaluation */
00412     for (i=size-1; i>0; --i)
00413     {
00414         /* set dx buffer */
00415         dxptr = stack->buffers[i-1].dydx;
00416 
00417         /* evaluate */
00418         gnn_node_eval_dx (subnode, dyptr, dxptr);
00419 
00420         /* move pointers for next iteration */
00421         dyptr = dxptr;
00422         subnode = gnn_node_sub_get_node_at (node, i-1);
00423     }
00424 
00425     /* last evaluation */
00426     dxptr = dx;
00427     gnn_node_eval_dx (subnode, dyptr, dxptr);
00428 
00429     return 0;
00430 }
00431 
00432 /**
00433  * @brief Computes \f$\frac{\partial E}{\partial w}\f$ for the node.
00434  * @ingroup gnn_serial_doc
00435  *
00436  * This function triggers the computation of the subnode's gradients
00437  * \f$\frac{\partial E}{\partial w_1}, \frac{\partial E}{\partial w_2},
00438  * \ldots, \frac{\partial E}{\partial w_N} \f$ involved in the \f$N \f$
00439  * sublayers \f$g_1, \ldots, g_N\f$.
00440  *
00441  * @param node A stack node.
00442  * @param x    The input vector \f$x\f$.
00443  * @param w    The current parameter vector \f$w\f$.
00444  * @param dy   The error-backpropagation vector
00445  *             \f$\frac{\partial E}{\partial y}\f$
00446  * @param dw   Not used, since the stack hasn't any local parameters.
00447  * @return 0 if succeeded.
00448  */
00449 static int
00450 gnn_serial_dw (gnn_node *node,
00451                const gsl_vector *x,
00452                const gsl_vector *w,
00453                const gsl_vector *dy,
00454                gsl_vector *dw)
00455 {
00456     int i;
00457     int size;
00458     
00459     assert (node != NULL);
00460 
00461     /* initialize stack evaluation */
00462     size = gnn_node_sub_get_number (node);
00463 
00464     /* traverse stack evaluation */
00465     for (i=0; i<size; ++i)
00466     {
00467         gnn_node *subnode;
00468 
00469         /* evaluate */
00470         subnode = gnn_node_sub_get_node_at (node, i);
00471         gnn_node_eval_dw (subnode);
00472     }
00473 
00474     return 0;
00475 }
00476 
00477 /**
00478  * @brief \ref gnn_serial destructor.
00479  * @ingroup gnn_serial_doc
00480  *
00481  * This function destroys the \ref gnn_serial's specific data.
00482  *
00483  * @param node A pointer to a \ref gnn_serial node.
00484  */
00485 static void
00486 gnn_serial_destroy (gnn_node *node)
00487 {
00488     assert (node != NULL);
00489     
00490     /* free buffers */
00491     gnn_serial_destroy_buffers (node);
00492 }
00493 
00494 
00495 
00496 /******************************************/
00497 /* Public Interface Implementation        */
00498 /******************************************/
00499 
00500 /**
00501  * @brief Build a node stack.
00502  * @ingroup gnn_serial_doc
00503  *
00504  * This function builds a new gnn_serial node. The node computes
00505  * the function \f$ Y = g_N \circ \ldots \circ g_1 (X)\f$, where 
00506  * \f$g_1, \ldots, g_N\f$ are the functions implemented by the subnodes
00507  * given as arguments, in the same order.
00508  *
00509  * The input and output sizes should match on each other, so that
00510  * the output size of a preceeding node must be the same as the input size
00511  * of the following node.
00512  *
00513  * Example:
00514  * \code
00515  * gnn_node *stack, *node1, *node2, *node3;
00516  *
00517  * // build the nodes 1 to 3
00518  * ...
00519  *
00520  * // build the stack
00521  * stack = gnn_serial_new (3, node1, node2, node3);
00522  * \endcode
00523  *
00524  * @param size The number of subnodes.
00525  * @param ...  The list of subnodes.
00526  * @return A new stack node.
00527  */
00528 gnn_node *
00529 gnn_serial_new (int size, ...)
00530 {
00531     size_t  i;
00532     va_list args;
00533     gnn_node        *s;
00534     gnn_node_vector *v;
00535 
00536     assert (size > 0);
00537 
00538     /* create node vector */
00539     v = gnn_node_vector_new (size);
00540     if (v == NULL)
00541     {
00542         GSL_ERROR_VAL ("couldn't allocate node vector for gnn_serial node",
00543                        GSL_ENOMEM, NULL);
00544     }
00545 
00546     /* prepare node array */
00547     va_start (args, size);
00548     for (i=0; i<size; ++i)
00549     {
00550         gnn_node *n;
00551         n = (gnn_node *) va_arg (args, gnn_node *);
00552         gnn_node_vector_set (v, i, n);
00553     }
00554     va_end (args);
00555 
00556     /* call stack node builder */
00557     s = gnn_serial_new_with_node_vector (v);
00558 
00559     /* free vector */
00560     gnn_node_vector_free (v);
00561 
00562     return s;
00563 }
00564 
00565 /**
00566  * @brief Build a node stack with a given node vector.
00567  * @ingroup gnn_serial_doc
00568  *
00569  * This function builds a new gnn_serial node. The node computes
00570  * the function \f$ Y = g_N \circ \ldots \circ g_1 (X)\f$, where
00571  * \f$g_1, \ldots, g_N\f$ are the functions implemented by the subnodes
00572  * given as arguments, in the same order.
00573  *
00574  * The input and output sizes should match on each other, so that
00575  * the output size of a preceeding node must be the same as the input size
00576  * of the following node.
00577  *
00578  * Example:
00579  * \code
00580  * gnn_node *stack;
00581  * gnn_node_vector *vector;
00582  *
00583  * // build the node vector
00584  * vector = gnn_node_vector_new (3);
00585  *
00586  * // build the nodes vector[0], vector[1] and vector[2]
00587  * ...
00588  *
00589  * // build the stack
00590  * stack = gnn_serial_new_with_node_vector (vector);
00591  * \endcode
00592  *
00593  * @todo  Automatic divergence-convergence insertion.
00594  * @param v A pointer to a \ref gnn_node_vector.
00595  * @return A new stack node.
00596  */
00597 gnn_node *
00598 gnn_serial_new_with_node_vector (gnn_node_vector *v)
00599 {
00600     int       status;
00601     size_t    n;
00602     size_t    m;
00603     size_t    size;
00604     gnn_node *node;
00605 
00606     assert (v != NULL);
00607 
00608     /* get size */
00609     size = gnn_node_vector_count_nodes (v);
00610 
00611     /* allocate stack node */
00612     node = (gnn_node *) malloc (sizeof (gnn_serial));
00613     if (node == NULL)
00614         GSL_ERROR_VAL ("couldn't allocate memory for gnn_serial node",
00615                        GSL_EINVAL, NULL);;
00616 
00617     /* initialize stack layer */
00618     status = gnn_node_init (node,
00619                             "gnn_serial",
00620                             gnn_serial_f,
00621                             gnn_serial_dx,
00622                             gnn_serial_dw,
00623                             gnn_serial_destroy);
00624     if (status)
00625     {
00626         free (node);
00627         GSL_ERROR_VAL ("couldn't init gnn_serial", GSL_ENOMEM, NULL);
00628     }
00629 
00630     /* store layers */
00631     status = gnn_node_sub_install_node_vector (node, v);
00632     if (status)
00633     {
00634         gnn_node_destroy (node);
00635         GSL_ERROR_VAL ("couldn't install subnodes for gnn_serial",
00636                        GSL_EINVAL, NULL);
00637     }
00638 
00639     /* set sizes */
00640     n = gnn_node_input_get_size  (gnn_node_sub_get_node_at (node, 0));
00641     m = gnn_node_output_get_size (gnn_node_sub_get_node_at (node, size-1));
00642 
00643     status = gnn_node_set_sizes (node, n, m, 0);
00644     if (status)
00645     {
00646         gnn_node_destroy (node);
00647         GSL_ERROR_VAL ("couldn't set sizes for gnn_serial", GSL_EINVAL, NULL);
00648     }
00649 
00650     /* install buffers */
00651     status = gnn_serial_make_buffers (node);
00652     if (status)
00653     {
00654         gnn_node_destroy (node);
00655         GSL_ERROR_VAL ("couldn't install buffers", GSL_EINVAL, NULL);
00656     }
00657 
00658     return node;
00659 }
00660 
00661 /**
00662  * @brief Get the i-th subnode.
00663  * @ingroup gnn_serial_doc
00664  *
00665  * This function returns a pointer to the stack's i-th node.
00666  *
00667  * @param node A stack node.
00668  * @param i    The node's index (should be within [0, size-1]).
00669  * @return A pointer to the i-th node or NULL if failed.
00670  */
00671 gnn_node *
00672 gnn_serial_get_node (gnn_node *node, int i)
00673 {
00674     return gnn_node_sub_get_node_at (node, i);
00675 }
00676 
00677 /**
00678  * @brief Gets the number of nodes that the stack is build of.
00679  * @ingroup gnn_serial_doc
00680  *
00681  * @param  node A stack node.
00682  * @return Size of the stack.
00683  */
00684 int
00685 gnn_serial_get_size (gnn_node *node)
00686 {
00687     return gnn_node_sub_get_number (node);
00688 }
00689 
00690 

Generated on Sun Jun 13 20:50:12 2004 for libgnn Gradient Retropropagation Machine Library by doxygen1.2.18