├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── pages.yml ├── Gemfile ├── known-methods.md ├── .gitignore ├── standard-library ├── svg.md ├── indices.md ├── bipartite.md ├── graphml.md ├── index.md ├── canvas.md ├── assertions.md ├── components.md ├── layout-noverlap.md ├── simple-path.md ├── layout-force.md ├── traversal.md ├── gexf.md ├── dag.md ├── layout-forceatlas2.md ├── cores.md ├── communities-louvain.md ├── shortest-path.md ├── operators.md ├── utils.md ├── layout.md ├── generators.md └── metrics.md ├── _config.yml ├── _local_config.yml ├── _libs.json ├── implementing-graphology.md ├── utilities.md ├── properties.md ├── Gemfile.lock ├── serialization.md ├── instantiation.md ├── performance-tips.md ├── index.md ├── events.md ├── read.md ├── design-choices.md └── mutation.md /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: / 5 | schedule: 6 | interval: daily 7 | allow: 8 | - dependency-type: direct 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem "jekyll", "~> 4.3.4" # installed by `gem jekyll` 4 | # gem "webrick" # required when using Ruby >= 3 and Jekyll <= 4.2.2 5 | 6 | gem "just-the-docs", "0.10.0" # pinned to the current release 7 | # gem "just-the-docs" # always download the latest release 8 | -------------------------------------------------------------------------------- /known-methods.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Known methods 4 | nav_order: 14 5 | menu_toc: 6 | - "#.toJSON" 7 | - "#.inspect" 8 | --- 9 | 10 | # Known methods 11 | 12 | ## #.toJSON 13 | 14 | Alias of the [`#.export`](#export) method used by JavaScript to serialize the `Graph` instance when using `JSON.stringify`. 15 | 16 | ```js 17 | JSON.stringify(graph); 18 | ``` 19 | 20 | ## #.inspect 21 | 22 | Used by node.js to pretty print the object when using `console.log`. 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Not sure what a .gitignore is? 2 | # See: https://git-scm.com/docs/gitignore 3 | 4 | # These are directly copied from Jekyll's first-party docs on `.gitignore` files: 5 | # https://jekyllrb.com/tutorials/using-jekyll-with-bundler/#commit-to-source-control 6 | 7 | # Ignore the default location of the built site, and caches and metadata generated by Jekyll 8 | _site/ 9 | .sass-cache/ 10 | .jekyll-cache/ 11 | .jekyll-metadata 12 | 13 | # Ignore folders generated by Bundler 14 | .bundle/ 15 | vendor/ 16 | -------------------------------------------------------------------------------- /standard-library/svg.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: svg 4 | nav_order: 19 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/svg" 8 | 9 | --- 10 | 11 | # Graphology SVG 12 | 13 | SVG rendering routines for [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-svg 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```js 24 | var render = require('graphology-svg'); 25 | 26 | render(graph, './graph.svg', () => console.log('Done!')); 27 | render(graph, './graph.svg', settings, () => console.log('Done!')); 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | pull_request: 7 | 8 | jobs: 9 | # Build job 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | - name: Setup Ruby 16 | uses: ruby/setup-ruby@v1 17 | with: 18 | ruby-version: '3.3' # Not needed with a .ruby-version file 19 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 20 | cache-version: 0 # Increment this number if you need to re-download cached gems 21 | - name: Build with Jekyll 22 | run: bundle exec jekyll build 23 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: Graphology 2 | theme: just-the-docs 3 | color_scheme: 'light' 4 | search_enabled: true 5 | aux_links: 6 | GitHub Repository: https://github.com/graphology/graphology 7 | aux_links_new_tab: false 8 | 9 | # Footer last edited timestamp 10 | last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter 11 | last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html 12 | 13 | # Footer "Edit this page on GitHub" link text 14 | gh_edit_link: true # show or hide edit this page link 15 | gh_edit_link_text: "Edit this page on GitHub." 16 | gh_edit_repository: "https://github.com/graphology/graphology.github.io" # the github URL for your repo 17 | gh_edit_branch: "main" # the branch that your docs is served from 18 | # gh_edit_source: docs # the source that your files originate from 19 | gh_edit_view_mode: "edit" # "tree" or "edit" if you want the user to jump into the editor immediate 20 | -------------------------------------------------------------------------------- /_local_config.yml: -------------------------------------------------------------------------------- 1 | title: Graphology 2 | theme: just-the-docs 3 | color_scheme: 'light' 4 | search_enabled: true 5 | aux_links: 6 | GitHub Repository: https://github.com/graphology/graphology 7 | aux_links_new_tab: false 8 | 9 | # Footer last edited timestamp 10 | last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter 11 | last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html 12 | 13 | # Footer "Edit this page on GitHub" link text 14 | gh_edit_link: true # show or hide edit this page link 15 | gh_edit_link_text: "Edit this page on GitHub." 16 | gh_edit_repository: "https://github.com/graphology/graphology.github.io" # the github URL for your repo 17 | gh_edit_branch: "main" # the branch that your docs is served from 18 | # gh_edit_source: docs # the source that your files originate from 19 | gh_edit_view_mode: "edit" # "tree" or "edit" if you want the user to jump into the editor immediate 20 | -------------------------------------------------------------------------------- /standard-library/indices.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: indices 4 | nav_order: 10 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/indices" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/indices/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Indices 12 | 13 | Miscellaneous indices to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-indices 19 | ``` 20 | 21 | ## Contents 22 | 23 | This library contains multiple low-level indexation structures used to optimize graph computations in other `graphology` libraries. This library is not meant to be used as such and this is why it is not thoroughly documented. 24 | 25 | For now, here are the exposed indices: 26 | 27 | - An unweighted and weighted neighborhood index used to speed up computations requiring many successive BSTs in a graph. 28 | - A directed and undirected index used to track an evolving community structure when running the Louvain community detection algorithm. 29 | - An indexed view of a graph's connected components sorted by order. 30 | - A specialized stack/set that can be used to perform memory-efficient DFS traversals. 31 | - A specialized queue/set that can be used to perform memory-efficient BFS traversals. 32 | 33 | -------------------------------------------------------------------------------- /standard-library/bipartite.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: bipartite 4 | nav_order: 1 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/bipartite" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/bipartite/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Bipartite 12 | 13 | Functions related to bipartite graphs and to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-bipartite 19 | ``` 20 | 21 | ## Usage 22 | 23 | - [isBypartiteBy](#isbypartiteby) 24 | 25 | ### isBypartiteBy 26 | 27 | Returns whether the given graph is bipartite according to the given partition scheme. 28 | 29 | ```js 30 | import {isBypartiteBy} from 'graphology-bipartite'; 31 | // Alternatively, to load only the relevant code: 32 | import isBypartiteBy from 'graphology-bipartite/is-bipartite-by'; 33 | 34 | // Wrt some node attribute: 35 | isBipartiteBy(graph, 'category'); 36 | 37 | // Using some getter function: 38 | isBipartiteBy(graph, (node, attr) => externalIndex[node].category); 39 | ``` 40 | 41 | _Arguments_ 42 | 43 | - **graph** _Graph_: target graph. 44 | - **getNodePartition** _string\|function_: node attribute name or getter function taking a node entry (node, attributes) and returning this node's partition. 45 | 46 | -------------------------------------------------------------------------------- /_libs.json: -------------------------------------------------------------------------------- 1 | { 2 | "assertions": "Miscellaneous assertions (same nodes, same edges etc.).", 3 | "bipartite": "Bipartite graph helper functions (coloring, projection etc.).", 4 | "canvas": "Canvas rendering routines for graphs.", 5 | "communities-louvain": "Louvain method for community detection.", 6 | "components": "Connected components (strong, weak etc.).", 7 | "dag": "Functions related to directed acyclic graphs (cycle detection, topological sorting etc.).", 8 | "cores": "Various utilities related to k-cores.", 9 | "generators": "Graph generators (random graphs, complete graphs etc.).", 10 | "gexf": "Parsers & writers for the GEXF file format.", 11 | "graphml": "Parsers & writers for the GRAPHML file format.", 12 | "indices": "Various specialized graph indices (neighborhood, louvain etc.)", 13 | "layout": "Basic graph layouts (random, circle etc.).", 14 | "layout-force": "Basic force layout algorithm.", 15 | "layout-forceatlas2": "ForceAtlas2 layout algorithm.", 16 | "layout-noverlap": "Noverlap anti-collision layout algorithm.", 17 | "metrics": "Modularity, density, centrality etc.", 18 | "operators": "Graph unary, binary & cast operators (reverse, union, intersection, conversion etc.)", 19 | "shortest-path": "Shortest path functions (Dijkstra, A\\* etc.)", 20 | "simple-path": "Simple path related functions (e.g. all paths between source & target)", 21 | "svg": "SVG export for graphs.", 22 | "traversal": "Traversal functions (DFS, BFS, etc.)", 23 | "utils": "Miscellaneous utils used by most of the other modules." 24 | } 25 | -------------------------------------------------------------------------------- /standard-library/graphml.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: graphml 4 | nav_order: 9 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/graphml" 8 | 9 | --- 10 | 11 | # Graphology GRAPHML Utilities 12 | 13 | GRAPHML parser & writer for [`graphology`](..). 14 | 15 | For more information about the GRAPHML file format, you can head [there](http://graphml.graphdrawing.org/). 16 | 17 | ## Installation 18 | 19 | ``` 20 | npm install graphology-graphml 21 | ``` 22 | 23 | ## Usage 24 | 25 | - [Parser](#parser) 26 | 27 | ### Parser 28 | 29 | The parser must be passed a `graphology` constructor and is able to read either a string, or an `XMLDocument` instance. 30 | 31 | ```js 32 | var Graph = require('graphology'); 33 | 34 | // Node 35 | var graphml = require('graphology-graphml'); 36 | // Browser 37 | var graphml = require('graphology-graphml/browser'); 38 | 39 | // Reading a string 40 | var graph = graphml.parse(Graph, string); 41 | 42 | // Reading a dom document 43 | var graph = graphml.parse(Graph, xmlDocument); 44 | 45 | // Passing options 46 | var graph = graphml.parse(Graph, string, {addMissingNodes: true}); 47 | ``` 48 | 49 | _Arguments_ 50 | 51 | - **constructor** _GraphClass_: graphology constructor to use. 52 | - **source** _string\|Document_: source data to parse. 53 | - **options** ?object: parsing options: 54 | - **addMissingNodes** ?boolean false: whether to add missing nodes referenced in the file's edges. 55 | 56 | -------------------------------------------------------------------------------- /implementing-graphology.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Implementing graphology 4 | nav_order: 15 5 | --- 6 | 7 | # Implementing graphology 8 | 9 | Although the `graphology` npm package proposes its reference implementation, `graphology`is actually only a specification for a JavaScript/TypeScript `Graph` that anyone remains free to implement however they like. 10 | 11 | What's more, any custom implementation of `graphology` remains completely able to use all of the [standard library](./standard-library) and type declarations, without any additional complications. 12 | 13 | Graphs are complex structures and, while we designed the reference implementation to handle most common cases with good performance, there is no silver bullet and one will always be able to implement the present specifications in a more performant fashion for very specific use cases. 14 | 15 | If you wish to implement `graphology` on your own and want to be sure you are doing it correctly, know that it is possible to test your own implementation against the lib's unit tests. 16 | 17 | ## Running the unit tests 18 | 19 | First, you need to install `graphology` and `mocha` from npm: 20 | 21 | ``` 22 | npm install --save-dev graphology mocha 23 | ``` 24 | 25 | Then you need to create a file that will be run by `mocha`: 26 | 27 | ```js 28 | // test.js 29 | import specs from 'graphology/specs'; 30 | import Graph from 'my-custom-graphology-implementation'; 31 | 32 | module.exports = specs(Graph, Graph); 33 | ``` 34 | 35 | *Arguments* 36 | 37 | * **class** function: the Graph class of your implementation. 38 | * **implementation** object: an object containing the rest of the implementation (alternative constructors, errors). Note that most of the time, or at least if you export your implementation through CommonJS, the `class` and `implementation` arguments will be the same. 39 | 40 | Then, run the tests using `mocha`: 41 | 42 | ``` 43 | mocha -u exports test.js 44 | ``` 45 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 7 | name: Deploy Jekyll site to Pages 8 | 9 | on: 10 | push: 11 | branches: ["master"] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 17 | permissions: 18 | contents: read 19 | pages: write 20 | id-token: write 21 | 22 | # Allow one concurrent deployment 23 | concurrency: 24 | group: "pages" 25 | cancel-in-progress: true 26 | 27 | jobs: 28 | # Build job 29 | build: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Setup Ruby 35 | uses: ruby/setup-ruby@v1 36 | with: 37 | ruby-version: '3.3' # Not needed with a .ruby-version file 38 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 39 | cache-version: 0 # Increment this number if you need to re-download cached gems 40 | - name: Setup Pages 41 | id: pages 42 | uses: actions/configure-pages@v5 43 | - name: Build with Jekyll 44 | # Outputs to the './_site' directory by default 45 | run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" 46 | env: 47 | JEKYLL_ENV: production 48 | - name: Upload artifact 49 | # Automatically uploads an artifact from the './_site' directory by default 50 | uses: actions/upload-pages-artifact@v3 51 | 52 | # Deployment job 53 | deploy: 54 | environment: 55 | name: github-pages 56 | url: ${{ steps.deployment.outputs.page_url }} 57 | runs-on: ubuntu-latest 58 | needs: build 59 | steps: 60 | - name: Deploy to GitHub Pages 61 | id: deployment 62 | uses: actions/deploy-pages@v4 63 | -------------------------------------------------------------------------------- /utilities.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Utilities 4 | nav_order: 13 5 | menu_toc: 6 | - "#.copy" 7 | - "#.nullCopy" 8 | - "#.emptyCopy" 9 | --- 10 | 11 | # Utilities 12 | 13 | ## #.copy 14 | 15 | Returns a copy of the current instance. 16 | 17 | *Example* 18 | 19 | ```js 20 | graph.mergeEdgeWithKey('T->E', 'Thomas', 'Eric', {type: 'KNOWS'}); 21 | 22 | const newGraph = graph.copy(); 23 | newGraph.hasNode('Eric'); 24 | >>> true 25 | newGraph.order 26 | >>> 2 27 | newGraph.size 28 | >>> 1 29 | graph.type === newGraph.type 30 | >>> true 31 | ``` 32 | 33 | *Arguments* 34 | 35 | * **options** [object]: options to merge to create a slightly different graph. Note that those options will be validated to ensure that created graph is an "upgrade" and so no information can be lost. For instance, a mixed graph can be created from a directed one, but the reverse is not true. 36 | 37 | ## #.nullCopy 38 | 39 | Returns a null copy, i.e. a copy of the graph without nodes nor edges, of the current instance while retaining the type & the options of the graph. 40 | 41 | *Example* 42 | 43 | ```js 44 | graph.mergeEdgeWithKey('T->E', 'Thomas', 'Eric', {type: 'KNOWS'}); 45 | 46 | const newGraph = graph.nullCopy(); 47 | newGraph.hasNode('Eric'); 48 | >>> false 49 | newGraph.order 50 | >>> 0 51 | newGraph.size 52 | >>> 0 53 | graph.type === newGraph.type 54 | >>> true 55 | ``` 56 | 57 | *Arguments* 58 | 59 | * **options** [object]: options to merge to create a graph with different characteristics. 60 | 61 | ## #.emptyCopy 62 | 63 | Returns an empty copy, i.e. a copy of the graph containing only nodes, of the current instance while retaining the type & the options of the graph. 64 | 65 | This is useful to functions needing to return subgraphs or near identical copies of a graph such as reversed graph or graph converted to another type altogether. 66 | 67 | *Example* 68 | 69 | ```js 70 | graph.mergeEdgeWithKey('T->E', 'Thomas', 'Eric', {type: 'KNOWS'}); 71 | 72 | const newGraph = graph.emptyCopy(); 73 | newGraph.hasNode('Eric'); 74 | >>> true 75 | newGraph.order 76 | >>> 2 77 | newGraph.size 78 | >>> 0 79 | newGraph.hasEdge('Thomas', 'Eric'); 80 | >>> false 81 | graph.type === newGraph.type 82 | >>> true 83 | ``` 84 | 85 | *Arguments* 86 | 87 | * **options** [object]: options to merge to create a graph with different characteristics. 88 | -------------------------------------------------------------------------------- /properties.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Properties 4 | nav_order: 4 5 | menu_toc: 6 | - "#.order" 7 | - "#.size" 8 | - "#.type" 9 | - "#.multi" 10 | - "#.allowSelfLoops" 11 | - "#.selfLoopCount" 12 | - "#.implementation" 13 | --- 14 | 15 | # Properties 16 | 17 | All the following properties are read-only and may not be changed by the user. 18 | 19 | ## #.order 20 | 21 | Number of nodes in the graph. 22 | 23 | *Example* 24 | 25 | ```js 26 | const graph = new Graph(); 27 | 28 | graph.addNode('John'); 29 | graph.addNode('Jack'); 30 | 31 | graph.order; 32 | >>> 2 33 | ``` 34 | 35 | ## #.size 36 | 37 | Number of edges in the graph. 38 | 39 | *Example* 40 | 41 | ```js 42 | const graph = new Graph(); 43 | 44 | graph.addNode('John'); 45 | graph.addNode('Jack'); 46 | 47 | graph.addEdge('John', 'Jack'); 48 | 49 | graph.size; 50 | >>> 1 51 | ``` 52 | 53 | *Variants* 54 | 55 | * `#.directedSize` 56 | * `#.undirectedSize` 57 | 58 | ## #.type 59 | 60 | Type of the graph. One of `mixed`, `directed` or `undirected`. 61 | 62 | *Example* 63 | 64 | ```js 65 | const graph = new Graph(); 66 | graph.type; 67 | >>> 'mixed' 68 | 69 | const directedGraph = new DirectedGraph(); 70 | directedGraph.type; 71 | >>> 'directed' 72 | ``` 73 | 74 | ## #.multi 75 | 76 | Whether the graph accepts parallel edges or not. 77 | 78 | *Example* 79 | 80 | ```js 81 | const graph = new Graph(); 82 | graph.multi; 83 | >>> false 84 | 85 | const multiGraph = new MultiGraph(); 86 | multiGraph.multi; 87 | >>> true 88 | ``` 89 | 90 | ## #.allowSelfLoops 91 | 92 | Whether the graph accepts self loops or not. 93 | 94 | ```js 95 | const graph = new Graph(); 96 | graph.allowSelfLoops; 97 | >>> true 98 | 99 | const otherGraph = new Graph({allowSelfLoops: false}); 100 | graph.allowSelfLoops; 101 | >>> false 102 | ``` 103 | 104 | ## #.selfLoopCount 105 | 106 | The total number of self loop included in the graph. 107 | 108 | ```js 109 | const graph = new Graph(); 110 | 111 | graph.selfLoopCount 112 | >>> 0 113 | 114 | graph.mergeEdge('John', 'John'); 115 | 116 | graph.selfLoopCount 117 | >>> 1 118 | ``` 119 | 120 | *Variants* 121 | 122 | * `#.directedSelfLoopCount` 123 | * `#.undirectedSelfLoopCount` 124 | 125 | ## #.implementation 126 | 127 | A string containing the graph instance's implementation name. 128 | 129 | ```js 130 | import Graph from 'graphology'; 131 | 132 | const graph = new Graph(); 133 | graph.implementation; 134 | >>> 'graphology' 135 | ``` 136 | 137 | It can be useful if you ever need to optimize libraries based upon the knowledge that the given instance is from a specific implementation. 138 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.7) 5 | public_suffix (>= 2.0.2, < 7.0) 6 | bigdecimal (3.1.8) 7 | colorator (1.1.0) 8 | concurrent-ruby (1.3.4) 9 | em-websocket (0.5.3) 10 | eventmachine (>= 0.12.9) 11 | http_parser.rb (~> 0) 12 | eventmachine (1.2.7) 13 | ffi (1.17.0-arm64-darwin) 14 | ffi (1.17.0-x86_64-darwin) 15 | ffi (1.17.0-x86_64-linux-gnu) 16 | forwardable-extended (2.6.0) 17 | google-protobuf (4.28.1-arm64-darwin) 18 | bigdecimal 19 | rake (>= 13) 20 | google-protobuf (4.28.1-x86_64-darwin) 21 | bigdecimal 22 | rake (>= 13) 23 | google-protobuf (4.28.1-x86_64-linux) 24 | bigdecimal 25 | rake (>= 13) 26 | http_parser.rb (0.8.0) 27 | i18n (1.14.6) 28 | concurrent-ruby (~> 1.0) 29 | jekyll (4.3.4) 30 | addressable (~> 2.4) 31 | colorator (~> 1.0) 32 | em-websocket (~> 0.5) 33 | i18n (~> 1.0) 34 | jekyll-sass-converter (>= 2.0, < 4.0) 35 | jekyll-watch (~> 2.0) 36 | kramdown (~> 2.3, >= 2.3.1) 37 | kramdown-parser-gfm (~> 1.0) 38 | liquid (~> 4.0) 39 | mercenary (>= 0.3.6, < 0.5) 40 | pathutil (~> 0.9) 41 | rouge (>= 3.0, < 5.0) 42 | safe_yaml (~> 1.0) 43 | terminal-table (>= 1.8, < 4.0) 44 | webrick (~> 1.7) 45 | jekyll-include-cache (0.2.1) 46 | jekyll (>= 3.7, < 5.0) 47 | jekyll-sass-converter (3.0.0) 48 | sass-embedded (~> 1.54) 49 | jekyll-seo-tag (2.8.0) 50 | jekyll (>= 3.8, < 5.0) 51 | jekyll-watch (2.2.1) 52 | listen (~> 3.0) 53 | just-the-docs (0.10.0) 54 | jekyll (>= 3.8.5) 55 | jekyll-include-cache 56 | jekyll-seo-tag (>= 2.0) 57 | rake (>= 12.3.1) 58 | kramdown (2.4.0) 59 | rexml 60 | kramdown-parser-gfm (1.1.0) 61 | kramdown (~> 2.0) 62 | liquid (4.0.4) 63 | listen (3.9.0) 64 | rb-fsevent (~> 0.10, >= 0.10.3) 65 | rb-inotify (~> 0.9, >= 0.9.10) 66 | mercenary (0.4.0) 67 | pathutil (0.16.2) 68 | forwardable-extended (~> 2.6) 69 | public_suffix (6.0.1) 70 | rake (13.2.1) 71 | rb-fsevent (0.11.2) 72 | rb-inotify (0.11.1) 73 | ffi (~> 1.0) 74 | rexml (3.3.7) 75 | rouge (4.3.0) 76 | safe_yaml (1.0.5) 77 | sass-embedded (1.78.0-arm64-darwin) 78 | google-protobuf (~> 4.27) 79 | sass-embedded (1.78.0-x86_64-darwin) 80 | google-protobuf (~> 4.27) 81 | sass-embedded (1.78.0-x86_64-linux-gnu) 82 | google-protobuf (~> 4.27) 83 | terminal-table (3.0.2) 84 | unicode-display_width (>= 1.1.1, < 3) 85 | unicode-display_width (2.6.0) 86 | webrick (1.8.1) 87 | 88 | PLATFORMS 89 | arm64-darwin 90 | x86_64-darwin-23 91 | x86_64-linux-gnu 92 | 93 | DEPENDENCIES 94 | jekyll (~> 4.3.4) 95 | just-the-docs (= 0.10.0) 96 | 97 | BUNDLED WITH 98 | 2.5.9 99 | -------------------------------------------------------------------------------- /standard-library/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Standard library 4 | nav_order: 1 5 | has_children: true 6 | has_toc: false 7 | --- 8 | 9 | # Standard library 10 | 11 | * [assertions](./assertions): *Miscellaneous assertions (same nodes, same edges etc.).* 12 | * [bipartite](./bipartite): *Bipartite graph helper functions (coloring, projection etc.).* 13 | * [canvas](./canvas): *Canvas rendering routines for graphs.* 14 | * [communities-louvain](./communities-louvain): *Louvain method for community detection.* 15 | * [components](./components): *Connected components (strong, weak etc.).* 16 | * [dag](./dag): *Functions related to directed acyclic graphs (cycle detection, topological sorting etc.).* 17 | * [cores](./cores): *Various utilities related to k-cores.* 18 | * [generators](./generators): *Graph generators (random graphs, complete graphs etc.).* 19 | * [gexf](./gexf): *Parsers & writers for the GEXF file format.* 20 | * [graphml](./graphml): *Parsers & writers for the GRAPHML file format.* 21 | * [indices](./indices): *Various specialized graph indices (neighborhood, louvain etc.)* 22 | * [layout](./layout): *Basic graph layouts (random, circle etc.).* 23 | * [layout-force](./layout-force): *Basic force layout algorithm.* 24 | * [layout-forceatlas2](./layout-forceatlas2): *ForceAtlas2 layout algorithm.* 25 | * [layout-noverlap](./layout-noverlap): *Noverlap anti-collision layout algorithm.* 26 | * [metrics](./metrics): *Modularity, density, centrality etc.* 27 | * [operators](./operators): *Graph unary, binary & cast operators (reverse, union, intersection, conversion etc.)* 28 | * [shortest-path](./shortest-path): *Shortest path functions (Dijkstra, A\* etc.)* 29 | * [simple-path](./simple-path): *Simple path related functions (e.g. all paths between source & target)* 30 | * [svg](./svg): *SVG export for graphs.* 31 | * [traversal](./traversal): *Traversal functions (DFS, BFS, etc.)* 32 | * [utils](./utils): *Miscellaneous utils used by most of the other modules.* 33 | 34 | ## Interactive rendering 35 | 36 | If what you need is interactive rendering of your graphs, in web applications for instance, 37 | be sure to check out [sigma.js](https://www.sigmajs.org/), a webgl renderer 38 | designed to work with `graphology` and which has been created for such endeavors. 39 | 40 | ## Installation 41 | 42 | Any of the above packages can be installed through npm likewise (just change the name to 43 | the desired package): 44 | 45 | ``` 46 | npm install graphology-metrics 47 | ``` 48 | 49 | For convenience, an aggregated package called `graphology-library` also exists 50 | and depends on all the listed packages at once for convenience (albeit maybe 51 | a little bit more complicated to optimize through tree-shaking). 52 | 53 | You can install it thusly: 54 | 55 | ``` 56 | npm install graphology-library 57 | ``` 58 | 59 | If you do so, here is how to access the required packages: 60 | 61 | ```js 62 | // Importing a sub package 63 | import * as metrics from 'graphology-library/metrics'; 64 | 65 | metrics.density(graph); 66 | 67 | // Importing select parts of the library 68 | import {metrics, layout} from 'graphology-library'; 69 | 70 | // Importing the whole library 71 | import * as lib from 'graphology-library'; 72 | 73 | // Importing the browser-specific library 74 | // (this is important for xml parsers and some layout's webworkers) 75 | import * as lib from 'graphology-library/browser'; 76 | ``` 77 | -------------------------------------------------------------------------------- /standard-library/canvas.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: canvas 4 | nav_order: 2 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/canvas" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/canvas/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Canvas 12 | 13 | Canvas rendering routines for [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-canvas 19 | ``` 20 | 21 | If you need to use this package's functions in node, you will also need to install [`node-canvas`](https://www.npmjs.com/package/canvas) thusly: 22 | 23 | ``` 24 | npm install canvas 25 | ``` 26 | 27 | If you experience any issue when installing the libray check that you have the required dependencies as listed [here](https://www.npmjs.com/package/canvas#compiling). 28 | 29 | ## Usage 30 | 31 | ### Pre-requisites 32 | 33 | Each node's position must be set before rendering a graph. Two attributes called `x` and `y` must therefore be defined for all the graph nodes. [graphology-layout](/standard-library/layout) or [graphology-layout-forceatlas2](/standard-library/layout-forceatlas2), for instance, can be used to give positions to nodes if they don't have one already. 34 | 35 | ### Rendering a graph in an arbitrary canvas context 36 | 37 | ```js 38 | import {render} from 'graphology-canvas'; 39 | 40 | render(graph, context, settings); 41 | ``` 42 | 43 | ### Rendering asynchronously to avoid freezing main thread 44 | 45 | ```js 46 | import {renderAsync} from 'graphology-canvas'; 47 | 48 | renderAsync(graph, context, settings, function () { 49 | console.log('Done!'); 50 | }); 51 | ``` 52 | 53 | ### Rendering a graph to PNG in node 54 | 55 | ```js 56 | import {renderToPNG} from 'graphology-canvas/node'; 57 | 58 | renderToPNG(graph, './graph.png', () => console.log('Done!')); 59 | renderToPNG(graph, './graph.png', settings, () => console.log('Done!')); 60 | ``` 61 | 62 | ### Settings 63 | 64 | - **width** ?number 2048: width of the canvas. Will be the same as `height` if not provided. 65 | - **height** ?number 2048: height of the canvas. Will be the same as `width` if not provided. 66 | - **padding** ?number 20: padding to keep around the drawing. 67 | - **nodes** ?object: node-related settings: 68 | - **defaultColor** ?string #999: default color for nodes. 69 | - **reducer** ?function: reducer fonction for nodes taking the rendering settings, the node key and its attributes and tasked to return rendering info such as `color`, `size` etc. 70 | - **edges** ?object: node-related settings: 71 | - **defaultColor** ?string #ccc: default color for edges. 72 | - **reducer** ?function: reducer fonction for edges taking the rendering settings, the node key and its attributes and tasked to return rendering info such as `color`, `size` etc. 73 | 74 | ### Async Settings 75 | 76 | - **batchSize** ?number 500: number of items to render on canvas on each animation frame, increase or decrease to tweak performance vs. UI availability. 77 | 78 | -------------------------------------------------------------------------------- /standard-library/assertions.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: assertions 4 | nav_order: 0 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/assertions" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/assertions/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Assertions 12 | 13 | Assertions to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-assertions 19 | ``` 20 | 21 | ## Usage 22 | 23 | - [#.isGraph](#isgraph) 24 | - [#.isGraphConstructor](#isgraphconstructor) 25 | - [#.haveSameNodes](#hassamenodes) 26 | - [#.haveSameNodesDeep](#hassamenodesdeep) 27 | - [#.areSameGraphs](#issamegraph) 28 | - [#.areSameGraphsDeep](#issamegraphdeep) 29 | - [#.haveSameEdges](#havesameedges) 30 | - [#.haveSameEdgesDeep](#havesameedgesdeep) 31 | 32 | ### #.isGraph 33 | 34 | Function returning whether the given value is a `graphology` implementation's instance. 35 | 36 | ```js 37 | import {isGraph} from 'graphology-assertions'; 38 | 39 | const graph = new Graph(); 40 | 41 | isGraph(graph); 42 | >>> true 43 | 44 | isGraph(45); 45 | >>> false 46 | 47 | isGraph({hello: 'world'}); 48 | >>> false 49 | ``` 50 | 51 | _Arguments_ 52 | 53 | - **value** _any_: value to test. 54 | 55 | ### #.isGraphConstructor 56 | 57 | Function returning whether the given value is a `graphology` constructor. 58 | 59 | ```js 60 | import {isGraphConstructor} from 'graphology-assertions'; 61 | 62 | isGraphConstructor(Graph); 63 | >>> true 64 | 65 | isGraphConstructor(45); 66 | >>> false 67 | 68 | isGraphConstructor(new Graph()); 69 | >>> false 70 | ``` 71 | 72 | _Arguments_ 73 | 74 | - **value** _any_: value to test. 75 | 76 | ### #.haveSameNodes 77 | 78 | Returns whether two graphs have the same nodes. 79 | 80 | ```js 81 | import {haveSameNodes} from 'graphology-assertions'; 82 | 83 | haveSameNodes(G, H); 84 | ``` 85 | 86 | ### #.haveSameNodesDeep 87 | 88 | Returns whether two graphs have the same nodes & whether those nodes have the same attributes. 89 | 90 | ```js 91 | import {haveSameNodesDeep} from 'graphology-assertions'; 92 | 93 | haveSameNodesDeep(G, H); 94 | ``` 95 | 96 | ### #.areSameGraphs 97 | 98 | Returns whether two graphs are the same. 99 | 100 | ```js 101 | import {areSameGraphs} from 'graphology-assertions'; 102 | 103 | areSameGraphs(G, H); 104 | ``` 105 | 106 | ### #.areSameGraphsDeep 107 | 108 | Returns whether two graphs as well as their node & edge attributes are the same. 109 | 110 | ```js 111 | import {areSameGraphsDeep} from 'graphology-assertions'; 112 | 113 | areSameGraphsDeep(G, H); 114 | ``` 115 | 116 | ### #.haveSameEdges 117 | 118 | Returns whether two graphs have the same edges. Note that it implies that both graphs' nodes are also the same. 119 | 120 | This is different than [areSameGraphs](#aresamegraphs) because it will allow different graph types to be compared, e.g. a mixed graph and a directed one having the same edges nonetheless. 121 | 122 | ```js 123 | import {haveSameEdges} from 'graphology-assertions'; 124 | 125 | haveSameEdges(G, H); 126 | ``` 127 | 128 | ### #.haveSameEdgesDeep 129 | 130 | Returns whether two graphs have the same edges & whether those edges have the same attributes. Note that it implies that both graphs' nodes are also the same. 131 | 132 | This is different than [areSameGraphsDeep](#aresamegraphsdeep) because it will allow different graph types to be compared, e.g. a mixed graph and a directed one having the same edges nonetheless. 133 | 134 | ```js 135 | import {haveSameEdgesDeep} from 'graphology-assertions'; 136 | 137 | haveSameEdgesDeep(G, H); 138 | ``` 139 | 140 | -------------------------------------------------------------------------------- /standard-library/components.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: components 4 | nav_order: 4 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/components" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/components/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Components 12 | 13 | Connected components for [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-components 19 | ``` 20 | 21 | ## Usage 22 | 23 | - [forEachConnectedComponent](#foreachconnectedcomponent) 24 | - [forEachConnectedComponentOrder](#foreachconnectedcomponentorder) 25 | - [countConnectedComponents](#countconnectedcomponents) 26 | - [connectedComponents](#connectedcomponents) 27 | - [largestConnectedComponent](#largestconnectedcomponent) 28 | - [largestConnectedComponentSubgraph](#largestconnectedcomponentsubgraph) 29 | - [cropToLargestConnectedComponent](#cropToLargestConnectedComponent) 30 | - [stronglyConnectedComponents](#stronglyconnectedcomponents) 31 | 32 | ### forEachConnectedComponent 33 | 34 | Iterates over the connected components of the given graph using a callback. 35 | 36 | ```js 37 | import {forEachConnectedComponent} from 'graphology-components'; 38 | 39 | forEachConnectedComponent(graph, component => { 40 | console.log(component); 41 | }); 42 | ``` 43 | 44 | ### forEachConnectedComponentOrder 45 | 46 | Specialized version of [forEachConnectedComponent](#foreachconnectedcomponent) that iterates over the connected component orders, i.e. the number of nodes they contain. 47 | 48 | It consumes less memory than a naive approach mapping the component lengths using `forEachConnectedComponent`. 49 | 50 | ```js 51 | import {forEachConnectedComponentOrder} from 'graphology-components'; 52 | 53 | forEachConnectedComponentOrder(graph, order => { 54 | console.log(order); 55 | }); 56 | ``` 57 | 58 | ### countConnectedComponents 59 | 60 | Returns the number of connected components of the given graph. 61 | 62 | ```js 63 | import {countConnectedComponents} from 'graphology-components'; 64 | 65 | const count = countConnectedComponents(graph); 66 | ``` 67 | 68 | ### connectedComponents 69 | 70 | Returns the list of connected components of the given graph. 71 | 72 | ```js 73 | import {connectedComponents} from 'graphology-components'; 74 | 75 | const components = connectedComponents(graph); 76 | ``` 77 | 78 | If your graph is mixed or directed, the result will be what are usually called **weakly connected components**. 79 | 80 | ### largestConnectedComponent 81 | 82 | Returns the largest connected component of the given graph. 83 | 84 | ```js 85 | import {largestConnectedComponent} from 'graphology-components'; 86 | 87 | const largest = largestConnectedComponent(graph); 88 | ``` 89 | 90 | ### largestConnectedComponentSubgraph 91 | 92 | Returns a subgraph of the largest connected component of the given graph. 93 | 94 | ```js 95 | import {largestConnectedComponentSubgraph} from 'graphology-components'; 96 | 97 | const subgraph = largestConnectedComponentSubgraph(graph); 98 | ``` 99 | 100 | ### cropToLargestConnectedComponent 101 | 102 | Mutates the given graph to remove nodes and edges that are not part of its largest connected component. 103 | 104 | ```js 105 | import {cropToLargestConnectedComponent} from 'graphology-components'; 106 | 107 | cropToLargestConnectedComponent(graph); 108 | ``` 109 | 110 | ### stronglyConnectedComponents 111 | 112 | Returns the list of strongly connected components of the given graph. (mixed or directed) 113 | 114 | ```js 115 | import {stronglyConnectedComponents} from 'graphology-components'; 116 | 117 | const components = stronglyConnectedComponents(graph); 118 | ``` 119 | 120 | -------------------------------------------------------------------------------- /serialization.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Serialization 4 | nav_order: 12 5 | menu_toc: 6 | - "Format" 7 | - "#.import" 8 | - "#.export" 9 | --- 10 | 11 | # Serialization 12 | 13 | ## Format 14 | 15 | **Node** 16 | 17 | A node is serialized as an object containing the following keys: 18 | * **key** any The node's key, 19 | * **attributes** [object] The node's attributes (can be omitted or null). 20 | 21 | ```js 22 | graph.addNode('Thomas', {age: 34}); 23 | // Serialized would be: 24 | >>> {key: 'Thomas', attributes: {age: 34}} 25 | ``` 26 | 27 | **Edge** 28 | 29 | An edge is serialized as an object containing the following keys: 30 | * **key** [any] The edge's key (can be omitted or null on import), 31 | * **source** any The edge's source, 32 | * **target** any The edge's target, 33 | * **attributes** [object] The edge's attributes (can be omitted or null), 34 | * **undirected** [boolean] Whether the edge is undirected (can be omitted or null). 35 | 36 | ```js 37 | graph.mergeEdgeWithKey('T->E', 'Thomas', 'Eric', {type: 'KNOWS'}); 38 | // Serialized would be: 39 | >>> { 40 | key: 'T->E', 41 | source: 'Thomas', 42 | target: 'Eric', 43 | attributes: {type: 'KNOWS'} 44 | } 45 | ``` 46 | 47 | **Graph** 48 | 49 | A graph is serialized as an object containing an `attributes`, a `nodes` & an `edges` key: 50 | * object `attributes`: containing the attributes of the graph (can be omitted). 51 | * object `options`: containing the options of the graph (`allowSelfLoops`, `multi` and `type`). 52 | * object `nodes`: containing a list of serialized nodes (can be omitted when merging). 53 | * object `edges`: containing a list of serialized edges (can be omitted). 54 | 55 | ```js 56 | graph.mergeEdgeWithKey('T->E', 'Thomas', 'Eric', {type: 'KNOWS'}); 57 | graph.setAttribute('name', 'My Graph'); 58 | graph.export(); 59 | >>> { 60 | attributes: { 61 | name: 'My Graph' 62 | }, 63 | options: { 64 | allowSelfLoops: true, 65 | multi: false, 66 | type: 'mixed' 67 | }, 68 | nodes: [ 69 | {key: 'Thomas'}, 70 | {key: 'Eric'} 71 | ], 72 | edges: [ 73 | { 74 | key: 'T->E', 75 | source: 'Thomas', 76 | target: 'Eric', 77 | attributes: {type: 'KNOWS'} 78 | } 79 | ] 80 | } 81 | ``` 82 | 83 | ## #.import 84 | 85 | Imports a whole serialized graph into the graph. 86 | 87 | *Example* 88 | 89 | ```js 90 | graph.import({ 91 | attributes: {name: 'My Graph'}, 92 | nodes: [{key: 'Thomas'}, {key: 'Eric'}], 93 | edges: [{source: 'Thomas', target: 'Eric'}] 94 | }); 95 | 96 | graph.hasNode('Thomas'); 97 | >>> true 98 | ``` 99 | 100 | *Arguments* 101 | 102 | * **data** serialized graph|Graph: serialized graph data or another Graph instance. 103 | * **merge** [boolean] false: whether to merge the imported data. 104 | 105 | ## #.export 106 | 107 | Exports the whole instance's data as a serialized graph. 108 | 109 | *Example* 110 | 111 | ```js 112 | graph.mergeEdgeWithKey('T->E', 'Thomas', 'Eric', {type: 'KNOWS'}); 113 | graph.setAttribute('name', 'My Graph'); 114 | graph.export(); 115 | >>> { 116 | attributes: { 117 | name: 'My Graph' 118 | }, 119 | nodes: [ 120 | {key: 'Thomas'}, 121 | {key: 'Eric'} 122 | ], 123 | edges: [ 124 | { 125 | key: 'T->E', 126 | source: 'Thomas', 127 | target: 'Eric', 128 | attributes: {type: 'KNOWS'} 129 | } 130 | ] 131 | } 132 | ``` 133 | -------------------------------------------------------------------------------- /instantiation.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Instantiation 4 | nav_order: 3 5 | menu_toc: 6 | - Options 7 | - Generics 8 | - Specialized constructors 9 | - "Static #.from method" 10 | --- 11 | 12 | # Instantiation 13 | 14 | Instantiating a `graphology` Graph object is merely an issue of requiring an implementation and calling it with some options. 15 | 16 | Note that if you need to create a Graph using some serialized data or another Graph's data, you should probably check the static [#.from](#static-from-method). 17 | 18 | ```js 19 | import Graph from 'graphology'; 20 | 21 | // Here you go: 22 | const graph = new Graph(); 23 | 24 | // With options: 25 | const graph = new Graph(options); 26 | ``` 27 | 28 | ## Options 29 | 30 | - **allowSelfLoops** [boolean] true: should the graph allow self-loops? 31 | - **multi** [boolean] false: Should the graph allow parallel edges? 32 | - **type** [string] "mixed": Type of the graph. One of `directed`, `undirected` or `mixed`. 33 | 34 | _Examples_ 35 | 36 | ```js 37 | // Creating a multi-graph with no self-loops 38 | const graph = new Graph({multi: true, allowSelfLoops: false}); 39 | ``` 40 | 41 | ## Generics 42 | 43 | The Graph class TypeScript declaration exposes three optional generics that you can use to restrict, in this order: node attributes, edge attributes and finally graph attributes. 44 | 45 | The given types must extend `Attributes`, which is a shorthand for `{[name: string]: any}`. 46 | 47 | ```ts 48 | type NodeAttributes = { 49 | label: string; 50 | color: string; 51 | } 52 | 53 | type EdgeAttributes = { 54 | weight: number; 55 | } 56 | 57 | type GraphAttributes = { 58 | name?: string; 59 | } 60 | 61 | const graph: Graph = new Graph(); 62 | ``` 63 | 64 | ## Specialized constructors 65 | 66 | Rather than providing tedious options to the constructor, one can use one of the many handy constructors provided by the implementation to create the desired graph: 67 | 68 | ```js 69 | import {MultiDirectedGraph} from 'graphology'; 70 | 71 | const myCustomGraph = new MultiDirectedGraph(); 72 | ``` 73 | 74 | By default, the `Graph` object is a simple mixed graph, but here are the different naming "components" that you can use to instantiate a more complex graph: 75 | 76 | - **Graph with parallel edges?**: `Multi` or none (simple graph). 77 | - **Type of the graph?**: `Directed`, `Undirected` or none (mixed graph). 78 | 79 | Then to build the name, one must order the components likewise: 80 | 81 | ``` 82 | Multi? + Type? + Graph 83 | ``` 84 | 85 | _List of all the specialized constructors_ 86 | 87 | ``` 88 | DirectedGraph 89 | UndirectedGraph 90 | MultiGraph 91 | MultiDirectedGraph 92 | MultiUndirectedGraph 93 | ``` 94 | 95 | ## Static #.from method 96 | 97 | Alternatively, one can create a graph from a serialized graph or another `Graph` instance using the static `#.from` method: 98 | 99 | _Example_ 100 | 101 | ```js 102 | const graph = Graph.from(data); 103 | 104 | // Need some options? 105 | const graph = Graph.from(data, options); 106 | 107 | // Also works with typed constructors 108 | const graph = UndirectedGraph.from(data); 109 | ``` 110 | 111 | _Arguments_ 112 | 113 | - **data** Graph|SerializedGraph: pre-existing data to give to the constructor. This data can either be an existing `Graph` instance, and in this case both nodes & edges will be imported from the given graph, or a serialized graph whose format is described [here](serialization#format). 114 | - **options** [object]: options passed to the created graph. 115 | 116 | Note that `graphology` will throw an error if you try to instantiate a [typed constructor](#typed-constructors) using inconsistent options. 117 | -------------------------------------------------------------------------------- /standard-library/layout-noverlap.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: layout-noverlap 4 | nav_order: 14 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/layout-noverlap" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/layout-noverlap/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Noverlap 12 | 13 | JavaScript implementation of the Noverlap anti-collision layout algorithm for [graphology](..). 14 | 15 | Note that this algorithm is iterative and might not converge easily in some cases. 16 | 17 | ## Installation 18 | 19 | ``` 20 | npm install graphology-layout-noverlap 21 | ``` 22 | 23 | ## Usage 24 | 25 | - [Pre-requisite](#pre-requisite) 26 | - [Settings](#settings) 27 | - [Synchronous layout](#synchronous-layout) 28 | - [Webworker](#webworker) 29 | 30 | ### Pre-requisites 31 | 32 | Each node's starting position must be set before running the Noverlap anti-collision layout. Two attributes called `x` and `y` must therefore be defined for all the graph nodes. 33 | 34 | ### Settings 35 | 36 | - **gridSize** ?number 20: number of grid cells horizontally and vertically subdivising the graph's space. This is used as an optimization scheme. Set it to `1` and you will have `O(n²)` time complexity, which can sometimes perform better with very few nodes. 37 | - **margin** ?number 5: margin to keep between nodes. 38 | - **expansion** ?number 1.1: percentage of current space that nodes could attempt to move outside of. 39 | - **ratio** ?number 1.0: ratio scaling node sizes. 40 | - **speed** ?number 3: dampening factor that will slow down node movements to ease the overall process. 41 | 42 | ### Synchronous layout 43 | 44 | ```js 45 | import noverlap from 'graphology-layout-noverlap'; 46 | 47 | const positions = noverlap(graph, {maxIterations: 50}); 48 | 49 | // With settings: 50 | const positions = noverlap(graph, { 51 | maxIterations: 50, 52 | settings: { 53 | ratio: 2 54 | } 55 | }); 56 | 57 | // With a custom input reducer 58 | const positions = noverlap(graph, { 59 | inputReducer: (key, attr) => ({ 60 | x: store[key].x, 61 | y: store[key].y, 62 | size: attr.size 63 | }), 64 | outputReducer: (key, pos) => ({x: pos.x * 10, y: pos.y * 10}) 65 | }); 66 | 67 | // To directly assign the positions to the nodes: 68 | noverlap.assign(graph); 69 | ``` 70 | 71 | _Arguments_ 72 | 73 | - **graph** _Graph_: target graph. 74 | - **options** _object_: options: 75 | - **maxIterations** ?number 500: maximum number of iterations to perform before stopping. Note that the algorithm will also stop as soon as converged. 76 | - **inputReducer** ?function: a function reducing each node attributes. This can be useful if the rendered positions/sizes of your graph are stored outside of the graph's data. This is the case when using sigma.js for instance. 77 | - **outputReducer** ?function: a function reducing node positions as computed by the layout algorithm. This can be useful to map back to a previous coordinates system. This is the case when using sigma.js for instance. 78 | - **settings** ?object: the layout's settings (see [#settings](#settings)). 79 | 80 | ### Webworker 81 | 82 | If you need to run the layout's computation in a web worker, the library comes with a utility to do so: 83 | 84 | _Example_ 85 | 86 | ```js 87 | import NoverlapLayout from 'graphology-layout-noverlap/worker'; 88 | 89 | const layout = new NoverlapLayout(graph, params); 90 | 91 | // To start the layout. It will automatically stop when converged 92 | layout.start(); 93 | 94 | // To stop the layout 95 | layout.stop(); 96 | 97 | // To kill the layout and release attached memory 98 | layout.kill(); 99 | 100 | // Assess whether the layout is currently running 101 | layout.isRunning(); 102 | ``` 103 | 104 | -------------------------------------------------------------------------------- /performance-tips.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Performance tips 4 | nav_order: 2.5 5 | --- 6 | 7 | # Performance tips 8 | {: .no_toc } 9 | 10 | 1. TOC 11 | {:toc} 12 | 13 | ## Iterate using callback methods 14 | 15 | This might seem counter-intuitive but iteration methods accepting old-fashioned callbacks (such as `#.forEachNode`, `#.forEachEdge`, for instance), are the fastest way to iterate through a graph. 16 | 17 | Here are some reasons why: 18 | 19 | 1. `#.nodes` and `#.edges` must create an array and allocate memory by doing so and you will actually double your loop if you need to iterate over them once more afterwards. 20 | 2. Callback iteration methods gives you access to almost everything you could need without additional lookup of the graph, such as attributes, edge source & target etc. 21 | 3. Iterators are nice but are still quite badly optimized by JavaScript engines (including v8). 22 | 23 | Long story short: if performance is critical, prefer callback methods. 24 | 25 | ```javascript 26 | // Slowest... 27 | graph.nodes().forEach(node => { 28 | const attr = graph.getNodeAttributes(node); 29 | console.log(node, attr); 30 | }); 31 | 32 | // Still quite slow... 33 | const nodes = graph.nodes(); 34 | 35 | for (let i = 0; i < nodes.length; i++) { 36 | const node = nodes[i]; 37 | const attr = graph.getNodeAttributes(node); 38 | console.log(node, attr); 39 | } 40 | 41 | // Ok, I guess... 42 | for (const [node, attr] of graph.nodeEntries()) { 43 | console.log(node, attr); 44 | } 45 | 46 | // Fast 🚀 47 | graph.forEachNode((node, attr) => { 48 | console.log(node, attr); 49 | }); 50 | ``` 51 | 52 | Be sure, also, to avoid callback nesting if you want to keep as fast as possible. 53 | 54 | ```javascript 55 | // BAD 56 | graph.forEachNode(node => { 57 | 58 | // You are creating a function at each step of the node loop here! 59 | graph.forEachNeighbor(node, neighbor => { 60 | console.log(node, neighbor); 61 | }); 62 | }); 63 | 64 | // EVEN BETTER BUT CURSED 65 | let currentNode; 66 | 67 | function neighborCallback(neighbor) { 68 | console.log(currentNode, neighbor); 69 | } 70 | 71 | graph.forEachNode(node => { 72 | currentNode = node; 73 | graph.forEachNeighbor(node, neighborCallback); 74 | }); 75 | ``` 76 | 77 | Finally, always know that this should only be a concern if you are implementing a library or writing low-level code. If not, please use what fits your style and personal preference as it will probably not matter. 78 | 79 | ## Handle edge keys by yourself 80 | 81 | If you know what you are doing, it can be a good idea to shunt the graph's automatic edge key creation by avoiding `#.addEdge` and relying on `#.addEdgeWithKey` instead. 82 | 83 | Indeed, the automatic edge key generator was designed to avoid many common pitfalls such as collision detection across instances and runtimes. 84 | 85 | So, if you know for sure how to avoid those pitfalls, you can probably generate keys yourself using a more performant method, such as basic incremental integers from `0` to `n`. 86 | 87 | ```javascript 88 | function incrementalId() { 89 | let i = 0; 90 | 91 | return () => i++; 92 | } 93 | 94 | const edgeKeyGenerator = incrementalId(); 95 | 96 | const graph = new Graph(); 97 | graph.addNode('one'); 98 | graph.addNode('two'); 99 | graph.addEdgeWithKey(edgeKeyGenerator(), 'one', 'two'); 100 | ``` 101 | 102 | ## Avoid iterating twice on edges in mixed graphs 103 | 104 | It is often desirable to handle directed & undirected egdes differently when operating on mixed graphs. 105 | 106 | However the following scheme might underperform: 107 | 108 | ```js 109 | graph.forEachDirectedEdge((edge) => { 110 | // Do something with directed edges... 111 | }); 112 | 113 | graph.forEachUndirectedEdge((edge) => { 114 | // Do something with undirected edges... 115 | }); 116 | ``` 117 | 118 | This is because you will actually iterate twice on the internal edge map. 119 | 120 | You should probably prefer using the `undirected` argument of edge iteration likewise: 121 | 122 | ```js 123 | graph.forEachEdge((edge, attr, source, target, sa, ta, undirected) => { 124 | if (undirected) { 125 | // Do something with undirected edges... 126 | } 127 | else { 128 | // Do something with directed edges... 129 | } 130 | }); 131 | ``` 132 | -------------------------------------------------------------------------------- /standard-library/simple-path.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: simple-path 4 | nav_order: 18 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/simple-path" 8 | 9 | --- 10 | 11 | # Graphology Simple Path 12 | 13 | Simple path related functions to be used with [`graphology`](..). A "simple path" is a path where a node is not repeated. 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-simple-path 19 | ``` 20 | 21 | ## Usage 22 | 23 | - [allSimplePaths](#allsimplepaths) 24 | - [allSimpleEdgePaths](#allsimpleedgepaths) 25 | - [allSimpleEdgeGroupPaths](#allsimpleedgegrouppaths) 26 | 27 | ### allSimplePaths 28 | 29 | Collects every simple path between a source and a target node in the given graph. 30 | 31 | Note that this function also works with cycles. 32 | 33 | ```js 34 | import {allSimplePaths} from 'graphology-simple-path'; 35 | 36 | const graph = new Graph(); 37 | graph.mergeEdge('1', '2'); 38 | graph.mergeEdge('1', '3'); 39 | graph.mergeEdge('2', '3'); 40 | 41 | const paths = allSimplePaths(graph, '1', '3'); 42 | >>> [ 43 | ['1', '3'], 44 | ['1', '2', '3'] 45 | ] 46 | 47 | // To get cycles, just pass same source & target 48 | const cycles = allSimplePaths(graph, '1', '1'); 49 | 50 | // To limit traversal to a certain depth 51 | const limitedPaths = allSimplePaths(graph, '1', '3', {maxDepth: 2}); 52 | ``` 53 | 54 | _Arguments_ 55 | 56 | - **graph** _Graph_: target graph. 57 | - **source** _string_: source node. 58 | - **target** _string_: target node. 59 | - **options** ?object: options: 60 | - **maxDepth** ?number: max traversal depth (default - no limit). 61 | 62 | ### allSimpleEdgePaths 63 | 64 | Collects every simple path, represented by the followed edges, between a source and a target node in the given graph. 65 | 66 | Note that this function also works with cycles but does not work with multi graphs yet. 67 | 68 | ```js 69 | import {allSimpleEdgePaths} from 'graphology-simple-path'; 70 | 71 | const graph = new Graph(); 72 | graph.mergeEdgeWithKey('1->2', '1', '2'); 73 | graph.mergeEdgeWithKey('1->3', '1', '3'); 74 | graph.mergeEdgeWithKey('2->3', '2', '3'); 75 | 76 | const paths = allSimpleEdgePaths(graph, '1', '3'); 77 | >>> [ 78 | ['1->3'], 79 | ['1->2', '2->3'] 80 | ] 81 | 82 | // To get cycles, just pass same source & target 83 | const cycles = allSimpleEdgePaths(graph, '1', '1'); 84 | 85 | // To limit traversal to a certain depth 86 | const limitedPaths = allSimpleEdgePaths(graph, '1', '3', {maxDepth: 2}); 87 | ``` 88 | 89 | _Arguments_ 90 | 91 | - **graph** _Graph_: target graph. 92 | - **source** _string_: source node. 93 | - **target** _string_: target node. 94 | - **options** ?object: options: 95 | - **maxDepth** ?number: max traversal depth (default - no limit). 96 | 97 | ### allSimpleEdgeGroupPaths 98 | 99 | Collects every simple path, represented by groups of equivalent followed edges, between a source and a target node in the given multi graph. 100 | 101 | Note that this function also works with cycles and that, even if it can work with a simple graph, it has not be designed to be useful in this case. 102 | 103 | ```js 104 | import {allSimpleEdgeGroupPaths} from 'graphology-simple-path'; 105 | 106 | const graph = new Graph(); 107 | graph.mergeEdgeWithKey('1->2a', '1', '2'); 108 | graph.mergeEdgeWithKey('1->2b', '1', '2'); 109 | graph.mergeEdgeWithKey('1->3a', '1', '3'); 110 | graph.mergeEdgeWithKey('2->3a', '2', '3'); 111 | 112 | const paths = allSimpleEdgeGroupPaths(graph, '1', '3'); 113 | >>> [ 114 | [['1->3a']], 115 | [['1->2a', '1->2b'], ['2->3a']] 116 | ] 117 | 118 | // To get cycles, just pass same source & target 119 | const cycles = allSimpleEdgeGroupPaths(graph, '1', '1'); 120 | 121 | // To limit traversal to a certain depth 122 | const limitedPaths = allSimpleEdgeGroupPaths(graph, '1', '3', {maxDepth: 2}); 123 | ``` 124 | 125 | _Arguments_ 126 | 127 | - **graph** _Graph_: target graph. 128 | - **source** _string_: source node. 129 | - **target** _string_: target node. 130 | - **options** ?object: options: 131 | - **maxDepth** ?number: max traversal depth (default - no limit). 132 | 133 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Home 4 | nav_order: 0 5 | --- 6 | 7 | # Graphology 8 | 9 | `graphology` is a robust & multipurpose `Graph` object for JavaScript and TypeScript. 10 | 11 | It aims at supporting various kinds of graphs with the same unified interface. 12 | 13 | A `graphology` graph can therefore be directed, undirected or mixed, allow self-loops or not, and can be simple or support parallel edges. 14 | 15 | Along with this `Graph` object, one will also find a comprehensive [standard library](standard-library) full of graph theory algorithms and common utilities such as graph generators, layouts, traversals etc. 16 | 17 | Finally, `graphology` graphs are able to emit a wide variety of [events](events), which makes them ideal to build interactive renderers for the browser. It is for instance used by [sigma.js](https://www.sigmajs.org/) as its data backend. 18 | 19 | ## Installation 20 | 21 | To install `graphology` using npm, run the following command: 22 | 23 | ``` 24 | npm install graphology 25 | ``` 26 | 27 | *Legacy bundle* 28 | 29 | Standalone builds of `graphology` and its full standard library can be found in the repository's [releases](https://github.com/graphology/graphology/releases) if you can only rely on your own `script` tags to load code. 30 | 31 | ```html 32 | 33 | 34 | 35 | 40 | 41 | 42 | 43 | 44 | 47 | ``` 48 | 49 | Be warned that the standard library bundle often lags behind and is not always completely up to date. 50 | 51 | *TypeScript usage* 52 | 53 | Note that `graphology` also exports type declaration that are installed along using peer dependencies so it can be used with [TypeScript](https://www.typescriptlang.org/) out of the box. 54 | 55 | If your version of npm is a bit old, you may need to install `graphology-types` yourself if the peer dependency resolution is not made for you already: 56 | 57 | ``` 58 | npm install graphology-types 59 | ``` 60 | 61 | It can also be useful to pin `graphology-types` version in your `package.json` to avoid resolution issues sometimes. 62 | 63 | ## Quick Start 64 | 65 | ```js 66 | import Graph from 'graphology'; 67 | 68 | const graph = new Graph(); 69 | 70 | // Adding some nodes 71 | graph.addNode('John'); 72 | graph.addNode('Martha'); 73 | 74 | // Adding an edge 75 | graph.addEdge('John', 'Martha'); 76 | 77 | // Displaying useful information about your graph 78 | console.log('Number of nodes', graph.order); 79 | console.log('Number of edges', graph.size); 80 | 81 | // Iterating over nodes 82 | graph.forEachNode(node => { 83 | console.log(node); 84 | }); 85 | ``` 86 | 87 | ## How to cite 88 | 89 | [![DOI](https://zenodo.org/badge/66482976.svg)](https://zenodo.org/badge/latestdoi/66482976) 90 | 91 | `graphology` is published on [Zenodo](https://zenodo.org/) as [https://doi.org/10.5281/zenodo.5681257](https://doi.org/10.5281/zenodo.5681257). 92 | 93 | You can cite it thusly: 94 | 95 | > Guillaume Plique. (2021). Graphology, a robust and multipurpose Graph object for JavaScript. Zenodo. https://doi.org/10.5281/zenodo.5681257 96 | 97 | 98 | ## Changelog 99 | 100 | A complete changelog can be found [here](https://github.com/graphology/graphology/blob/master/CHANGELOG.md). 101 | 102 | ## Implementing graphology 103 | 104 | There are many ways to implement a Graph data structure and even if `graphology` chose to optimize for most common use cases, you might need a more specialized implementation yourself. 105 | 106 | In this case, know that `graphology` can be re-implemented completely without losing the benefit of being able to use its [standard library](standard-library). 107 | 108 | For more information, be sure to read [this](implementing-graphology) section of the documentation. 109 | 110 | ## Acknowledgments 111 | 112 | This documentation has been built with [Jekyll](https://jekyllrb.com/) using the [Just the Docs](https://pmarsceill.github.io/just-the-docs/) theme. 113 | -------------------------------------------------------------------------------- /standard-library/layout-force.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: layout-force 4 | nav_order: 12 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/layout-force" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/layout-force/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Force layout 12 | 13 | JavaScript implementation of a basic [force directed layout algorithm](https://en.wikipedia.org/wiki/Force-directed_graph_drawing) for [graphology](..). 14 | 15 | In some few cases, for very small graphs, [ForceAtlas2](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0098679) can be "too efficient". This simpler force algorithm cannot spatialize larger networks, but will offer more organic movements which are more suited to some interactions with the graph such as drag and drop etc. 16 | 17 | ## Installation 18 | 19 | ``` 20 | npm install graphology-layout-force 21 | ``` 22 | 23 | ## Usage 24 | 25 | - [Pre-requisite](#pre-requisite) 26 | - [Settings](#settings) 27 | - [Synchronous layout](#synchronous-layout) 28 | - [Supervisor](#supervisor) 29 | 30 | ### Pre-requisites 31 | 32 | Each node's starting position must be set before running the force layout algorithm. Two attributes called `x` and `y` must therefore be defined for all the graph nodes. 33 | 34 | Note also that the algorithm has an edge-case where the layout cannot be computed if all of your nodes starts with `x=0` and `y=0`. 35 | 36 | ### Settings 37 | 38 | - **attraction** ?number 0.0005: importance of the attraction force, that attracts each pair of connected nodes like elastics. 39 | - **repulsion** ?number 0.1: importance of the repulsion force, that attracts each pair of nodes like magnets. 40 | - **gravity** ?number 0.0001: importance of the gravity force, that attracts all nodes to the center. 41 | - **inertia** ?number 0.6: percentage of a node vector displacement that is preserved at each step. `0` means no inertia, `1` means no friction. 42 | - **maxMove** ?number 200: Maximum length a node can travel at each step, in pixel. 43 | 44 | ### Synchronous layout 45 | 46 | ```js 47 | import forceLayout from 'graphology-layout-force'; 48 | 49 | const positions = forceLayout(graph, {maxIterations: 50}); 50 | 51 | // With settings: 52 | const positions = forceLayout(graph, { 53 | maxIterations: 50, 54 | settings: { 55 | gravity: 10 56 | } 57 | }); 58 | 59 | // To directly assign the positions to the nodes: 60 | forceLayout.assign(graph); 61 | ``` 62 | 63 | _Arguments_ 64 | 65 | - **graph** _Graph_: target graph. 66 | - **options** _object_: options: 67 | - **nodeXAttribute** ?string x: name of the `x` position attribute for nodes. 68 | - **nodeYAttribute** ?string y: name of the `y` position attribute for edges. 69 | - **isNodeFixed** ?string\|function fixed: name of the `fixed` attribute for nodes or a function getting a node entry (key, attributes) and returning whether the node is fixed. 70 | - **shouldSkipNode** ?function: function returning whether the layout computations should skip this node. 71 | - **shouldSkipEdge** ?function: function returning whether the layout computations should skip this edge. 72 | - **maxIterations** ?number 500: maximum number of iterations to perform before stopping. Note that the algorithm will also stop as soon as converged. 73 | - **settings** ?object: the layout's settings (see [#settings](#settings)). 74 | 75 | ### Supervisor 76 | 77 | Layout supervisor relying on [`window.requestAnimationFrame`](https://developer.mozilla.org/fr/docs/Web/API/Window/requestAnimationFrame) to run the layout live without hampering the UI thread. 78 | 79 | _Example_ 80 | 81 | ```js 82 | import ForceSupervisor from 'graphology-layout-force/worker'; 83 | 84 | const layout = new ForceSupervisor(graph, params); 85 | 86 | // To start the layout. It will automatically stop when converged 87 | layout.start(); 88 | 89 | // To stop the layout 90 | layout.stop(); 91 | 92 | // To kill the layout and release attached memory and listeners 93 | layout.kill(); 94 | 95 | // Assess whether the layout is currently running 96 | layout.isRunning(); 97 | ``` 98 | 99 | -------------------------------------------------------------------------------- /standard-library/traversal.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: traversal 4 | nav_order: 20 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/traversal" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/traversal/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Traversal 12 | 13 | Miscellaneous traversal functions to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-traversal 19 | ``` 20 | 21 | ## Usage 22 | 23 | - [bfs](#bfs) 24 | - [bfsFromNode](#bfsfromnode) 25 | - [dfs](#dfs) 26 | - [dfsFromNode](#bfsfromnode) 27 | 28 | ### bfs 29 | 30 | Perform a BFS (Breadth-First Search) over the given graph using a callback. 31 | 32 | ```js 33 | import {bfs} from 'graphology-traversal'; 34 | // Alternatively, to only load the relevant code 35 | import {bfs} from 'graphology-traversal/bfs'; 36 | 37 | bfs(graph, function (node, attr, depth) { 38 | console.log(node, attr, depth); 39 | }); 40 | 41 | // Stopping at depth 3 42 | bfs(graph, function (node, attr, depth) { 43 | return depth >= 3; 44 | }); 45 | ``` 46 | 47 | _Arguments_ 48 | 49 | - **graph** _Graph_: a graphology instance. 50 | - **callback** _function_: iteration callback taking the traversed node, its attributes and the traversal's depth. Returning `true` will prevent the traversal from following the next neighbors. 51 | - **options** ?object: traversal options: 52 | - **mode** ?string outbound: type of neighbors to traverse. 53 | 54 | ### bfsFromNode 55 | 56 | Perform a BFS (Breadth-First Search) over the given graph, starting from the given node, using a callback. 57 | 58 | ```js 59 | import {bfsFromNode} from 'graphology-traversal'; 60 | // Alternatively, to only load the relevant code 61 | import {bfsFromNode} from 'graphology-traversal/bfs'; 62 | 63 | bfsFromNode(graph, 'node1', function (node, attr, depth) { 64 | console.log(node, attr, depth); 65 | }); 66 | 67 | // Stopping at depth 3 68 | bfsFromNode(graph, 'node1', function (node, attr, depth) { 69 | return depth >= 3; 70 | }); 71 | ``` 72 | 73 | _Arguments_ 74 | 75 | - **graph** _Graph_: a graphology instance. 76 | - **node** _string\|number_: starting node. 77 | - **callback** _function_: iteration callback taking the traversed node, its attributes and the traversal's depth. Returning `true` will prevent the traversal from following the next neighbors. 78 | - **options** ?object: traversal options: 79 | - **mode** ?string outbound: type of neighbors to traverse. 80 | 81 | ### dfs 82 | 83 | Perform a DFS (Depth-First Search) over the given graph using a callback. 84 | 85 | ```js 86 | import {dfs} from 'graphology-traversal'; 87 | // Alternatively, to only load the relevant code 88 | import {dfs} from 'graphology-traversal/dfs'; 89 | 90 | dfs(graph, function (node, attr, depth) { 91 | console.log(node, attr, depth); 92 | }); 93 | 94 | // Stopping at depth 3 95 | dfs(graph, function (node, attr, depth) { 96 | return depth >= 3; 97 | }); 98 | ``` 99 | 100 | _Arguments_ 101 | 102 | - **graph** _Graph_: a graphology instance. 103 | - **callback** _function_: iteration callback taking the traversed node, its attributes and the traversal's depth. Returning `true` will prevent the traversal from following the next neighbors. 104 | - **options** ?object: traversal options: 105 | - **mode** ?string outbound: type of neighbors to traverse. 106 | 107 | ### dfsFromNode 108 | 109 | Perform a DFS (Depth-First Search) over the given graph, starting from the given node, using a callback. 110 | 111 | ```js 112 | import {dfsFromNode} from 'graphology-traversal'; 113 | // Alternatively, to only load the relevant code 114 | import {dfsFromNode} from 'graphology-traversal/dfs'; 115 | 116 | dfsFromNode(graph, 'node1', function (node, attr, depth) { 117 | console.log(node, attr, depth); 118 | }); 119 | 120 | // Stopping at depth 3 121 | dfsFromNode(graph, 'node1', function (node, attr, depth) { 122 | return depth >= 3; 123 | }); 124 | ``` 125 | 126 | _Arguments_ 127 | 128 | - **graph** _Graph_: a graphology instance. 129 | - **node** _string\|number_: starting node. 130 | - **callback** _function_: iteration callback taking the traversed node, its attributes and the traversal's depth. Returning `true` will prevent the traversal from following the next neighbors. 131 | - **options** ?object: traversal options: 132 | - **mode** ?string outbound: type of neighbors to traverse. 133 | 134 | -------------------------------------------------------------------------------- /standard-library/gexf.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: gexf 4 | nav_order: 8 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/gexf" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/gexf/CHANGELOG.md" 9 | --- 10 | 11 | [![Build Status](/standard-library/gexf/workflows/Tests/badge.svg)](/standard-library/gexf/actions) 12 | 13 | # Graphology GEXF Utilities 14 | 15 | GEXF parser & writer for [`graphology`](..). 16 | 17 | For more information about the GEXF file format, you can head [there](https://gexf.net). 18 | 19 | ## Installation 20 | 21 | ``` 22 | npm install graphology-gexf 23 | ``` 24 | 25 | ## Usage 26 | 27 | - [Parser](#parser) 28 | - [Writer](#writer) 29 | - [Notes](#notes) 30 | 31 | ### Parser 32 | 33 | The parser must be passed a `graphology` constructor and is able to read either a string, or an `XMLDocument` instance. 34 | 35 | ```js 36 | var Graph = require('graphology'); 37 | 38 | // Node 39 | var gexf = require('graphology-gexf'); 40 | // Browser 41 | var gexf = require('graphology-gexf/browser'); 42 | 43 | // Reading a string 44 | var graph = gexf.parse(Graph, string); 45 | 46 | // Reading a dom document 47 | var graph = gexf.parse(Graph, xmlDocument); 48 | 49 | // Passing options 50 | var graph = gexf.parse(Graph, string, {addMissingNodes: true}); 51 | ``` 52 | 53 | _Arguments_ 54 | 55 | - **constructor** _GraphClass_: graphology constructor to use. 56 | - **source** _string\|Document_: source data to parse. 57 | - **options** ?object: parsing options: 58 | - **addMissingNodes** ?boolean false: whether to add missing nodes referenced in the file's edges. 59 | - **allowUndeclaredAttributes** ?boolean false: whether to allow undeclared attributes for both nodes & edges, which will be considered as strings. 60 | - **respectInputGraphType** ?boolean false: whether to make sure the output graph type is the same as the passed constructor. By default the parser will try to optimize the graph representation to avoid wasting time and memory. But if `respectInputGraphType` is `true` the output graph is guaranteed to be an instance of the typed constructor you gave as first argument. This also means the parser will throw when reading a graph that cannot be represented with the given typed constructor. 61 | 62 | ### Writer 63 | 64 | The writer must be passed a `graphology` instance and will output a GEXF string. 65 | 66 | ```js 67 | // Node 68 | var gexf = require('graphology-gexf'); 69 | // Browser 70 | var gexf = require('graphology-gexf/browser'); 71 | 72 | // Writing the graph 73 | var gexfString = gexf.write(graph); 74 | 75 | // Using custom formatting for nodes & edges 76 | var gexfString = gexf.write(graph, { 77 | formatNode: function (key, attributes) { 78 | return { 79 | label: attributes.label, 80 | attributes: { 81 | age: attributes.age, 82 | name: attributes.name 83 | }, 84 | viz: { 85 | color: '#FF0', 86 | x: attributes.x, 87 | y: attributes.y, 88 | shape: 'circle', 89 | size: 20 90 | } 91 | }; 92 | }, 93 | formatEdge: function (key, attributes) { 94 | return { 95 | label: attributes.label, 96 | attributes: { 97 | number: attributes.number 98 | }, 99 | weight: attributes.weight, 100 | viz: { 101 | color: '#FF0', 102 | x: attributes.x, 103 | y: attributes.y, 104 | shape: 'dotted', 105 | thickness: 20 106 | } 107 | }; 108 | } 109 | }); 110 | ``` 111 | 112 | _Arguments_ 113 | 114 | - **graph** _Graph_: graphology instance to write. 115 | - **options** ?object: Options: 116 | - **encoding** ?string UTF-8: encoding declaration. 117 | - **formatNode** ?function: function returning the node's data to write. 118 | - **formatEdge** ?function: function returning the edge's data to write. 119 | - **pretty** ?boolean true: pretty-print output? 120 | - **pedantic** ?boolean false: whether to output a stricter gexf file to make sure it can be validated using the most restrictive gexf xsd schemas. Note that the output may lose some graph attributes when doing so. 121 | - **version** ?string 1.2: gexf version to emit. Should be one of `1.2` or `1.3`. 122 | 123 | ### Notes 124 | 125 | Currently, `mutual` (a specific gexf type for edges that is seldom used in practice) edges are parsed as undirected ones rather than two directed ones because it could produce a key conflict. An option to deal differently with this may be added in the future if it becomes a problem. 126 | 127 | -------------------------------------------------------------------------------- /standard-library/dag.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: dag 4 | nav_order: 5 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/dag" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/dag/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology DAG 12 | 13 | Functions related to Directed Acyclic Graphs (DAGs) and to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-dag 19 | ``` 20 | 21 | ## Usage 22 | 23 | - [hasCycle](#hascycle) 24 | - [willCreateCycle](#willcreatecycle) 25 | - [topologicalSort](#topologicalsort) 26 | - [topologicalGenerations](#topologicalgenerations) 27 | - [forEachNodeInTopologicalOrder](#foreachnodeintopologicalorder) 28 | - [forEachTopologicalGeneration](#foreachtopologicalgeneration) 29 | 30 | ### hasCycle 31 | 32 | Function returning whether the given directed graph contains at least one cycle. 33 | 34 | Note that this function will also work with a disconnected graph. 35 | 36 | ```js 37 | import {hasCycle} from 'graphology-dag'; 38 | // Alternatively, to load only the relevant code: 39 | import hasCycle from 'graphology-dag/has-cycle'; 40 | 41 | const graph = new DirectedGraph(); 42 | graph.mergeEdge(0, 1); 43 | graph.mergeEdge(1, 2); 44 | graph.mergeEdge(2, 3); 45 | 46 | hasCycle(graph); 47 | >>> false 48 | 49 | graph.mergeEdge(3, 0); 50 | 51 | hasCycle(graph); 52 | >>> true 53 | ``` 54 | 55 | ### willCreateCycle 56 | 57 | Function returning whether adding the given directed edge to a DAG will create an undesired cycle. 58 | 59 | Note that this function expects a valid DAG and even if passing a cyclic graph could work it could also very well lead to undefined behavior ranging from an infinite loop to overkill memory usage. 60 | 61 | Note finally that this function will also work with DAG forests (sets of disconnected DAGs living in the same graph instance). 62 | 63 | ```js 64 | import {willCreateCycle} from 'graphology-dag'; 65 | // Alternatively, to load only the relevant code: 66 | import willCreateCycle from 'graphology-dag/will-create-cycle'; 67 | 68 | const graph = new DirectedGraph(); 69 | graph.mergeEdge(0, 1); 70 | graph.mergeEdge(1, 2); 71 | graph.mergeEdge(2, 3); 72 | 73 | willCreateCycle(graph, 3, 0); 74 | >>> true 75 | willCreateCycle(graph, 0, 2); 76 | >>> false 77 | ``` 78 | 79 | ### topologicalSort 80 | 81 | Function returning an array of nodes representing a possible topological ordering of the given DAG. 82 | 83 | Note that this function will throw if given graph has any cycle, is able to work on mixed graphs containing only directed edges and can work on disconnected graphs (a DAG forest). 84 | 85 | ```js 86 | import {topologicalSort} from 'graphology-dag'; 87 | // Alternatively, to load only the relevant code: 88 | import {topologicalSort} from 'graphology-dag/topological-sort'; 89 | 90 | const graph = new DirectedGraph(); 91 | graph.mergeEdge(0, 1); 92 | graph.mergeEdge(1, 2); 93 | graph.mergeEdge(2, 3); 94 | 95 | topologicalSort(graph); 96 | >>> ['0', '1', '2', '3'] 97 | ``` 98 | 99 | ### topologicalGenerations 100 | 101 | Function returning an array of array of nodes representing the successive generations of the topological ordering of the given DAG. 102 | 103 | Note that this function will throw if given graph has any cycle, is able to work on mixed graphs containing only directed edges and can work on disconnected graphs (a DAG forest). 104 | 105 | ```js 106 | import {topologicalGenerations} from 'graphology-dag'; 107 | // Alternatively, to load only the relevant code: 108 | import {topologicalGenerations} from 'graphology-dag/topological-sort'; 109 | 110 | const graph = new DirectedGraph(); 111 | graph.mergeEdge(0, 1); 112 | graph.mergeEdge(1, 2); 113 | graph.mergeEdge(0, 3); 114 | 115 | topologicalGenerations(graph); 116 | >>> [[ '0' ], ['1', '3'], ['2']] 117 | ``` 118 | 119 | ### forEachNodeInTopologicalOrder 120 | 121 | Function iterating over the given DAG's nodes in topological order using a callback function. 122 | 123 | Note that this function will throw if given graph has any cycle, is able to work on mixed graphs containing only directed edges and can work on disconnected graphs (a DAG forest). 124 | 125 | ```js 126 | import {forEachNodeInTopologicalOrder} from 'graphology-dag'; 127 | // Alternatively, to load only the relevant code: 128 | import {forEachNodeInTopologicalOrder} from 'graphology-dag/topological-sort'; 129 | 130 | const graph = new DirectedGraph(); 131 | graph.mergeEdge(0, 1); 132 | graph.mergeEdge(1, 2); 133 | graph.mergeEdge(2, 3); 134 | 135 | forEachNodeInTopologicalOrder(graph, (node, attr, generationIndex) => { 136 | // Note that generationIndex will be monotonically increasing from 0 to n. 137 | console.log(node, attr, generationIndex); 138 | }); 139 | ``` 140 | 141 | ### forEachTopologicalGeneration 142 | 143 | Function iterating over the given DAG's generations, represented by an array of node keys, using a callback function. 144 | 145 | Note that this function will throw if given graph has any cycle, is able to work on mixed graphs containing only directed edges and can work on disconnected graphs (a DAG forest). 146 | 147 | ```js 148 | import {forEachTopologicalGeneration} from 'graphology-dag'; 149 | // Alternatively, to load only the relevant code: 150 | import {forEachTopologicalGeneration} from 'graphology-dag/topological-sort'; 151 | 152 | const graph = new DirectedGraph(); 153 | graph.mergeEdge(0, 1); 154 | graph.mergeEdge(1, 2); 155 | graph.mergeEdge(2, 3); 156 | 157 | forEachTopologicalGeneration(graph, generation => { 158 | console.log(generation); 159 | }); 160 | ``` 161 | 162 | -------------------------------------------------------------------------------- /standard-library/layout-forceatlas2.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: layout-forceatlas2 4 | nav_order: 13 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/layout-forceatlas2" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/layout-forceatlas2/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology ForceAtlas2 12 | 13 | JavaScript implementation of the [ForceAtlas2](https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0098679) algorithm for [graphology](..). 14 | 15 | ## Reference 16 | 17 | > Jacomy M, Venturini T, Heymann S, Bastian M (2014) ForceAtlas2, a Continuous Graph Layout Algorithm for Handy Network Visualization Designed for the Gephi Software. PLoS ONE 9(6): e98679. https://doi.org/10.1371/journal.pone.0098679 18 | 19 | ## Installation 20 | 21 | ``` 22 | npm install graphology-layout-forceatlas2 23 | ``` 24 | 25 | ## Usage 26 | 27 | - [Pre-requisite](#pre-requisite) 28 | - [Settings](#settings) 29 | - [Synchronous layout](#synchronous-layout) 30 | - [Webworker](#webworker) 31 | - [#.inferSettings](#infersettings) 32 | 33 | ### Pre-requisites 34 | 35 | Each node's starting position must be set before running ForceAtlas 2 layout. Two attributes called `x` and `y` must therefore be defined for all the graph nodes. [graphology-layout](/standard-library/layout) can be used to initialize these attributes to a [random](/standard-library/layout#random) or [circular](/standard-library/layout#circular) layout, if needed. 36 | 37 | Note also that the algorithm has an edge-case where the layout cannot be computed if all of your nodes starts with `x=0` and `y=0`. 38 | 39 | ### Settings 40 | 41 | - **adjustSizes** ?boolean false: should the node's sizes be taken into account? 42 | - **barnesHutOptimize** ?boolean false: whether to use the Barnes-Hut approximation to compute repulsion in `O(n*log(n))` rather than default `O(n^2)`, `n` being the number of nodes. 43 | - **barnesHutTheta** ?number 0.5: Barnes-Hut approximation theta parameter. 44 | - **edgeWeightInfluence** ?number 1: influence of the edge's weights on the layout. To consider edge weight, don't forget to pass `weighted` as `true` when applying the [synchronous layout](#synchronous-layout) or when instantiating the [worker](#webworker). 45 | - **gravity** ?number 1: strength of the layout's gravity. 46 | - **linLogMode** ?boolean false: whether to use Noack's LinLog model. 47 | - **outboundAttractionDistribution** ?boolean false 48 | - **scalingRatio** ?number 1 49 | - **slowDown** ?number 1 50 | - **strongGravityMode** ?boolean false 51 | 52 | ### Synchronous layout 53 | 54 | ```js 55 | import forceAtlas2 from 'graphology-layout-forceatlas2'; 56 | 57 | const positions = forceAtlas2(graph, {iterations: 50}); 58 | 59 | // With settings: 60 | const positions = forceAtlas2(graph, { 61 | iterations: 50, 62 | settings: { 63 | gravity: 10 64 | } 65 | }); 66 | 67 | // To directly assign the positions to the nodes: 68 | forceAtlas2.assign(graph); 69 | ``` 70 | 71 | _Arguments_ 72 | 73 | - **graph** _Graph_: target graph. 74 | - **options** _object_: options: 75 | - **iterations** _number_: number of iterations to perform. 76 | - **getEdgeWeight** ?string\|function weight: name of the edge weight attribute or getter function. Defaults to `weight`. 77 | - **settings** ?object: the layout's settings (see [#settings](#settings)). 78 | 79 | ### Webworker 80 | 81 | If you need to run the layout's computation in a web worker, the library comes with a utility to do so: 82 | 83 | _Example_ 84 | 85 | ```js 86 | import FA2Layout from 'graphology-layout-forceatlas2/worker'; 87 | 88 | // The parameters are the same as for the synchronous version, minus `iterations` of course 89 | const layout = new FA2Layout(graph, { 90 | settings: {gravity: 1} 91 | }); 92 | 93 | // To start the layout 94 | layout.start(); 95 | 96 | // To stop the layout 97 | layout.stop(); 98 | 99 | // To kill the layout and release attached memory 100 | layout.kill(); 101 | 102 | // Assess whether the layout is currently running 103 | layout.isRunning(); 104 | ``` 105 | 106 | **WARNING!**: if you are using [`webpack`](https://webpack.js.org/) to bundle your code, avoid the `cheap-eval`-like options for the [`devtool`](https://webpack.js.org/configuration/devtool/) setting. Some users noticed that it interacts in mysterious ways with the library's code and cause performance to drop dramatically when using the worker. Note that this should have been fixed from v0.5.0. 107 | 108 | ### #.inferSettings 109 | 110 | If you don't know how to tune the layout's settings and want to infer them from your graph, you can use the `#.inferSettings` method: 111 | 112 | ```js 113 | import forceAtlas2 from 'graphology-layout-forceatlas2'; 114 | 115 | const sensibleSettings = forceAtlas2.inferSettings(graph); 116 | const positions = forceAtlas2(graph, { 117 | iterations: 50, 118 | settings: sensibleSettings 119 | }); 120 | 121 | // Alternatively using the graph's order instead of a graph instance 122 | const sensibleSettings = forceAtlas2.inferSettings(500); 123 | ``` 124 | 125 | -------------------------------------------------------------------------------- /standard-library/cores.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: cores 4 | nav_order: 6 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/cores" 8 | 9 | --- 10 | 11 | # Graphology Cores 12 | 13 | Various functions related to k-cores of graphs and to be used with [graphology](..). 14 | 15 | The k-core of a graph is the maximal connected subgraph in which all nodes have a degree of k or more. The main core of a graph is the k-core subgraph with the highest possible k. 16 | 17 | If the graph is directed, node degrees are considered to be the sum of all the inbound and outbound neighbors of the node. 18 | 19 | > An O(m) Algorithm for Cores Decomposition of Networks Vladimir Batagelj and Matjaz Zaversnik, 2003. [https://arxiv.org/abs/cs.DS/0310049](https://arxiv.org/abs/cs.DS/0310049) 20 | 21 | > Generalized Cores Vladimir Batagelj and Matjaz Zaversnik, 2002. [https://arxiv.org/pdf/cs/0202039](https://arxiv.org/pdf/cs/0202039) 22 | 23 | ## Installation 24 | 25 | ``` 26 | npm install graphology-cores 27 | ``` 28 | 29 | ## Usage 30 | 31 | - [coreNumber](#corenumber) 32 | - [kCore](#kcore) 33 | - [kShell](#kshell) 34 | - [kCrust](#kcrust) 35 | - [kCorona](#kcorona) 36 | - [kTruss](#ktruss) 37 | - [onionLayers](#onionlayers) 38 | 39 | ### coreNumber 40 | 41 | Returns the core number for each node. The core number of a node is the largest k of a k-core subgraph containing this node. 42 | 43 | This implementation doesn't allow graphs with parallel edges or self loops. 44 | 45 | ```js 46 | import coreNumber from 'graphology-cores/coreNumber'; 47 | 48 | // Return the core number for each node 49 | const numbers = coreNumber(graph); 50 | 51 | // Assign to each node its core number 52 | coreNumber.assign(graph); 53 | 54 | // Assign with a custom attribute label 55 | coreNumber.assign(graph, 'core'); 56 | ``` 57 | 58 | _Arguments_ 59 | 60 | - **graph** _Graph_: target graph. 61 | - **nodeCoreAttribute** ?string : the name of the attribute to use if core numbers are assigned to the nodes. 62 | 63 | ### kCore 64 | 65 | Returns the maximal connected subgraph containing nodes with k degree or more. If k isn't provided, k is the highest core number present in the graph. 66 | 67 | ```js 68 | import kCore from 'graphology-cores/kCore'; 69 | 70 | // Return the main k-core of the graph 71 | const core = kCore(graph); 72 | 73 | // Return the k-core subgraph with an arbitrary k value 74 | const core = kCore(graph, 4); 75 | ``` 76 | 77 | _Arguments_ 78 | 79 | - **graph** _Graph_: target graph. 80 | - **k** ?number: custom k value to use. 81 | - **customCore** ?object: custom core numbers to use. 82 | 83 | ### kShell 84 | 85 | Returns the k-shell subgraph. The k-shell subgraph is the maximal connected subgraph containing the nodes with k degree. 86 | 87 | ```js 88 | import kShell from 'graphology-cores/kShell'; 89 | 90 | // Return the main k-shell of the graph 91 | const shell = kShell(graph); 92 | 93 | // Return the k-shell subgraph with an arbitrary k value 94 | const shell = kShell(graph, 5); 95 | ``` 96 | 97 | _Arguments_ 98 | 99 | - **graph** _Graph_: target graph. 100 | - **k** ?number: custom k value to use. 101 | - **customCore** ?object: custom core numbers to use. 102 | 103 | ### kCrust 104 | 105 | Returns the k-crust subgraph. The k-crust subgraph is the maximal connected subgraph containing nodes with less than k degree. 106 | 107 | ```js 108 | import kCrust from 'graphology-cores/kCrust'; 109 | 110 | // Return the main k-crust of the graph 111 | const crust = kCrust(graph); 112 | 113 | // Return the k-crust subgraph with an arbitrary k value 114 | const crust = kCrust(graph, 4); 115 | ``` 116 | 117 | _Arguments_ 118 | 119 | - **graph** _Graph_: target graph. 120 | - **k** ?number: custom k value to use. 121 | - **customCore** ?object: custom core numbers to use. 122 | 123 | ### kCorona 124 | 125 | Returns the k-corona subgraph. The k-corona subgraph contains nodes in the k-core with exactly k neighbors in the k-core. 126 | 127 | ```js 128 | import kCorona from 'graphology-cores/kCorona'; 129 | 130 | // Return the main k-corona of the graph 131 | const corona = kCorona(graph); 132 | 133 | // Return the k-corona subgraph with an arbitrary k value 134 | const corona = kCorona(graph, 4); 135 | ``` 136 | 137 | _Arguments_ 138 | 139 | - **graph** _Graph_: target graph. 140 | - **k** ?number: custom k value to use. 141 | - **customCore** ?object: custom core numbers to use. 142 | 143 | ### kTruss 144 | 145 | Returns the k-truss subgraph. The k-truss subgraph contains at least three nodes for which every edge is incident to at least `k-2` triangles. 146 | 147 | K-Truss is not implemented for directed graphs and multigraphs. 148 | 149 | ```js 150 | import kTruss from 'graphology-cores/kTruss'; 151 | 152 | // Return the k-truss of the graph with k = 4 153 | const truss = kTruss(graph, 4); 154 | ``` 155 | 156 | _Arguments_ 157 | 158 | - **graph** _Graph_: target graph. 159 | - **k** _number_: k value to use. 160 | 161 | ### onionLayers 162 | 163 | Computes the onion decomposition of a given graph. Onion layers can't be calculated if the graph is directed. 164 | 165 | > Multi-scale structure and topological anomaly detection via a new network statistic: The onion decomposition L. Hébert-Dufresne, J. A. Grochow, and A. Allard Scientific Reports 6, 31708 (2016) [http://doi.org/10.1038/srep31708](http://doi.org/10.1038/srep31708) 166 | 167 | ```js 168 | import onionLayers from 'graphology-cores/onionLayers'; 169 | 170 | // Return the onion layers for each node 171 | const onion = onionLayers(graph); 172 | 173 | // Assign to each node its onion layer 174 | onionLayers.assign(graph); 175 | 176 | // Assign with a custom attribute label 177 | onionLayers.assign(graph, 'onion'); 178 | ``` 179 | 180 | _Arguments_ 181 | 182 | - **graph** _Graph_: target graph. 183 | - **nodeOnionLayerAttribute** ?string : the name of the attribute to use if onion layers are assigned to the nodes. 184 | 185 | -------------------------------------------------------------------------------- /standard-library/communities-louvain.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: communities-louvain 4 | nav_order: 3 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/communities-louvain" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/communities-louvain/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Communities Louvain 12 | 13 | Implementation of the [Louvain algorihtm](https://en.wikipedia.org/wiki/Louvain_modularity) for community detection to be used with [`graphology`](..). 14 | 15 | ## References 16 | 17 | > M. E. J. Newman, « Modularity and community structure in networks », Proc. Natl. Acad. Sci. USA, vol. 103, no 23, 2006, p. 8577–8582 https://dx.doi.org/10.1073%2Fpnas.0601602103 18 | 19 | > Newman, M. E. J. « Community detection in networks: Modularity optimization and maximum likelihood are equivalent ». Physical Review E, vol. 94, no 5, novembre 2016, p. 052315. arXiv.org, doi:10.1103/PhysRevE.94.052315. https://arxiv.org/pdf/1606.02319.pdf 20 | 21 | > Blondel, Vincent D., et al. « Fast unfolding of communities in large networks ». Journal of Statistical Mechanics: Theory and Experiment, vol. 2008, no 10, octobre 2008, p. P10008. DOI.org (Crossref), doi:10.1088/1742-5468/2008/10/P10008. https://arxiv.org/pdf/0803.0476.pdf 22 | 23 | > Nicolas Dugué, Anthony Perez. Directed Louvain: maximizing modularity in directed networks. [Research Report] Université d’Orléans. 2015. hal-01231784 https://hal.archives-ouvertes.fr/hal-01231784 24 | 25 | > R. Lambiotte, J.-C. Delvenne and M. Barahona. Laplacian Dynamics and Multiscale Modular Structure in Networks, doi:10.1109/TNSE.2015.2391998. https://arxiv.org/abs/0812.1770 26 | 27 | > Traag, V. A., et al. « From Louvain to Leiden: Guaranteeing Well-Connected Communities ». Scientific Reports, vol. 9, no 1, décembre 2019, p. 5233. DOI.org (Crossref), doi:10.1038/s41598-019-41695-z. https://arxiv.org/abs/1810.08473 28 | 29 | ## Installation 30 | 31 | ``` 32 | npm install graphology-communities-louvain 33 | ``` 34 | 35 | ## Usage 36 | 37 | Runs the Louvain algorithm to detect communities in the given graph. It works both for undirected & directed graph by using the relevant modularity computations. 38 | 39 | This function also works on multi graphs but won't work with mixed graph as it is not trivial to adapt modularity to this case. As such, if you need to process a true mixed graph (this function will correctly handle mixed graphs containing only directed or undirected edges), cast your graph as either directed or undirected using [graphology-operators](/standard-library/operators) dedicated functions. 40 | 41 | Note also that this algorithm's run time is bounded by the number of edges in your graph. As such, it might be preferable, in some cases, to cast your multi graph as a simple one using [graphology-operators](/standard-library/operators) functions for better performance. 42 | 43 | Note that the community labels are returned as an integer range from 0 to n. 44 | 45 | ```js 46 | import louvain from 'graphology-communities-louvain'; 47 | 48 | // To retrieve the partition 49 | const communities = louvain(graph); 50 | 51 | // To directly assign communities as a node attribute 52 | louvain.assign(graph); 53 | 54 | // If you need to pass custom options 55 | louvain.assign(graph, { 56 | resolution: 0.8 57 | }); 58 | 59 | // If you want to return some details about the algorithm's execution 60 | var details = louvain.detailed(graph); 61 | 62 | // If you want to ignore your graph's weights 63 | louvain.assign(graph, {getEdgeWeight: null}); 64 | ``` 65 | 66 | _Arguments_ 67 | 68 | - **graph** _Graph_: target graph. 69 | - **options** ?object: options: 70 | - **getEdgeWeight** ?string\|function weight: name of the edges' weight attribute or getter function. 71 | - **nodeCommunityAttribute** ?string community: name of the community attribute. 72 | - **fastLocalMoves** ?boolean true: whether to use a well-known optimization relying on a queue set to traverse the graph more efficiently. 73 | - **randomWalk** ?boolean true: whether to traverse the graph randomly. 74 | - **resolution** ?number 1: resolution parameter. An increased resolution should produce more communities. 75 | - **rng** ?function Math.random: RNG function to use for `randomWalk`. Useful if you need to seed your rng using, for instance, [seedrandom](https://www.npmjs.com/package/seedrandom). 76 | 77 | _Detailed Output_ 78 | 79 | - **communities** object: partition of the graph. 80 | - **count** number: number of communities in the partition. 81 | - **deltaComputations** number: number of delta computations that were run to produce the partition. 82 | - **dendrogram** array: array of partitions. 83 | - **modularity** number: final modularity of the graph given the found partition. 84 | - **moves** array: array of array of number of moves if `fastLocalMoves` was false or array of number of moves if `fastLocalMoves` was true. 85 | - **nodesVisited** number: number of times nodes were visited. 86 | 87 | ## Benchmark 88 | 89 | To run the benchmark: 90 | 91 | ``` 92 | npm install --no-save ngraph.louvain.native 93 | node benchmark/comparison.js 94 | ``` 95 | 96 | ``` 97 | Clustered Undirected graph with 1000 nodes and 9724 edges. 98 | 99 | graphology undirected1000: 52.732ms 100 | Communities 8 101 | Modularity 0.42944314393593924 102 | 103 | jlouvain undirected1000: 2368.053ms 104 | Communities 8 105 | Modularity 0.4302331134880074 106 | 107 | ngraph.louvain undirected1000: 71.108ms 108 | Communities 8 109 | 110 | ngraph.louvain.native undirected1000: 39.185ms 111 | Communities 7 112 | 113 | --- 114 | 115 | EuroSIS Directed graph with 1285 nodes and 7524 edges. 116 | 117 | graphology euroSis: 30.809ms 118 | Communities 19 119 | Modularity 0.7375260763995757 120 | 121 | jlouvain euroSis: 1310.008ms 122 | Communities 23 123 | Modularity 0.7376116434498033 124 | 125 | ngraph euroSis: 38.262ms 126 | Communities 16 127 | 128 | ngraph.native euroSis: 20.018ms 129 | Communities 16 130 | 131 | --- 132 | 133 | Big Undirected graph with 50000 nodes and 994713 edges. 134 | 135 | graphology bigGraph: 937.942ms 136 | Communities 43 137 | Modularity 0.481431448861252 138 | 139 | jLouvain is too slow... 140 | 141 | ngraph bigGraph: 7783.050ms 142 | Communities 44 143 | 144 | ngraph.native bigGraph: 8415.692ms 145 | Communities 1 146 | ``` 147 | 148 | -------------------------------------------------------------------------------- /events.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Events 4 | nav_order: 11 5 | menu_toc: 6 | - nodeAdded 7 | - edgeAdded 8 | - nodeDropped 9 | - edgeDropped 10 | - cleared 11 | - edgesCleared 12 | - attributesUpdated 13 | - nodeAttributesUpdated 14 | - edgeAttributesUpdated 15 | - eachNodeAttributesUpdated 16 | - eachEdgeAttributesUpdated 17 | --- 18 | 19 | 20 | # Events 21 | 22 | The `Graph` instance is a node.js-like event emitter. As such, you can listen to its events in order to be able to react. This is particularly useful if you need to render your graph or maintain external indexes. 23 | 24 | Documentation about node.js event emitters can be found [here](https://nodejs.org/api/events.html). 25 | 26 | Note that the emitted payload is always an object with various keys. 27 | 28 | ## nodeAdded 29 | 30 | Emitted whenever a node is added to the graph. 31 | 32 | *Example* 33 | 34 | ```js 35 | graph.on('nodeAdded', function({key}) { 36 | console.log(key); 37 | }) 38 | 39 | graph.addNode('Thomas'); 40 | // Will print: 41 | >>> 'Thomas' 42 | ``` 43 | 44 | *Payload* 45 | 46 | * **key** any: the added node. 47 | * **attributes** object: the node's attributes. 48 | 49 | ## edgeAdded 50 | 51 | Emitted whenever an edge is added to the graph. 52 | 53 | *Example* 54 | 55 | ```js 56 | graph.on('edgeAdded', function({key, source, target}) { 57 | console.log(key, source, target); 58 | }) 59 | 60 | graph.addEdgeWithKey('T->R', 'Thomas', 'Richard'); 61 | // Will print: 62 | >>> 'T->R', 'Thomas', 'Richard' 63 | ``` 64 | 65 | *Payload* 66 | 67 | * **key** any: the added edge. 68 | * **source** any: the added edge's source. 69 | * **target** any: the added edge's target. 70 | * **attributes** object: the edge's attributes. 71 | * **undirected** boolean: whether the edge is undirected. 72 | 73 | ## nodeDropped 74 | 75 | Emitted whenever a node is dropped from the graph. 76 | 77 | *Example* 78 | 79 | ```js 80 | graph.on('nodeDropped', function({key}) { 81 | console.log(key); 82 | }) 83 | 84 | graph.dropNode('Thomas'); 85 | // Will print: 86 | >>> 'Thomas' 87 | ``` 88 | 89 | *Payload* 90 | 91 | * **key** any: the dropped node's key. 92 | * **attributes** object: the node's attributes. 93 | 94 | ## edgeDropped 95 | 96 | Emitted whenever an edge is dropped from the graph. 97 | 98 | *Example* 99 | 100 | ```js 101 | graph.on('edgeDropped', function({key, source, target}) { 102 | console.log(key, source, target); 103 | }) 104 | 105 | graph.addEdgeWithKey('T->R', 'Thomas', 'Richard'); 106 | graph.dropEdge('T->R'); 107 | // Will print: 108 | >>> 'T->R', 'Thomas', 'Richard' 109 | ``` 110 | 111 | *Payload* 112 | 113 | * **key** any: the dropped edge's key. 114 | * **source** any: the dropped edge's source. 115 | * **target** any: the dropped edge's target. 116 | * **attributes** object: the edge's attributes. 117 | * **undirected** boolean: whether the edge is undirected. 118 | 119 | ## cleared 120 | 121 | Emitted whenever the graph is cleared when using the [`#.clear`](mutations#clear) method. Note that when using this method, the `nodeDropped` & the `edgeDropped` events won't be emitted. 122 | 123 | *Example* 124 | 125 | ```js 126 | graph.on('cleared', function() { 127 | console.log(graph.order, graph.size); 128 | }); 129 | 130 | graph.clear(); 131 | // Will print: 132 | >>> 0, 0 133 | ``` 134 | 135 | ## edgesCleared 136 | 137 | Emitted whenever the graph is cleared of its edges when using the [`#.clearEdges`](mutations#clearedges) method. Note that when using this method the `edgeDropped` events won't be emitted. 138 | 139 | *Example* 140 | 141 | ```js 142 | graph.on('edgesCleared', function() { 143 | console.log(graph.order, graph.size); 144 | }); 145 | 146 | graph.clearEdges(); 147 | // Will print: 148 | >>> 45, 0 149 | ``` 150 | 151 | ## attributesUpdated 152 | 153 | Emitted whenever the attributes of the graph are updated. 154 | 155 | *Example* 156 | 157 | ```js 158 | graph.on('attributesUpdated', function({type}) { 159 | console.log(type); 160 | }); 161 | 162 | graph.setAttribute('name', 'My Beautiful Graph'); 163 | // Will print: 164 | 'set' 165 | ``` 166 | 167 | *Payload* 168 | 169 | * **type** string: type of the update, one of `set`, `remove`, `replace`, `merge` or `update`. 170 | * **attributes** object: the graph's attributes. 171 | * **name** [string]: the name of the edited attributes if type is `set` or `remove`. 172 | * **data** [object]: merged data in case the type is `merge`. 173 | 174 | ## nodeAttributesUpdated 175 | 176 | Emitted whenever the attributes of the node are updated. 177 | 178 | *Example* 179 | 180 | ```js 181 | graph.on('nodeAttributesUpdated', function({key, type}) { 182 | console.log(key, type); 183 | }); 184 | 185 | graph.setNodeAttribute('Thomas', 'age', 54); 186 | // Will print: 187 | 'Thomas', 'set' 188 | ``` 189 | 190 | *Payload* 191 | 192 | * **type** string: type of the update, one of `set`, `remove`, `replace`, `merge` or `update`. 193 | * **key** string: the affected node's key. 194 | * **attributes** object: the node's attributes. 195 | * **name** [string]: the name of the edited attributes if type is `set` or `remove`. 196 | * **data** [object]: merged data in case the type is `merge`. 197 | 198 | ## edgeAttributesUpdated 199 | 200 | Emitted whenever the attributes of the edge are updated. 201 | 202 | *Example* 203 | 204 | ```js 205 | graph.on('edgeAttributesUpdated', function({key, type}) { 206 | console.log(key, type); 207 | }); 208 | 209 | graph.setEdgeAttribute('T->R', 'type', 'KNOWS'); 210 | // Will print: 211 | 'Thomas', 'set' 212 | ``` 213 | 214 | *Payload* 215 | 216 | * **type** string: type of the update, one of `set`, `remove`, `replace`, `merge` or `update`. 217 | * **key** string: the affected edge's key. 218 | * **attributes** object: the edge's attributes. 219 | * **name** [string]: the name of the edited attributes if type is `set` or `remove`. 220 | * **data** [object]: merged data in case the type is `merge`. 221 | 222 | ## eachNodeAttributesUpdated 223 | 224 | Emitted whenever the [#.updateEachNodeAttributes](attributes#updateeachnodeattributes) is called. 225 | 226 | *Payload* 227 | 228 | * **hints** [object]: hints (only if they were provided by user, else `null`): 229 | * **attributes** [array]: the list of updated attribute names. 230 | 231 | ## eachEdgeAttributesUpdated 232 | 233 | Emitted whenever the [#.updateEachEdgeAttributes](attributes#updateeachedgeattributes) is called. 234 | 235 | *Payload* 236 | 237 | * **hints** [object]: hints (only if they were provided by user, else `null`): 238 | * **attributes** [array]: the list of updated attribute names. 239 | -------------------------------------------------------------------------------- /standard-library/shortest-path.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: shortest-path 4 | nav_order: 17 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/shortest-path" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/shortest-path/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Shortest Path 12 | 13 | Shortest path functions for [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-shortest-path 19 | ``` 20 | 21 | ## Usage 22 | 23 | - [Unweighted](#unweighted) 24 | - [bidirectional](#bidirectional) 25 | - [singleSource](#singlesource) 26 | - [singleSourceLength](#singlesourcelength) 27 | - [undirectedSingleSourceLength](#undirectedsinglesourcelength) 28 | - [Dijkstra](#dijkstra) 29 | - [bidirectional](#dijkstra-bidirectional) 30 | - [singleSource](#dijkstra-singlesource) 31 | - [A-star](#a-star) 32 | - [bidirectional](#astar-bidirectional) 33 | - [Utilities](#utilities) 34 | - [edgePathFromNodePath](#edgepathfromnodepath) 35 | 36 | ## Unweighted 37 | 38 | ### bidirectional 39 | 40 | Returns the shortest path in the graph between source & target or `null` if such a path does not exist. 41 | 42 | ```js 43 | import {bidirectional} from 'graphology-shortest-path'; 44 | // Alternatively, if you want to load only the relevant code 45 | import {bidirectional} from 'graphology-shortest-path/unweighted'; 46 | 47 | // Returning the shortest path between source & target 48 | const path = bidirectional(graph, source, target); 49 | ``` 50 | 51 | _Arguments_ 52 | 53 | - **graph** _Graph_: a `graphology` instance. 54 | - **source** _any_: source node. 55 | - **target** _any_: target node. 56 | 57 | ### singleSource 58 | 59 | Return a map of every shortest path between the given source & all the nodes of the graph. 60 | 61 | ```js 62 | import {singleSource} from 'graphology-shortest-path'; 63 | // Alternatively, if you want to load only the relevant code 64 | import {singleSource} from 'graphology-shortest-path/unweighted'; 65 | 66 | // Returning every shortest path between source & every node of the graph 67 | const paths = singleSource(graph, source); 68 | ``` 69 | 70 | _Arguments_ 71 | 72 | - **graph** _Graph_: a `graphology` instance. 73 | - **source** _any_: source node. 74 | 75 | ### singleSourceLength 76 | 77 | Return a map of every shortest path length between the given source & all the nodes of the graph. 78 | 79 | ```js 80 | import {singleSourceLength} from 'graphology-shortest-path'; 81 | // Alternatively, if you want to load only the relevant code 82 | import {singleSourceLength} from 'graphology-shortest-path/unweighted'; 83 | 84 | // Returning every shortest path between source & every node of the graph 85 | const paths = singleSourceLength(graph, source); 86 | ``` 87 | 88 | _Arguments_ 89 | 90 | - **graph** _Graph_: a `graphology` instance. 91 | - **source** _any_: source node. 92 | 93 | ### undirectedSingleSourceLength 94 | 95 | Return a map of every shortest path length between the given source & all the nodes of the graph. This is basically the same as [singleSourceLength](#singlesourcelength) except that it will consider any given graph as undirected when traversing. 96 | 97 | ```js 98 | import {undirectedSingleSourceLength} from 'graphology-shortest-path'; 99 | // Alternatively, if you want to load only the relevant code 100 | import {undirectedSingleSourceLength} from 'graphology-shortest-path/unweighted'; 101 | 102 | // Returning every shortest path between source & every node of the graph 103 | const paths = undirectedSingleSourceLength(graph, source); 104 | ``` 105 | 106 | _Arguments_ 107 | 108 | - **graph** _Graph_: a `graphology` instance. 109 | - **source** _any_: source node. 110 | 111 | ## Dijkstra 112 | 113 |

