[{"content":"","date":"January 1, 1","permalink":"/authors/","section":"Authors","summary":"","title":"Authors"},{"content":" Plot for each Class # Below are detailed plots for the tested benchmark classes as scatter plots, where the x-axis is the number of vertices of the graph, and the y-axis the runtime in seconds. Click a plot to view in fullscreen. For the description of the benchmark setup, a summary of the results, as well as other plots, see our main benchmark page.\n","date":"January 1, 1","permalink":"/graphs_benchmarks/plots_by_size/","section":"Graphs \u0026 Benchmarks","summary":"Benchmark plots comparing state-of-the-art solvers to dejavu, as scatter plots by size.","title":"Benchmark Plots by Size"},{"content":" Here, you can find benchmarks comparing state-of-the-art solvers to dejavu.\nBenchmark Suite # We test 44 graph classes, comprised of almost all sets linked in the standard benchmark library. Specifically, we test all graph classes listed under bliss, conauto, saucy, random graphs, miscellaneous graphs, Dawar-Yeung graphs, as well as the shrunken multipedes. We solely left out very few sets for at least some brevity (e.g., slightly easier variants of shrunken multipedes).\nWe made the following additions to the benchmark suite, which we believe to be meaningful:\nmip17: practical graphs stemming from symmetry exploitation in mixed-integer solvers, provided by Marc Pfetsch and Christopher Hojny sat21: graphs stemming from SAT solving, preprocessed using unit and pure pace23: graphs from the pace challenge 2023 (twin width) groups128: some multiplication tables of groups of order 128, provided by Jendrik Brachter CFI and complete graphs: extended the sets with larger graphs The benchmark graphs can either be found in 📖 Adolfo Piperno\u0026rsquo;s collection, or on our graph page. We configured dejavu with an error probability below 0.1%, but used a pseudo random number generator. More details on the precise benchmark setup can be found further below.\nResults Summary # We give a brief overview of the results of our benchmarks.\nFastest on Graph Class First, taking the sum of all the runtimes of a graph class, dejavu is fastest on 32 classes, with Traces being fastest on 8, saucy on 3, and nauty on 1 (out of 44 total classes). dejavu 32 Traces 8 saucy 3 Competitive (within factor 2 of best) on Graph Class We get a more refined picture if we define a solver to be competitive, if it finishes within a factor of two of the fastest solver. Considering this, dejavu is competitive on 39 classes, Traces on 22, saucy on 15, nauty on 11, and bliss on 3 (out of 44 total classes). dejavu 39 Traces 22 saucy 15 nauty 11 bliss 3 Timeouts We ran all benchmarks with a time limit of 100 seconds. On all graph classes, dejavu posted the lowest number of timeouts achieved in the benchmarks (in the majority of classes the score was tied at 0, though). Summing over all the graphs in the benchmark suite, dejavu reported 31 timeouts, Traces reported 558, bliss reported 489, nauty reported 840, and saucy reported 557. We also remark that nauty and Traces ran out of memory on some graphs, wich we counted as a timeout. More details can be found in the table and figures shown below. Results Table # Below is a table summarizing our benchmark results for each graph class and solver. For each class we report its size, meaning the number of graphs contained in the class. Then, for each class and solver, report the sum of the runtimes of all the graphs in a class as t. If the solver did not solve a graph within the timelimit of 100 seconds, we classify the graph as a timeout. The number of timeouts can be found in the rows labeled t/o.\nThe best reported runtime and timeout for each graph class is signified in bold. If a solver finished within a factor of 2 of the best runtime for a given class, we report this as \u0026ldquo;competitive\u0026rdquo;, signified in italic. All runtimes are reported in seconds.\ngraph class dejavu Traces","date":"January 1, 1","permalink":"/graphs_benchmarks/benchmarks/","section":"Graphs \u0026 Benchmarks","summary":"Benchmarks comparing state-of-the-art solvers to dejavu.","title":"Benchmarks"},{"content":"","date":"January 1, 1","permalink":"/categories/","section":"Categories","summary":"","title":"Categories"},{"content":" Colors, or more specifically vertex colors, are a powerful tool when modelling objects as graphs. On this page, we explain the meaning of colors, and how to make use of them.\nTo avoid any confusion, the colors discussed here have nothing to do with colorings discussed in the well-known \u0026ldquo;graph coloring problems\u0026rdquo;.\nWhat do vertex colors mean? # Vertex colors restrict the potential set of symmetries. Vertices that are colored differently, can never be mapped to each other using a symmetry. Or phrased differently, a symmetry of a graph may only map equally colored vertices onto each other.\nObserve how in the example above, the two opposing sides can be interchanged in the uncolored graph. In the colored graph, this is not possible anymore. Formally, given a graph \\(G = (V, E)\\), a vertex coloring is a map \\(\\pi : V \\to \\mathbb{N}\\). We may construct the vertex-colored graph \\(G\u0026rsquo; := (V, E, \\pi)\\). Recall that for the uncolored graph \\(G\\), a symmetry is a bijection \\(\\varphi : V \\to V\\) that satisfies \\(\\varphi((V, E)) = (V, E)\\).\nFor the vertex-colored version \\(G\u0026rsquo;\\), we additionally require that for all \\(v_1, v_2 \\in V\\), if \\(\\varphi(v_1) = v_2\\), then \\(\\pi(v_1) = v_2\\) holds.\nColoring Graphs # We construct the vertex-colored graph above in dejavu. We identify the color blue with 0, orange with 1, and red with 2. We highlight the lines where the red and orange vertices are added to the graph.\ndejavu::static_graph g; g.initialize_graph(7, 6); // 7 vertices, 6 edges // the colors constexpr int blue = 0; constexpr int orange = 1; constexpr int red = 2; // add vertices const int v0 = g.add_vertex(blue, 2); // center of the graph const int v1 = g.add_vertex(orange, 3); // the orange vertex const int v2 = g.add_vertex(red, 3); // the red vertex const int v3 = g.add_vertex(blue, 1); // leaf const int v4 = g.add_vertex(blue, 1); // leaf const int v5 = g.add_vertex(blue, 1); // leaf const int v6 = g.add_vertex(blue, 1); // leaf // add edges g.add_edge(v0, v1); g.add_edge(v0, v2); g.add_edge(v1, v3); g.add_edge(v1, v4); g.add_edge(v2, v5); g.add_edge(v2, v6); The function add_vertex(int color, int degree) adds a vertex to the graph, where the first parameter is the \u0026ldquo;color\u0026rdquo; of the vertex, and the second parameter the degree in the graph (the number of edges connected to the vertex).\n","date":"January 1, 1","permalink":"/quick_start/vertex_colors/","section":"Quick Start","summary":"The meaning of vertex colors, and how to make use of them.","title":"Colors"},{"content":"","date":"January 1, 1","permalink":"/download/","section":"Download","summary":"","title":"Download"},{"content":" Latest Version # The latest build can always be found on GitHub, but we also provide a version of the library here. Further below, you can find all the previously released versions of dejavu.\nGitHub Project Download dejavu 2.1 Bug Reports 🪳 # If you come across any bugs or have any feedback to share, please feel free to reach out to me at markus.anders (at) tu-darmstadt.de.\nCite # If you use dejavu in your research projects, please cite our paper from ESA 2021.\n@inproceedings{DBLP:conf/esa/AndersS21, author = {Markus Anders and Pascal Schweitzer}, title = {Parallel Computation of Combinatorial Symmetries}, booktitle = {29th Annual European Symposium on Algorithms, {ESA} 2021, September 6-8, 2021, Lisbon, Portugal (Virtual Conference)}, series = {LIPIcs}, volume = {204}, pages = {6:1--6:18}, publisher = {Schloss Dagstuhl - Leibniz-Zentrum f{\\\u0026#34;{u}}r Informatik}, year = {2021}, url = {https://doi.org/10.4230/LIPIcs.ESA.2021.6}, doi = {10.4230/LIPIcs.ESA.2021.6}, } If you use dejavu as a preprocessor (i.e. sassy), please cite our paper from SEA 2023.\n@article{DBLP:journals/corr/abs-2302-06351, author = {Markus Anders and Pascal Schweitzer and Julian Stie{\\ss}}, title = {Engineering a Preprocessor for Symmetry Detection}, journal = {CoRR}, volume = {abs/2302.06351}, year = {2023}, url = {https://doi.org/10.48550/arXiv.2302.06351}, doi = {10.48550/arXiv.2302.06351}, eprinttype = {arXiv}, eprint = {2302.06351} } Here is a list of all the publications which describe the design of dejavu and sassy.\nTitle Description Published at Efficient Algorithms for Symmetry Detection most up-to-date description of all the algorithms TUprints Engineering a Preprocessor for Symmetry Detection symmetry detection preprocessor (w/ Julian Stieß) SEA2023 Parallel Computation of Combinatorial Symmetries main dejavu algorithm for automorphism search ESA2021 Engineering a Fast Probabilistic Isomorphism Test individual isomorphism solver based on Monte Carlo search ALENEX2021 Search Problems in Trees with Symmetries theoretical foundations for the near-optimal Monte Carlo search ICALP2021 All releases # Here is a list of all current and older versions of dejavu and sassy. At this point, all versions starting from dejavu 2.0 onwards neither contain the solver for individual isomorphism testing, nor parallelization.\nVersion Remarks dejavu 2.1 Current version (12.01.2025). dejavu 2.0 Release version (04.06.2024). dejavu 2.0preview2 Preview version (19.09.2023). dejavu 2.0preview1 Preview version (13.09.2023). dejavu 2.0preview0 Preview version (28.06.2023). sassy 1.1 Preprocessor (included into dejavu starting from version 2.0). dejavu 1.2 Documentation \u0026amp; guides on this site do not apply to this version. dejavu 1.1 Documentation \u0026amp; guides on this site do not apply to this version. dejavu 1.0 Documentation \u0026amp; guides on this site do not apply to this version. ","date":"January 1, 1","permalink":"/download/download/","section":"Download","summary":"All downloads available on this page.","title":"Download"},{"content":" We discuss in detail how symmetries are certified in dejavu, and how certification can be made more thorough if so desired.\nError? Certification? # The underlying algorithm of dejavu is a randomized algorithm: a Monte Carlo algorithm with bounded error. The user may set a likelihood for error, e.g., the default is \\( \\epsilon \\leq 0.001 \\), and the algorithm may only err in a given run with probability \\( \\epsilon \\).\nMore specifically, the error in dejavu is one-sided, meaning the solver may miss some symmetries, but it will never report permutations that are not symmetries of the graph. Crucially, for almost all applications that we know of, this kind of error does indeed not matter for correctness. Note that in practice, the error probability of course also depends on the quality of the employed random number generator. Options for random number generation and error probability are available through the class solver:\n// let\u0026#39;s say we have some graph dejavu::static_graph g; // ... dejavu::solver d; d.set_error_bound(10) d.automorphisms(\u0026amp;g); The set_error_bound(e) method sets the error to be at most \\( 1/2^e \\), assuming uniform random numbers. There are more options: the method set_seed which can be used to set a seed for pseudo random numbers used in the solver, whereas set_true_random attempts to use a random device as provided by the operating system.\nMoreover, dejavu certifies its results, meaning for a permutation \\( \\varphi \\) reported as a symmetry, the test \\( \\varphi(G) = G \\) \u0026ndash; with some caveats \u0026ndash; is performed. The limitations of the certification itself are discussed below.\nLimitations # Some limitations apply to the certification.\nNon-isomorphism # First of all, there are no certificates for non-isomorphism. Hence, we may not certify whether the graph actually contains more symmetries than stated. Note that this is precisely the case in which dejavu has one-sided bounded error: the solver may miss symmetries.\nPreprocessed Graphs # Secondly, dejavu preprocesses the graph, i.e., it removes vertices of low degree and other simple structures from the graphs (see our page on the preprocessor). The main solver in turn only certifies generators on the smaller, already preprocessed graph. In a way, this means that the preprocessor itself is part of the trusted code base. This reliance can however be mitigated, as is described below.\nStrong Certification # If a more thorough certification is desired, it is possible to use the strong certification flag to certify all the generators on the original graph. The solver makes a copy of the original graph, and only returns generators which pass certification on that graph.\n// let\u0026#39;s say we have some graph dejavu::static_graph g; // ... // let\u0026#39;s say we want to call the orbit_hook below dejavu::groups::orbit orbits(n); dejavu::hooks::orbit_hook hook(orbits); // create the solver object dejavu::solver d; // set strong certification d.set_strong_certification(); // call the solver d.automorphisms(\u0026amp;g, hook.get_hook()); // all generators are now certified on the original graph, and only // if they pass certification, hook1 is called ","date":"January 1, 1","permalink":"/quick_start/certification/","section":"Quick Start","summary":"A description use the preprocessor of dejavu in conjunction with other solvers.","title":"Error \u0026 Certification"},{"content":" Benchmark Suite # The largest collection of benchmark graphs is 📖 Adolfo Piperno\u0026rsquo;s collection. It is the de-facto default benchmark library, which we thoroughly test in our benchmarks. Below, we give some additional or extended graph classes for testing, and a justification as to why they are interesting.\nAll these graphs were tested in our benchmarks.\nmip17 # [Download ] Graphs stemming from symmetry exploitation in mixed-integer solvers, in particular from instances of the MIPLIB 2017. This set was provided by Marc Pfetsch and Christopher Hojny.\nsat21 # [Download ] Graphs made from the SAT competition instances of 2021. The CNF instances were first preprocessed using the unit and pure literal rule. This removes some symmetries which are not interesting in the context of the application.\npace23 # [Download ] Graphs from the pace challenge 2023. Many of the graphs contain twins and a lot of very sparse symmetries. The graphs were taken from the original benchmark suite and converted to the DIMACS format.\ngroups128 # [Download ] Graph encoding of the multiplication tables of groups of order 128. Symmetry detection can be used to compute the automorphism group of the underlying group. This set was provided by Jendrik Brachter.\ncfixl # [Download ] The cfi set (Cai-Fürer-Immerman graphs) from the bliss distribution, extended with larger graphs.\nkxl # [Download ] The k set (complete graphs) from the bliss distribution, extended with larger graphs.\n","date":"January 1, 1","permalink":"/graphs_benchmarks/graphs/","section":"Graphs \u0026 Benchmarks","summary":"Benchmarks graphs for comparing state-of-the-art solvers for symmetry detection.","title":"Graphs"},{"content":"","date":"January 1, 1","permalink":"/graphs_benchmarks/","section":"Graphs \u0026 Benchmarks","summary":"","title":"Graphs \u0026 Benchmarks"},{"content":" dejavu \u0026nbsp; Markus Anders \u0026 Pascal Schweitzer dejavu is a monte carlo solver and library for the fast detection of symmetry; freely available under the MIT license\nDownload On GitHub Get started ","date":"January 1, 1","permalink":"/","section":"Home","summary":"","title":"Home"},{"content":"","date":"January 1, 1","permalink":"/how_it_works/","section":"How_it_works","summary":"","title":"How_it_works"},{"content":" Pointwise Stabilizer # Given a permutation group \\(\\Gamma \\subseteq Sym(\\Omega)\\) and a point of the domain \\(\\omega \\in \\Omega\\) (i.e., a vertex of the graph), the pointwise stabilizer \\(\\Gamma_{(\\omega)}\\) restricts the group to precisely those that do not move \\(\\omega\\) around. In other words, it stabilizes or fixes the point \\(\\omega\\). Formally, we define \\(\\Gamma_{(\\omega)} := \\lbrace \\varphi \\in \\Gamma \\thickspace \\vert \\thickspace \\varphi(\\omega) = \\omega \\rbrace\\).\nOn a graph, the pointwise stabilizer corresponds precisely to coloring a vertex of a graph with its own color (see our description of vertex colors).\nIn the first graph above, no point is stabilized. In the second one, the orange vertex is stabilized, whereas in the last one the red and orange vertex are stabilized. We call a chain of consecutively stabilized points, such as the red and orange vertex, a (partial) base. Note how in the last graph, no symmetries are left \u0026ndash; the pointwise stabilizer of the red and orange vertex is trivial. In this case, we call the base complete\nFeeding a Schreier structure # We compute chains of pointwise stabilizer using the Schreier-Sims algorithm. The class random_schreier contains a rudimentary implementation of the random Schreier-Sims algorithm, as used internally by the solver itself. There is a simple way to feed the automorphisms of a graph into the structure: the hook schreier_hook does the job, as is shown in the example below.\n// let\u0026#39;s say we have a graph with n vertices and m edges dejavu::static_graph g; g.initialize_graph(n, m); // (insert definition of graph here...) // initialize a random schreier structure with number of vertices dejavu::groups::random_schreier rschreier(n); // when computing automorphisms, we can feed them directly into the // schreier structure with a schreier_hook dejavu::hooks::schreier_hook hook(rschreier); dejavu::solver d; d.automorphisms(\u0026amp;g, hook.get_hook()); // rschreier now contains all found automorphisms of g The structure can then be inspected and manipulated as described below.\nOrbits and Generators # We can quite easily set a base (AKA a chain of stabilized points) for a given Schreier structure. For example, if we want to set a base of 0, 4, 7, we can do this as follows:\n// we need a vector containing the new base std::vector\u0026lt;int\u0026gt; base = {0, 4, 7}; // now we set the base rschreier.set_base(base); There is two ways to retrieve orbits from the structure. Firstly, if we are only interested in the orbit of the fixed point (i.e., before fixing the point), this can be done quite efficiently as follows: // orbit at base position 1 of fixed point (4) std::vector\u0026lt;int\u0026gt; test_orbit = s.get_fixed_orbit(1);\nNow, test_orbit contains all points in the orbit of 4. Note that the information is readily available in the stored structure.\nHowever, if we are interested in the entire orbit partition at a certain point, more information has to be computed. This can be done with the following method: orbit orbits(n); rschreier.get_stabilizer_orbit(1, orbits);\nThis fills the orbits structure with an orbit partition. Note that the information may be incomplete, in particular in case the Schreier structure is used with an incomplete base.\n","date":"January 1, 1","permalink":"/quick_start/orbit_stabilizer/","section":"Quick Start","summary":"How to deal with orbit stabilizers.","title":"Orbit Stabilizer"},{"content":" All state-of-the-art symmetry detection tools work using essentially the same paradigm, called individualization-refinement. However, within this paradigm, there are many potential design choices. In the following, we want to highlight, at a glance, some of the unique and novel features of dejavu.\nIn this section of the website, you can find more in-depth descriptions of some of the fundamental building blocks of symmetry detection tools.\nNear-Optimal Probabilistic Search # Restarts \u0026amp; Cell Selector Strategy # Preprocessing # Inprocessing # ","date":"January 1, 1","permalink":"/how_it_works/philosophy/","section":"How_it_works","summary":"","title":"Philosophy"},{"content":" The library contains a preprocessor that can be used in conjunction with symmetry detection tools. The dejavu solver always automatically applies the preprocessor, but it may also be used in conjunction with other tools.\nPreprocessor # The preprocessor is designed to shrink large, sparse graphs. Before giving a graph to an off-the-shelf symmetry detection solver (such as bliss, nauty, saucy, Traces), the graph is instead first handed to the preprocessor. The preprocessor shrinks the graph, in turn hopefully speeding up the subsequent solver.\nSome technicalities apply, though: a hook for symmetries must be provided (a dejavu_hook), and symmetries of the reduced graph must be translated back to the original graph. The preprocessor can do the reverse translation automatically, by providing a special hook that is in turn given to the backend solver (see the examples below). The graph format used by the preprocessor is described below as well.\nExample using bliss # #include \u0026#34;bliss/graph.hh\u0026#34; #include \u0026#34;dejavu/preprocessor.h\u0026#34; #include \u0026#34;dejavu/tools/bliss_converter.h\u0026#34; ... dejavu::static_graph g; // graph must be parsed into g here! // lets preprocess... dejavu::preprocessor p; // hook is a dejavu_hook callback function p.reduce(\u0026amp;g, \u0026amp;hook); // ...and then we give the graph to bliss: first, convert the graph bliss::Graph bliss_graph; convert_dejavu_to_bliss(\u0026amp;g, \u0026amp;bliss_graph); // then call bliss bliss::Stats bliss_stat; bliss_graph.find_automorphisms(bliss_stat, dejavu::preprocessor::bliss_hook, (void*) \u0026amp;p); // done! Note that the bliss_hook uses the field p.saved_hook to call the user-defined dejavu_hook (i.e., in the example above hook). This also holds for all other solvers described below.\nExample using nauty # #include \u0026#34;dejavu/preprocessor.h\u0026#34; #include \u0026#34;dejavu/tools/nauty_converter.h\u0026#34; #include \u0026#34;nauty/naugroup.h\u0026#34; ... dejavu::static_graph g; // graph must be parsed into g here! // lets preprocess... dejavu::preprocessor p; // hook is a dejavu_hook callback function p.reduce(\u0026amp;g, \u0026amp;hook); // ...and then we give the graph to nauty: first, convert the graph sparsegraph nauty_graph; DYNALLSTAT(int, lab, lab_sz); DYNALLSTAT(int, ptn, ptn_sz); convert_dejavu_to_nauty(\u0026amp;g, \u0026amp;nauty_graph, \u0026amp;lab, \u0026amp;lab_sz, \u0026amp;ptn, \u0026amp;ptn_sz); // then call nauty statsblk stats; DYNALLSTAT(int, orbits, orbits_sz); DYNALLOC1(int, orbits, orbits_sz, nauty_graph.nv, \u0026#34;malloc\u0026#34;); static DEFAULTOPTIONS_SPARSEGRAPH(options); options.schreier = true; options.defaultptn = false; options.userautomproc = dejavu::preprocessor::nauty_hook; if(nauty_graph.nv \u0026gt; 0) { sparsenauty(\u0026amp;nauty_graph, lab, ptn, orbits, \u0026amp;options, \u0026amp;stats, NULL); } // clean up DYNFREE(lab, lab_sz); DYNFREE(ptn, ptn_sz); SG_FREE(nauty_graph); // done! Note that the nauty_hook uses the static field preprocessor::save_preprocessor to access p again, which in turn accesses p.saved_hook. If multi-threading is used in this configuration, preprocessor::save_preprocessor should be changed to thread_local. This also holds for Traces.\nThe convert_dejavu_to_nauty method allocates memory for the graph, lab and ptn using the respective macros of nauty. Freeing up the memory has to be handled by the user.\nExample using Traces # #include \u0026#34;dejavu/preprocessor.h\u0026#34; #include \u0026#34;dejavu/tools/traces_converter.h\u0026#34; #include \u0026#34;nauty/traces.h\u0026#34; ... dejavu::static_graph g; // graph must be parsed into g here! // lets preprocess... dejavu::preprocessor p; // hook is a dejavu_hook callback function p.reduce(\u0026amp;g, \u0026amp;hook); // ...and then we give the graph to Traces: first, convert the graph sparsegraph traces_graph; DYNALLSTAT(int, lab, lab_sz); DYNALLSTAT(int, ptn, ptn_sz); convert_dejavu_to_traces(\u0026amp;g, \u0026amp;traces_graph, \u0026amp;lab, \u0026amp;lab_sz, \u0026amp;ptn, \u0026amp;ptn_sz); // then call Traces statsblk stats; DYNALLSTAT(int, orbits, orbits_sz); DYNALLOC1(int, orbits, orbits_sz, traces_graph.nv, \u0026#34;malloc\u0026#34;); static DEFAULTOPTIONS_TRACES(options); options.schreier = true; options.defaultptn = false; options.userautomproc = dejavu::preprocessor::traces_hook; if(nauty_graph.nv \u0026gt; 0) { Traces(\u0026amp;traces_graph, lab, ptn, orbits, \u0026amp;options, \u0026amp;stats, NULL); } // clean up DYNFREE(lab, lab_sz); DYNFREE(ptn, ptn_sz); SG_FREE(traces_graph); // done! The convert_dejavu_to_traces method allocates memory for the graph, lab and ptn using the respective macros of Traces. Freeing up the memory has to be handled by the user.\nExample using saucy # #include \u0026#34;dejavu/preprocessor.h\u0026#34; #include \u0026#34;dejavu/tools/saucy_converter.h\u0026#34; #include \u0026#34;dejavu/saucy.h\u0026#34; ... dejavu::static_graph g; // graph must be parsed into g here! // lets preprocess... dejavu::preprocessor p; // hook is a dejavu_hook callback function p.reduce(\u0026amp;g, \u0026amp;hook); // ...and then we give the graph to saucy: first, convert the graph saucy_graph _saucy_graph; int* colors = nullptr; convert_dejavu_to_saucy(\u0026amp;g, \u0026amp;_saucy_graph, \u0026amp;colors); // then call saucy struct saucy_stats stats; if(g.v_size \u0026gt; 0) { struct saucy *s = saucy_alloc(_saucy_graph.n); saucy_search(s, \u0026amp;_saucy_graph, 0, colors, \u0026amp;dejavu::preprocessor::saucy_hook, \u0026amp;p, \u0026amp;stats); saucy_free(s); } // clean up delete[] colors; delete[] _saucy_graph.edg; delete[] _saucy_graph.adj; // done! I want to mention that I have also seen a saucy version that uses a slightly different graph format. In this version, saucy_graph contains another field colors. In order to translate to this format, we just need to additionally set _saucy_graph.colors = colors, and remove colors from the parameter list of saucy_search: ... saucy_search(s, \u0026amp;_saucy_graph, 0, \u0026amp;dejavu::preprocessor::saucy_hook, \u0026amp;p, \u0026amp;stats); ...\nAgain, the convert_dejavu_to_saucy method allocates memory for the graph and colors, which has to be handled by the user.\n","date":"January 1, 1","permalink":"/quick_start/preprocessor/","section":"Quick Start","summary":"A description use the preprocessor of dejavu in conjunction with other solvers.","title":"Preprocessor"},{"content":"","date":"January 1, 1","permalink":"/quick_start/","section":"Quick Start","summary":"","title":"Quick Start"},{"content":" On this page, we explain how to use the C++ API of dejavu. If you instead want to use dejavu as a standalone solver, there is a quick start guide for this as well.\nIncluding dejavu # First of all, dejavu is a header-only library. You can simply add dejavu to your C++ project by including the respective header file:\n#include \u0026#34;dejavu.h\u0026#34; Note that currently, dejavu requires to be compiled with C++ version 14. By default, dejavu is compiled without assertions. We recommend activating assertions for debugging purposes (by adding the definition DEJDEBUG). Assertions do however slow the code considerably.\nIn the following, we explain the most important basics on how to use the library. We describe elementary data structures, graphs and symmetries, necessary to communicate with dejavu.\nGraphs # Let us first describe how graphs are represented in dejavu. The most convenient way to pass a graph is to use the static_graph class. We explain the class by constructing a simple example: a path of length 3.\nWe now reproduce the graph using code. First, note that the graph consists of 4 vertices and 3 edges. There are two vertices of degree 1 (the ends of the path), and two vertices of degree 2 (inner nodes of the path). The code to produce this graph is as follows: dejavu::static_graph g; g.initialize_graph(4, 3); // 4 vertices, 3 edges // add vertices const int v0 = g.add_vertex(0, 1); // = 0 const int v1 = g.add_vertex(0, 2); // = 1 const int v2 = g.add_vertex(0, 2); // = 2 const int v3 = g.add_vertex(0, 1); // = 3 // add edges g.add_edge(v0, v1); g.add_edge(v1, v2); g.add_edge(v2, v3);\nWe first initialize the graph with 4 vertices and 3 edges. Note that the static_graph class requires us to immediately pass the final number of vertices and edges (hence, \u0026ldquo;static\u0026rdquo;). Then, we add vertices with their corresponding degree. Again, we need to give the final degree of each vertex. Finally, we add the edges connecting the previously defined nodes as desired.\nTo be more precise, the function add_vertex(int color, int degree) adds a vertex to the graph, where the first parameter is the \u0026ldquo;color\u0026rdquo; of the vertex, and the second parameter the degree in the graph (the number of edges connected to the vertex). The meaning of vertex colors is that vertices of different colors can not be mapped to each other using any symmetry. A more thorough explanation can be found here.\nSymmetries # The goal of dejavu is to compute the symmetries of a given graph. Hence, we need a way of retrieving the symmetries from the solver. We have a guide describing what symmetries are, and how they can be encoded in a computer. Generally, the solver returns a generating set, which is also explained in the guide mentioned above.\nOn the level of the API, symmetries (that together form a generating set) are returned using a hook function. More specifically, the user provides a hook of type dejavu_hook, which is in turn called for every symmetry returned by the solver. The definition for a dejavu_hook is as follows:\ntypedef const std::function\u0026lt;void(int, const int *, int, const int *)\u0026gt; dejavu_hook; Note that a hook has four parameters, int n, const int* p, int nsupp, const int* supp. The meaning is as follows. The integer n gives the size of the domain of the symmetry, or in simple terms, the number of vertices of the graph. The array p is an array of length n. The described symmetry maps i to p[i].\nCrucially, nsupp and supp tell us which i\u0026rsquo;s are interesting at all: whenever p[i] = i, we do not want to iterate over i. To enable this, the array supp tells us all the points where p[i] != i. In particular, supp[j] for 0 \u0026lt;= j \u0026lt; nsupp gives us the j-th vertex where p[supp[j]] != supp[j]. Note that nsupp gives the size of supp. In many applications, reading symmetries in this manner is crucial for adequate performance.\nAn example is provided below:\nvoid my_hook(int n, const int *p, int nsupp, const int *supp) { for(int j = 0; j \u0026lt; nsupp; ++j) { const int i = supp[j]; // do something with p[i] } } We want to mention that in general, dejavu might return redundant or even equivalent symmetries.\nCalling dejavu # Having constructed the graph above, and ready to receive symmetries using our hook, we now want to call dejavu to compute the symmetries. This is done through the use of the class solver and method automorphisms:\ndejavu::solver d; d.automorphisms(\u0026amp;g, std::function\u0026lt;dejavu_hook\u0026gt;(my_hook)); It should be mentioned that it is also possible to call automorphisms without a hook:\ndejavu::solver d; d.automorphisms(\u0026amp;g); After a call to automorphisms has ended, further statistics can be retrieved. For example, the size of the computed automorphism group can be retrieved and printed to console as follows:\nstd::cout \u0026lt;\u0026lt; d.get_automorphism_group_size() \u0026lt;\u0026lt; std::endl; The automorphism computation may be prone to error: due to randomization, dejavu might miss out on some symmetries in a given run (but it will never report generators that are not symmetries). See our discussion on errors for more information. However, the solver is sometimes sure that it found the entire automorphism group. This can be retrieved using as follows:\nstd::cout \u0026lt;\u0026lt; d.get_deterministic_termination() \u0026lt;\u0026lt; std::endl; Example # A full example can be found below. In the example, we produce a path of length 4, pass it to dejavu, and print out all the symmetries returned by dejavu through the use of the hook function print_hook.\n#include \u0026#34;dejavu.h\u0026#34; void print_hook(int n, const int *p, int nsupp, const int *supp) { std::cout \u0026lt;\u0026lt; \u0026#34;We found a symmetry: \u0026#34;; for(int j = 0; j \u0026lt; nsupp; ++j) { const int i = supp[j]; // do something with p[i] std::cout \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34;-\u0026gt;\u0026#34; \u0026lt;\u0026lt; p[i] \u0026lt;\u0026lt; \u0026#34;, \u0026#34;; } std::cout \u0026lt;\u0026lt; std::endl; } int main(int argc, char *argv[]) { dejavu::static_graph g; g.initialize_graph(4, 3); // 3 vertices, 2 edges // add vertices const int v0 = g.add_vertex(0, 1); // = 0 const int v1 = g.add_vertex(0, 2); // = 1 const int v2 = g.add_vertex(0, 2); // = 2 const int v3 = g.add_vertex(0, 1); // = 3 // add edges g.add_edge(v0, v1); g.add_edge(v1, v2); g.add_edge(v2, v3); dejavu::solver d; auto hook = dejavu_hook(print_hook); d.automorphisms(\u0026amp;g, \u0026amp;hook); std::cout \u0026lt;\u0026lt; \u0026#34;#syms \u0026#34; \u0026lt;\u0026lt; d.get_automorphism_group_size() \u0026lt;\u0026lt; std::endl; return 0; } Vertex IDs # The function add_vertex guarantees to return vertex IDs starting from 0 going up to the number of vertices. Hence, in our example above, there is no need to track vertex IDs, and the example could be simplified as follows:\n#include \u0026#34;dejavu.h\u0026#34; void print_hook(int n, const int *p, int nsupp, const int *supp) { std::cout \u0026lt;\u0026lt; \u0026#34;We found a symmetry: \u0026#34;; for(int j = 0; j \u0026lt; nsupp; ++j) { const int i = supp[j]; // do something with p[i] std::cout \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34;-\u0026gt;\u0026#34; \u0026lt;\u0026lt; p[i] \u0026lt;\u0026lt; \u0026#34;, \u0026#34;; } std::cout \u0026lt;\u0026lt; std::endl; } int main(int argc, char *argv[]) { dejavu::static_graph g; g.initialize_graph(4, 3); // 3 vertices, 2 edges // add vertices g.add_vertex(0, 1); g.add_vertex(0, 2); g.add_vertex(0, 2); g.add_vertex(0, 1); // add edges g.add_edge(0, 1); g.add_edge(1, 2); g.add_edge(2, 3); dejavu::solver d; auto hook = dejavu_hook(print_hook); d.automorphisms(\u0026amp;g, \u0026amp;hook); std::cout \u0026lt;\u0026lt; \u0026#34;#syms \u0026#34; \u0026lt;\u0026lt; d.get_automorphism_group_size() \u0026lt;\u0026lt; std::endl; return 0; } ","date":"January 1, 1","permalink":"/quick_start/cpp_api/","section":"Quick Start","summary":"How to use the C++ API of dejavu.","title":"Quick Start - C++ API"},{"content":" On this page, you can find a description on how to compile and use dejavu as a command-line program. If you instead want to use the C++ API, there is a quick start guide for this as well.\nCompilation # Using cmake, the project should compile without any further dependencies:\ncmake . make Compilation produces a binary dejavu.\nUsage # The binary dejavu accepts files in the DIMACS graph format (see paragraph below). The program then computes the symmetry group of the graph, with several different output options (such as a generating set or group size) Only undirected graphs can be handled \u0026ndash; but the graphs may contain vertex colors. An example call solving the graph pp-25-100 might be:\ndejavu pp-25-100 The output will end in two lines describing the result as follows:\nsymmetries=1.0000*10^4, deterministic=false, error=1/2^10, solve_time=1879.5741ms Here, symmetries tells us the number of found symmetries, deterministc whether the solver terminated without error, and in case it did not error tells us the configured error probability assuming uniform random numbers.\nOptions are available to configure the solver, as is listed below:\nCommand Line Argument Effect --err [n] sets the error to be bounded by 1/2^N, assuming uniform random numbers --silent does not print progress of the solver --gens prints found generators line-by-line to console --gens-file [f] writes found generators line-by-line to file F --grp-sz prints group size to console (even if \u0026ndash;silent) --pseudo-random uses pseudo random numbers (default) --true-random uses random device of OS --true-random-seed seeds pseudo random with random device of OS --permute randomly permutes the given graph --permute-seed [n] seed for the previous option with N For example, if we want to output just the found generators of a graph, we may call the solver as follows:\ndejavu k-10 --silent --gens This gives us the following output, where each line describes a generator of the graph:\n(7 8) (6 7) (5 6) (4 5) (3 4) (2 3) (1 2) (0 1) (9 0) DIMACS graph files # Let us explain DIMACS files with a simple example: a path of length 3:\nA DIMACS graph file starts with a line describing how many vertices and edges a graph contains. In our case, this is 4 vertices and 3 edges:\np edge 4 3 Then, we can add lines each describing an (undirected) edge of the graph. Note that each edge is only added once, i.e., we only define either e 1 2 or e 2 1, but not both.\np edge 4 3 e 1 2 e 2 3 e 3 4 Additionally, we may color vertices as follows. A vertex v with color c can be defined using a line n v c.\n","date":"January 1, 1","permalink":"/quick_start/standalone/","section":"Quick Start","summary":"A description on how to compile and use dejavu as a standalone program.","title":"Quick Start - Standalone"},{"content":"","date":"January 1, 1","permalink":"/related/","section":"Related Software","summary":"","title":"Related Software"},{"content":" Projects using dejavu # Name Description satsuma Static symmetry breaking for Boolean satisfiability (SAT) testing SCIP Solver for mixed integer programming (MIP) and mixed integer nonlinear programming (MINLP). ⭐ Are you using dejavu in your project? I\u0026rsquo;d love to know! Feel free to reach out to me at markus (at) automorphisms.org.\nOther symmetry detection and canonical labeling tools # Name Description Traces Automorphism group \u0026amp; canonical labelling tool by Adolfo Piperno. Features advanced search strategies, most likely the all-round fastest deterministic solver. nauty Automorphism group \u0026amp; canonical labelling tool by Brendan McKay. Highly customizable, excells on small graphs. bliss Automorphism group \u0026amp; canonical labelling tool by Tommi Junttila and Petteri Kaski. Features an easy-to-use, convenient C++ API. saucy Automorphism group tool. Broke ground regarding sparse, practical graphs. ","date":"January 1, 1","permalink":"/related/related/","section":"Related Software","summary":"A list of software related to dejavu.","title":"Related Software"},{"content":"","date":"January 1, 1","permalink":"/series/","section":"Series","summary":"","title":"Series"},{"content":" Symmetries are what this library is all about. Here, we describe some of the basic concepts surrounding symmetries on graphs.\nWhat are symmetries? # Symmetries (AKA automorphisms) are bijections on the vertices of graphs, mapping the graph back to itself. In other words, a symmetry is a mapping of vertices that preserves the connections between vertices. If we apply a symmetry to a graph, the resulting graph will have the same structure as the original one.\nAs you can see above, the resulting graph after applying a symmetry is always the same as the original graph. Formally, given a graph \\(G = (V, E)\\), a bijection \\(\\varphi : V \\to V\\) is a symmetry of \\(G\\) if and only if it satisfies \\(\\varphi((V, E)) = (V, E)\\). The set of all symmetries is a permutation group under the composition operation. We call this group the automorphism group of G, denoted as \\(Aut(G)\\). In particular, this means that the set of symmetries of a graph is closed under composition.\nLet us look more closely at a symmetry of a graph.\nOne potential symmetry of the above graph interchanges vertices 3 and 4, while leaving the rest of the graph unchanged. In order to keep things brief, in the following, we use the *cycle notation* for symmetries. Using cycle notation, a symmetry is expressed as a product of disjoint cycles. A cycle is simply a sequence of elements that are cyclically permuted. To keep things brief, we always omit cycles of length 1, as these vertices are simply fixed (mapped to themselves) in the permutation.\nLet\u0026rsquo;s break down the notation and illustrate it with an example. In cycle notation, the symmetry above is written as \\(( 3 \\; 4 )\\). Vertex 3 goes to 4, and 4 goes back to 3. All other vertices (0, 1, 2, 5, 6) are fixed and thus omitted.\nStoring Symmetry # Writing out all the symmetries for a graph is not only tedious, but also very inefficient. There can be an exponential amount of symmetries for a given graph. However, there is always a way to write down the symmetries in a compact way using the power of generating sets.\nA generating set, is a subset of the symmetries that, through the use of exhaustively composing the symmetries of the set, will result in the entire group. For the example graph above, a potential generating set is \\( S = \\{( 3 \\; 4 ), ( 1 \\; 2 )( 4 \\; 6 )( 3 \\; 5 )\\}\\). We denote that it generates the entire group with \\( \\langle S \\rangle = Aut(G) \\).\nOn the technical side, storing and working with symmetries can be conveniently achieved using the classes automorphism_workspace and stored_automorphism.\n// let\u0026#39;s say we stored an automorphism here dejavu::groups::stored_automorphism automorphism; // let\u0026#39;s say our graph has n vertices dejavu::groups::automorphism_workspace workspace(n); // load the stored automorphism into the workspace workspace.load(aw); // now, we can conveniently access and manipulate the automorphism std::cout \u0026lt;\u0026lt; \u0026#34;5 maps to \u0026#34; \u0026lt;\u0026lt; workspace[5] \u0026lt;\u0026lt; std::endl; Loading the stored automorphism \\(\\varphi\\) in particular runs in time \\( \\mathcal{O}(\\text{supp}(\\varphi)) \\). The workspace contains several features to efficiently access and manipulate automorphisms (see the documentation).\nOrbits # Let us now describe the concept of orbits. The concept of orbits helps us address the question of whether two vertices in a graph can be interchanged by a symmetry.\nAn orbit is a partition of the vertices of a graph. Two vertices are in the same orbit, if and only if one can be reached from the other by applying symmetries of the graph. (If two vertices belong to different orbits, there is no symmetry that can swap them.) We can illustrate the orbit partition through coloring a graph, where each color represents an orbit.\nIn the above example, the red vertices form an orbit, the orange ones, and the blue ones. Formally, the orbits are \\( \\Delta(G) := \\{\\{0\\}, \\{1,2\\}, \\{3,4,5,6\\} \\} \\).\nIn the library, it is easy to compute the orbits of a graph. The class orbit can represent the orbit partition of a graph. It contains methods to inspect and manipulate an orbit partition.\n// let\u0026#39;s say we have a graph with n vertices and m edges dejavu::static_graph g; g.initialize_graph(n, m); // (insert definition of graph here...) // initialize an orbit structure with number of vertices dejavu::groups::orbit orbits(n); // when computing automorphisms, we can feed them directly into the // orbit partition with an orbit_hook dejavu::hooks::orbit_hook hook(orbits); dejavu::solver d; d.automorphisms(\u0026amp;g, hook.get_hook()); // orbits now contains the orbit partition for found automorphisms of g We can then access the orbit partition by, for example, calling orbits.are_in_same_orbit(v1, v2), which tells us whether v1 and v2 are in the same orbit. The class also contains methods to give a canonical representative for an orbit, retrieve the orbit size, and more. For a more thorough technical description, please see the documentation.\n","date":"January 1, 1","permalink":"/quick_start/symmetry/","section":"Quick Start","summary":"Symmetries are what this library is all about. Here, we describe some of the basic concepts surrounding symmetries on graphs.","title":"Symmetry"},{"content":"","date":"January 1, 1","permalink":"/tags/","section":"Tags","summary":"","title":"Tags"},{"content":"","date":"January 1, 1","permalink":"/topics/","section":"Topics","summary":"","title":"Topics"}]