Project: GraphScope (App Definition and Development / Database)
File: tutorial_dev_algo_cpp_pie.md

In this tutorial, we will demonstrate how to develop an algorithm using the PIE model in C++ programming language. Familiarity with the Install GraphScope by using the command `pip3 install graphscope` Then, We will guide you through developing a simple PIE algorithm that computes the degree for each vertex in the graph. The full API can be found in and . First, we create a context class that inherits from `grape::VertexDataContext`. This class will store and manage algorithm-specific data and parameters. ```cpp template <typename FRAG_T> class MyAppContext : public grape::VertexDataContext<FRAGT, uint64t> { using oidt = typename FRAGT::oid_t; using vidt = typename FRAGT::vid_t; using vertext = typename FRAGT::vertex_t; public: explicit MyAppContext(const FRAG_T& fragment) : grape::VertexDataContext<FRAGT, uint64t>(fragment, true), result(this->data()) {} void Init(grape::ParallelMessageManager& messages, int param1) { this->step = 0; this->param1 = param1; result.SetValue(0); } int step = 0; int param1 = 0; typename FRAGT::template vertexarrayt<uint64t>& result; }; ``` As shown in the code, the MyAppContext class defines two member variables called `step` and `param1` to store the current superstep and algorithm-specific parameter, respectively. And we also define a member variable named `result` with `uint64_t` type to store the degree for each vertex in the fragment. The `Init` method is used to initialize the context of the computation. In current example, we initialize the `step` and `param1` variables to zero and the algorithm-specific parameter. We also set the result to zero for each vertex. Next, we define the `MyApp` class, which is responsible for implementing the algorithm by using the `MyAppContext` class. ```cpp template <typename FRAG_T> class MyApp : public grape::ParallelAppBase<FRAGT, MyAppContext<FRAGT>>, public grape::ParallelEngine, public grape::Communicator { public: INSTALLPARALLELWORKER(MyApp<FRAGT>, MyAppContext<FRAGT>, FRAG_T) static constexpr grape::MessageStrategy message_strategy = grape::MessageStrategy::kSyncOnOuterVertex; static constexpr grape::LoadStrategy load_strategy = grape::LoadStrategy::kBothOutIn; using vertext = typename fragmentt::vertex_t; void PEval(const fragmentt& fragment, contextt& context, messagemanagert& messages) { messages.InitChannels(thread_num()); // We put all compute logic in IncEval phase, thus do nothing but force continue. messages.ForceContinue(); } void IncEval(const fragmentt& fragment, contextt& context, messagemanagert& messages) { // superstep ++context.step; // Process received messages sent by other fragment here. { messages.ParallelProcess<fragment_t, double>( thread_num(), fragment, { // Implement your logic here }); } // Compute the degree for each vertex, set the result in context auto inner_vertices = fragment.InnerVertices(); ForEach(innervertices.begin(),

{ context.result[u] = staticcast<uint64t>(fragment.GetOutgoingAdjList(u).Size() + fragment.GetIncomingAdjList(u).Size()); }); } }; ``` The `MyApp` class inherits from the `grape::ParallelAppBase`, which provides the basic functionality for implementing a parallel graph algorithm. It also inherits from the `grape::ParallelEngine` and `grape::Communicator` classes, which provide the communication and parallel processing capabilities. The MyApp class defines two `static constexpr` variables called `messagestrategy` and `loadstrategy`, these variables specify the message strategy and load strategy used in the computation. For more information please refer to the . The `PEval` method is used to implement the partial evaluation phase of the computation. In current example, we initialize the communication channels and do nothing else, instead, we put the computing logic into `IncEval` method. The `IncEval` method is used to implement the incremental evaluation phase of the computation. In this method, we increment the superstep counter in the context, process received messages sent by other fragments, and compute the degree for each vertex in the graph. The `ForEach` method is used to iterate over the inner vertices of the fragment, and the `GetOutgoingAdjList` and `GetIncomingAdjList` methods are used to get the outgoing and incoming adjacency lists of each vertex. The result for each vertex then set in the context. To make the algorithm runnable on the GraphScope, we need to package it as a `.gar` file. The package should include the above C++ files and a configuration file named `.gs_conf.yaml` with the following content: ```yaml app: algo: my_app type: cpp_pie class_name: gs::MyApp src: my_app.h contexttype: vertexdata compatible_graph: gs::ArrowProjectedFragment gs::DynamicProjectedFragment vineyard::ArrowFragment ``` the codebase structure is as follows: ``` . my_app.h algorithm logics myappcontext.h context with auxiliary data for the algorithm .gs_conf.yaml configuration file ``` then, we package the algorithm by command: ` zip -jr 'myapp.gar' '*.h' ''.gsconf.yaml'` Use the following Python code to run the algorithm on GraphScope. ```python import graphscope from graphscope.framework.app import load_app from graphscope.dataset import loadp2pnetwork sess = graphscope.session() simplegraph = loadp2pnetwork(sess).projecttosimple() myapp = loadapp('<pathtoyourgarresource>') result = myapp(simplegraph, 10) # pass 10 as param1 defined in 'MyAppContext.h' print(result.to_numpy('r')) ``` To help you develop your algorithms more efficiently, we provide a to help you get started with your algorithm development. It includes examples and best practices for implementing PIE algorithms in C++.