Boost Graph Library助我一生

本文的第一部分在此处介绍,其中包含作者的各种考虑因素,这些考虑因素是在基于Boost Graph Library(BGL)的用于搜索社交关系的专用系统的长期开发过程中积累的。 本(技术性)部分总结了作者使用该库的印象,提出了创建图形应用程序时的检测问题,并谈到了C ++元编程的一些实际问题。

BGL及其与之一起吃


遇到图形任务的任何开发人员都可能知道BGL模板库 。 她在2000年的Boost 1.18.1中露面,立即获得了亚历山大·史蒂芬诺夫(Alexander Stepanov)等经典音乐的好评。 该库指南由Jeremy Sik,Lei-Kwan Lee和Andrew Lamsdane编写,由Peter于2006年用俄语出版(原著-Jeremy G. Siek,Lie-Quan Lee和Andrew Lumsdaine,“ Boost Graph Library”,2001年) ,Addison-Wesley)。 对该库进行了密集的更新和开发,直到2013年底为止(Boost 1.55.0)。 特别是在2005年,发布了其分布式版本(PBGL)的公告,该版本从2009年的1.40版开始被包含在Boost中,直到今天,它仍然是一种高性能群集上图形计算的事实上的标准,无论如何,在学术界。 就提交的历史而言,直到2005年,图书馆的主要开发者是杰里米·西克(Jeremy Sik),2005年之后是道格拉斯·格雷戈尔(Douglas Gregor),并且通常在不同的时间,图书馆中都有大量不同的人在工作。 致力于它的出版物已经多次出现在habr.com上:首先,应注意Vadim Androsov撰写的一系列文章:[ 1,2,3 ]。 因此,原则上,图书馆会使用丰富多样的文献,但由于以下事实,图书馆本身的文献(通常而言)也相当丰富:

  1. 自2001年以来,它的目录和根目录声称提供了关键实体的详尽列表,但从未改变。 例如,这些文章的作者天真地相信:
    BGL当前提供两个图类和一个边缘列表适配器:

    adjacency_list
    adjacency_matrix
    edge_list
    ,过了一段时间,我很惊讶地发现2005年实现了compression_sparse_row_graph(稀疏矩阵)表示形式。 Bron-Kerbosch算法也发生了类似的情况。 不要相信目录,直接在头文件中搜索;
  2. 实现您自己的视图所需的库的内部类别(container_category,parallel_edge_traits,iterator_stability等)没有单个注释列表。 显然,了解正在发生的事情的问题已经超过了图书馆的所有用户,他们想要更深入地挖掘,这导致出现“一种工作代码”,这需要花费大量时间和精力才能使其完全完成:例如,参见典型的讨论

类别和各种选择器(包括那些令人困惑的相似选择)的数量是如此之大,以至于作者自己有时对此感到困惑。 例如,在上面已经提到的compressed_sparse_row_graph的设计器中,在当前版本中,存在一个系统错误,导致在尝试复制无向邻接表时导致崩溃:



有时可能会注意到,对这种灵活机制的全面测试是一个单独的问题,因为它伴随着可能的替代数量的爆炸性增长。

值得遗憾地指出,目前主要开发者显然已经对图书馆的进一步工作失去了兴趣,并且在过去六年中,它绝不是在耗尽其开发潜力,甚至没有完全摆脱内部矛盾和直接错误的情况下自由发展的。 尚未实现的计划是,在2011年左右计划大范围扩展方法集并覆盖图论的新领域(包括通过增加内部图分区支持以读取METIS格式的能力)。 从2011年以后成为标准的新产品的广泛使用中,图书馆似乎也可以从中受益(至少在可读性方面)。

因此,从2019年开始查看时,为图形应用程序选择参考库的问题看起来并不像我们想要的那么明确,并且在过去5年中,不确定性在增加而不是减少。

这种情况引起了一些悲伤,因为就方法的力量和已实现的通用方法的丰富性而言,创建类似于BGL的通用机制本身就是一种智力专长(一个好的一百五十个单线程和几十个分布式)就这些行的作者而言,该库仍然没有平等地位。

