GraphLab: Distributed Graph-Parallel API  2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ivertex_program.hpp
1 /**
2  * Copyright (c) 2009 Carnegie Mellon University.
3  * All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an "AS
13  * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14  * express or implied. See the License for the specific language
15  * governing permissions and limitations under the License.
16  *
17  * For more about this software visit:
18  *
19  * http://www.graphlab.ml.cmu.edu
20  *
21  * Also contains code that is Copyright 2011 Yahoo! Inc. All rights
22  * reserved.
23  *
24  */
25 
26 #ifndef GRAPHLAB_IVERTEX_PROGRAM_HPP
27 #define GRAPHLAB_IVERTEX_PROGRAM_HPP
28 
29 
30 #include <graphlab/vertex_program/icontext.hpp>
31 #include <graphlab/util/empty.hpp>
32 #include <graphlab/graph/graph_basic_types.hpp>
33 #include <graphlab/graph/distributed_graph.hpp>
34 #include <graphlab/serialization/serialization_includes.hpp>
35 #include <graphlab/vertex_program/op_plus_eq_concept.hpp>
36 
37 #include <graphlab/macros_def.hpp>
38 
39 #if defined(__cplusplus) && __cplusplus >= 201103L
40 // for whatever reason boost concept is broken under C++11.
41 // Temporary workaround. TOFIX
42 #undef BOOST_CONCEPT_ASSERT
43 #define BOOST_CONCEPT_ASSERT(unused)
44 #endif
45 
46 
47 namespace graphlab {
48 
49  /**
50  * \brief The ivertex_program class defines the vertex program
51  * interface that all vertex programs should extend and implement.
52  * The vertex-program is used to encode the user-define computation
53  * in a GraphLab program.
54  *
55  * Overview
56  * ==================
57  *
58  * A vertex program represents the primary user defined computation
59  * in GraphLab. A unique instance of the vertex program is run on
60  * each vertex in the graph and can interact with neighboring vertex
61  * programs through the gather and scatter functions as well as by
62  * signaling neighboring vertex-programs. Conceptually the
63  * vertex-program is a class which represents the parts of an
64  * update-function in the original formulation of the GraphLab
65  * abstraction. Moreover many graph-structured programs can be
66  * written in the following pattern:
67  *
68  * \code
69  * graphlab::update_function(Vertex center, Neighborhood nbrs) {
70  * // nbrs represents the state of neighboring vertices and edges
71  *
72  * // Gather Phase:
73  * sum = EMPTY;
74  * for(edge in nbrs.in_edges()) {
75  * // The sum is a general commutative associative operation
76  * if(sum == EMPTY) sum = gather_function(center, edge, edge.neighbor());
77  * else sum += gather_function(center, edge, edge.neighbor());
78  * }
79  *
80  * // Apply Phase:
81  * center = apply_function(center, sum);
82  *
83  * // Scatter Phase:
84  * for(edge in nbrs.out_edges()) {
85  * edge = scatter_function(center, edge, edge.neighbor());
86  * if(condition is met) trigger_neighbor();
87  * }
88  *
89  * }
90  * \endcode
91  *
92  * Vertex programs express computation by implementing what we call
93  * the *Gather-Apply-Scatter (GAS)* model which decomposes the
94  * vertex program into a parallel gather phase, followed by an
95  * atomic apply phase, and finally a parallel scatter phase. This
96  * decomposition allows us to execute a single vertex program on
97  * several machines simultaneously and move computation to the data.
98  *
99  * We therefore decompose the update function logic into member
100  * functions of the vertex-program class that are invoked in the
101  * following manner:
102  *
103  * \code
104  * For the center vertex vtx:
105  * vprog.init(ctx, vtx, msg);
106  * // Gather Phase:
107  * vprog::gather_type sum = EMPTY;
108  * ParallelFor(adjacent edges in direction vprog.gather_edges(ctx, vtx) )
109  * if(sum == EMPTY) sum = vprog.gather(ctx, vtx, edge);
110  * else sum += vprog.gather(ctx, vtx, edge);
111  * // Apply Phase
112  * vprog.apply(ctx, vtx, sum);
113  * // Scatter Phase
114  * ParallelFor(adjacent edges in direction vprog.scatter_edges(ctx, vtx) )
115  * vprog.scatter(ctx, vtx, edge);
116  * // Vertex program is destroyed
117  * vprog = vertex_program();
118  * \endcode
119  *
120  * All user define vertex programs must extend the ivertex_program
121  * interface and implement the ivertex_program::apply function.
122  * Most vertex programs will also implement the
123  * ivertex_program::gather and ivertex_program::scatter functions.
124  *
125  * The state of a vertex program *does not* persist between
126  * invocations of \ref ivertex_program::init. Moreover prior to
127  * each call to init the vertex program's previous state is
128  * cleared. Therefore any persistent state must be saved into the
129  * vertex data.
130  *
131  * The vertex program depends on several key types which are
132  * template arguments to ivertex_program interface.
133  *
134  * \li graph_type: the type of graph used to store the data for this
135  * vertex program. This currently always the distributed_graph.
136  *
137  * \li gather_type: the type used in the gather phase and must
138  * implement the operator+= function.
139  *
140  * \li message_type: The type used for signaling and is typically
141  * empty. However if a message type is desired it must implement
142  * the operator+= to allow message merging across the network. In
143  * addition the message type may also implement the priority()
144  * function which returns a double assigning a priority to the
145  * reception of the message (used by the asynchronous engines). We
146  * provide a basic set of simple prioritized messages in
147  * \ref graphlab::signals.
148  *
149  * All user-defined types including the vertex data, edge data,
150  * vertex-program, gather type, and message type must be
151  * serializable (see \ref sec_serializable) and default
152  * constructible to enable movement between machines.
153  *
154  * Advanced Features
155  * ======================
156  *
157  * While the basic Gather-Apply-Scatter approach to graph structure
158  * computation can express a wide range of algorithms there are some
159  * situation where additional features could either simplify the
160  * design or provide additional efficiency.
161  *
162  *
163  * Messaging
164  * ----------------------
165  *
166  * Vertex-programs can trigger adjacent vertex programs by sending a
167  * signal which can contain a message to neighbor vertices. By
168  * default the message type is empty however it is possible for the
169  * user to define a message type. For example the following
170  * message_type could be used to implement pagerank:
171  *
172  * \code
173  * struct pagerank_message : public graphlab::IS_POD_TYPE {
174  * double value;
175  * double priority() const { return std::fabs(value); }
176  * message_type& operator+=(const message_type& other) {
177  * value += other.value;
178  * return *this;
179  * }
180  * };
181  * \endcode
182  *
183  * Unlike other messaging abstractions, GraphLab always _merges_
184  * messages destined to the same vertex. This allows the GraphLab
185  * engines to minimize network communication and more evenly balance
186  * computation. Messages are combined using the operator+=
187  * function.
188  *
189  * As mentioned earlier some engines may prioritize the _reception_
190  * of messages. Messages can optionally (it is not required)
191  * provide a priority function which is used to prioritize message
192  * reception. The engine then attempts to prioritize the reception
193  * of higher priority messages first.
194  *
195  * The message is received in the \ref ivertex_program::init
196  * function. The single message passed into
197  * \ref ivertex_program::init represents the sum of all messages
198  * destined to that vertex since the vertex-program was last invoked.
199  *
200  * The GraphLab messaging framework allows us to write
201  * Pregel-like vertex-programs of the form:
202  *
203  * \code
204  * typedef graphlab::empty gather_type;
205  * class pregel_pagerank :
206  * public ivertex_program<graph_type, gather_type, pagerank_message>,
207  * public graphlab::IS_POD_TYPE {
208  *
209  * // Store a local copy of the message data
210  * double message_value;
211  *
212  * // Receive the inbound message (sum of messages)
213  * void init(icontext_type& context, const vertex_type& vertex,
214  * const message_type& msg) {
215  * message_value = message.value;
216  * }
217  *
218  * // Skip the gather phase
219  * edge_dir_type gather_edges(icontext_type& context,
220  * const vertex_type& vertex) const {
221  * return graphlab::NO_EDGES;
222  * }
223  *
224  * // Update the pagerank using the message
225  * void apply(icontext_type& context, vertex_type& vertex,
226  * const gather_type& total) {
227  * vertex.data() += message_value;
228  * }
229  *
230  * // Scatter along out edges
231  * edge_dir_type scatter_edges(icontext_type& context,
232  * const vertex_type& vertex) const {
233  * return OUT_EDGES;
234  * }
235  *
236  * // Compute new messages encoding the change in the pagerank of
237  * // adjacent vertices.
238  * void scatter(icontext_type& context, const vertex_type& vertex,
239  * edge_type& edge) const {
240  * pagerank_message msg;
241  * msg.value = message_value * (1 - RESET_PROBABILITY);
242  * context.signal(edge.target(), msg);
243  * }
244  * };
245  * \endcode
246  *
247  * Notice that the gather phase is skipped and instead the gather
248  * computation is accomplished using the messages. However unlike
249  * Pregel the scatter function which computs and sends the new
250  * message is actually run on the machine that is receiving the
251  * message.
252  *
253  * The message abstraction is surprisingly powerful and can often
254  * often express computation that can be written using the Gather
255  * operation. However, the message combination is done outside of
256  * the consistency model and so can lead to more confusing code.
257  *
258  * Gather Caching
259  * ---------------------
260  *
261  * In many applications the gather computation can be costly and
262  * high-degree vertices will be signaled often even only a small
263  * fraction of its neighbors values have changed. In this case
264  * running the gather function on all neighbors can be wasteful. To
265  * address this important issue the GraphLab engines expose a gather
266  * caching mechanism. However to take advantage of the gather
267  * caching the vertex-program must notify the engine when a cache is
268  * no longer valid and can even correct the cache to ensure that it
269  * remains valid.
270  *
271  * \todo finish documenting gather caching
272  *
273  *
274  *
275  */
276  template<typename Graph,
277  typename GatherType,
278  typename MessageType = graphlab::empty>
280  public:
281 
282  // User defined type members ==============================================
283  /**
284  * \brief The user defined vertex data associated with each vertex
285  * in the graph (see \ref distributed_graph::vertex_data_type).
286  *
287  * The vertex data is the data associated with each vertex in the
288  * graph. Unlike the vertex-program the vertex data of adjacent
289  * vertices is visible to other vertex programs during the gather
290  * and scatter phases and persists between executions of the
291  * vertex-program.
292  *
293  * The vertex data type must be serializable
294  * (see \ref sec_serializable)
295  */
296  typedef typename Graph::vertex_data_type vertex_data_type;
297 
298  /**
299  * \cond GRAPHLAB_INTERNAL
300  *
301  * \brief GraphLab Requires that the vertex data type be Serializable. See
302  * \ref sec_serializable for details.
303  */
304  BOOST_CONCEPT_ASSERT((graphlab::Serializable<vertex_data_type>));
305  /// \endcond
306 
307 
308 
309  /**
310  * \brief The user defined edge data associated with each edge in
311  * the graph.
312  *
313  * The edge data type must be serializable
314  * (see \ref sec_serializable)
315  *
316  */
317  typedef typename Graph::edge_data_type edge_data_type;
318 
319  /**
320  * \cond GRAPHLAB_INTERNAL
321  *
322  * \brief GraphLab Requires that the edge data type be Serializable. See
323  * \ref sec_serializable for details.
324  */
325  BOOST_CONCEPT_ASSERT((graphlab::Serializable<edge_data_type>));
326  /// \endcond
327 
328 
329  /**
330  * \brief The user defined gather type is used to accumulate the
331  * results of the gather function during the gather phase and must
332  * implement the operator += operation.
333  *
334  * The gather type plays the following role in the vertex program:
335  *
336  * \code
337  * gather_type sum = EMPTY;
338  * for(edges in vprog.gather_edges()) {
339  * if(sum == EMPTY) sum = vprog.gather(...);
340  * else sum += vprog.gather( ... );
341  * }
342  * vprog.apply(..., sum);
343  * \endcode
344  *
345  * In addition to implementing the operator+= operation the gather
346  * type must also be serializable (see \ref sec_serializable).
347  */
348  typedef GatherType gather_type;
349 
350  /**
351  * \cond GRAPHLAB_INTERNAL
352  * \brief GraphLab Requires that the gather type be default
353  * constructible.
354  *
355  * \code
356  * class gather_type {
357  * public:
358  * gather_type() { }
359  * };
360  * \endcode
361  */
362  BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<GatherType>));
363  /// \endcond
364 
365  /**
366  * \cond GRAPHLAB_INTERNAL
367  *
368  * \brief GraphLab Requires that gather type be Serializable. See
369  * \ref sec_serializable for detials
370  */
371  BOOST_CONCEPT_ASSERT((graphlab::Serializable<GatherType>));
372  /// \endcond
373 
374  /**
375  * \cond GRAPHLAB_INTERNAL
376  *
377  * \brief GraphLab Requires that gather type support operator+=.
378  */
379  BOOST_CONCEPT_ASSERT((graphlab::OpPlusEq<GatherType>));
380  /// \endcond
381 
382 
383 
384  /**
385  * \cond GRAPHLAB_INTERNAL
386  *
387  * \brief GraphLab Requires that the gather type be serializable
388  *
389  * \code
390  * class gather_type {
391  * public:
392  * gather_type() { }
393  * };
394  * \endcode
395  */
396  BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<GatherType>));
397  /// \endcond
398 
399 
400 
401  /**
402  * The message type which must be provided by the vertex_program
403  */
404  typedef MessageType message_type;
405 
406 
407  /**
408  * \cond GRAPHLAB_INTERNAL
409  *
410  * \brief GraphLab requires that the message type be default
411  * constructible.
412  *
413  * \code
414  * class message_type {
415  * public:
416  * message_type() { }
417  * };
418  * \endcode
419  *
420  */
421  BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<MessageType>));
422  /// \endcond
423 
424  /**
425  * \cond GRAPHLAB_INTERNAL
426  *
427  * \brief GraphLab requires that the message type be Serializable.
428  * See \ref sec_serializable for detials
429  */
430  BOOST_CONCEPT_ASSERT((graphlab::Serializable<MessageType>));
431  /// \endcond
432 
433  /**
434  * \cond GRAPHLAB_INTERNAL
435  *
436  * \brief GraphLab requires that message type support operator+=.
437  */
438  BOOST_CONCEPT_ASSERT((graphlab::OpPlusEq<MessageType>));
439  /// \endcond
440 
441 
442  // Graph specific type members ============================================
443  /**
444  * \brief The graph type associative with this vertex program.
445  *
446  * The graph type is specified as a template argument and will
447  * usually be \ref distributed_graph.
448  */
449  typedef Graph graph_type;
450 
451  /**
452  * \brief The unique integer id used to reference vertices in the graph.
453  *
454  * See \ref graphlab::vertex_id_type for details.
455  */
456  typedef typename graph_type::vertex_id_type vertex_id_type;
457 
458  /**
459  * \brief The opaque vertex object type used to get vertex
460  * information.
461  *
462  * The vertex type is defined by the graph.
463  * See \ref distributed_graph::vertex_type for details.
464  */
466 
467  /**
468  * \brief The opaque edge_object type used to access edge
469  * information.
470  *
471  * The edge type is defined by the graph.
472  * See \ref distributed_graph::edge_type for details.
473  */
475 
476  /**
477  * \brief The type used to define the direction of edges used in
478  * gather and scatter.
479  *
480  * Possible values include:
481  *
482  * \li graphlab::NO_EDGES : Do not process any edges
483  *
484  * \li graphlab::IN_EDGES : Process only inbound edges to this
485  * vertex
486  *
487  * \li graphlab::OUT_EDGES : Process only outbound edges to this
488  * vertex
489  *
490  * \li graphlab::ALL_EDGES : Process both inbound and outbound
491  * edges on this vertes.
492  *
493  * See \ref graphlab::edge_dir_type for details.
494  */
496 
497  // Additional Types =======================================================
498 
499  /**
500  * \brief The context type is used by the vertex program to
501  * communicate with the engine.
502  *
503  * The context and provides facilities for signaling adjacent
504  * vertices (sending messages), interacting with the GraphLab
505  * gather cache (posting deltas), and accessing engine state.
506  *
507  */
509 
510  // Functions ==============================================================
511  /**
512  * \brief Standard virtual destructor for an abstract class.
513  */
514  virtual ~ivertex_program() { }
515 
516  /**
517  * \brief This called by the engine to receive a message to this
518  * vertex program. The vertex program can use this to initialize
519  * any state before entering the gather phase. The init function
520  * is invoked _once_ per execution of the vertex program.
521  *
522  * If the vertex program does not implement this function then the
523  * default implementation (NOP) is used.
524  *
525  * \param [in,out] context The context is used to interact with the engine
526  *
527  * \param [in] vertex The vertex on which this vertex-program is
528  * running. Note that the vertex is constant and its value should
529  * not be modified within the init function. If there is some
530  * message state that is needed then it must be saved to the
531  * vertex-program and not the vertex data.
532  *
533  * \param [in] message The sum of all the signal calls to this
534  * vertex since it was last run.
535  */
536  virtual void init(icontext_type& context,
537  const vertex_type& vertex,
538  const message_type& msg) { /** NOP */ }
539 
540 
541  /**
542  * \brief Returns the set of edges on which to run the gather
543  * function. The default edge direction is in edges.
544  *
545  * The gather_edges function is invoked after the init function
546  * has completed.
547  *
548  * \warning The gather_edges function may be invoked multiple
549  * times for the same execution of the vertex-program and should
550  * return the same value. In addition it cannot modify the
551  * vertex-programs state or the vertex data.
552  *
553  * Possible return values include:
554  *
555  * \li graphlab::NO_EDGES : The gather phase is completely skipped
556  * potentially reducing network communication.
557  *
558  * \li graphlab::IN_EDGES : The gather function is only run on
559  * inbound edges to this vertex.
560  *
561  * \li graphlab::OUT_EDGES : The gather function is only run on
562  * outbound edges to this vertex.
563  *
564  * \li graphlab::ALL_EDGES : The gather function is run on both
565  * inbound and outbound edges to this vertes.
566  *
567  * \param [in,out] context The context is used to interact with
568  * the engine
569  *
570  * \param [in] vertex The vertex on which this vertex-program is
571  * running. Note that the vertex is constant and its value should
572  * not be modified.
573  *
574  * \return One of graphlab::NO_EDGES, graphlab::IN_EDGES,
575  * graphlab::OUT_EDGES, or graphlab::ALL_EDGES.
576  *
577  */
579  const vertex_type& vertex) const {
580  return IN_EDGES;
581  }
582 
583 
584  /**
585  * \brief The gather function is called on all the
586  * \ref ivertex_program::gather_edges in parallel and returns the
587  * \ref gather_type which are added to compute the final output of
588  * the gather phase.
589  *
590  * The gather function is the core computational element of the
591  * Gather phase and is responsible for collecting the information
592  * about the state of adjacent vertices and edges.
593  *
594  * \warning The gather function is executed in parallel on
595  * multiple machines and therefore cannot modify the
596  * vertex-program's state or the vertex data.
597  *
598  * A default implementation of the gather function is provided
599  * which will fail if invoked.
600  *
601  * \param [in,out] context The context is used to interact with
602  * the engine
603  *
604  * \param [in] vertex The vertex on which this vertex-program is
605  * running. Note that the vertex is constant and its value should
606  * not be modified.
607  *
608  * \param [in,out] edge The adjacent edge to be processed. The
609  * edge is not constant and therefore the edge data can be
610  * modified.
611  *
612  * \return the result of the gather computation which will be
613  * "summed" to produce the input to the apply operation. The
614  * behavior of the "sum" is defined by the \ref gather_type.
615  *
616  */
618  const vertex_type& vertex,
619  edge_type& edge) const {
620  logstream(LOG_FATAL) << "Gather not implemented!" << std::endl;
621  return gather_type();
622  };
623 
624 
625  /**
626  * \brief The apply function is called once the gather phase has
627  * completed and must be implemented by all vertex programs.
628  *
629  * The apply function is responsible for modifying the vertex data
630  * and is run only once per vertex per execution of a vertex
631  * program. In addition the apply function can modify the state
632  * of the vertex program.
633  *
634  * If a vertex has no neighbors than the apply function is called
635  * passing the default value for the gather_type.
636  *
637  * \param [in,out] context The context is used to interact with
638  * the engine
639  *
640  * \param [in,out] vertex The vertex on which this vertex-program is
641  * running.
642  *
643  * \param [in] total The result of the gather phase. If a vertex
644  * has no neighbors then the total is the default value (i.e.,
645  * gather_type()) of the gather type.
646  *
647  */
648  virtual void apply(icontext_type& context,
649  vertex_type& vertex,
650  const gather_type& total) = 0;
651 
652  /**
653  * \brief Returns the set of edges on which to run the scatter
654  * function. The default edge direction is out edges.
655  *
656  * The scatter_edges function is invoked after the apply function
657  * has completed.
658  *
659  * \warning The scatter_edges function may be invoked multiple
660  * times for the same execution of the vertex-program and should
661  * return the same value. In addition it cannot modify the
662  * vertex-programs state or the vertex data.
663  *
664  * Possible return values include:
665  *
666  * \li graphlab::NO_EDGES : The scatter phase is completely
667  * skipped potentially reducing network communication.
668  *
669  * \li graphlab::IN_EDGE : The scatter function is only run on
670  * inbound edges to this vertex.
671  *
672  * \li graphlab::OUT_EDGES : The scatter function is only run on
673  * outbound edges to this vertex.
674  *
675  * \li graphlab::ALL_EDGES : The scatter function is run on both
676  * inbound and outbound edges to this vertes.
677  *
678  * \param [in,out] context The context is used to interact with
679  * the engine
680  *
681  * \param [in] vertex The vertex on which this vertex-program is
682  * running. Note that the vertex is constant and its value should
683  * not be modified.
684  *
685  * \return One of graphlab::NO_EDGES, graphlab::IN_EDGES,
686  * graphlab::OUT_EDGES, or graphlab::ALL_EDGES.
687  *
688  */
690  const vertex_type& vertex) const {
691  return OUT_EDGES;
692  }
693 
694  /**
695  * \brief Scatter is called on all scatter_edges() in parallel
696  * after the apply function has completed and is typically
697  * responsible for updating edge data, signaling (messaging)
698  * adjacent vertices, and updating the gather cache state when
699  * caching is enabled.
700  *
701  * The scatter function is almost identical to the gather function
702  * except that nothing is returned.
703  *
704  * \warning The scatter function is executed in parallel on
705  * multiple machines and therefore cannot modify the
706  * vertex-program's state or the vertex data.
707  *
708  * A default implementation of the gather function is provided
709  * which will fail if invoked.
710  *
711  * \param [in,out] context The context is used to interact with
712  * the engine
713  *
714  * \param [in] vertex The vertex on which this vertex-program is
715  * running. Note that the vertex is constant and its value should
716  * not be modified.
717  *
718  * \param [in,out] edge The adjacent edge to be processed. The
719  * edge is not constant and therefore the edge data can be
720  * modified.
721  *
722  */
723  virtual void scatter(icontext_type& context, const vertex_type& vertex,
724  edge_type& edge) const {
725  logstream(LOG_FATAL) << "Scatter not implemented!" << std::endl;
726  };
727 
728 
729  /**
730  * \internal
731  * Used to signal the start of a local gather.
732  * Called on each machine which is doing a gather operation.
733  * Semantics are that, a complete gather involves
734  *
735  * \code
736  * On each machine with edges adjacent to vertex being updated:
737  * vprogram.pre_local_gather(g) // passed by reference
738  * foreach edge adjacent to vertex:
739  * if ( ... first gather ... ) g = vprogram.gather(edge)
740  * else g += vprogram.gather(edge)
741  * end
742  * vprogram.post_local_gather(g) // passed by reference
743  * \endcode
744  */
745  virtual void pre_local_gather(gather_type&) const {
746  }
747 
748  /**
749  * \internal
750  * Used to signal the end of a local gather.
751  * Called on each machine which is doing a gather operation.
752  * Semantics are that, a complete gather involves
753  *
754  * \code
755  * On each machine with edges adjacent to vertex being updated:
756  * vprogram.pre_local_gather(g) // passed by reference
757  * foreach edge adjacent to vertex:
758  * if ( ... first gather ... ) g = vprogram.gather(edge)
759  * else g += vprogram.gather(edge)
760  * end
761  * vprogram.post_local_gather(g) // passed by reference
762  * \endcode
763  */
764  virtual void post_local_gather(gather_type&) const {
765  }
766 
767  }; // end of ivertex_program
768 
769 }; //end of namespace graphlab
770 #include <graphlab/macros_undef.hpp>
771 
772 #endif