bidirectional

114 | 115 | Returns the shortest path in the weighted graph between source & target or `null` if such a path does not exist. 116 | 117 | ```js 118 | import {dijkstra} from 'graphology-shortest-path'; 119 | // Alternatively, if you want to load only the relevant code 120 | import dijkstra from 'graphology-shortest-path/dijkstra'; 121 | 122 | // Returning the shortest path between source & target 123 | const path = dijkstra.bidirectional(graph, source, target); 124 | 125 | // If you store edges' weight in custom attribute 126 | const path = dijkstra.bidirectional(graph, source, target, 'customWeight'); 127 | 128 | // Using a custom weight getter function 129 | const path = dijkstra.bidirectional( 130 | graph, 131 | source, 132 | target, 133 | (_, attr) => attr.importance 134 | ); 135 | ``` 136 | 137 | _Arguments_ 138 | 139 | - **graph** _Graph_: a `graphology` instance. 140 | - **source** _any_: source node. 141 | - **target** _any_: target node. 142 | - **getEdgeWeight** ?string\|function weight: name of the weight attribute or getter function. 143 | 144 |

singleSource

145 | 146 | Return a map of every shortest path between the given source & all the nodes of the weighted graph. 147 | 148 | ```js 149 | import {dijkstra} from 'graphology-shortest-path'; 150 | // Alternatively, if you want to load only the relevant code 151 | import dijkstra from 'graphology-shortest-path/dijkstra'; 152 | 153 | // Returning every shortest path between source & every node of the graph 154 | const paths = dijkstra.singleSource(graph, source); 155 | 156 | // If you store edges' weight in custom attribute 157 | const paths = dijkstra.singleSource(graph, source, 'customWeight'); 158 | 159 | // Using a custom weight getter function 160 | const path = dijkstra.singleSource(graph, source, (_, attr) => attr.importance); 161 | ``` 162 | 163 | _Arguments_ 164 | 165 | - **graph** _Graph_: a `graphology` instance. 166 | - **source** _any_: source node. 167 | - **getEdgeWeight** ?string\|function weight: name of the weight attribute or getter function. 168 | 169 | ## A-star 170 | 171 |