目前,只有该库原则上允许在不损失性能的情况下,对数据的表示施加严格的协议,并且对库本身的内部机制失去控制,才能完全分离图形算法和图形表示,从而使后者完全独立于与边和顶点关联的元数据的表示(从原则上讲,这显然是最正确的处理方式)。

出于某种原因,在这里使用“根本”一词。 考虑到一个特定的情况,使用上面已经提到的具有持久性的compression_sparse_row_graph类的示例,例如,我们可以注意到与高标准的以下差异:

  1. 邻接表和稀疏矩阵的运算符[]对边的内部和外部属性(内部和捆绑属性)的处理方式不同:第一个仅返回外部属性(内部只能通过property_map访问),第二个返回包含通用属性列表的框架结构属性。
  2. 使用boost :: property_map <compressed_sparse_row_graph,boost :: edge_index_t> :: type来获取边缘索引的get函数属于boost :: detail,而不是像其他所有情况一样,属于boost。

最后,在compressed_sparse_row_graph模板中,未实现图的专用化(boost :: undirectedS)仍未实现。

在这方面,当使用edge_index属性(边缘序列号)时,由于以下事实而引起了额外的困难:对于邻接列表,此属性必须显式设置为内部,因此可以任意更改,但是对于无向图,其值不取决于方向肋骨通过的地方。 对于稀疏矩阵(始终是有向矩阵),它是一种特殊形式的内置常量property_map(计算为边数组中的索引)。 因此,即将到来的边沿(代表无向图)的值不能更改,并且始终是不同的。

所有这些差异导致在调用算法函数时“用等价的图形表示简单地替换图形表示”是不可能的,这大大损害了库的主要优势。 在实践中,在这种情况下,要么需要过多的代码专业化,要么需要对其进行处理以排除具有不同行为的元素,或者对图形模板进行这种调整,以使它们“具有相同的属性”并具有不同的属性定义,或者最终从库中删除单个文件并进行创建。 “个人增强版。”

此外,可以注意到以下不那么重要的不便之处:

  • 图表示形式的内部描述符的尺寸对存储图所需的内存消耗有重大影响,有时还会影响算法的性能。

    一些视图(相同的compressed_sparse_row_graph)使您可以控制这些尺寸。 其他(adjacency_list)没有此类参数,并且始终使用64位整数(通常是冗余的),如果不修改代码就无法替换这些整数;
  • 尽管该库的作者提供了非常非常多的事实,但是库中并未包含一些显然必要的原语。 例如,没有像reverse_edge这样的函数可以执行边反转。

    当然,此类功能的实现取决于图形表示形式:在这种情况下,可以通过平凡地交换一对元素,或多或少地按容器进行搜索或根本不进行实现。 最终用户很难理解所有这些不同的选择,尤其是因为根据库的意识形态,描述符的内部成员不应该引起他的兴趣。
  • 同样,一些不值钱的脚本也从库中掉了出来。 例如,您可以定义使用谓词的边缘谓词,以使用filtered_graph将无向图变成有向图,但是这种方法无法引起库的注意。 因此,用于有向图的常规算法将无法与此类对象一起编译,而用于无向图的算法将无法与此对象一起正常工作。

    在附近的某个地方,存在对技术上无方向的图的支持的主题,这些图在边缘具有服务方向标记。 但是,对此观点的更多关注可能是由于作者解决的任务的特殊性质,对于支持此类对象的广泛兴趣并不明显。
  • 至于上面示例中的reverse_edge函数,根本没有令人难以置信的选择,即所需函数存在于库的肠道中某处,但由于某种原因却收到了一个不明显的名称。 这会导致以下问题,乍一看并不严重,但会显着降低使用复杂模板库的速度(不仅是BGL,尽管显然是按此标准,它也处于领先地位):使用隐式互连函数的广泛系统,而无需显式参数键入,并且使用使用的非显而易见的语义(通常比经过深思熟虑的透明度更不透明)在物理上很困难,并且现有的开发环境在开发人员中对此不提供任何支持:



    实际上,自动助手:

    1. 当一组功能根据其类型绑定到右侧的对象时,主要是为OOP支持而设计的。 全局函数可以位于类型的左侧(更不用说一组类型),即使所有类型都已知,它们的帮助也更糟。
    2. 他们甚至无法使用简单的模板。 作者使用的视觉助手版本在其前面具有带有默认参数的模板类的定义,该版本提供了指定“测试替换”的功能,以便能够为该类生成提示。 如果遇到她,绝对不会发生任何事情。
    3. 而且,他们不太了解元程序限定符,即使是最简单的限定符,如enable_if。
    4. 关于一个典型的场景:“我们处于一个模板函数的内部,该函数是由不确定数量的其他函数(包括模板函数)的不确定长度的链调用的”,这是不言而喻的。 在这种情况下,vim实际上仍然是程序员的最好朋友。

    可以使用上图中所示的代码片段的第一行来说明同一情况的另一方面。 邀请读者完成“提升当前时间”与“ CRT当前时间”查询并比较结果。 是的,boost :: date_time(现在已部分移动到std)使正确执行许多复杂的事情成为可能,而CRT允许您错误地执行一些琐碎的操作,但是在日常的日常家庭情况下,从所有角度来看,CRT都比多项式更方便posix_time :: second_clock :: local_time形式的构造(一个温和的示例)倾向于变成在程序中徘徊的象形文字。 剥夺了开发人员对此类象形文字的访问权限, 开发速度将为零

    Boost :: string_algo使得可以对字符串进行任何操作,但是老实说,每个不太琐碎的操作都伴随着重新读取文档的会话,以刷新库的一般逻辑,谓词的名称以及单独的练习来查找参数的兼容性。 类似的情况也会发生在boost :: regexp中的令牌化操作中,后者的内部逻辑完美无缺。

    如果在最常用的库中发生这种情况,那么BGL作为一个更专业的库就不足为奇了,例如,其中不存在彼此不相关的make_property_map_function和make_function_property_map函数,以及将圣礼的get函数重新加载到任何任何类型的参数数量都会引起同样的问题,但是形式是肥大的。 是的,任何任务都可以通过get调用链解决,但是,可惜,并非每个get链都能解决此问题。

    读取这样的代码既容易又愉快,甚至看起来像是自然语言中正式编写的算法的提要,但是在编写代码时,不可能用同义词替换单词等。刚度的表现对于真正的人而言是不典型的。
  • 一般而言,人们不能不重复这种平庸的说法,但也不会变得不那么真实,这是因为观察到C ++中的元编程实际上仍然是基于语言工具的副作用,而语言工具的最初目的是不同的,甚至是基于语言工具的最简单的想法。结果,难以表达和阅读元语言,并且将模板代码链接到包含文件的旧系统不会使开发人员的工作更轻松,也不会减少编译器处理的代码量。

    (另一方面,boost和std的定期更新带来了很多不太琐碎且通常非常有用的构造和意外的解决方案,它们确实允许以较低的成本编写更清晰,紧凑的代码。但是,新产品流是如此之大,不平等且结构不良,以至于最重要的是标准库的附加内容,即使是显而易见的附加内容,例如options / apply_visitor或下面提到的任何内容,如果它们在特定项目中的应用的概念优势不相关的话 不言而喻,如果您没有花费大量的工作时间直接沉思地跟踪新产品,研究其使用的重要实例以及将它们应用于现有代码的精神尝试,那么如果没有愉快的事件的帮助,它们可能会长时间失焦。为了解决这个问题-每5名C + +编程人员要一名C + +-一个理论家,只关注新产品的优先级及其实现 在项目tions和选择性的教育工作者。 结论: 不要启动C ++-开发人员较少的项目 )。
  • 最后,客观地讲, 使用BGL样板代码时发生的最严重的问题。 假设我们正在使用一些模板算法,该算法通过图并以图G的表示形式作为参数。 在典型情况下,此表示取决于叠加在顶点和边上的滤镜 FvFe和体重计划 W。 为了处理过滤图,BGL提供了上面提到的filter_graph模板类,将权重方案附加到其上的方式由用户决定。 函子代表 FvFeW可能至少包括以下视图:

    • 直接用于表示加权方案的函数的包装器和表示过滤器的谓词(缓慢,没有初始化损失);
    • 在这些包装器上进行高速缓存,将边缘/节点描述符映射到边缘/节点索引,寻址位图和值数组(没有初始化损失,使用的速度逐渐提高);
    • 节点/边描述符的直接映射到填充值数组(需要初始化,但可以在先前的表示形式上构建;速度达到最大值)。

    因此,如果以传统风格编写此算法,则其主体中将出现三个选择器,每个选择器中至少具有三个分支(并且在出现新表示形式时需要调整主体)。 由于算法主体中的每个分支(在遍历图形时都会执行大量次数)会导致明显的时间损失,因此在保持相同传统样式的代码的同时避免这些损失的愿望可能会导致该算法实现27种以上的表示形式组合。

    元程序样式应该使您免于这些麻烦,使您能够支持一个元函数,该元函数描述了隐式生成所有必要实现的算法(如果运行时代码结构实际上未生成某些类型组合,则还可能有一些,甚至是相当多的不必要, ), .

    , , inline- , –O2. - ( 1:3 1:5, – , , ).

    , . . , ( ) , «» «» . , . : «» «» , «» , «» .

    , : , 100% , , «» . ( , , - , , , , , ).
  • , , , . C++ , -, , .

    , , :

    void type_selector_fun(type_a a, type_b b, ...) { if (condition_1(a, b, ...)) { auto arg = get_type_1_obj(a, b, ...); run_calc(arg, a, b, ...); } else if (condition_1(a, b, ...)) { auto arg = get_type_2_obj(a, b, ...); run_calc(arg, a, b, ...); } else ... } 

    可以使用变体<...>以大约以下形式更紧凑地重写它:

      void type_selector_fun(type_a a, type_b b, ...) { variant<type_1, type_2, ...> arg; if (condition_1(a, b, ...)) { arg = get_type_1_obj(a, b, ...); } else if ... ... apply_visitor([&](auto arg_){run_calc(arg_, a, b, ...); }, arg); } 

    这种写法的缺点是需要在变量声明中显式枚举type_1,type_2,...类型。 这些类型可能很麻烦,使用declval / result_of_t进行记录也很麻烦。

    使用任何类型时,都无需列出类型,但是无法获取模拟apply_visitor。

    建议使用一些模板函数make_variant来编写以下类型的代码:

      auto arg = make_variant ( bind(condition_1, a, b, ...), bind(get_type_1_obj, a, b, ...), bind(condition_2, a, b, ...), bind(get_type_2_obj, a, b, ...), ... ); 

    但治愈似乎并不比疾病好。

    通常,在C ++中存在一种元编程的典型情况,当要表达一个非常简单的想法时,您必须使用整个辅助工具库,其结果在可读性和易于记录方面并不十分令人满意。 本质上,我希望能够编写如下内容:

      //   variant<...>      //  ,   : type_1, type_2 etc. variant<auto...> get_type_obj(typa_a a, type_b b, ...) { if (condition_1(a, b, ...)) { return get_type_1_obj(a, b, ...); } else if (condition_2(a, b, ...)) { return get_type_2_obj(a, b, ...); } else ... } 

    甚至:

      select_value_type(arg) { if (condition_1(a, b, ...)) { arg = get_type_1_obj(a, b, ...); } else ... ... } run_calc(arg, a, b, …); 

    尽管后一种选择完全被C ++风格淘汰了,但它看起来是最实用的,因为可以为一个以上的arg变量选择类型,并且没有理由预期其构造的逻辑
  • 相同情况的另一面是使用辅助结构(例如,缓存),该辅助结构实现的脚本应具有“模板变量”的名称,但不同于同名的C ++ 14标准的扩展。

    相应的代码可能如下所示:

      struct CacheHolder { boost::variant< container<T1>, container<T2>, // ... container<TN>> ct; template<typename T> struct result_type_selector { typedef typename if_c<is_compatible<T, T1>::value, T1, if_c<is_compatible<T, T2>::value, T2, // ... if_c<is_compatible<T, TN>::value, TN, std::decay_t<T>>>>::type type; }; template<typename T> auto get() const -> const container<typename result_type_selector<T>::type>& { return boost::get<container<typename result_type_selector<T>::type>>(ct); } }; 

    在这里,如上所述,长结构表达了一个简单的想法,即通过特定名称访问代表缓存的变量,而不管缓存值的大小如何(透明地通过调用代码)。

    为简洁起见,给出了仅当一种类型可以激活时的情况的代码,但实际上,当多个容器可以同时存在时,情况更常见(可以使用元组和可选以相同的样式轻松实现)。

    get <...>函数的实现假定调用代码对要访问哪种缓存值(例如,整数或浮点数)有所了解。

    确切的类型值对调用者不重要的情况也同样常见。 在这种情况下,将播放上一段中的select_value_type / apply_visitor脚本(已针对可能的多个值进行了调整,这意味着将按优先级的降序查看类型)。
  • 到目前为止,本文中几乎没有提及PBGL。 这是因为作者在图书馆的这一部分工作的经验逐渐消失(与之相关的是,作者本人对此持怀疑态度,指的是本段以下所写的所有内容,并呼吁其他人也是如此)。 实际上,这样的实验可以归结为几个实验,针对的是相同类型的搜索问题,在实际数据中,分布式数据丢失了本地版本3-5次,整体性能却下降了15-20倍,这在实际数据中得到了证明(此令人恐惧的数字的来源在此进行了说明,并在以下段落中进行了评论) 。 鉴于使用分布式结构的工作更为复杂,因此在这种情况下选择使用本地版本是不言而喻的。

    让我们使用增量漫游算法的典型示例来解释PBGL操作的机制。 在Dijkstra算法的并行版本中,优先级队列被替换为“存储桶”数组。 属于一个“存储桶”的元素将并行处理。 在其原始形式中,增量调速是共享内存系统的一种典型算法。

    在分布式版本中,会发生以下情况:在PBGL中,加载时,图形分散在各个进程之间,并且每个进程都有一个连续范围的顶点号。 因此,通过全局顶点号,很容易知道它属于哪个进程。 因此,算法每一轮的每个过程都存储“桶”的一部分,其中包含属于该过程的顶点。 所有进程一次同时从“存储桶”的各个部分中选择并处理这些顶点,同时向拥有相邻顶点的进程发出有关需要更新以下“存储桶”的消息。 不难看出,ceteris paribus进程数量的增加导致它们发送的消息数量的增加。 结果,算法的执行时间不仅会减少,甚至会增加。 特别是,启动几个MPI进程以一定的概率在一台物理计算机上解决此问题只会导致总处理器负载增加,而不会增加任何时间。

    应该注意的是,增量节奏是最快的分布式搜索算法(库支持的三种算法中的一种)。

    因此,如果先前未准备好图形,则应将其划分为最大大小的块,每个物理机一个块。 通过初步准备,我们的意思是在此处对图的顶点进行重新编号,以便PBGL使用的连续数字范围(如果可能)对应于松散连接的子图。 诸如METIS,paraMETIS和Zoltan之类的软件包用于这些目的。 在这种模式下使用动态图很困难。

    通常,根据描述的实验结果,作者有一个印象,即只有使用特殊的通信设备才能使PBGL群集正常运行,并且将具有最少核心数和最大线程性能的机器用作此类群集的节点是有意义的。 Trinity的作者在他们的文章中指出,分布式存储的工作效率更高-作者发现很难对此陈述发表评论,但是鉴于上述情况,发现很有可能:PBGL体系结构明显地表明了多核计算机尚未获得广泛分布的时代。

    PBGL还遇到了单线程版本的问题:一些代码同步,文档和示例,由于系统的更高复杂性和愿意分享有用经验的用户越来越少而加剧。

BGL和其他动物


考虑到一长串的具体投诉,提出这个问题将是不妥当的:作者可以为2019年的新项目推荐BGL吗? 答案是这样的:作者认为,这种风格的库和基于它们的应用程序必须有未来 。 至于为特定项目选择参考库,我们应该认真考虑仪器,而不忽略上面列出的问题。 显然,答案取决于许多情况,包括但不限于以下段落中列出的情况:

  • 在项目中使用图形是功能的基础还是可选任务;
  • 一个项目是否可以通过使用多种表示形式获得优势,或者使用硬类型算法就足够了?
  • 项目最有利的并发类型;
  • 组织上的细微差别:员工(尤其是数学程序员)对C ++元编程的渴望等。

也许是在其他方面,在一次非常少的使用(挤出或复制一个工作代码并忘记)的情况下,或者对于大型系统(使用这种系统增加灵活性将随着时间的推移而付出沉重的代价和其他费用),使用BGL是合理的。 在其他情况下,仔细研究其他选择是有意义的。

至于可能的选择,他们的清单至少包括以下项目
职称柠檬味
图书馆类型C ++模板标题
网址柠檬柠檬
分散式没有啦
多线程没有啦
操作系统任何
最新版本2014年
由档案馆分发
Stackoverflow提及〜100([柠檬图库]部分中的36)
评注根据某些报告, 单线程模式下的速度大大超过了BGL
从下面的对话中可以明显看出作者对多线程的态度。 鉴于以上关于PBGL的部分,此立场值得怀疑。
职称快照
图书馆类型C ++
网址github.com/snap-stanford/snap.git
分散式没有啦
多线程是(方法的一部分)
操作系统Linux,Mac,Cygwin
最新版本2018年
存储库正在积极更新。
Stackoverflow提及<50
评注最大的(超过10 Mb的代码)网络分析库之一(Network Ananlysis),它已经积极开发了很多年。 以一种奇怪的方式,它被公众的关注相对地忽略了。
请参阅系统思想描述 。 第12页上表达的对并行方法的实现态度与本文的作者很接近。 在典型的现代化机械厂的运行条件下,它是最自然的。 范式转换发生在有条件的2011年,上面的LEMON声明提到了这一点。
职称MTGL
图书馆类型C ++模板标题
网址software.sandia.gov/svn/public/mtgl/trunk
分散式
多线程是的
操作系统任何
最新版本
Stackoverflow提及3
评注会议的神秘成员。 该图书馆在2005年至2012年期间积极发展。 来源于2017年上传。 状态不明,提及该项目已从桑迪亚网站上删除。 意识形态上受同一个BGL的启发,但是代码是完全独立的。 源代码的总量(包括大量测试和示例)达到17 MB。 该代码设计良好。 参见说明
职称
图书馆类型ç
网址github.com/igraph/igraph.git
分散式没有啦
多线程没有啦
操作系统有吗
最新版本2014年
存储库正在积极更新。
Stackoverflow提及[igraph] [c ++]和[igraph] [c]部分中大约有100个,总计超过500个(对于所有语言)
评注显然,另一个网络分析库非常受欢迎(主要在python专家等中)。 在这里描述。
职称图形工具
图书馆类型C ++ Python库
网址git.skewed.de/count0/graph-tool.git
分散式没有啦
多线程是的
操作系统仅通过使用autoconf-* nix进行判断,但是可能会简单地适应其他系统
最新版本2019年
Stackoverflow提及<20
评注另一个积极开发的网络分析库,具有悠久的提交历史,直接使用BGL(在本地修补版本中)。
请参阅性能比较表。
职称发光二极管
图书馆类型C ++
网址www.algorithmic-solutions.com/index.php/products/leda-for-c
分散式没有啦
多线程
操作系统任何
最新版本
Stackoverflow提及〜10
评注商业许可证。 一个大型的(可能会说是旧的)科学和技术计算库,其中包括一个图形部分。 显然,它依赖于自己的基础结构,而不依赖于stl / boost,从这个意义上讲,它是过时的。

特别令人关注的是面向图形的各种软件产品的分类问题。 它们的多样性,更不用说数量了,非常大。 但是,在不假装完成(甚至在形式上正确的)分类的前提下,我们可以尝试突出显示图形应用程序开发中的以下重要领域:
  1. 图形DBMS(neo4j等)。

    这种系统专注于在大型(分布式磁盘)图上执行事务操作。 尽管可以高度开发这种系统的API,但据人们可以判断,图算法本身的执行速度并不是第一要务。 系统甚至可能不会尝试将整个图形加载到内存中。 对于图形修改和遍历,支持声明性语言(SPARQL,Cypher和Gremlin)。 非常重视确保与传统SQL系统的连续性。
  2. 在地图中使用的大数据处理系统的图形扩展/减少范式(对于Hadoop使用Spark,Pegasus和Giraph的GraphX)和独立的集群系统( MS Trinity / MS Graph Engine ,GraphLab)。 第一个对图形执行操作的人实现了Google Pregel模型(但不仅限于此),并且可以配置为包括大规模并行计算节点在内使用。 这些和其他都可以用作公司软件项目的基础。

    尽管此类系统的API可以相当完善(除其他外,GraphX支持SPARQL和Cypher),但使用它们时的主要重点是解决基础结构问题。 GraphX的特点是数据不变,并且所有操作的流水线都有偏差。 MS Trinity当前不包括高级方法,仅提供一组用于处理节点和边缘的原语。 原则上,在Hadoop之上运行的系统很少用于解决任意图形问题。
  3. 实际上是通用的工具库,可实现或多或少的方法集(BGL / PBGL,LEMON等),包括大量并行方法(nvGraph,Gunrock)。

    基于它们,可以创建使图形算法适应特定主题领域的应用程序系统。
  4. 专门处理具有普遍重要性的特殊复杂问题的系统和库(METIS,paraMETIS,Zoltran:图分区,GraphViz,Gephi:可视化,GraphBLAS:用于处理图的代数算法等)。

    可以有条件地将许多独立的图形应用程序分配给该类别,对此进行详细分析将需要太多时间。 后者包含所有可能的品种的应用程序:学术和商业,单用户和多用户,最近出现和存在超过十年等等。

图形应用程序的一个模糊但重要的部分集中在网络分析以及已经存在的社会网络分析(社区检测)的任务上。 奇怪的是,链接分析系统(通常由各种“犯罪分子”使用)通常不多见,它与我们正在开发的系统有一定的相似性。 在所有情况下,如果没有特殊检查,就很难确定各种系统使用的数据模型的性质以及相关的性能限制,支持的数量,操作集等。

注意事项


  1. BGL并不是纯粹的头文件库,但是目前唯一需要链接的功能是(而是可选的)使用GraphViz DOT文件。 因此,在大多数情况下,不需要进行链接,也不需要使用正确版本的libbost-graph进行自动链接,以在Boost配置中不包含BGL标头。 因此,为了与非标头BGL函数使用的libboost-regex库保持一致,即使项目代码不使用正则表达式,也可以简单地从项目代码中插入boost \ regex.hpp标头,这很方便。
  2. 实体的存在会引起额外的混乱,这些实体的明显对等会鼓励在暗室中寻找(可能不存在)黑猫
  3. 在继续对其进行描述之前(使用一个特定的示例,在该示例中它表现得特别强烈而令人不愉快),我们注意到作者是相对少数幸运的人之一,他们在功能强大的Windows操作系统和上帝保留的MSVC编译器系列中处理已加载的项目。 下面描述的问题很可能是这一系列编译器的产物:各种特殊情况使在* nix环境中使用gcc / clang进行比较实验变得困难。 如果是这样,您只能祝贺其他编译器的用户。
  4. 为了软化某些情况,最近出现的constexpr可能会有所帮助。
  5. 在我们的案例中,这引起了对状态保存功能的特别关注,该功能可以方便地进行调试,首先以优化的组件将系统置于所需的初始状态。
  6. 在我的实践中,出于各种原因,有必要将运行时参数转换为模板参数,而且我常常不得不采用非常准确,非常精心的方法(受到现在过时的C ++ 98的boost typeof和boost lambda实现的启发,该实现直接影响了将C ++的编程技术视为解决重传问题的方法),其中, 星星通过将两半分开来表示对参数的选择 ,但总的来说,此类操作的主要问题始终与无法导出有关 在范围之外选择类型,从而产生异国情调的图案。
  7. ( — 80 4 50 200 , ) ( ) - . , . , 6-8 — , .
  8. , . ( , - , . , , , , , , , ).
  9. , , – , , «» (-- ..) . ( , ), , «» , — ( ). , , , - . , , : «» ( ) , «» ( ), , . . , - , « », . , « » ? , , , : – , , , , .
  10. . , , .
  11. «LIBNAME C++ graph» , stackoverflow. , BGL 500 [boost-graph].

Source: https://habr.com/ru/post/zh-CN471652/


All Articles