bidirectional

172 | 173 | Returns the shortest path in the weighted graph between source & target or `null` if such a path does not exist. 174 | 175 | ```js 176 | import {astar} from 'graphology-shortest-path'; 177 | // Alternatively, if you want to load only the relevant code 178 | import astar from 'graphology-shortest-path/astar'; 179 | 180 | // Returning the shortest path between source & target 181 | const path = astar.bidirectional( 182 | graph, 183 | source, 184 | target, 185 | (_, attr) => attr.importance 186 | (node, finalTarget) => euclideanDistance(points[node], points[finalTarget]) 187 | ); 188 | ``` 189 | 190 | _Arguments_ 191 | 192 | - **graph** _Graph_: a `graphology` instance. 193 | - **source** _any_: source node. 194 | - **target** _any_: target node. 195 | - **getEdgeWeight** ?string\|function weight: name of the weight attribute or getter function. 196 | - **heuristic** ?function: heuristic function to compute distance between current node and final target. 197 | 198 | ## Utilities 199 | 200 | ### edgePathFromNodePath 201 | 202 | Helper function that can convert a node path to an edge path. 203 | 204 | ```js 205 | import {edgePathFromNodePath} from 'graphology-shortest-path'; 206 | // Alternatively, if you want to load only the relevant code 207 | import {edgePathFromNodePath} from 'graphology-shortest-path/utils'; 208 | 209 | const edgePath = edgePathFromNodePath(graph, nodePath); 210 | ``` 211 | 212 | _Arguments_ 213 | 214 | - **graph** _Graph_: a `graphology` instance. 215 | - **nodePath** _Array_: node path to convert. 216 | 217 | -------------------------------------------------------------------------------- /standard-library/operators.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: operators 4 | nav_order: 16 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/operators" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/operators/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Operators 12 | 13 | Miscellaneous operators to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-operators 19 | ``` 20 | 21 | ## Usage 22 | 23 | _Unary_ 24 | 25 | - [subgraph](#subgraph) 26 | - [reverse](#reverse) 27 | 28 | _Binary_ 29 | 30 | - [disjointUnion](#disjointunion) 31 | - [union](#union) 32 | 33 | _Cast_ 34 | 35 | - [toDirected](#todirected) 36 | - [toMixed](#tomixed) 37 | - [toMulti](#tomulti) 38 | - [toSimple](#tosimple) 39 | - [toUndirected](#toundirected) 40 | 41 | ### subgraph 42 | 43 | Return a graph's subgraph containing the given nodes and their relevant edges. 44 | 45 | ```js 46 | import {subgraph} from 'graphology-operators'; 47 | // Alternatively, to load only the relevant code: 48 | import subgraph from 'graphology-operators/subgraph'; 49 | 50 | // From an array of nodes 51 | const sub = subgraph(graph, ['John', 'Mary', 'Sue']); 52 | 53 | // From a set of nodes 54 | const sub = subgraph(graph, new Set(['John', 'Mary', 'Sue'])); 55 | 56 | // From a filtering function 57 | const sub = subgraph(graph, function (key, attr) { 58 | return key.startsWith('J') || attr.color === 'red'; 59 | }); 60 | ``` 61 | 62 | _Arguments_ 63 | 64 | - **graph** _Graph_: target graph. 65 | - **nodes** _array\|Set\|function_: either an array of nodes to keep, or a set of nodes to keep or a function taking a node's key and its attributes and tasked to filter the nodes to keep. 66 | 67 | ### reverse 68 | 69 | Reverse the given graph's directed edges. 70 | 71 | ```js 72 | import {reverse} from 'graphology-operators'; 73 | // Alternatively, to load only the relevant code: 74 | import reverse from 'graphology-operators/reverse'; 75 | 76 | const reversedGraph = reverse(graph); 77 | ``` 78 | 79 | _Arguments_ 80 | 81 | - **graph** _Graph_: target graph. 82 | 83 | ### disjointUnion 84 | 85 | Returns the disjoin union of the given graphs. To do so, the function will 86 | relabel your nodes & edges so both graphs can remain disjoint. 87 | 88 | ```js 89 | import {disjointUnion} from 'graphology-operators'; 90 | // Alternatively, to load only the relevant code: 91 | import disjointUnion from 'graphology-operators/disjoint-union'; 92 | 93 | const R = disjointUnion(G, H); 94 | ``` 95 | 96 | _Arguments_ 97 | 98 | - **G** _Graph_: first graph. 99 | - **H** _Graph_: second graph. 100 | 101 | ### union 102 | 103 | Returns the union of the given graphs. Nodes & edges present in both graph will have their attributes merges with precedence given to the second graph. 104 | 105 | ```js 106 | import {union} from 'graphology-operators'; 107 | // Alternatively, to load only the relevant code: 108 | import union from 'graphology-operators/union'; 109 | 110 | const R = union(G, H); 111 | ``` 112 | 113 | _Arguments_ 114 | 115 | - **G** _Graph_: first graph. 116 | - **H** _Graph_: second graph. 117 | 118 | ### toDirected 119 | 120 | Returns the directed version of the given graph where any undirected edge will be now considered as mutual. 121 | 122 | Note that you can pass a function to merge edge attributes in case of mixed edges conflicts. This can be useful to sum weights and so on... 123 | 124 | If an already directed graph is passed, the function will only return a copy of it. 125 | 126 | If passing a multi graph, undirected edges will only be converted as pairs ofmutual ones and will never be merged. 127 | 128 | ```js 129 | import {toDirected} from 'graphology-operators'; 130 | // Alternatively, to load only the relevant code: 131 | import toDirected from 'graphology-operators/to-directed'; 132 | 133 | const directedGraph = toDirected(graph); 134 | 135 | // Using a merging function 136 | const directedGraph = toDirected(graph, (currentAttr, nextAttr) => { 137 | return { 138 | ...currentAttr, 139 | weight: currentAttr.weight + nextAttr.weight 140 | }; 141 | }); 142 | ``` 143 | 144 | _Arguments_ 145 | 146 | - **graph** _Graph_: target graph. 147 | - **mergeOrOptions** ?function\|object: either a merging function or an options object: 148 | - **mergeEdge** ?function: merging function to use. 149 | 150 | ### toMixed 151 | 152 | Returns the given graph as mixed. 153 | 154 | If an already mixed graph is passed, the function will only return a copy of it. 155 | 156 | ```js 157 | import {toMixed} from 'graphology-operators'; 158 | // Alternatively, to load only the relevant code: 159 | import toMixed from 'graphology-operators/to-mixed'; 160 | 161 | const mixedGraph = toMixed(graph); 162 | ``` 163 | 164 | ### toMulti 165 | 166 | Returns the given graph as multi. 167 | 168 | If an already multi graph is passed, the function will only return a copy of it. 169 | 170 | ```js 171 | import {toMulti} from 'graphology-operators'; 172 | // Alternatively, to load only the relevant code: 173 | import toMulti from 'graphology-operators/to-multi'; 174 | 175 | const mixedGraph = toMulti(graph); 176 | ``` 177 | 178 | ### toSimple 179 | 180 | Returns the simple version of the given multigraph where we only keep a single edge of each type between nodes. 181 | 182 | Note that you can pass a function to merge edge attributes in case of mutual edges or mixed edges conflicts. This can be useful to sum weights and so on... 183 | 184 | If an already simple graph is passed, the function will only return a copy of it. 185 | 186 | ```js 187 | import {toSimple} from 'graphology-operators'; 188 | // Alternatively, to load only the relevant code: 189 | import toSimple from 'graphology-operators/to-simple'; 190 | 191 | const simpleGraph = toSimple(multiGraph); 192 | 193 | // Using a merging function 194 | const simpleGraph = toSimple(graph, (currentAttr, nextAttr) => { 195 | return { 196 | ...currentAttr, 197 | weight: currentAttr.weight + nextAttr.weight 198 | }; 199 | }); 200 | ``` 201 | 202 | _Arguments_ 203 | 204 | - **graph** _Graph_: target graph. 205 | - **mergeOrOptions** ?function|object: either a merging function or an options object: 206 | - **mergeEdge** ?function: merging function to use. 207 | 208 | ### toUndirected 209 | 210 | Returns the undirected version of the given graph where any directed edge will be now considered as undirected. 211 | 212 | Note that you can pass a function to merge edge attributes in case of mutual edges or mixed edges conflicts. This can be useful to sum weights and so on... 213 | 214 | If an already undirected graph is passed, the function will only return a copy of it. 215 | 216 | If passing a multi graph, directed edges will only be converted as undirected ones and will never be merged. 217 | 218 | ```js 219 | import {toUndirected} from 'graphology-operators'; 220 | // Alternatively, to load only the relevant code: 221 | import toUndirected from 'graphology-operators/to-undirected'; 222 | 223 | const undirectedGraph = toUndirected(graph); 224 | 225 | // Using a merging function 226 | const undirectedGraph = toUndirected(graph, (currentAttr, nextAttr) => { 227 | return { 228 | ...currentAttr, 229 | weight: currentAttr.weight + nextAttr.weight 230 | }; 231 | }); 232 | ``` 233 | 234 | _Arguments_ 235 | 236 | - **graph** _Graph_: target graph. 237 | - **mergeOrOptions** ?function|object: either a merging function or an options object: 238 | - **mergeEdge** ?function: merging function to use. 239 | 240 | -------------------------------------------------------------------------------- /standard-library/utils.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: utils 4 | nav_order: 21 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/utils" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/utils/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Utils 12 | 13 | Miscellaneous utility functions to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-utils 19 | ``` 20 | 21 | ## Usage 22 | 23 | _Assertions_ 24 | 25 | - [#.isGraph](#isgraph) 26 | - [#.isGraphConstructor](#isgraphconstructor) 27 | 28 | _Introspection_ 29 | 30 | - [#.inferMulti](#infermulti) 31 | - [#.inferType](#infertype) 32 | 33 | _Typical edge patterns_ 34 | 35 | - [#.mergeClique](#mergeclique) 36 | - [#.mergeCycle](#mergecycle) 37 | - [#.mergePath](#mergepath) 38 | - [#.mergeStar](#mergestar) 39 | 40 | _Miscellaneous helpers_ 41 | 42 | - [#.renameGraphKeys](#renamegraphkeys) 43 | - [#.updateGraphKeys](#updategraphkeys) 44 | 45 | ### #.isGraph 46 | 47 | Function returning whether the given value is a `graphology` implementation's instance. 48 | 49 | ```js 50 | import {isGraph} from 'graphology-utils'; 51 | // Alternatively, if you want to only load the relevant code: 52 | import isGraph from 'graphology-utils/is-graph'; 53 | 54 | const graph = new Graph(); 55 | 56 | isGraph(graph); 57 | >>> true 58 | 59 | isGraph(45); 60 | >>> false 61 | 62 | isGraph({hello: 'world'}); 63 | >>> false 64 | ``` 65 | 66 | _Arguments_ 67 | 68 | - **value** _any_: value to test. 69 | 70 | ### #.isGraphConstructor 71 | 72 | Function returning whether the given value is a `graphology` constructor. 73 | 74 | ```js 75 | import {isGraphConstructor} from 'graphology-utils'; 76 | // Alternatively, if you want to only load the relevant code: 77 | import isGraphConstructor from 'graphology-utils/is-graph-constructor'; 78 | 79 | isGraphConstructor(Graph); 80 | >>> true 81 | 82 | isGraphConstructor(45); 83 | >>> false 84 | 85 | isGraphConstructor(new Graph()); 86 | >>> false 87 | ``` 88 | 89 | _Arguments_ 90 | 91 | - **value** _any_: value to test. 92 | 93 | ### #.inferMulti 94 | 95 | Function returning whether the given graph is truly multi, i.e. if we can find at least one occurrence of multiple edges of the same type and direction between two nodes. 96 | 97 | ```js 98 | import {inferMulti} from 'graphology-utils'; 99 | // Alternatively, if you want to only load the relevant code: 100 | import inferMulti from 'graphology-utils/infer-multi'; 101 | 102 | const graph = new MultiGraph(); 103 | graph.addEdge(1, 2); 104 | 105 | inferMulti(graph); 106 | >>> false 107 | 108 | graph.addEdge(1, 2); 109 | 110 | inferMulti(graph); 111 | >>> true 112 | ``` 113 | 114 | ### #.inferType 115 | 116 | Function returning the inferred type of the given graph. This function is useful to check whether a given mixed graph is in fact a mere `directed` or `undirected` graph based on its actual edges. 117 | 118 | ```js 119 | import {inferType} from 'graphology-utils'; 120 | // Alternatively, if you want to only load the relevant code: 121 | import inferType from 'graphology-utils/infer-type'; 122 | 123 | const graph = new Graph(); 124 | graph.addUndirectedEdge(1, 2); 125 | 126 | inferType(graph); 127 | >>> 'directed' 128 | ``` 129 | 130 | ### #.mergeClique 131 | 132 | Function adding a clique to the given graph. 133 | 134 | ```js 135 | import {mergeClique} from 'graphology-utils'; 136 | // Alternatively, if you want to only load the relevant code: 137 | import mergeClique from 'graphology-utils/merge-clique'; 138 | 139 | const graph = new Graph(); 140 | 141 | mergeClique(graph, [1, 2, 3]); 142 | graph.edges().map(e => graph.extremities(e)); 143 | >>> [[1, 2], [1, 3], [2, 3]] 144 | ``` 145 | 146 | ### #.mergeCycle 147 | 148 | Function adding a cycle to the given graph. 149 | 150 | ```js 151 | import {mergeCycle} from 'graphology-utils'; 152 | // Alternatively, if you want to only load the relevant code: 153 | import mergeCycle from 'graphology-utils/merge-cycle'; 154 | 155 | const graph = new Graph(); 156 | 157 | mergeCycle(graph, [1, 2, 3, 4, 5]); 158 | graph.edges().map(e => graph.extremities(e)); 159 | >>> [[1, 2], [2, 3], [3, 4], [4, 5], [5, 1]] 160 | ``` 161 | 162 | _Arguments_ 163 | 164 | - **graph** _Graph_: target graph. 165 | - **cycle** _array_: array of nodes representing the cycle to add. 166 | 167 | ### #.mergePath 168 | 169 | Function adding a path to the given graph. 170 | 171 | ```js 172 | import {mergePath} from 'graphology-utils'; 173 | // Alternatively, if you want to only load the relevant code: 174 | import mergePath from 'graphology-utils/merge-path'; 175 | 176 | const graph = new Graph(); 177 | 178 | mergePath(graph, [1, 2, 3, 4, 5]); 179 | graph.edges().map(e => graph.extremities(e)); 180 | >>> [[1, 2], [2, 3], [3, 4], [4, 5]] 181 | ``` 182 | 183 | _Arguments_ 184 | 185 | - **graph** _Graph_: target graph. 186 | - **path** _array_: array of nodes representing the path to add. 187 | 188 | ### #.mergeStar 189 | 190 | Function adding a star to the given graph. 191 | 192 | ```js 193 | import {mergeStar} from 'graphology-utils'; 194 | // Alternatively, if you want to only load the relevant code: 195 | import mergeStar from 'graphology-utils/merge-star'; 196 | 197 | const graph = new Graph(); 198 | 199 | mergeStar(graph, [1, 2, 3, 4, 5]); 200 | graph.edges().map(e => graph.extremities(e)); 201 | >>> [[1, 2], [1, 3], [1, 4], [1, 5]] 202 | ``` 203 | 204 | _Arguments_ 205 | 206 | - **graph** _Graph_: target graph. 207 | - **star** _array_: array of nodes representing the star to add. 208 | 209 | ### #.renameGraphKeys 210 | 211 | Function renaming the nodes & edges key of a graph using mappings and returning a new graph with renamed keys. 212 | 213 | ```js 214 | import {renameGraphKeys} from 'graphology-utils'; 215 | // Alternatively, if you want to only load the relevant code: 216 | import renameGraphKeys from 'graphology-utils/rename-graph-keys'; 217 | 218 | const graph = new Graph(); 219 | graph.addNode('Martha'); 220 | graph.addNode('Catherine'); 221 | graph.addNode('John'); 222 | graph.addEdgeWithKey('M->C', 'Martha', 'Catherine'); 223 | graph.addEdgeWithKey('C->J', 'Catherine', 'John'); 224 | 225 | const renamedGraph = renameGraphKeys( 226 | graph, 227 | {Martha: 1, Catherine: 2, John: 3}, 228 | {'M->C': 'rel1', 'C->J': 'rel2'} 229 | ); 230 | 231 | renamedGraph.nodes(); 232 | >>> [1, 2, 3]; 233 | 234 | renamedGraph.edges(); 235 | >>> ['rel1', 'rel2']; 236 | ``` 237 | 238 | _Arguments_ 239 | 240 | - **graph** _Graph_: target graph. 241 | - **nodeKeyMapping** _object_: A key/value map for the node key mapping. 242 | - **edgeKeyMapping** ?object: A key/value map for the edge key mapping. 243 | 244 | ### #.updateGraphKeys 245 | 246 | Function updating the nodes & edges key of a graph using functions and returning a new graph with updated keys. 247 | 248 | ```js 249 | import {updateGraphKeys} from 'graphology-utils'; 250 | // Alternatively, if you want to only load the relevant code: 251 | import updateGraphKeys from 'graphology-utils/update-graph-keys'; 252 | 253 | const graph = new Graph(); 254 | graph.addNode('Martha'); 255 | graph.addNode('Catherine'); 256 | graph.addNode('John'); 257 | graph.addEdgeWithKey('M->C', 'Martha', 'Catherine'); 258 | graph.addEdgeWithKey('C->J', 'Catherine', 'John'); 259 | 260 | const updatedGraph = updateGraphKeys( 261 | graph, 262 | (key)=> { 263 | if (key === 'Martha') return 1; 264 | if (key === 'Catherine') return 2; 265 | return 3; 266 | }, 267 | (key) => { 268 | if (key === 'M->C') return 'rel1'; 269 | return 'rel2'; 270 | } 271 | ); 272 | 273 | updatedGraph.nodes(); 274 | >>> [1, 2, 3]; 275 | 276 | updatedGraph.edges(); 277 | >>> ['rel1', 'rel2']; 278 | ``` 279 | 280 | _Arguments_ 281 | 282 | - **graph** _Graph_: target graph. 283 | - **nodeKeyUdater** _function_: A function to compute a new node key from the same arguments that would be given to [`#.forEachNode`](https://graphology.github.io/iteration.html#foreachnode). 284 | - **edgeKeyUpdater** _function_: A function to compute a new edge key from the same arguments that would be given to [`#.forEachEdge`](https://graphology.github.io/iteration.html#foreachedge). 285 | 286 | -------------------------------------------------------------------------------- /read.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Read 4 | nav_order: 5 5 | menu_toc: 6 | - "#.hasNode" 7 | - "#.hasEdge" 8 | - "#.edge" 9 | - "#.degree" 10 | - "#.degreeWithoutSelfLoops" 11 | - "#.source" 12 | - "#.target" 13 | - "#.opposite" 14 | - "#.extremities" 15 | - "#.hasExtremity" 16 | - "#.isDirected" 17 | - "#.isSelfLoop" 18 | - "#.areNeighbors" 19 | --- 20 | 21 | # Read 22 | 23 | ## #.hasNode 24 | 25 | Returns whether the given node is found in the graph. 26 | 27 | *Example* 28 | 29 | ```js 30 | graph.addNode('Timothy'); 31 | 32 | graph.hasNode('Timothy'); 33 | >>> true 34 | 35 | graph.hasNode('Jack'); 36 | >>> false 37 | ``` 38 | 39 | *Arguments* 40 | 41 | * **node** any: node to find. 42 | 43 | ## #.hasEdge 44 | 45 | Returns whether the given edge is found in the graph or whether an edge links the given source & target. 46 | 47 | See also [#.areNeighbors](#areneighbors). 48 | 49 | *Example* 50 | 51 | ```js 52 | graph.addNode('Timothy'); 53 | graph.addNode('Clarice'); 54 | const edge = graph.addEdge('Clarice', 'Timothy'); 55 | 56 | // Using the edge's key: 57 | graph.hasEdge(edge); 58 | >>> true 59 | 60 | // Using the edge's source & target: 61 | graph.hasEdge('Clarice', 'Timothy'); 62 | >>> true 63 | 64 | graph.hasEdge('Clarice', 'John'); 65 | >>> false 66 | ``` 67 | 68 | *Arguments* 69 | 70 | 1. Using the key: 71 | * **edge** any: edge to find. 72 | 2. Using the source & target: 73 | * **source** any: source of the edge to find. 74 | * **target** any: target of the edge to find. 75 | 76 | *Variants* 77 | 78 | * `#.hasDirectedEdge` 79 | * `#.hasUndirectedEdge` 80 | 81 | ## #.edge 82 | 83 | Returns the key of the edge between given source & target or `undefined` if such an edge does not exist. 84 | 85 | Note that this method will throw if either source or target is not found in the graph. 86 | 87 | In addition, this method won't work on a multi graph and will throw because the graph cannot know which edge to return since there might be multiple edges between source & target. 88 | 89 | *Example* 90 | 91 | ```js 92 | graph.addNode('Timothy'); 93 | graph.addNode('Clarice'); 94 | graph.addNode('Olivia'); 95 | graph.addEdgeWithKey('C->T', 'Clarice', 'Timothy'); 96 | 97 | graph.edge('Clarice', 'Timothy'); 98 | >>> 'C->T' 99 | 100 | graph.edge('Clarice', 'Olivia'); 101 | >>> undefined 102 | ``` 103 | 104 | *Arguments* 105 | 106 | * **source** any: source of the edge to find. 107 | * **target** any: target of the edge to find. 108 | 109 | *Variants* 110 | 111 | * `#.directedEdge` 112 | * `#.undirectedEdge` 113 | 114 | ## #.degreeWithoutSelfLoops 115 | 116 | Returns the degree of the given node, without taking self loops into account. 117 | 118 | Will throw if the node is not found in the graph. 119 | 120 | *Example* 121 | 122 | ```js 123 | graph.addNodeFrom(['Timothy', 'Jack', 'Clarice', 'Martha']); 124 | graph.addEdge('Timothy', 'Timothy'); 125 | graph.addEdge('Timothy', 'Jack'); 126 | graph.addEdge('Timothy', 'Clarice'); 127 | graph.addEdge('Martha', 'Timothy'); 128 | 129 | graph.degreeWithoutSelfLoops('Timothy'); 130 | >>> 3 131 | graph.inWithoutSelfLoops('Timothy'); 132 | >>> 1 133 | graph.outWithoutSelfLoops('Timothy'); 134 | >>> 2 135 | ``` 136 | 137 | *Arguments* 138 | 139 | * **node** any: target node. 140 | 141 | *Variants* 142 | 143 | * `#.inDegreeWithoutSelfLoops` 144 | * `#.outDegreeWithoutSelfLoops` 145 | * `#.directedDegreeWithoutSelfLoops` (`#.inDegreeWithoutSelfLoops` + `#.outDegreeWithoutSelfLoops`) 146 | * `#.undirectedDegreeWithoutSelfLoops` 147 | * `#.degreeWithoutSelfLoops` (`#.directedDegreeWithoutSelfLoops` + `#.undirectedDegreeWithoutSelfLoops`) 148 | 149 | ## #.degree 150 | 151 | Returns the degree of the given node. 152 | 153 | Will throw if the node is not found in the graph. 154 | 155 | *Example* 156 | 157 | ```js 158 | graph.addNodeFrom(['Timothy', 'Jack', 'Clarice', 'Martha']); 159 | graph.addEdge('Timothy', 'Jack'); 160 | graph.addEdge('Timothy', 'Clarice'); 161 | graph.addEdge('Martha', 'Timothy'); 162 | 163 | graph.degree('Timothy'); 164 | >>> 3 165 | graph.inDegree('Timothy'); 166 | >>> 1 167 | graph.outDegree('Timothy'); 168 | >>> 2 169 | ``` 170 | 171 | *Arguments* 172 | 173 | * **node** any: target node. 174 | 175 | *Variants* 176 | 177 | * `#.inDegree` 178 | * `#.outDegree` 179 | * `#.directedDegree` (`#.inDegree` + `#.outDegree`) 180 | * `#.undirectedDegree` 181 | * `#.degree` (`#.directedDegree` + `#.undirectedDegree`) 182 | 183 | ## #.source 184 | 185 | Returns the source of the given edge. 186 | 187 | *Example* 188 | 189 | ```js 190 | graph.addNode('Timothy'); 191 | graph.addNode('Clarice'); 192 | const edge = graph.addEdge('Clarice', 'Timothy'); 193 | 194 | graph.source(edge); 195 | >>> 'Clarice' 196 | ``` 197 | 198 | *Arguments* 199 | 200 | * **edge** any: target edge. 201 | 202 | ## #.target 203 | 204 | Returns the target of the given edge. 205 | 206 | *Example* 207 | 208 | ```js 209 | graph.addNode('Timothy'); 210 | graph.addNode('Clarice'); 211 | const edge = graph.addEdge('Clarice', 'Timothy'); 212 | 213 | graph.target(edge); 214 | >>> 'Timothy' 215 | ``` 216 | 217 | *Arguments* 218 | 219 | * **edge** any: target edge. 220 | 221 | ## #.opposite 222 | 223 | Given a node & an edge, returns the node at the other end of the edge. 224 | 225 | *Example* 226 | 227 | ```js 228 | graph.addNode('Timothy'); 229 | graph.addNode('Clarice'); 230 | const edge = graph.addEdge('Clarice', 'Timothy'); 231 | 232 | graph.opposite('Timothy', edge); 233 | >>> 'Clarice' 234 | ``` 235 | 236 | *Arguments* 237 | 238 | * **node** any: target node. 239 | * **edge** any: target edge. 240 | 241 | ## #.extremities 242 | 243 | Returns both extremities of the given edge. 244 | 245 | *Example* 246 | 247 | ```js 248 | graph.addNode('Timothy'); 249 | graph.addNode('Clarice'); 250 | const edge = graph.addEdge('Clarice', 'Timothy'); 251 | 252 | graph.extremities(edge); 253 | >>> ['Timothy', 'Clarice'] 254 | ``` 255 | 256 | *Arguments* 257 | 258 | * **edge** any: target edge. 259 | 260 | ## #.hasExtremity 261 | 262 | Returns whether the given edge has the given node as extremity. 263 | 264 | ```js 265 | graph.addNode('Lucy'); 266 | graph.addNode('Timothy'); 267 | graph.addNode('Clarice'); 268 | const edge = graph.addEdge('Clarice', 'Timothy'); 269 | 270 | graph.hasExtremity(edge, 'Timothy'); 271 | >>> true 272 | 273 | graph.hasExtremity(edge, 'Lucy'); 274 | >>> false 275 | ``` 276 | 277 | ## #.isDirected 278 | 279 | Returns whether the given edge is directed. 280 | 281 | *Example* 282 | 283 | ```js 284 | graph.addNode('Timothy'); 285 | graph.addNode('Clarice'); 286 | const edge = graph.addEdge('Clarice', 'Timothy'); 287 | const undirectedEdge = graph.addUndirectedEdge('Clarice', 'Timothy'); 288 | 289 | graph.isDirected(edge); 290 | >>> true 291 | graph.isDirected(undirectedEdge); 292 | >>> false 293 | ``` 294 | 295 | *Arguments* 296 | 297 | * **edge** any: target edge. 298 | 299 | *Variants* 300 | 301 | * `#.isUndirected` 302 | 303 | ## #.isSelfLoop 304 | 305 | Returns whether the given edge is a self-loop. 306 | 307 | *Example* 308 | 309 | ```js 310 | graph.addNode('Timothy'); 311 | const edge = graph.addEdge('Timothy', 'Timothy'); 312 | 313 | graph.isSelfLoop(edge); 314 | >>> true 315 | ``` 316 | 317 | *Arguments* 318 | 319 | * **edge** any: target edge. 320 | 321 | ## #.areNeighbors 322 | 323 | Returns whether both nodes are neighbors. 324 | 325 | See also [#.hasEdge](#hasedge). 326 | 327 | *Examples* 328 | 329 | ```js 330 | graph.addNode('Timothy'); 331 | graph.addNode('Clarice'); 332 | graph.addNode('Zendar'); 333 | graph.addEdge('Clarice', 'Timothy'); 334 | 335 | graph.areNeighbors('Clarice', 'Timothy'); 336 | >>> true 337 | 338 | graph.areNeighbors('Zendar', 'Clarice'); 339 | >>> false 340 | ``` 341 | 342 | *Arguments* 343 | 344 | * **node** any: target node. 345 | * **neighbord** any: potential neighbor. 346 | 347 | *Variants* 348 | 349 | * `#.areDirectedNeighbors` 350 | * `#.areUndirectedNeighbors` 351 | * `#.areInNeighbors` 352 | * `#.areOutNeighbors` 353 | * `#.areInboundNeighbors` (in + undirected) 354 | * `#.areOutboundNeighbors` (out + undirected) 355 | -------------------------------------------------------------------------------- /standard-library/layout.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: layout 4 | nav_order: 11 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/layout" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/layout/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology Layout 12 | 13 | Collection of basic layout algorithms to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-layout 19 | ``` 20 | 21 | ## Usage 22 | 23 | _Basic_ 24 | 25 | - [circular](#circular) 26 | - [random](#random) 27 | 28 | _Advanced_ 29 | 30 | - [circlePack](#circlePack) 31 | 32 | _Utilities_ 33 | 34 | - [rotation](#rotation) 35 | - [collectLayout](#collectlayout) 36 | - [collectLayoutAsFlatArray](#collectlayoutasflatarray) 37 | - [assignLayout](#assignlayout) 38 | - [assignLayoutAsFlatArray](#assignlayoutasflatarray) 39 | 40 | ### circular 41 | 42 | Arranges the node in a circle (or an sphere/hypersphere in higher dimensions). 43 | 44 | _Example_ 45 | 46 | ```js 47 | import {circular} from 'graphology-layout'; 48 | // Alternatively, to load only the relevant code: 49 | import circular from 'graphology-layout/circular'; 50 | 51 | const positions = circular(graph); 52 | 53 | // With options: 54 | const positions = circular(graph, {scale: 100}); 55 | 56 | // To directly assign the positions to the nodes: 57 | circular.assign(graph); 58 | 59 | // To pass custom dimensions 60 | const positions = random(graph, {dimensions: ['x1', 'x2']}); 61 | ``` 62 | 63 | _Arguments_ 64 | 65 | - **graph** _Graph_: target graph. 66 | - **options** ?object: options: 67 | - **dimensions** ?array ['x', 'y']: dimensions of the layout. Cannot work with dimensions != 2. 68 | - **center** ?number 0.5: center of the layout. 69 | - **scale** ?number 1: scale of the layout. 70 | 71 | ### random 72 | 73 | Random layout positioning every node by choosing each coordinates uniformly at random on the interval `[0, 1)`. 74 | 75 | _Example_ 76 | 77 | ```js 78 | import {random} from 'graphology-layout'; 79 | // Alternatively, to load only the relevant code: 80 | import random from 'graphology-layout/random'; 81 | 82 | const positions = random(graph); 83 | 84 | // With options: 85 | const positions = random(graph, {rng: customRngFunction}); 86 | 87 | // To directly assign the positions to the nodes: 88 | random.assign(graph); 89 | 90 | // To pass custom dimensions 91 | const positions = random(graph, {dimensions: ['x', 'y', 'z']}); 92 | ``` 93 | 94 | _Arguments_ 95 | 96 | - **graph** _Graph_: target graph. 97 | - **options** ?object: options: 98 | - **dimensions** ?array ['x', 'y']: dimensions of the layout. 99 | - **center** ?number 0.5: center of the layout. 100 | - **rng** ?function Math.random: custom RNG function to use. 101 | - **scale** ?number 1: scale of the layout. 102 | 103 | ### circlePack 104 | 105 | Arranges the nodes as a bubble chart, according to specified attributes. 106 | 107 | _Example_ 108 | 109 | ```js 110 | import {circlepack} from 'graphology-layout'; 111 | // Alternatively, to load only the relevant code: 112 | import circlepack from 'graphology-layout/circlepack'; 113 | 114 | const positions = circlepack(graph); 115 | 116 | // With options 117 | const positions = circlepack(graph, { 118 | hierarchyAttributes: ['degree', 'community'], 119 | rng: customRngFunction 120 | }); 121 | 122 | // To directly assign the positions to the nodes: 123 | circlepack.assign(graph); 124 | ``` 125 | 126 | _Arguments_ 127 | 128 | - **graph** _Graph_: target graph. 129 | - **options** ?object: options: 130 | - **attributes** ?object: attributes to map: 131 | - **x** ?string x: name of the x position. 132 | - **y** ?string y: name of the y position. 133 | - **center** ?number 0: center of the layout. 134 | - **hierarchyAttributes** ?list []: attributes used to group nodes. 135 | - **rng** ?function Math.random: custom RNG function to use. 136 | - **scale** ?number 1: scale of the layout. 137 | 138 | ### rotation 139 | 140 | Rotates the node coordinates of the given graph by the given angle in radians (or in degrees using an option). 141 | 142 | Note that this function rotates your graph based on its center. If you want to use zero as the center for your rotation, use the `centeredOnZero` option. This option can also be used as an optimization strategy if you know your graph is already centered on zero to avoid needing to compute the graph's extent. 143 | 144 | _Example_ 145 | 146 | ```js 147 | import {rotation} from 'graphology-layout'; 148 | // Alternatively, to load only the relevant code: 149 | import rotation from 'graphology-layout/rotation'; 150 | 151 | const positions = rotation(graph, Math.PI / 2); 152 | 153 | // With options: 154 | const positions = rotation(graph, Math.PI / 2, {centeredOnZero: true}); 155 | 156 | // To directly assign the positions to the nodes: 157 | rotation.assign(graph, Math.PI / 2); 158 | ``` 159 | 160 | _Arguments_ 161 | 162 | - **graph** _Graph_: target graph. 163 | - **angle** _number_: rotation angle in radians (or degrees using an option below). 164 | - **options** ?object: options: 165 | - **dimensions** ?array ['x', 'y']: dimensions to use for the rotation. Cannot work with dimensions != 2. 166 | - **degrees** ?boolean false: whether the given angle is in degrees. 167 | - **centeredOnZero** ?boolean false: whether to rotate the graph around `0`, rather than the graph's center. 168 | 169 | ### collectLayout 170 | 171 | Function returning the given graph's layout as `{node: {x, y}}`. 172 | 173 | _Example_ 174 | 175 | ```js 176 | import {collectLayout} from 'graphology-layout/utils'; 177 | 178 | collectLayout(graph); 179 | 180 | // Custom dimensions 181 | collectLayout(graph, {dimensions: ['x', 'y', 'z']}); 182 | 183 | // Non exhaustive (i.e. node having missing dimensions will be returned also) 184 | collectLayout(graph, {exhaustive: false}); 185 | ``` 186 | 187 | _Arguments_ 188 | 189 | - **graph** _Graph_: target graph. 190 | - **options** ?object: options: 191 | - **dimensions** ?array ['x', 'y']: array of attribute names for the dimensions. 192 | - **exhaustive** ?boolean true: whether to collect positions of nodes having all the dimensions set. 193 | 194 | ### collectLayoutAsFlatArray 195 | 196 | Function returning the given graph's layout as a flat array of length `order * dimensions`. 197 | 198 | _Example_ 199 | 200 | ```js 201 | import {collectLayoutAsFlatArray} from 'graphology-layout/utils'; 202 | 203 | collectLayoutAsFlatArray(graph); 204 | 205 | // Custom dimensions 206 | collectLayoutAsFlatArray(graph, {dimensions: ['x', 'y', 'z']}); 207 | 208 | // Custom type 209 | collectLayoutAsFlatArray(graph, {type: Float32Array}); 210 | ``` 211 | 212 | _Arguments_ 213 | 214 | - **graph** _Graph_: target graph. 215 | - **options** ?object: options: 216 | - **dimensions** ?array ['x', 'y']: array of attribute names for the dimensions. 217 | - **type** ?constructor Float64Array: array class to use. 218 | 219 | ### assignLayout 220 | 221 | Function assigning a `{node: {x, y}}` layout to the given graph. 222 | 223 | _Example_ 224 | 225 | ```js 226 | import {assignLayout} from 'graphology-layout/utils'; 227 | 228 | assignLayout(graph, layout); 229 | 230 | // Custom dimensions 231 | assignLayout(graph, layout, {dimensions: ['x', 'y', 'z']}); 232 | ``` 233 | 234 | _Arguments_ 235 | 236 | - **graph** _Graph_: target graph. 237 | - **layout** _object_: layout mapping. 238 | - **options** ?object: options: 239 | - **dimensions** ?array ['x', 'y']: array of attribute names for the dimensions. 240 | 241 | ### assignLayoutAsFlatArray 242 | 243 | Function assigning a flat array layout to the given graph. 244 | 245 | _Example_ 246 | 247 | ```js 248 | import {assignLayoutAsFlatArray} from 'graphology-layout/utils'; 249 | 250 | assignLayoutAsFlatArray(graph, layout); 251 | 252 | // Custom dimensions 253 | assignLayoutAsFlatArray(graph, layout, {dimensions: ['x', 'y', 'z']}); 254 | ``` 255 | 256 | _Arguments_ 257 | 258 | - **graph** _Graph_: target graph. 259 | - **layout** _array_: layout flat array. 260 | - **options** ?object: options: 261 | - **dimensions** ?array ['x', 'y']: array of attribute names for the dimensions. 262 | 263 | -------------------------------------------------------------------------------- /design-choices.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Design choices 4 | nav_order: 2 5 | --- 6 | 7 | # Design choices 8 | {: .no_toc } 9 | 10 | 1. TOC 11 | {:toc} 12 | 13 | ## Keys 14 | 15 | Both nodes & edges are represented by keys in the graph. For simplicity's sake, like JavaScript's native objects, the graph will always coerce the given keys as strings. 16 | 17 | We could technically handle references as keys but finally decided against it for the following reasons: 18 | 19 | * Ensure that serialization remains as straightforward as possible. 20 | * Not to put a pressure on end-users' code by having to force the usage of, for instance, ES6 Maps to ensure their code will handle every Graph case possible. 21 | 22 | This said, the user should keep in mind that the graph have the same quirks as a JavaScript native object: 23 | 24 | * Numbers as keys will be coerced to strings. 25 | * Giving objects as keys will result in a `[object Object]` key etc. 26 | 27 | ## #.addEdge as sugar 28 | 29 | As explained in the [keys](#keys) section above, *all* edges must have a key in a `graphology` instance. 30 | 31 | Unfortunately it can be tedious to force users to provide their own key each time they want to add an edge to the graph. And even if edge keys can be very practical sometimes - especially with multigraphs - users do not even care about those in simpler use-cases since they can still reach those edges through their source & target very easily. 32 | 33 | So to make sure `graphology` remains convenient to use, we added an [`#.addEdge`](mutation#addedge) method that actually automatically creates a key for the user and returns it. 34 | 35 | As such, this method should be considered as [sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) as it will not create a special kind of edge that would be different than any other one stored within a `graphology` instance. 36 | 37 | This means, for instance, that serialized edges exported from `graphology` will always have a key and that this key outlives the current instance if you want to use it in a direct copy, or across runtimes. 38 | 39 | This decision seemed the best to us, in a Social Network Analysis context where serialization and portability across many tools is to be ensured and where many paradigms coexists regarding the identification of edges in a graph, with or without a key, sometimes even both. 40 | 41 | Here are two other solutions that we considered at one point but finally dismissed: 42 | 43 | 1. Distinguishing between edges having a key and edges without one.
We did not choose this solution because, even if edges without keys can be accessed through their source & target, it makes edge indexing outside a graph (as it is often the case in outside library when aggregating metrics, for instance) very painful. 44 | 2. Using ES6 Symbol as keys for edges created without one.
We did not choose this solution for the following reasons: 45 | 1. This would mean that those keys would be instance-specific or at least runtime-specific. 46 | 2. This would usually lead to mixed-type indexing (both symbols and strings) in objects or maps outside of the graph and those are often badly optimized by most JS engines. 47 | 3. Symbols are not very well known by most JS developers and have many quirks, such as the fact they are not enumerable which can very easily confuse beginners. 48 | 3. Using number keys for edges created without one.
We did not choose this solution for the same reasons as symbols, namely mixed indexing, the quirkiness of it all and the fact that JavaScript has a tradition of coercing object keys to strings that would make them unfit for external indexing of edges. 49 | 50 | ## Mixed graphs & type precedence 51 | 52 | When using mixed graphs, one should consider that directed edges will always take precedence over the undirected ones. 53 | 54 | Use the typed methods to solve the ambiguity. 55 | 56 | ```js 57 | import Graph from 'graphology'; 58 | 59 | const graph = new Graph(); 60 | graph.addNode(1); 61 | graph.addNode(2); 62 | graph.addNode(3); 63 | 64 | // This will add a directed edge 65 | graph.addEdge(1, 2); 66 | // Same as: 67 | graph.addDirectedEdge(1, 2); 68 | 69 | // This will add an undirected edge 70 | graph.addUndirectedEdge(1, 2); 71 | 72 | // So, for instance, if you need to target the undirected edge 73 | graph.setUndirectedEdgeAttribute(1, 2, 'type', 'LIKES'); 74 | ``` 75 | 76 | ## Errors 77 | 78 | Rather than failing silently, `graphology` API will often throw errors to notify the developer that something inconsistent was performed so they can fix their code. 79 | 80 | `graphology` errors are designed to be as helpful as possible to help the developer fix the issue. For instance, the implementation will gladly tell you not to use the `#.addUndirectedEdge` method on a `directed` graph and point you towards the `#.addEdge` or `#.addDirectedEdge` method instead. 81 | 82 | ```js 83 | import {DirectedGraph} from 'graphology'; 84 | 85 | const graph = new DirectedGraph(); 86 | graph.addNode('Lucy'); 87 | graph.addNode('Catherine'); 88 | graph.addUndirectedEdge('Lucy', 'Catherine'); 89 | >>> Error `DirectedGraph.addUndirectedEdge: You cannot add an undirected edge. 90 | to a directed graph Use the #.addEdge or #.addDirectedEdge method instead.` 91 | ``` 92 | 93 | ## Chaining 94 | 95 | By convention, you can assume that if the documentation does not tell you what a specific method returns then it will return the instance itself for chaining purposes. 96 | 97 | One might note that, contrary to some other libraries, `graphology` returns the node or edge on the `#.addNode` & `#.addEdge` method rather than enabling chaining methods. This was made so that the "get/has" pattern remains usable and to avoid unnecessary reads of the graph when building one. 98 | 99 | ## Concerning order 100 | 101 | The user should not expect the `Graph` to retain insertion order. It might be a side effect of the used implementation to retain an order but it is not guaranteed by the specification. 102 | 103 | However, if no node/edge was added or dropped from the graph, the order should remain stable from one iteration to the next. 104 | 105 | ```js 106 | const graph = new Graph(); 107 | graph.addNode('First Node'); 108 | graph.addNode('Second Node'); 109 | 110 | // Won't necessarily print 'First Node' then 'Second Node' 111 | // Might be the other way around. 112 | graph.forEachNode(node => { 113 | console.log(node); 114 | }); 115 | ``` 116 | 117 | ## Polling edges 118 | 119 | Note that two different ways are generally accessible to you when targeting edges in the graph: 120 | 121 | * Either you can provide their key. 122 | * Or you can use their source & target to find them. 123 | 124 | Note however that, since graph instances can support parallel edges, you might sometimes want to avoid using the second way since it will throw if you handle a multi-graph. 125 | 126 | ```js 127 | const graph = new Graph(); 128 | graph.addNode('Eric'); 129 | graph.addNode('Martha'); 130 | 131 | // Adding our edge 132 | const edge = graph.addEdge('Eric', 'Martha'); 133 | 134 | // Now we can see if the edge exists either by key: 135 | graph.hasEdge(edge); 136 | >>> true 137 | 138 | // or by source & target: 139 | graph.hasEdge('Eric', 'Martha'); 140 | >>> true 141 | ``` 142 | 143 | ## Avoiding methods affecting indexing constraints 144 | 145 | TL;DR: we won't be adding methods like `#.renameNode`, `#.renameEdge` or `#.setNodeExtremities` etc. 146 | 147 | Indeed, even though is possible to expose methods able to change a node's key, or even an edge's extremities, we decided against it for the following reasons: 148 | 149 | 1. It affects indexing constraints that may be enforced outside of the graph, using events for instance, and this needlessly complexify what you need to react to when developing external indexations of the graphs. For instance, instead of `addNode` and `dropNode` one would also be required to handle something like `renameNode`. 150 | 2. In most languages, one is not able to change dictionary keys. But you can still somehow do it by deleting the key and add its value using another one. You can also do so with a `Graph`, even if it can feel more cumbersome. But under the hood, since the internal indices will need to update this unique key constraint, an internal method would probably amount to the same operations. 151 | 152 | Finally, note that [`graphology-utils`](standard-library/utils) exposes helpers for those kind of scenarios such as the [`renameGraphKeys`](standard-library/utils#renamegraphkeys) function. 153 | 154 | ## Order of undirected edge extremities 155 | 156 | By convention, undirected edge extremities are recorded in the order they were first provided by the user. 157 | 158 | Also, it can be surprising but when iterating over undirected edges, the argument given as source may not be the node from which we are iterating from: 159 | 160 | ```js 161 | graph.forEachUndirectedEdge(node, (edge, attr, source, target) => { 162 | console.log(node === source); // Sometimes true, sometimes false 163 | }); 164 | ``` 165 | 166 | Indeed, we chose to guarantee that the "source" & the "target" will always be the same when accessing information about an undirected edge. 167 | 168 | This means that, even if this feels arbitrary because source & target are irrelevant for an undirected edge, the [`#.source`](read#source) method will always return the same node, and will always be referenced as this edge's source when iterating, even from a node. 169 | 170 | It could be nice sugar to reorder source & target of undirected edges in iteration but it would definitely be confusing or make no sense in some scenarios as `graphology` Graphs can be many things. For instance, let's consider the case when we want to iterate over inbound (undirected + in) or even all edges in a mixed graph. 171 | -------------------------------------------------------------------------------- /standard-library/generators.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: generators 4 | nav_order: 7 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/generators" 8 | 9 | --- 10 | 11 | # Graphology Generators 12 | 13 | Various graph generators to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-generators 19 | ``` 20 | 21 | ## Usage 22 | 23 | - [Classic graphs](#classic-graphs) 24 | - [Complete](#complete) 25 | - [Empty](#empty) 26 | - [Ladder](#ladder) 27 | - [Path](#path) 28 | - [Community graphs](#community-graphs) 29 | - [Caveman](#caveman) 30 | - [Connected Caveman](#connected-caveman) 31 | - [Random graphs](#random-graphs) 32 | - [Clusters](#clusters) 33 | - [Erdos-Renyi](#erdos-renyi) 34 | - [Girvan-Newman](#girvan-newman) 35 | - [Small graphs](#small-graphs) 36 | - [Krackhardt Kite](#krackhardt-kite) 37 | - [Social graphs](#social-graphs) 38 | - [Florentine Families](#florentine-families) 39 | - [Karate Club](#karate-club) 40 | 41 | ### Classic graphs 42 | 43 | #### Complete 44 | 45 | Creates a [complete](https://en.wikipedia.org/wiki/Complete_graph) graph. 46 | 47 | ```js 48 | import Graph, {UndirectedGraph} from 'graphology'; 49 | import {complete} from 'graphology-generators/classic'; 50 | // Alternatively, if you only want to load relevant code 51 | import complete from 'graphology-generators/classic/complete'; 52 | 53 | // Creating a complete graph 54 | const graph = complete(Graph, 10); 55 | 56 | // Using another constuctor to create, say, a complete undirected graph 57 | const graph = complete(UndirectedGraph, 10); 58 | ``` 59 | 60 | **Arguments** 61 | 62 | - **constructor** _Class_: a `graphology` constructor. 63 | - **order** _number_: number of nodes in the generated graph. 64 | 65 | #### Empty 66 | 67 | Creates an empty graph with the desired number of nodes and no edges. 68 | 69 | ```js 70 | import Graph, {UndirectedGraph} from 'graphology'; 71 | import {empty} from 'graphology-generators/classic'; 72 | // Alternatively, if you only want to load relevant code 73 | import empty from 'graphology-generators/classic/empty'; 74 | 75 | // Creating an empty graph 76 | const graph = empty(Graph, 10); 77 | 78 | // Using another constuctor to create, say, an empty undirected graph 79 | const graph = empty(UndirectedGraph, 10); 80 | ``` 81 | 82 | **Arguments** 83 | 84 | - **constructor** _Class_: a `graphology` constructor. 85 | - **order** _number_: number of nodes in the generated graph. 86 | 87 | #### Ladder 88 | 89 | Creates a ladder graph with the desired length. Note that the generated graph will logically have twice the number of nodes. 90 | 91 | ```js 92 | import Graph, {UndirectedGraph} from 'graphology'; 93 | import {ladder} from 'graphology-generators/classic'; 94 | // Alternatively, if you only want to load relevant code 95 | import ladder from 'graphology-generators/classic/ladder'; 96 | 97 | // Creating a ladder graph 98 | const graph = ladder(Graph, 10); 99 | 100 | // Using another constuctor to create, say, a undirected ladder graph 101 | const graph = ladder(UndirectedGraph, 10); 102 | ``` 103 | 104 | **Arguments** 105 | 106 | - **constructor** _Class_: a `graphology` constructor. 107 | - **length** _number_: length of the ladder. 108 | 109 | #### Path 110 | 111 | Creates a path graph. 112 | 113 | ```js 114 | import Graph, {UndirectedGraph} from 'graphology'; 115 | import {path} from 'graphology-generators/classic'; 116 | // Alternatively, if you only want to load relevant code 117 | import path from 'graphology-generators/classic/path'; 118 | 119 | // Creating a path graph 120 | const graph = path(Graph, 10); 121 | 122 | // Using another constuctor to create, say, a path undirected graph 123 | const graph = path(UndirectedGraph, 10); 124 | ``` 125 | 126 | **Arguments** 127 | 128 | - **constructor** _Class_: a `graphology` constructor. 129 | - **order** _number_: number of nodes in the generated graph. 130 | 131 | ### Community graphs 132 | 133 | #### Caveman 134 | 135 | Creates a Caveman graph containing `l` components of `k` nodes. 136 | 137 | ```js 138 | import Graph, {UndirectedGraph} from 'graphology'; 139 | import {caveman} from 'graphology-generators/community'; 140 | // Alternatively, if you only want to load relevant code 141 | import caveman from 'graphology-generators/community/caveman'; 142 | 143 | // Creating a caveman graph 144 | const graph = caveman(Graph, 6, 8); 145 | ``` 146 | 147 | **Arguments** 148 | 149 | - **constructor** _Class_: a `graphology` constructor. 150 | - **l** _number_: number of components in the graph. 151 | - **k** _number_: number of nodes of the components. 152 | 153 | #### Connected Caveman 154 | 155 | Creates a Connected Caveman graph containing `l` components of `k` nodes. 156 | 157 | ```js 158 | import Graph, {UndirectedGraph} from 'graphology'; 159 | import {connectedCaveman} from 'graphology-generators/community'; 160 | // Alternatively, if you only want to load relevant code 161 | import connectedCaveman from 'graphology-generators/community/connected-caveman'; 162 | 163 | // Creating a connected caveman graph 164 | const graph = connectedCaveman(Graph, 6, 8); 165 | ``` 166 | 167 | **Arguments** 168 | 169 | - **constructor** _Class_: a `graphology` constructor. 170 | - **l** _number_: number of components in the graph. 171 | - **k** _number_: number of nodes of the components. 172 | 173 | ### Random graphs 174 | 175 | #### Clusters 176 | 177 | Creates a graph with the desired number of nodes & edges and having a given number of clusters. 178 | 179 | ```js 180 | import Graph from 'graphology'; 181 | import {clusters} from 'graphology-generators/random'; 182 | // Alternatively, if you only want to load relevant code 183 | import clusters from 'graphology-generators/random/clusters'; 184 | 185 | // Creating a random clustered graph 186 | const graph = clusters(Graph, { 187 | order: 100, 188 | size: 1000, 189 | clusters: 5 190 | }); 191 | ``` 192 | 193 | **Arguments** 194 | 195 | - **constructor** _Class_: a `graphology` constructor. 196 | - **options** _object_: options: 197 | - **order** _number_: number of nodes of the generated graph. 198 | - **size** _number_: number of edges of the generated graph. 199 | - **clusters** _number_: number of clusters of the generated graph. 200 | - **clusterDensity** ?number 0.5: Probability that an edge will link two nodes of the same cluster. 201 | - **rng** ?function: custom RNG function. 202 | 203 | #### Erdos-Renyi 204 | 205 | Creates an [Erdos-Renyi](https://en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93R%C3%A9nyi_model), or binomial graph. 206 | 207 | ```js 208 | import Graph from 'graphology'; 209 | import {erdosRenyi} from 'graphology-generators/random'; 210 | // Alternatively, if you only want to load relevant code 211 | import erdosRenyi from 'graphology-generators/random/erdos-renyi'; 212 | 213 | // Creating a binomial graph 214 | const graph = erdosRenyi(Graph, {order: 10, probability: 0.5}); 215 | 216 | // If your graph is sparse (low probability), you can use the `sparse` version 217 | // which runs in O(m + n) rather than O(n^2) 218 | const graph = erdosRenyi.sparse(Graph, {order: 1000, probability: 0.1}); 219 | ``` 220 | 221 | **Arguments** 222 | 223 | - **constructor** _Class_: a `graphology` constructor. 224 | - **options** _object_: options: 225 | - **order** _number_: number of nodes of the generated graph. 226 | - **probability** _number_: probability for edge creation. (i.e. density you try to approximate in the generated graph). 227 | - **approximateSize**: alternatively, you can pass an approximate number of edges you are trying to get in the generated graph. 228 | - **rng** ?function: custom RNG function. 229 | 230 | #### Girvan-Newman 231 | 232 | Creates a [Girvan-Newman](http://www.pnas.org/content/99/12/7821.full.pdf) random graph as described in: 233 | 234 | > Community Structure in social and biological networks. Girvan Newman, 2002. PNAS June, vol 99 n 12 235 | 236 | ```js 237 | import Graph from 'graphology'; 238 | import {girvanNewman} from 'graphology-generators/random'; 239 | // Alternatively, if you only want to load relevant code 240 | import girvanNewman from 'graphology-generators/random/girvan-newman'; 241 | 242 | // Creating a binomial graph 243 | const graph = girvanNewman(Graph, {zOut: 4}); 244 | ``` 245 | 246 | **Arguments** 247 | 248 | - **constructor** _Class_: a `graphology` constructor. 249 | - **options** _object_: options: 250 | - **zOut** _number_: _zout_ parameter. 251 | - **rng** ?function: custom RNG function. 252 | 253 | ### Small graphs 254 | 255 | #### Krackhardt kite 256 | 257 | Returns the [Krackhardt kite](https://en.wikipedia.org/wiki/Krackhardt_kite_graph) graph. 258 | 259 | ```js 260 | import Graph from 'graphology'; 261 | import {krackhardtKite} from 'graphology-generators/small'; 262 | // Alternatively, if you only want to load relevant code 263 | import krackhardtKite from 'graphology-generators/small/krackhardt-kite'; 264 | 265 | // Creating a random clustered graph 266 | const graph = krackhardtKite(Graph); 267 | ``` 268 | 269 | **Arguments** 270 | 271 | - **constructor** _Class_: a `graphology` constructor. 272 | 273 | ### Social graphs 274 | 275 | #### Florentine Families 276 | 277 | Returns the Florentine families' graph. 278 | 279 | ```js 280 | import Graph from 'graphology'; 281 | import {florentineFamilies} from 'graphology-generators/florentine-families'; 282 | // Alternatively, if you only want to load relevant code 283 | import florentineFamilies from 'graphology-generators/social/florentine-families'; 284 | 285 | // Generating the graph 286 | const graph = florentineFamilies(Graph); 287 | ``` 288 | 289 | **Arguments** 290 | 291 | - **constructor** _Class_: a `graphology` constructor. 292 | 293 | #### Karate Club 294 | 295 | Returns [Zachary's karate club](https://en.wikipedia.org/wiki/Zachary%27s_karate_club) graph. 296 | 297 | ```js 298 | import Graph from 'graphology'; 299 | import {karateClub} from 'graphology-generators/karate-club'; 300 | // Alternatively, if you only want to load relevant code 301 | import karateClub from 'graphology-generators/social/karate-club'; 302 | 303 | // Generating the graph 304 | const graph = karateClub(Graph); 305 | ``` 306 | 307 | **Arguments** 308 | 309 | - **constructor** _Class_: a `graphology` constructor. 310 | 311 | -------------------------------------------------------------------------------- /mutation.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Mutation 4 | nav_order: 6 5 | menu_toc: 6 | - "#.addNode" 7 | - "#.mergeNode" 8 | - "#.updateNode" 9 | - "#.addEdge" 10 | - "#.addEdgeWithKey" 11 | - "#.mergeEdge" 12 | - "#.mergeEdgeWithKey" 13 | - "#.updateEdge" 14 | - "#.updateEdgeWithKey" 15 | - "#.dropNode" 16 | - "#.dropEdge" 17 | - "#.clear" 18 | - "#.clearEdges" 19 | --- 20 | 21 | 22 | # Mutation 23 | 24 | ## #.addNode 25 | 26 | Adds a single node to the graph with optional attributes and returns the node. 27 | 28 | Will throw if the node already exists in the graph. 29 | 30 | *Example* 31 | 32 | ```js 33 | // Adding a simple node: 34 | const node = graph.addNode('John'); 35 | 36 | // Adding a node with attributes: 37 | const node = graph.addNode('John', { 38 | age: 24, 39 | eyes: 'blue' 40 | }); 41 | ``` 42 | 43 | *Arguments* 44 | 45 | * **node** any: the key referencing the node. 46 | * **attributes** [object]: optional attributes. 47 | 48 | ## #.mergeNode 49 | 50 | Adds a node only if the node does not exist in the graph yet. Else it will merge the provided attributes with the already existing ones. 51 | 52 | This methods return a 2-tuple containing: 53 | 54 | 1. the node's key 55 | 2. a boolean indicating whether a node was actually added. 56 | 57 | *Example* 58 | 59 | ```js 60 | // Since 'John' does not exist in the graph, a node will be added 61 | graph.mergeNode('John'); 62 | 63 | // Since 'John' already exists in the graph, this will do nothing 64 | graph.mergeNode('John'); 65 | 66 | // Note that if the node already exists, attributes are merged 67 | graph.mergeNode('John', {eyes: 'blue'}); 68 | graph.getNodeAttributes('John'); 69 | >>> { 70 | eyes: 'blue' 71 | } 72 | 73 | // The method returns a 2-tuple containing useful info 74 | const [key, nodeWasAdded] = graph.mergeNode('John'); 75 | ``` 76 | 77 | *Arguments* 78 | 79 | * **node** any: the node's key. 80 | * **attributes** object: the node's initial attributes or attributes to merge with the already existing ones. 81 | 82 | 83 | ## #.updateNode 84 | 85 | Adds a node only if the node does not exist in the graph yet. Else it will update the already existing node's attributes using the provided function. 86 | 87 | If the node does not yet exist, the updated function must still return the initial attributes and will be given an empty object to do so. 88 | 89 | This methods return a 2-tuple containing: 90 | 91 | 1. the node's key 92 | 2. a boolean indicating whether a node was actually added. 93 | 94 | *Example* 95 | 96 | ```js 97 | // Since 'John' does not exist in the graph, a node will be added 98 | graph.updateNode('John', attr => { 99 | return { 100 | ...attr, 101 | count: (attr.count || 0) + 1 102 | }; 103 | }); 104 | 105 | // Since 'John' already exist in the graph, this time its count will get incremented 106 | graph.updateNode('John', attr => { 107 | return { 108 | ...attr, 109 | count: (attr.count || 0) + 1 110 | }; 111 | }); 112 | 113 | graph.getNodeAttribute('John', 'count'); 114 | >>> 2 115 | 116 | // The method returns a 2-tuple containing useful info 117 | const [key, nodeWasAdded] = graph.updateNode('John'); 118 | ``` 119 | 120 | *Arguments* 121 | 122 | * **node** any: the node's key. 123 | * **updater** [function]: a function tasked to update target node's attributes. 124 | 125 | ## #.addEdge 126 | 127 | Adds a single directed edge if the graph is `directed` or `mixed`, or an undirected edge if the graph is `undirected` and returns the automatically generated edge's key. 128 | 129 | *Example* 130 | 131 | ```js 132 | graph.addNode('John'); 133 | graph.addNode('Jack'); 134 | 135 | // Adding a simple edge between John & Jack: 136 | const edge = graph.addEdge('John', 'Jack'); 137 | 138 | // Adding an edge with attributes between John & Jack; 139 | const edge = graph.addEdge('John', 'Jack', { 140 | type: 'KNOWS', 141 | weight: 0 142 | }); 143 | ``` 144 | 145 | *Arguments* 146 | 147 | * **source** any: the source node. 148 | * **target** any: the target node. 149 | * **attributes** [object]: optional attributes. 150 | 151 | *Important* 152 | 153 | This method is a convenience built on top of the [`#.addEdgeWithKey`](#addedgewithkey) method so that the user may add an edge in the graph without having to create a specific key for it. 154 | 155 | For more information, check out this [section](design-choices#addedge-as-sugar) of the design choices. 156 | 157 | *Variants* 158 | 159 | * `#.addDirectedEdge` 160 | * `#.addUndirectedEdge` 161 | 162 | ## #.addEdgeWithKey 163 | 164 | Adds a single directed edge if the graph is `directed` or `mixed`, or an undirected edge if the graph is `undirected`, using the provided key, and returns the edge's key. 165 | 166 | This is quite useful when dealing with a `MultiGraph` if you need to retrieve precise edges since polling the graph using both the source & the target node might return several edges rather than only one. 167 | 168 | *Example* 169 | 170 | ```js 171 | graph.addNode('John'); 172 | graph.addNode('Jack'); 173 | 174 | // Adding a simple edge between John & Jack: 175 | const edge = graph.addEdgeWithKey('John->Jack', 'John', 'Jack'); 176 | 177 | // Adding an edge with attributes between John & Jack; 178 | const edge = graph.addEdgeWithKey('John->Jack', 'John', 'Jack', { 179 | type: 'KNOWS', 180 | weight: 0 181 | }); 182 | ``` 183 | 184 | *Arguments* 185 | 186 | * **edge** any: the key referencing the edge. 187 | * **source** any: the source node. 188 | * **target** any: the target node. 189 | * **attributes** [object]: optional attributes. 190 | 191 | *Variants* 192 | 193 | * `#.addDirectedEdgeWithKey` 194 | * `#.addUndirectedEdgeWithKey` 195 | 196 | ## #.mergeEdge 197 | 198 | Adds an edge with the given key only if the edge does not exist in the graph yet. Else it will merge the provided attributes with the already existing ones. 199 | 200 | Furthermore, this method will also add the source and/or target nodes to the graph if not found. 201 | 202 | Note that an edge is deemed to already exist in a simple graph if the graph can find one edge of same type going from the same source to the same target. 203 | 204 | In a multi graph, this method will therefore always add a new edge. 205 | 206 | This methods return a 4-tuple containing: 207 | 208 | 1. the edge's key 209 | 2. a boolean indicating whether an edge was actually added. 210 | 3. a boolean indicating whether the source node was added to the graph. 211 | 4. a boolean indicating whether the target node was added to the graph. 212 | 213 | ```js 214 | const graph = new UndirectedGraph(); 215 | 216 | // Since the edge does not exist, it will be added 217 | graph.mergeEdge('John', 'Martha'); 218 | 219 | // Now, since the edge already exists, this will do nothing 220 | graph.mergeEdge('John', 'Martha'); 221 | 222 | // Note that if the edge already exists, attributes are merged 223 | graph.mergeEdge('John', 'Martha', {type: 'KNOWS'}); 224 | graph.getEdgeAttributes('John', 'Martha'); 225 | >>> { 226 | type: 'KNOWS' 227 | } 228 | 229 | // The method returns a 4-tuple containing useful info 230 | const [key, edgeWasAdded, sourceWasAdded, targetWasAdded] = graph.mergeEdge('John', 'Martha'); 231 | ``` 232 | 233 | *Arguments* 234 | 235 | * **source** any: the source node. 236 | * **target** any: the target node. 237 | * **attributes** [object]: optional attributes to merge. 238 | 239 | *Variants* 240 | 241 | * `#.mergeDirectedEdge` 242 | * `#.mergeUndirectedEdge` 243 | 244 | ## #.mergeEdgeWithKey 245 | 246 | Adds an edge with the given key only if the edge does not exist in the graph yet. Else it will merge the provided attributes with the already existing ones. 247 | 248 | Furthermore, this method will also add the source and/or target nodes to the graph if not found. 249 | 250 | Note that in this case, an edge is deemed to already exist in the graph if an edge with the same key, same type and same source & target is found in the graph. 251 | 252 | If one tries to add an edge with the given key and if the graph has an edge with the same key but a different source & target, the method will throw to notify of the inconsistency. 253 | 254 | This methods return a 4-tuple containing: 255 | 256 | 1. the edge's key 257 | 2. a boolean indicating whether an edge was actually added. 258 | 3. a boolean indicating whether the source node was added to the graph. 259 | 4. a boolean indicating whether the target node was added to the graph. 260 | 261 | ```js 262 | const graph = new UndirectedGraph(); 263 | graph.addNode('John'); 264 | graph.addNode('Martha'); 265 | graph.addNode('Thomas'); 266 | 267 | // Since the edge does not exist, it will be added 268 | graph.mergeEdgeWithKey('J->M', 'John', 'Martha'); 269 | 270 | // Now, since the edge already exists, this will do nothing 271 | graph.mergeEdgeWithKey('J->M', 'John', 'Martha'); 272 | 273 | // Note that if the edge already exists, attributes are merged 274 | graph.mergeEdgeWithKey('J->M', 'John', 'Martha', {type: 'KNOWS'}); 275 | graph.getEdgeAttributes('J->M'); 276 | >>> { 277 | type: 'KNOWS' 278 | } 279 | 280 | // However, the following will throw an error 281 | graph.mergeEdgeWithKey('J->M', 'Thomas', 'Martha'); 282 | 283 | // The method returns a 4-tuple containing useful info 284 | const [key, edgeWasAdded, sourceWasAdded, targetWasAdded] = graph.mergeEdgeWithKey('J->M', 'John', 'Martha'); 285 | ``` 286 | 287 | *Arguments* 288 | 289 | * **edge** any: the edge's key. 290 | * **source** any: the source node. 291 | * **target** any: the target node. 292 | * **attributes** [object]: optional attributes to merge. 293 | 294 | *Variants* 295 | 296 | * `#.mergeDirectedEdgeWithKey` 297 | * `#.mergeUndirectedEdgeWithKey` 298 | 299 | ## #.updateEdge 300 | 301 | Adds an edge with the given key only if the edge does not exist in the graph yet. Else it will update the already existing edge's attributes using the provided function. 302 | 303 | If the edge does not yet exist, the updated function must still return the initial attributes and will be given an empty object to do so. 304 | 305 | Furthermore, this method will also add the source and/or target nodes to the graph if not found. 306 | 307 | Note that an edge is deemed to already exist in a simple graph if the graph can find one edge of same type going from the same source to the same target. 308 | 309 | In a multi graph, this method will therefore always add a new edge. 310 | 311 | This methods return a 4-tuple containing: 312 | 313 | 1. the edge's key 314 | 2. a boolean indicating whether an edge was actually added. 315 | 3. a boolean indicating whether the source node was added to the graph. 316 | 4. a boolean indicating whether the target node was added to the graph. 317 | 318 | ```js 319 | const graph = new UndirectedGraph(); 320 | 321 | // Since the edge does not exist, it will be added 322 | graph.updateEdgeWithKey('John', 'Martha', attr => { 323 | return { 324 | ...attr, 325 | weight: (attr.weight || 0) + 1 326 | }; 327 | }); 328 | 329 | // Since the edge already exist, its weight will get incremented this time 330 | graph.updateEdge('John', 'Martha', attr => { 331 | return { 332 | ...attr, 333 | weight: (attr.weight || 0) + 1 334 | }; 335 | }); 336 | 337 | graph.getEdgeAttribute('John', 'Martha', 'weight'); 338 | >>> 2 339 | 340 | // The method returns a 4-tuple containing useful info 341 | const [key, edgeWasAdded, sourceWasAdded, targetWasAdded] = graph.updateEdge('John', 'Martha'); 342 | ``` 343 | 344 | *Arguments* 345 | 346 | * **edge** 347 | * **source** any: the source node. 348 | * **target** any: the target node. 349 | * **updater** [function]: optional function tasked to update the edge's attributes. 350 | 351 | *Variants* 352 | 353 | * `#.updateDirectedEdge` 354 | * `#.updateUndirectedEdge` 355 | 356 | ## #.updateEdgeWithKey 357 | 358 | Adds an edge with the given key only if the edge does not exist in the graph yet. Else it will update the already existing edge's attributes using the provided function. 359 | 360 | If the edge does not yet exist, the updated function must still return the initial attributes and will be given an empty object to do so. 361 | 362 | Furthermore, this method will also add the source and/or target nodes to the graph if not found. 363 | 364 | Note that in this case, an edge is deemed to already exist in the graph if an edge with the same key, same type and same source & target is found in the graph. 365 | 366 | If one tries to add an edge with the given key and if the graph has an edge with the same key but a different source & target, the method will throw to notify of the inconsistency. 367 | 368 | This methods return a 4-tuple containing: 369 | 370 | 1. the edge's key 371 | 2. a boolean indicating whether an edge was actually added. 372 | 3. a boolean indicating whether the source node was added to the graph. 373 | 4. a boolean indicating whether the target node was added to the graph. 374 | 375 | ```js 376 | const graph = new UndirectedGraph(); 377 | 378 | // Since the edge does not exist, it will be added 379 | graph.updateEdgeWithKey('J->M', 'John', 'Martha', attr => { 380 | return { 381 | ...attr, 382 | weight: (attr.weight || 0) + 1 383 | }; 384 | }); 385 | 386 | // Since the edge already exist, its weight will get incremented this time 387 | graph.updateEdgeWithKey('J->M', 'John', 'Martha', attr => { 388 | return { 389 | ...attr, 390 | weight: (attr.weight || 0) + 1 391 | }; 392 | }); 393 | 394 | graph.getEdgeAttribute('J->M', 'weight'); 395 | >>> 2 396 | 397 | // The method returns a 4-tuple containing useful info 398 | const [key, edgeWasAdded, sourceWasAdded, targetWasAdded] = graph.updateEdgeWithKey('J->M', 'John', 'Martha'); 399 | ``` 400 | 401 | *Arguments* 402 | 403 | * **edge** any: the edge's key. 404 | * **source** any: the source node. 405 | * **target** any: the target node. 406 | * **updater** [function]: optional function tasked to update the edge's attributes. 407 | 408 | *Variants* 409 | 410 | * `#.updateDirectedEdgeWithKey` 411 | * `#.updateUndirectedEdgeWithKey` 412 | 413 | ## #.dropNode 414 | 415 | Drops a single node & all its attached edges from the graph. 416 | 417 | *Example* 418 | 419 | ```js 420 | graph.addNode('John'); 421 | graph.dropNode('John'); 422 | 423 | graph.dropNode('Martha'); 424 | >>> Error "Martha not in the graph" 425 | ``` 426 | 427 | *Arguments* 428 | 429 | * **node** any: the node to drop. 430 | 431 | ## #.dropEdge 432 | 433 | Drops a single edge from the graph. 434 | 435 | *Example* 436 | 437 | ```js 438 | graph.addNode('John'); 439 | graph.addNode('Martha'); 440 | 441 | const edge = graph.addEdge('John', 'Martha'); 442 | 443 | // Dropping the edge using its key: 444 | graph.dropEdge(edge); 445 | 446 | // Dropping the first matching edge between John & Martha 447 | graph.dropEdge('John', 'Martha'); 448 | ``` 449 | 450 | *Arguments* 451 | 452 | 1. Using the key: 453 | * **edge** any: the edge to drop. 454 | 2. Using the source & target: 455 | * **source** any: source node of the edge to drop. 456 | * **target** any: target node of the edge to drop. 457 | 458 | *Variants* 459 | 460 | * `#.dropDirectedEdge` 461 | * `#.dropUndirectedEdge` 462 | 463 | ## #.clear 464 | 465 | Drop every node & every edge from the graph, leaving it empty. 466 | 467 | *Example* 468 | 469 | ```js 470 | graph.addNode('John'); 471 | graph.addNode('Jack'); 472 | graph.addEdge('John', 'Jack'); 473 | 474 | console.log(graph.order, graph.size); 475 | >>> 2, 1 476 | 477 | graph.clear(); 478 | 479 | console.log(graph.order, graph.size); 480 | >>> 0, 0 481 | 482 | graph.hasNode('John'); 483 | >>> false 484 | ``` 485 | 486 | ## #.clearEdges 487 | 488 | Drop every every edge from the graph, keeping only nodes. 489 | 490 | *Example* 491 | 492 | ```js 493 | graph.addNode('John'); 494 | graph.addNode('Jack'); 495 | graph.addEdge('John', 'Jack'); 496 | 497 | console.log(graph.order, graph.size); 498 | >>> 2, 1 499 | 500 | graph.clearEdges(); 501 | 502 | console.log(graph.order, graph.size); 503 | >>> 2, 0 504 | 505 | graph.hasEdge('John', 'Jack'); 506 | >>> false 507 | ``` 508 | -------------------------------------------------------------------------------- /standard-library/metrics.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: metrics 4 | nav_order: 15 5 | parent: Standard library 6 | aux_links: 7 | "Library directory": "https://github.com/graphology/graphology/tree/master/src/metrics" 8 | "Changelog": "https://github.com/graphology/graphology/tree/master/src/metrics/CHANGELOG.md" 9 | --- 10 | 11 | # Graphology metrics 12 | 13 | Miscellaneous metrics to be used with [`graphology`](..). 14 | 15 | ## Installation 16 | 17 | ``` 18 | npm install graphology-metrics 19 | ``` 20 | 21 | ## Usage 22 | 23 | _Graph metrics_ 24 | 25 | - [Density](#density) 26 | - [Diameter](#diameter) 27 | - [Extent](#extent) 28 | - [Modularity](#modularity) 29 | - [Simple size](#simple-size) 30 | - [Weighted size](#weighted-size) 31 | 32 | _Node metrics_ 33 | 34 | - [Eccentricity](#eccentricity) 35 | - [Weighted degree](#weighted-degree) 36 | 37 | _Edge metrics_ 38 | 39 | - [Disparity](#disparity) 40 | - [Simmelian strength](#simmelian-strength) 41 | 42 | _Centrality_ 43 | 44 | - [Betweenness centrality](#betweenness-centrality) 45 | - [Edge betweenness centrality](#edge-betweenness-centrality) 46 | - [Closeness centrality](#closeness-centrality) 47 | - [Degree centrality](#degree-centrality) 48 | - [Eigenvector centrality](#eigenvector-centrality) 49 | - [HITS](#hits) 50 | - [Pagerank](#pagerank) 51 | 52 | _Layout quality metrics_ 53 | 54 | - [Connected Closeness](#connected-closeness) 55 | - [Edge Uniformity](#edge-uniformity) 56 | - [Neighborhood Preservation](#neighborhood-preservation) 57 | - [Stress](#stress) 58 | 59 | ## Graph metrics 60 | 61 | ### Density 62 | 63 | Computes the density of the given graph. Note that multi variants can exceed `0`, as it is also the case when considering self loops. 64 | 65 | ```js 66 | import {density} from 'graphology-metrics/graph/density'; 67 | 68 | // Passing a graph instance 69 | const d = density(graph); 70 | 71 | // Passing the graph's order & size 72 | const d = density(order, size); 73 | 74 | // Or to force the kind of density being computed 75 | import { 76 | mixedDensity, 77 | directedDensity, 78 | undirectedDensity, 79 | multiMixedDensity, 80 | multiDirectedDensity, 81 | multiUndirectedDensity 82 | } from 'graphology-metric/graph/density'; 83 | 84 | const d = undirectedDensity(mixedGraph); 85 | 86 | // If you need to chose the kind of density dynamically 87 | import {abstractDensity} from 'graphology-metric/graph/density'; 88 | 89 | abstractDensity('directed', true, 10, 24); 90 | ``` 91 | 92 | _Arguments_ 93 | 94 | Either: 95 | 96 | - **graph** _Graph_: target graph. 97 | 98 | Or: 99 | 100 | - **order** _number_: number of nodes in the graph. 101 | - **size** _number_: number of edges in the graph. 102 | 103 | _Abstract version arguments_ 104 | 105 | Either: 106 | 107 | - **type** _string_: type of density to compute (`directed`, `undirected` or `mixed`). 108 | - **multi** _boolean_: whether to compute density for the multi of simple case. 109 | - **graph** _Graph_: target graph. 110 | 111 | Or: 112 | 113 | - **type** _string_: type of density to compute (`directed`, `undirected` or `mixed`). 114 | - **multi** _boolean_: whether to compute density for the multi of simple case. 115 | - **order** _number_: number of nodes in the graph. 116 | - **size** _number_: number of edges in the graph. 117 | 118 | ### Diameter 119 | 120 | Computes the diameter, i.e the maximum eccentricity of any node of the given graph. 121 | 122 | ```js 123 | import diameter from 'graphology-metrics/graph/diameter'; 124 | 125 | const graph = new Graph(); 126 | graph.addNode('1'); 127 | graph.addNode('2'); 128 | graph.addNode('3'); 129 | graph.addUndirectedEdge(1, 2); 130 | graph.addUndirectedEdge(2, 3); 131 | 132 | diameter(graph); 133 | >>> 2 134 | 135 | ``` 136 | 137 | _Arguments_ 138 | 139 | - **graph** _Graph_: target graph. 140 | 141 | ### Extent 142 | 143 | Computes the extent - min, max - of a node or edge's attribute. 144 | 145 | ```js 146 | import {nodeExtent, edgeExtent} from 'graphology-metrics/graph'; 147 | // Alternatively, to load only the relevant code: 148 | import {nodeExtent, edgeExtent} from 'graphology-metrics/graph/extent'; 149 | 150 | // Retrieving a single node attribute's extent 151 | nodeExtent(graph, 'size'); 152 | >>> [1, 34] 153 | 154 | // Retrieving multiple node attributes' extents 155 | nodeExtent(graph, ['x', 'y']); 156 | >>> {x: [-4, 3], y: [-34, 56]} 157 | 158 | // The same for edges 159 | edgeExtent(graph, 'weight'); 160 | >>> [0, 5.7] 161 | ``` 162 | 163 | _Arguments_ 164 | 165 | - **graph** _Graph_: target graph. 166 | - **attributes** _string|array_: single attribute names or array of attribute names. 167 | 168 | ### Modularity 169 | 170 | Computes the modularity, given the graph and a node partition. It works on both directed & undirected networks and will return the relevant modularity. 171 | 172 | ```js 173 | import modularity from 'graphology-metrics/graph/modularity'; 174 | 175 | // Simplest way 176 | const Q = modularity(graph); 177 | 178 | // Custom node partition 179 | const Q = modularity(graph, { 180 | getNodeCommunity(node, attr) { 181 | return attr.customPartition; 182 | } 183 | }); 184 | ``` 185 | 186 | _Arguments_ 187 | 188 | - **graph** _Graph_: target graph. 189 | - **options** ?object: options: 190 | - **getNodeCommunity** ?string\|function community: name of the node community attribute or getter function. 191 | - **getEdgeWeight** ?string\|function weight: name of the edges' weight attribute or getter function. 192 | - **resolution** ?number: resolution parameter (`γ`). 193 | 194 | ### Simple size 195 | 196 | Computes the simple size of a given graph, i.e. its number of edges if we consider the graph simple, even if it has multiple edges between pairs of nodes. 197 | 198 | ```js 199 | import {simpleSize} from 'graphology-metrics'; 200 | // Alternatively, to load only the relevant code: 201 | import simpleSize from 'graphology-metrics/graph/simple-size'; 202 | 203 | const graph = new MultiGraph(); 204 | graph.mergeEdge(1, 2); 205 | graph.mergeEdge(1, 2); 206 | graph.mergeEdge(4, 3); 207 | graph.mergeUndirectedEdge(5, 6); 208 | 209 | simpleSize(graph); 210 | >>> 3 211 | ``` 212 | 213 | ### Weighted size 214 | 215 | Computes the weighted size, i.e. the sum of the graph's edges' weight, of the given graph. 216 | 217 | ```js 218 | import weightedSize from 'graphology-metrics/graph/weighted-size'; 219 | 220 | const graph = new Graph(); 221 | graph.mergeEdge(1, 2, {weight: 3}); 222 | graph.mergeEdge(1, 2, {weight: 1}); 223 | 224 | // Simplest way 225 | weightedSize(graph); 226 | >>> 4 227 | 228 | // With custom weight attribute 229 | weightedSize(graph, 'myWeightAttribute'); 230 | >>> 4 231 | 232 | // With custom getter 233 | weightedSize(graph, (_, attr) => attr.importance); 234 | ``` 235 | 236 | _Arguments_ 237 | 238 | - **graph** _Graph_: target graph. 239 | - **getEdgeWeight** ?string\|function weight: name of the weight attribute or getter function. 240 | 241 | ## Node metrics 242 | 243 | ### Weighted degree 244 | 245 | Computes the weighted degree of nodes. The weighted degree of a node is the sum of its edges' weights. 246 | 247 | ```js 248 | import { 249 | weightedDegree, 250 | weightedInDegree, 251 | weightedOutDegree, 252 | weightedInboundDegree, 253 | weightedOutboundDegree, 254 | weightedUndirectedDegree, 255 | weightedDirectedDegree 256 | } from 'graphology-metrics/node/weighted-degree'; 257 | 258 | // To compute weighted degree of a node 259 | weightedDegree(graph, 'A'); 260 | 261 | // To use a custom weight 262 | weightedDegree(graph, 'A', function (_, attr) { 263 | return attr.importance; 264 | }); 265 | ``` 266 | 267 | _Arguments_ 268 | 269 | - **graph** _Graph_: target graph. 270 | - **node** _any_: desired node. 271 | - **getEdgeWeight** ?string\|function: name of the edge weight attribute or getter function. 272 | 273 | ### Eccentricity 274 | 275 | Computes the eccentricity which is the maximum of the shortest paths between the given node and any other node. 276 | 277 | ```js 278 | import eccentricity from 'graphology-metrics/node/eccentricity'; 279 | 280 | graph.addNode('1'); 281 | graph.addNode('2'); 282 | graph.addNode('3'); 283 | graph.addNode('4'); 284 | graph.addUndirectedEdge(1, 2); 285 | graph.addUndirectedEdge(2, 3); 286 | graph.addUndirectedEdge(3, 1); 287 | graph.addUndirectedEdge(3, 4); 288 | 289 | eccentricity(graph, 3) >> 1; 290 | ``` 291 | 292 | _Arguments_ 293 | 294 | - **graph** _Graph_: target graph. 295 | - **node** _any_: desired node. 296 | 297 | ## Edge metrics 298 | 299 | ### Disparity 300 | 301 | Function computing a score for each edge which is necessary to apply a "disparity filter" as described in the following paper: 302 | 303 | > Serrano, M. Ángeles, Marián Boguná, and Alessandro Vespignani. "Extracting the multiscale backbone of complex weighted networks." Proceedings of the national academy of sciences 106.16 (2009): 6483-6488. 304 | 305 | Note that this metric requires a weighted graph or will return a useless result. 306 | 307 | Beware, the results must be interpreted thusly: a lower score means a more relevant edge, as is intuited in the paper's formulae. This means you can prune edges that have a score greater than a given threshold, as a statistical test. Some other implementations might differ in that they offer the opposite intuition (i.e. greater score = more relevant edge). 308 | 309 | ```js 310 | import disparity from 'graphology-metrics/edge/disparity'; 311 | 312 | // To compute strength for every edge: 313 | const disparities = disparity(graph); 314 | 315 | // To directly map the result onto edge attributes (`disparity`): 316 | disparity.assign(graph); 317 | 318 | // Using custom weights 319 | disparity.assign(graph, {getEdgeWeight: (_, attr) => attr.importance}); 320 | ``` 321 | 322 | _Arguments_ 323 | 324 | - **graph** _Graph_: target graph. 325 | - **options** ?object: options: 326 | - **edgeDisparityAttribute** ?string disparity: Name of the disparity attribute to assign. 327 | - **getEdgeWeight** ?string\|function weight: Name of the edge weight attribute or getter function. 328 | 329 | ### Simmelian strength 330 | 331 | Function returning the simmelian strength, i.e. the number of triangles an edge is part of, of all the edges in the given graph. 332 | 333 | ```js 334 | import simmelianStrength from 'graphology-metrics/edge/simmelian-strength'; 335 | 336 | // To compute strength for every edge: 337 | const strengths = simmelianStrength(graph); 338 | 339 | // To directly map the result onto edge attributes (`simmelianStrength`): 340 | simmelianStrength.assign(graph); 341 | ``` 342 | 343 | ## Centrality 344 | 345 | ### Betweenness centrality 346 | 347 | Computes the betweenness centrality for every node. 348 | 349 | ```js 350 | import betweennessCentrality from 'graphology-metrics/centrality/betweenness'; 351 | 352 | // To compute centrality for every node: 353 | const centralities = betweennessCentrality(graph); 354 | 355 | // To directly map the result onto nodes' attributes (`betweennessCentrality`): 356 | betweennessCentrality.assign(graph); 357 | 358 | // To directly map the result onto a custom attribute: 359 | betweennessCentrality.assign(graph, {nodeCentralityAttribute: 'myCentrality'}); 360 | 361 | // To ignore weights 362 | const centralities = betweennessCentrality(graph, {getEdgeWeight: null}); 363 | 364 | // To use a getter function for weights 365 | const centralities = betweennessCentrality(graph, { 366 | getEdgeWeight: (_, attr) => attr.importance 367 | }); 368 | ``` 369 | 370 | _Arguments_ 371 | 372 | - **graph** _Graph_: target graph. 373 | - **options** ?object: options: 374 | - **nodeCentralityAttribute** ?string betweennessCentrality: Name of the centrality attribute to assign. 375 | - **getEdgeWeight** ?string\|function weight: Name of the edge weight attribute or getter function. 376 | - **normalized** ?boolean true: should the result be normalized? 377 | 378 | ### Edge betweenness centrality 379 | 380 | Computes the betweenness centrality for every edge. 381 | 382 | ```js 383 | import edgeBetweennessCentrality from 'graphology-metrics/centrality/edge-betweenness'; 384 | 385 | // To compute centrality for every edge: 386 | const centralities = edgeBetweennessCentrality(graph); 387 | 388 | // To directly map the result onto edges' attributes (`edgeBetweennessCentrality`): 389 | edgeBetweennessCentrality.assign(graph); 390 | 391 | // To directly map the result onto a custom attribute: 392 | edgeBetweennessCentrality.assign(graph, { 393 | edgeCentralityAttribute: 'myCentrality' 394 | }); 395 | 396 | // To ignore weights 397 | const centralities = edgeBetweennessCentrality(graph, {getEdgeWeight: null}); 398 | 399 | // To use a getter function for weights 400 | const centralities = edgeBetweennessCentrality(graph, { 401 | getEdgeWeight: (_, attr) => attr.importance 402 | }); 403 | ``` 404 | 405 | _Arguments_ 406 | 407 | - **graph** _Graph_: target graph. 408 | - **options** ?object: options: 409 | - **edgeCentralityAttribute** ?string betweennessCentrality: Name of the centrality attribute to assign. 410 | - **getEdgeWeight** ?string\|function weight: Name of the edge weight attribute or getter function. 411 | - **normalized** ?boolean true: should the result be normalized? 412 | 413 | ### Closeness centrality 414 | 415 | Computes the closeness centrality of a graph's nodes. 416 | 417 | ```js 418 | import closenessCentrality from 'graphology-metrics/centrality/closeness'; 419 | 420 | // To compute the eigenvector centrality and return the score per node: 421 | const scores = closenessCentrality(graph); 422 | 423 | // To directly map the result to nodes' attributes: 424 | closenessCentrality.assign(graph); 425 | 426 | // Note that you can also pass options to customize the algorithm: 427 | const p = closenessCentrality(graph, {wassermanFaust: true}); 428 | ``` 429 | 430 | _Arguments_ 431 | 432 | - **graph** _Graph_: target graph. 433 | - **options** ?object: options: 434 | - **nodeCentralityAttribute** ?string closenessCentrality: name of the node attribute that will be assigned the closeness centrality. 435 | - **wassermanFaust** ?boolean false: whether to use Wasserman & Faust's normalization scheme. 436 | 437 | ### Degree centrality 438 | 439 | Computes the degree centrality for every node. 440 | 441 | ```js 442 | import { 443 | degreeCentrality, 444 | inDegreeCentrality, 445 | outDegreeCentrality 446 | } from 'graphology-metrics/centrality/degree'; 447 | 448 | // To compute degree centrality for every node: 449 | const centralities = degreeCentrality(graph); 450 | 451 | // To directly map the result onto nodes' attributes (`degreeCentrality`): 452 | degreeCentrality.assign(graph); 453 | 454 | // To directly map the result onto a custom attribute: 455 | degreeCentrality.assign(graph, {nodeCentralityAttribute: 'myCentrality'}); 456 | ``` 457 | 458 | _Arguments_ 459 | 460 | - **graph** _Graph_: target graph. 461 | - **options** ?object: options: 462 | - **nodeCentralityAttribute** ?string degreeCentrality: name of the centrality attribute to assign. 463 | 464 | ### Eigenvector centrality 465 | 466 | Computes the eigenvector centrality of a graph's nodes. 467 | 468 | ```js 469 | import eigenvectorCentrality from 'graphology-metrics/centrality/eigenvector'; 470 | 471 | // To compute the eigenvector centrality and return the score per node: 472 | const scores = eigenvectorCentrality(graph); 473 | 474 | // To directly map the result to nodes' attributes: 475 | eigenvectorCentrality.assign(graph); 476 | 477 | // Note that you can also pass options to customize the algorithm: 478 | const p = eigenvectorCentrality(graph, {tolerance: 1e-3}); 479 | 480 | // To ignore your graph's weights 481 | eigenvectorCentrality.assign(graph, {getEdgeWeight: null}); 482 | ``` 483 | 484 | _Arguments_ 485 | 486 | - **graph** _Graph_: target graph. 487 | - **options** ?object: options: 488 | - **nodeCentralityAttribute** ?string eigenvectorCentrality: name of the node attribute that will be assigned the eigenvector centrality. 489 | - **getEdgeWeight** ?string\|function weight: name of the edges' weight attribute or getter function. 490 | - **maxIterations** ?number 100: maximum number of iterations to perform. 491 | - **tolerance** ?number 1.e-6: convergence error tolerance. 492 | 493 | ### HITS 494 | 495 | Computes the hub/authority metrics for each node using the HITS algorithm. 496 | 497 | ```js 498 | import hits from 'graphology-metrics/centrality/hits'; 499 | 500 | // To compute and return the result as 'hubs' & 'authorities': 501 | const {hubs, authorities} = hits(graph); 502 | 503 | // To directly map the result to nodes' attributes: 504 | hits.assign(graph); 505 | 506 | // Note that you can also pass options to customize the algorithm: 507 | const {hubs, authorities} = hits(graph, {normalize: false}); 508 | ``` 509 | 510 | _Arguments_ 511 | 512 | - **graph** _Graph_: target graph. 513 | - **options** ?object: options: 514 | - **getEdgeWeight** ?string\|function weight: name of the edges' weight attribute or getter function. 515 | - **nodeHubAttribute** ?string hub: name of the node attribute holding hub information. 516 | - **nodeAuthorityAttribute** ?string authority: name of the node attribute holding authority information. 517 | - **maxIterations** ?number 100: maximum number of iterations to perform. 518 | - **normalize** ?boolean true: should the result be normalized by the sum of values. 519 | - **tolerance** ?number 1.e-8: convergence error tolerance. 520 | 521 | ### Pagerank 522 | 523 | Computes the pagerank metrics for each node. 524 | 525 | ```js 526 | import pagerank from 'graphology-metrics/centrality/pagerank'; 527 | 528 | // To compute pagerank and return the score per node: 529 | const scores = pagerank(graph); 530 | 531 | // To directly map the result to nodes' attributes: 532 | pagerank.assign(graph); 533 | 534 | // Note that you can also pass options to customize the algorithm: 535 | const p = pagerank(graph, {alpha: 0.9}); 536 | 537 | // To ignore your graph's weights 538 | pagerank.assign(graph, {getEdgeWeight: null}); 539 | ``` 540 | 541 | _Arguments_ 542 | 543 | - **graph** _Graph_: target graph. 544 | - **options** ?object: options: 545 | - **nodePagerankAttribute** ?string pagerank: name of the node attribute that will be assigned the pagerank score. 546 | - **getEdgeWeight** ?string\|function weight: name of the edges' weight attribute or getter function. 547 | - **alpha** ?number 0.85: damping parameter of the algorithm. 548 | - **maxIterations** ?number 100: maximum number of iterations to perform. 549 | - **tolerance** ?number 1.e-6: convergence error tolerance. 550 | 551 | ## Layout quality metrics 552 | 553 | ### Connected Closeness 554 | 555 | TODO... 556 | 557 | ### Edge Uniformity 558 | 559 | Computes the edge uniformity layout quality metric from the given graph having `x` and `y` positions attached to its nodes. Edge uniformity is the normalized standard deviation of edge length of the graph. Lower values should be synonym of better layout according to this particular metric. 560 | 561 | Runs in `O(E)`. 562 | 563 | ```js 564 | import edgeUniformity from 'graphology-metrics/layout-quality/edge-uniformity'; 565 | 566 | edgeUniformity(graph); 567 | >>> ~1.132 568 | ``` 569 | 570 | ### Neighborhood preservation 571 | 572 | Computes the "neighborhood preservation" layout quality metric from the given graph having `x` and `y` positions attached to its nodes. Neighborhood preservation is the average proportion of node neighborhood being the same both in the graph's topology and its 2d layout space. The metric is therefore comprised between `0` and `1`, `1` being the best, meaning that every node keeps its neighborhood perfectly intact within the layout space. 573 | 574 | Runs in approximately `O(N * log(N))`. 575 | 576 | ```js 577 | import neighborhoodPreservation from 'graphology-metrics/layout-quality/neighborhood-preservation'; 578 | 579 | neighborhoodPreservation(graph); 580 | // >>> 0.456 581 | ``` 582 | 583 | ### Stress 584 | 585 | Computes the "stress" layout quality metric from the given graph having `x` and `y` positions attached to its nodes. Stress is the sum of normalized delta between node topology distances and their layout space distances. Lower values should be synonym of better layout according to this particular metric. 586 | 587 | Note that this metric does not work very well when the graph has multiple connected components. 588 | 589 | Note also that this metric traverses any given graph as an undirected one. 590 | 591 | Runs in `O(N^2)`. 592 | 593 | ```js 594 | import stress from 'graphology-metrics/layout-quality/stress'; 595 | 596 | stress(graph); 597 | // >>> ~24510.2914 598 | ``` 599 | 600 | --------------------------------------------------------------------------------