├── CHANGELOG.md ├── LICENSE ├── README.md ├── Setup.hs ├── algebraic-graphs-io.cabal ├── assets ├── basic.gml ├── karate.gml ├── labeled.gml ├── lesmiserables.gml ├── simulated_blockmodel_graph_50_nodes.tsv └── simulated_blockmodel_graph_50_nodes_truePartition.tsv ├── src └── Algebra │ └── Graph │ ├── IO.hs │ └── IO │ ├── Datasets.hs │ ├── Datasets │ ├── LINQS.hs │ └── LINQS │ │ ├── Citeseer.hs │ │ └── Cora.hs │ ├── Dot.hs │ ├── GML.hs │ ├── Internal │ ├── Conduit.hs │ └── Megaparsec.hs │ ├── JSON.hs │ ├── MatrixMarket.hs │ ├── SV.hs │ └── Serialise.hs ├── stack.yaml └── test ├── Algebra └── Graph │ └── IO │ ├── JSONSpec.hs │ └── SerialiseSpec.hs └── Spec.hs /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | * 0.5 2 | 3 | - no 'Dot' and 'matrix-market' support for now: 4 | Dot/graphviz export is already supported within algebraic-graphs and matrix-market can be made available on a need basis 5 | 6 | - add JSON codec 7 | 8 | - add 'serialise'-based binary codec 9 | 10 | * 0.4 11 | 12 | LINQS datasets : 13 | 14 | - Add integer identifier to node metadata 15 | 16 | * 0.3 17 | 18 | Add Cora dataset 19 | 20 | * 0.2 21 | 22 | Citeseer dataset : 23 | 24 | - add Ord instances 25 | - user can now specify data storage directory 26 | - add streaming interface 27 | 28 | * 0.1.5 29 | 30 | Add Citeseer dataset 31 | 32 | * 0.1.4 33 | 34 | Add .tsv format and networking/decompression utilities 35 | 36 | Add blockmodel50 small dataset 37 | 38 | Provide example datasets as `IO (Graph .. )` 39 | 40 | 41 | * 0.1.2 42 | 43 | Add "Les Miserables" and "Karate Club" datasets 44 | 45 | 46 | * 0.1.0.0 (20/12/2012) 47 | 48 | Release candidate 49 | 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Marco Zocca (c) 2020 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Marco Zocca nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # algebraic-graphs-io 2 | 3 | This package collects I/O utilities for `algebraic-graphs` : parsers and serializers for common graph data interchange formats, as well as functionality for downloading and caching larger datasets. 4 | 5 | ## Formats 6 | 7 | Currently the following formats are supported : 8 | 9 | * GML : used by a few common graph software packages (NetworkX, Gephi, graphviz, and others) 10 | 11 | * .tsv : tab-separated list of edge data, used e.g. for the Graph Challenge dataset [1] 12 | 13 | * JSON : via 'aeson' 14 | 15 | * CBOR : via 'serialise' 16 | 17 | 18 | ## Datasets 19 | 20 | The package contains some small example datasets (e.g. "lesmiserables" and "karateclub"); these are provided ready for consumption in `Algebra.Graph.IO.Datasets`. 21 | 22 | There are also bindings to larger datasets, such as the ones provided by the LINQS group [2] (e.g. "citeseer" and "cora"). 23 | 24 | 25 | ## Contributing 26 | 27 | PRs and contributions welcome! 28 | 29 | 30 | ## References 31 | 32 | [1] GraphChallenge https://graphchallenge.mit.edu/data-sets 33 | 34 | [2] LINQS https://linqs.soe.ucsc.edu/data 35 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /algebraic-graphs-io.cabal: -------------------------------------------------------------------------------- 1 | name: algebraic-graphs-io 2 | version: 0.6.0.0 3 | synopsis: I/O utilities and datasets for algebraic-graphs 4 | description: I/O utilities and datasets for algebraic-graphs. See README for details 5 | homepage: https://github.com/ocramz/algebraic-graphs-io 6 | license: BSD3 7 | license-file: LICENSE 8 | author: Marco Zocca 9 | maintainer: ocramz 10 | copyright: 2020-2021 ocramz 11 | category: Graphs, Parsing, Data Mining 12 | build-type: Simple 13 | extra-source-files: README.md 14 | CHANGELOG.md 15 | assets/basic.gml 16 | assets/labeled.gml 17 | assets/karate.gml 18 | assets/lesmiserables.gml 19 | assets/simulated_blockmodel_graph_50_nodes_truePartition.tsv 20 | assets/simulated_blockmodel_graph_50_nodes.tsv 21 | cabal-version: >=1.10 22 | tested-with: GHC == 8.8.4, GHC == 9.0.2 23 | 24 | library 25 | default-language: Haskell2010 26 | ghc-options: -Wall 27 | hs-source-dirs: src 28 | exposed-modules: Algebra.Graph.IO 29 | Algebra.Graph.IO.GML 30 | Algebra.Graph.IO.Datasets 31 | Algebra.Graph.IO.Datasets.LINQS 32 | Algebra.Graph.IO.Datasets.LINQS.Citeseer 33 | Algebra.Graph.IO.Datasets.LINQS.Cora 34 | Algebra.Graph.IO.JSON 35 | Algebra.Graph.IO.Serialise 36 | Algebra.Graph.IO.SV 37 | Algebra.Graph.IO.Internal 38 | other-modules: Algebra.Graph.IO.Internal.Conduit 39 | Algebra.Graph.IO.Internal.Megaparsec 40 | build-depends: aeson 41 | , algebraic-graphs 42 | , attoparsec 43 | , base >= 4.7 && < 5 44 | , binary 45 | , binary-conduit 46 | , bytestring 47 | , conduit 48 | , conduit-extra 49 | , containers 50 | , csv-conduit 51 | , directory 52 | , exceptions 53 | , filepath 54 | , http-conduit 55 | , megaparsec 56 | , mtl 57 | , parser-combinators 58 | , primitive 59 | , serialise 60 | , tar-conduit 61 | , text 62 | , transformers 63 | , vector 64 | 65 | test-suite spec 66 | default-language: Haskell2010 67 | ghc-options: -Wall 68 | type: exitcode-stdio-1.0 69 | hs-source-dirs: test 70 | main-is: Spec.hs 71 | build-depends: base 72 | , aeson 73 | , algebraic-graphs 74 | , algebraic-graphs-io 75 | , hspec 76 | , serialise 77 | 78 | source-repository head 79 | type: git 80 | location: https://github.com/ocramz/algebraic-graphs-io 81 | -------------------------------------------------------------------------------- /assets/basic.gml: -------------------------------------------------------------------------------- 1 | graph 2 | [ 3 | node 4 | [ 5 | id A 6 | ] 7 | node 8 | [ 9 | id B 10 | ] 11 | node 12 | [ 13 | id C 14 | ] 15 | edge 16 | [ 17 | source B 18 | target A 19 | ] 20 | edge 21 | [ 22 | source C 23 | target A 24 | ] 25 | ] -------------------------------------------------------------------------------- /assets/karate.gml: -------------------------------------------------------------------------------- 1 | Creator "Mark Newman on Fri Jul 21 12:39:27 2006" 2 | graph 3 | [ 4 | node 5 | [ 6 | id 1 7 | ] 8 | node 9 | [ 10 | id 2 11 | ] 12 | node 13 | [ 14 | id 3 15 | ] 16 | node 17 | [ 18 | id 4 19 | ] 20 | node 21 | [ 22 | id 5 23 | ] 24 | node 25 | [ 26 | id 6 27 | ] 28 | node 29 | [ 30 | id 7 31 | ] 32 | node 33 | [ 34 | id 8 35 | ] 36 | node 37 | [ 38 | id 9 39 | ] 40 | node 41 | [ 42 | id 10 43 | ] 44 | node 45 | [ 46 | id 11 47 | ] 48 | node 49 | [ 50 | id 12 51 | ] 52 | node 53 | [ 54 | id 13 55 | ] 56 | node 57 | [ 58 | id 14 59 | ] 60 | node 61 | [ 62 | id 15 63 | ] 64 | node 65 | [ 66 | id 16 67 | ] 68 | node 69 | [ 70 | id 17 71 | ] 72 | node 73 | [ 74 | id 18 75 | ] 76 | node 77 | [ 78 | id 19 79 | ] 80 | node 81 | [ 82 | id 20 83 | ] 84 | node 85 | [ 86 | id 21 87 | ] 88 | node 89 | [ 90 | id 22 91 | ] 92 | node 93 | [ 94 | id 23 95 | ] 96 | node 97 | [ 98 | id 24 99 | ] 100 | node 101 | [ 102 | id 25 103 | ] 104 | node 105 | [ 106 | id 26 107 | ] 108 | node 109 | [ 110 | id 27 111 | ] 112 | node 113 | [ 114 | id 28 115 | ] 116 | node 117 | [ 118 | id 29 119 | ] 120 | node 121 | [ 122 | id 30 123 | ] 124 | node 125 | [ 126 | id 31 127 | ] 128 | node 129 | [ 130 | id 32 131 | ] 132 | node 133 | [ 134 | id 33 135 | ] 136 | node 137 | [ 138 | id 34 139 | ] 140 | edge 141 | [ 142 | source 2 143 | target 1 144 | ] 145 | edge 146 | [ 147 | source 3 148 | target 1 149 | ] 150 | edge 151 | [ 152 | source 3 153 | target 2 154 | ] 155 | edge 156 | [ 157 | source 4 158 | target 1 159 | ] 160 | edge 161 | [ 162 | source 4 163 | target 2 164 | ] 165 | edge 166 | [ 167 | source 4 168 | target 3 169 | ] 170 | edge 171 | [ 172 | source 5 173 | target 1 174 | ] 175 | edge 176 | [ 177 | source 6 178 | target 1 179 | ] 180 | edge 181 | [ 182 | source 7 183 | target 1 184 | ] 185 | edge 186 | [ 187 | source 7 188 | target 5 189 | ] 190 | edge 191 | [ 192 | source 7 193 | target 6 194 | ] 195 | edge 196 | [ 197 | source 8 198 | target 1 199 | ] 200 | edge 201 | [ 202 | source 8 203 | target 2 204 | ] 205 | edge 206 | [ 207 | source 8 208 | target 3 209 | ] 210 | edge 211 | [ 212 | source 8 213 | target 4 214 | ] 215 | edge 216 | [ 217 | source 9 218 | target 1 219 | ] 220 | edge 221 | [ 222 | source 9 223 | target 3 224 | ] 225 | edge 226 | [ 227 | source 10 228 | target 3 229 | ] 230 | edge 231 | [ 232 | source 11 233 | target 1 234 | ] 235 | edge 236 | [ 237 | source 11 238 | target 5 239 | ] 240 | edge 241 | [ 242 | source 11 243 | target 6 244 | ] 245 | edge 246 | [ 247 | source 12 248 | target 1 249 | ] 250 | edge 251 | [ 252 | source 13 253 | target 1 254 | ] 255 | edge 256 | [ 257 | source 13 258 | target 4 259 | ] 260 | edge 261 | [ 262 | source 14 263 | target 1 264 | ] 265 | edge 266 | [ 267 | source 14 268 | target 2 269 | ] 270 | edge 271 | [ 272 | source 14 273 | target 3 274 | ] 275 | edge 276 | [ 277 | source 14 278 | target 4 279 | ] 280 | edge 281 | [ 282 | source 17 283 | target 6 284 | ] 285 | edge 286 | [ 287 | source 17 288 | target 7 289 | ] 290 | edge 291 | [ 292 | source 18 293 | target 1 294 | ] 295 | edge 296 | [ 297 | source 18 298 | target 2 299 | ] 300 | edge 301 | [ 302 | source 20 303 | target 1 304 | ] 305 | edge 306 | [ 307 | source 20 308 | target 2 309 | ] 310 | edge 311 | [ 312 | source 22 313 | target 1 314 | ] 315 | edge 316 | [ 317 | source 22 318 | target 2 319 | ] 320 | edge 321 | [ 322 | source 26 323 | target 24 324 | ] 325 | edge 326 | [ 327 | source 26 328 | target 25 329 | ] 330 | edge 331 | [ 332 | source 28 333 | target 3 334 | ] 335 | edge 336 | [ 337 | source 28 338 | target 24 339 | ] 340 | edge 341 | [ 342 | source 28 343 | target 25 344 | ] 345 | edge 346 | [ 347 | source 29 348 | target 3 349 | ] 350 | edge 351 | [ 352 | source 30 353 | target 24 354 | ] 355 | edge 356 | [ 357 | source 30 358 | target 27 359 | ] 360 | edge 361 | [ 362 | source 31 363 | target 2 364 | ] 365 | edge 366 | [ 367 | source 31 368 | target 9 369 | ] 370 | edge 371 | [ 372 | source 32 373 | target 1 374 | ] 375 | edge 376 | [ 377 | source 32 378 | target 25 379 | ] 380 | edge 381 | [ 382 | source 32 383 | target 26 384 | ] 385 | edge 386 | [ 387 | source 32 388 | target 29 389 | ] 390 | edge 391 | [ 392 | source 33 393 | target 3 394 | ] 395 | edge 396 | [ 397 | source 33 398 | target 9 399 | ] 400 | edge 401 | [ 402 | source 33 403 | target 15 404 | ] 405 | edge 406 | [ 407 | source 33 408 | target 16 409 | ] 410 | edge 411 | [ 412 | source 33 413 | target 19 414 | ] 415 | edge 416 | [ 417 | source 33 418 | target 21 419 | ] 420 | edge 421 | [ 422 | source 33 423 | target 23 424 | ] 425 | edge 426 | [ 427 | source 33 428 | target 24 429 | ] 430 | edge 431 | [ 432 | source 33 433 | target 30 434 | ] 435 | edge 436 | [ 437 | source 33 438 | target 31 439 | ] 440 | edge 441 | [ 442 | source 33 443 | target 32 444 | ] 445 | edge 446 | [ 447 | source 34 448 | target 9 449 | ] 450 | edge 451 | [ 452 | source 34 453 | target 10 454 | ] 455 | edge 456 | [ 457 | source 34 458 | target 14 459 | ] 460 | edge 461 | [ 462 | source 34 463 | target 15 464 | ] 465 | edge 466 | [ 467 | source 34 468 | target 16 469 | ] 470 | edge 471 | [ 472 | source 34 473 | target 19 474 | ] 475 | edge 476 | [ 477 | source 34 478 | target 20 479 | ] 480 | edge 481 | [ 482 | source 34 483 | target 21 484 | ] 485 | edge 486 | [ 487 | source 34 488 | target 23 489 | ] 490 | edge 491 | [ 492 | source 34 493 | target 24 494 | ] 495 | edge 496 | [ 497 | source 34 498 | target 27 499 | ] 500 | edge 501 | [ 502 | source 34 503 | target 28 504 | ] 505 | edge 506 | [ 507 | source 34 508 | target 29 509 | ] 510 | edge 511 | [ 512 | source 34 513 | target 30 514 | ] 515 | edge 516 | [ 517 | source 34 518 | target 31 519 | ] 520 | edge 521 | [ 522 | source 34 523 | target 32 524 | ] 525 | edge 526 | [ 527 | source 34 528 | target 33 529 | ] 530 | ] 531 | -------------------------------------------------------------------------------- /assets/labeled.gml: -------------------------------------------------------------------------------- 1 | graph 2 | [ 3 | node 4 | [ 5 | id A 6 | label "Node A" 7 | ] 8 | node 9 | [ 10 | id B 11 | label "Node B" 12 | ] 13 | node 14 | [ 15 | id C 16 | label "Node C" 17 | ] 18 | edge 19 | [ 20 | source B 21 | target A 22 | label "Edge B to A" 23 | ] 24 | edge 25 | [ 26 | source C 27 | target A 28 | label "Edge C to A" 29 | ] 30 | ] -------------------------------------------------------------------------------- /assets/lesmiserables.gml: -------------------------------------------------------------------------------- 1 | Creator "Mark Newman on Fri Jul 21 12:44:53 2006" 2 | graph 3 | [ 4 | node 5 | [ 6 | id 0 7 | label "Myriel" 8 | ] 9 | node 10 | [ 11 | id 1 12 | label "Napoleon" 13 | ] 14 | node 15 | [ 16 | id 2 17 | label "MlleBaptistine" 18 | ] 19 | node 20 | [ 21 | id 3 22 | label "MmeMagloire" 23 | ] 24 | node 25 | [ 26 | id 4 27 | label "CountessDeLo" 28 | ] 29 | node 30 | [ 31 | id 5 32 | label "Geborand" 33 | ] 34 | node 35 | [ 36 | id 6 37 | label "Champtercier" 38 | ] 39 | node 40 | [ 41 | id 7 42 | label "Cravatte" 43 | ] 44 | node 45 | [ 46 | id 8 47 | label "Count" 48 | ] 49 | node 50 | [ 51 | id 9 52 | label "OldMan" 53 | ] 54 | node 55 | [ 56 | id 10 57 | label "Labarre" 58 | ] 59 | node 60 | [ 61 | id 11 62 | label "Valjean" 63 | ] 64 | node 65 | [ 66 | id 12 67 | label "Marguerite" 68 | ] 69 | node 70 | [ 71 | id 13 72 | label "MmeDeR" 73 | ] 74 | node 75 | [ 76 | id 14 77 | label "Isabeau" 78 | ] 79 | node 80 | [ 81 | id 15 82 | label "Gervais" 83 | ] 84 | node 85 | [ 86 | id 16 87 | label "Tholomyes" 88 | ] 89 | node 90 | [ 91 | id 17 92 | label "Listolier" 93 | ] 94 | node 95 | [ 96 | id 18 97 | label "Fameuil" 98 | ] 99 | node 100 | [ 101 | id 19 102 | label "Blacheville" 103 | ] 104 | node 105 | [ 106 | id 20 107 | label "Favourite" 108 | ] 109 | node 110 | [ 111 | id 21 112 | label "Dahlia" 113 | ] 114 | node 115 | [ 116 | id 22 117 | label "Zephine" 118 | ] 119 | node 120 | [ 121 | id 23 122 | label "Fantine" 123 | ] 124 | node 125 | [ 126 | id 24 127 | label "MmeThenardier" 128 | ] 129 | node 130 | [ 131 | id 25 132 | label "Thenardier" 133 | ] 134 | node 135 | [ 136 | id 26 137 | label "Cosette" 138 | ] 139 | node 140 | [ 141 | id 27 142 | label "Javert" 143 | ] 144 | node 145 | [ 146 | id 28 147 | label "Fauchelevent" 148 | ] 149 | node 150 | [ 151 | id 29 152 | label "Bamatabois" 153 | ] 154 | node 155 | [ 156 | id 30 157 | label "Perpetue" 158 | ] 159 | node 160 | [ 161 | id 31 162 | label "Simplice" 163 | ] 164 | node 165 | [ 166 | id 32 167 | label "Scaufflaire" 168 | ] 169 | node 170 | [ 171 | id 33 172 | label "Woman1" 173 | ] 174 | node 175 | [ 176 | id 34 177 | label "Judge" 178 | ] 179 | node 180 | [ 181 | id 35 182 | label "Champmathieu" 183 | ] 184 | node 185 | [ 186 | id 36 187 | label "Brevet" 188 | ] 189 | node 190 | [ 191 | id 37 192 | label "Chenildieu" 193 | ] 194 | node 195 | [ 196 | id 38 197 | label "Cochepaille" 198 | ] 199 | node 200 | [ 201 | id 39 202 | label "Pontmercy" 203 | ] 204 | node 205 | [ 206 | id 40 207 | label "Boulatruelle" 208 | ] 209 | node 210 | [ 211 | id 41 212 | label "Eponine" 213 | ] 214 | node 215 | [ 216 | id 42 217 | label "Anzelma" 218 | ] 219 | node 220 | [ 221 | id 43 222 | label "Woman2" 223 | ] 224 | node 225 | [ 226 | id 44 227 | label "MotherInnocent" 228 | ] 229 | node 230 | [ 231 | id 45 232 | label "Gribier" 233 | ] 234 | node 235 | [ 236 | id 46 237 | label "Jondrette" 238 | ] 239 | node 240 | [ 241 | id 47 242 | label "MmeBurgon" 243 | ] 244 | node 245 | [ 246 | id 48 247 | label "Gavroche" 248 | ] 249 | node 250 | [ 251 | id 49 252 | label "Gillenormand" 253 | ] 254 | node 255 | [ 256 | id 50 257 | label "Magnon" 258 | ] 259 | node 260 | [ 261 | id 51 262 | label "MlleGillenormand" 263 | ] 264 | node 265 | [ 266 | id 52 267 | label "MmePontmercy" 268 | ] 269 | node 270 | [ 271 | id 53 272 | label "MlleVaubois" 273 | ] 274 | node 275 | [ 276 | id 54 277 | label "LtGillenormand" 278 | ] 279 | node 280 | [ 281 | id 55 282 | label "Marius" 283 | ] 284 | node 285 | [ 286 | id 56 287 | label "BaronessT" 288 | ] 289 | node 290 | [ 291 | id 57 292 | label "Mabeuf" 293 | ] 294 | node 295 | [ 296 | id 58 297 | label "Enjolras" 298 | ] 299 | node 300 | [ 301 | id 59 302 | label "Combeferre" 303 | ] 304 | node 305 | [ 306 | id 60 307 | label "Prouvaire" 308 | ] 309 | node 310 | [ 311 | id 61 312 | label "Feuilly" 313 | ] 314 | node 315 | [ 316 | id 62 317 | label "Courfeyrac" 318 | ] 319 | node 320 | [ 321 | id 63 322 | label "Bahorel" 323 | ] 324 | node 325 | [ 326 | id 64 327 | label "Bossuet" 328 | ] 329 | node 330 | [ 331 | id 65 332 | label "Joly" 333 | ] 334 | node 335 | [ 336 | id 66 337 | label "Grantaire" 338 | ] 339 | node 340 | [ 341 | id 67 342 | label "MotherPlutarch" 343 | ] 344 | node 345 | [ 346 | id 68 347 | label "Gueulemer" 348 | ] 349 | node 350 | [ 351 | id 69 352 | label "Babet" 353 | ] 354 | node 355 | [ 356 | id 70 357 | label "Claquesous" 358 | ] 359 | node 360 | [ 361 | id 71 362 | label "Montparnasse" 363 | ] 364 | node 365 | [ 366 | id 72 367 | label "Toussaint" 368 | ] 369 | node 370 | [ 371 | id 73 372 | label "Child1" 373 | ] 374 | node 375 | [ 376 | id 74 377 | label "Child2" 378 | ] 379 | node 380 | [ 381 | id 75 382 | label "Brujon" 383 | ] 384 | node 385 | [ 386 | id 76 387 | label "MmeHucheloup" 388 | ] 389 | edge 390 | [ 391 | source 1 392 | target 0 393 | value 1 394 | ] 395 | edge 396 | [ 397 | source 2 398 | target 0 399 | value 8 400 | ] 401 | edge 402 | [ 403 | source 3 404 | target 0 405 | value 10 406 | ] 407 | edge 408 | [ 409 | source 3 410 | target 2 411 | value 6 412 | ] 413 | edge 414 | [ 415 | source 4 416 | target 0 417 | value 1 418 | ] 419 | edge 420 | [ 421 | source 5 422 | target 0 423 | value 1 424 | ] 425 | edge 426 | [ 427 | source 6 428 | target 0 429 | value 1 430 | ] 431 | edge 432 | [ 433 | source 7 434 | target 0 435 | value 1 436 | ] 437 | edge 438 | [ 439 | source 8 440 | target 0 441 | value 2 442 | ] 443 | edge 444 | [ 445 | source 9 446 | target 0 447 | value 1 448 | ] 449 | edge 450 | [ 451 | source 11 452 | target 10 453 | value 1 454 | ] 455 | edge 456 | [ 457 | source 11 458 | target 3 459 | value 3 460 | ] 461 | edge 462 | [ 463 | source 11 464 | target 2 465 | value 3 466 | ] 467 | edge 468 | [ 469 | source 11 470 | target 0 471 | value 5 472 | ] 473 | edge 474 | [ 475 | source 12 476 | target 11 477 | value 1 478 | ] 479 | edge 480 | [ 481 | source 13 482 | target 11 483 | value 1 484 | ] 485 | edge 486 | [ 487 | source 14 488 | target 11 489 | value 1 490 | ] 491 | edge 492 | [ 493 | source 15 494 | target 11 495 | value 1 496 | ] 497 | edge 498 | [ 499 | source 17 500 | target 16 501 | value 4 502 | ] 503 | edge 504 | [ 505 | source 18 506 | target 16 507 | value 4 508 | ] 509 | edge 510 | [ 511 | source 18 512 | target 17 513 | value 4 514 | ] 515 | edge 516 | [ 517 | source 19 518 | target 16 519 | value 4 520 | ] 521 | edge 522 | [ 523 | source 19 524 | target 17 525 | value 4 526 | ] 527 | edge 528 | [ 529 | source 19 530 | target 18 531 | value 4 532 | ] 533 | edge 534 | [ 535 | source 20 536 | target 16 537 | value 3 538 | ] 539 | edge 540 | [ 541 | source 20 542 | target 17 543 | value 3 544 | ] 545 | edge 546 | [ 547 | source 20 548 | target 18 549 | value 3 550 | ] 551 | edge 552 | [ 553 | source 20 554 | target 19 555 | value 4 556 | ] 557 | edge 558 | [ 559 | source 21 560 | target 16 561 | value 3 562 | ] 563 | edge 564 | [ 565 | source 21 566 | target 17 567 | value 3 568 | ] 569 | edge 570 | [ 571 | source 21 572 | target 18 573 | value 3 574 | ] 575 | edge 576 | [ 577 | source 21 578 | target 19 579 | value 3 580 | ] 581 | edge 582 | [ 583 | source 21 584 | target 20 585 | value 5 586 | ] 587 | edge 588 | [ 589 | source 22 590 | target 16 591 | value 3 592 | ] 593 | edge 594 | [ 595 | source 22 596 | target 17 597 | value 3 598 | ] 599 | edge 600 | [ 601 | source 22 602 | target 18 603 | value 3 604 | ] 605 | edge 606 | [ 607 | source 22 608 | target 19 609 | value 3 610 | ] 611 | edge 612 | [ 613 | source 22 614 | target 20 615 | value 4 616 | ] 617 | edge 618 | [ 619 | source 22 620 | target 21 621 | value 4 622 | ] 623 | edge 624 | [ 625 | source 23 626 | target 16 627 | value 3 628 | ] 629 | edge 630 | [ 631 | source 23 632 | target 17 633 | value 3 634 | ] 635 | edge 636 | [ 637 | source 23 638 | target 18 639 | value 3 640 | ] 641 | edge 642 | [ 643 | source 23 644 | target 19 645 | value 3 646 | ] 647 | edge 648 | [ 649 | source 23 650 | target 20 651 | value 4 652 | ] 653 | edge 654 | [ 655 | source 23 656 | target 21 657 | value 4 658 | ] 659 | edge 660 | [ 661 | source 23 662 | target 22 663 | value 4 664 | ] 665 | edge 666 | [ 667 | source 23 668 | target 12 669 | value 2 670 | ] 671 | edge 672 | [ 673 | source 23 674 | target 11 675 | value 9 676 | ] 677 | edge 678 | [ 679 | source 24 680 | target 23 681 | value 2 682 | ] 683 | edge 684 | [ 685 | source 24 686 | target 11 687 | value 7 688 | ] 689 | edge 690 | [ 691 | source 25 692 | target 24 693 | value 13 694 | ] 695 | edge 696 | [ 697 | source 25 698 | target 23 699 | value 1 700 | ] 701 | edge 702 | [ 703 | source 25 704 | target 11 705 | value 12 706 | ] 707 | edge 708 | [ 709 | source 26 710 | target 24 711 | value 4 712 | ] 713 | edge 714 | [ 715 | source 26 716 | target 11 717 | value 31 718 | ] 719 | edge 720 | [ 721 | source 26 722 | target 16 723 | value 1 724 | ] 725 | edge 726 | [ 727 | source 26 728 | target 25 729 | value 1 730 | ] 731 | edge 732 | [ 733 | source 27 734 | target 11 735 | value 17 736 | ] 737 | edge 738 | [ 739 | source 27 740 | target 23 741 | value 5 742 | ] 743 | edge 744 | [ 745 | source 27 746 | target 25 747 | value 5 748 | ] 749 | edge 750 | [ 751 | source 27 752 | target 24 753 | value 1 754 | ] 755 | edge 756 | [ 757 | source 27 758 | target 26 759 | value 1 760 | ] 761 | edge 762 | [ 763 | source 28 764 | target 11 765 | value 8 766 | ] 767 | edge 768 | [ 769 | source 28 770 | target 27 771 | value 1 772 | ] 773 | edge 774 | [ 775 | source 29 776 | target 23 777 | value 1 778 | ] 779 | edge 780 | [ 781 | source 29 782 | target 27 783 | value 1 784 | ] 785 | edge 786 | [ 787 | source 29 788 | target 11 789 | value 2 790 | ] 791 | edge 792 | [ 793 | source 30 794 | target 23 795 | value 1 796 | ] 797 | edge 798 | [ 799 | source 31 800 | target 30 801 | value 2 802 | ] 803 | edge 804 | [ 805 | source 31 806 | target 11 807 | value 3 808 | ] 809 | edge 810 | [ 811 | source 31 812 | target 23 813 | value 2 814 | ] 815 | edge 816 | [ 817 | source 31 818 | target 27 819 | value 1 820 | ] 821 | edge 822 | [ 823 | source 32 824 | target 11 825 | value 1 826 | ] 827 | edge 828 | [ 829 | source 33 830 | target 11 831 | value 2 832 | ] 833 | edge 834 | [ 835 | source 33 836 | target 27 837 | value 1 838 | ] 839 | edge 840 | [ 841 | source 34 842 | target 11 843 | value 3 844 | ] 845 | edge 846 | [ 847 | source 34 848 | target 29 849 | value 2 850 | ] 851 | edge 852 | [ 853 | source 35 854 | target 11 855 | value 3 856 | ] 857 | edge 858 | [ 859 | source 35 860 | target 34 861 | value 3 862 | ] 863 | edge 864 | [ 865 | source 35 866 | target 29 867 | value 2 868 | ] 869 | edge 870 | [ 871 | source 36 872 | target 34 873 | value 2 874 | ] 875 | edge 876 | [ 877 | source 36 878 | target 35 879 | value 2 880 | ] 881 | edge 882 | [ 883 | source 36 884 | target 11 885 | value 2 886 | ] 887 | edge 888 | [ 889 | source 36 890 | target 29 891 | value 1 892 | ] 893 | edge 894 | [ 895 | source 37 896 | target 34 897 | value 2 898 | ] 899 | edge 900 | [ 901 | source 37 902 | target 35 903 | value 2 904 | ] 905 | edge 906 | [ 907 | source 37 908 | target 36 909 | value 2 910 | ] 911 | edge 912 | [ 913 | source 37 914 | target 11 915 | value 2 916 | ] 917 | edge 918 | [ 919 | source 37 920 | target 29 921 | value 1 922 | ] 923 | edge 924 | [ 925 | source 38 926 | target 34 927 | value 2 928 | ] 929 | edge 930 | [ 931 | source 38 932 | target 35 933 | value 2 934 | ] 935 | edge 936 | [ 937 | source 38 938 | target 36 939 | value 2 940 | ] 941 | edge 942 | [ 943 | source 38 944 | target 37 945 | value 2 946 | ] 947 | edge 948 | [ 949 | source 38 950 | target 11 951 | value 2 952 | ] 953 | edge 954 | [ 955 | source 38 956 | target 29 957 | value 1 958 | ] 959 | edge 960 | [ 961 | source 39 962 | target 25 963 | value 1 964 | ] 965 | edge 966 | [ 967 | source 40 968 | target 25 969 | value 1 970 | ] 971 | edge 972 | [ 973 | source 41 974 | target 24 975 | value 2 976 | ] 977 | edge 978 | [ 979 | source 41 980 | target 25 981 | value 3 982 | ] 983 | edge 984 | [ 985 | source 42 986 | target 41 987 | value 2 988 | ] 989 | edge 990 | [ 991 | source 42 992 | target 25 993 | value 2 994 | ] 995 | edge 996 | [ 997 | source 42 998 | target 24 999 | value 1 1000 | ] 1001 | edge 1002 | [ 1003 | source 43 1004 | target 11 1005 | value 3 1006 | ] 1007 | edge 1008 | [ 1009 | source 43 1010 | target 26 1011 | value 1 1012 | ] 1013 | edge 1014 | [ 1015 | source 43 1016 | target 27 1017 | value 1 1018 | ] 1019 | edge 1020 | [ 1021 | source 44 1022 | target 28 1023 | value 3 1024 | ] 1025 | edge 1026 | [ 1027 | source 44 1028 | target 11 1029 | value 1 1030 | ] 1031 | edge 1032 | [ 1033 | source 45 1034 | target 28 1035 | value 2 1036 | ] 1037 | edge 1038 | [ 1039 | source 47 1040 | target 46 1041 | value 1 1042 | ] 1043 | edge 1044 | [ 1045 | source 48 1046 | target 47 1047 | value 2 1048 | ] 1049 | edge 1050 | [ 1051 | source 48 1052 | target 25 1053 | value 1 1054 | ] 1055 | edge 1056 | [ 1057 | source 48 1058 | target 27 1059 | value 1 1060 | ] 1061 | edge 1062 | [ 1063 | source 48 1064 | target 11 1065 | value 1 1066 | ] 1067 | edge 1068 | [ 1069 | source 49 1070 | target 26 1071 | value 3 1072 | ] 1073 | edge 1074 | [ 1075 | source 49 1076 | target 11 1077 | value 2 1078 | ] 1079 | edge 1080 | [ 1081 | source 50 1082 | target 49 1083 | value 1 1084 | ] 1085 | edge 1086 | [ 1087 | source 50 1088 | target 24 1089 | value 1 1090 | ] 1091 | edge 1092 | [ 1093 | source 51 1094 | target 49 1095 | value 9 1096 | ] 1097 | edge 1098 | [ 1099 | source 51 1100 | target 26 1101 | value 2 1102 | ] 1103 | edge 1104 | [ 1105 | source 51 1106 | target 11 1107 | value 2 1108 | ] 1109 | edge 1110 | [ 1111 | source 52 1112 | target 51 1113 | value 1 1114 | ] 1115 | edge 1116 | [ 1117 | source 52 1118 | target 39 1119 | value 1 1120 | ] 1121 | edge 1122 | [ 1123 | source 53 1124 | target 51 1125 | value 1 1126 | ] 1127 | edge 1128 | [ 1129 | source 54 1130 | target 51 1131 | value 2 1132 | ] 1133 | edge 1134 | [ 1135 | source 54 1136 | target 49 1137 | value 1 1138 | ] 1139 | edge 1140 | [ 1141 | source 54 1142 | target 26 1143 | value 1 1144 | ] 1145 | edge 1146 | [ 1147 | source 55 1148 | target 51 1149 | value 6 1150 | ] 1151 | edge 1152 | [ 1153 | source 55 1154 | target 49 1155 | value 12 1156 | ] 1157 | edge 1158 | [ 1159 | source 55 1160 | target 39 1161 | value 1 1162 | ] 1163 | edge 1164 | [ 1165 | source 55 1166 | target 54 1167 | value 1 1168 | ] 1169 | edge 1170 | [ 1171 | source 55 1172 | target 26 1173 | value 21 1174 | ] 1175 | edge 1176 | [ 1177 | source 55 1178 | target 11 1179 | value 19 1180 | ] 1181 | edge 1182 | [ 1183 | source 55 1184 | target 16 1185 | value 1 1186 | ] 1187 | edge 1188 | [ 1189 | source 55 1190 | target 25 1191 | value 2 1192 | ] 1193 | edge 1194 | [ 1195 | source 55 1196 | target 41 1197 | value 5 1198 | ] 1199 | edge 1200 | [ 1201 | source 55 1202 | target 48 1203 | value 4 1204 | ] 1205 | edge 1206 | [ 1207 | source 56 1208 | target 49 1209 | value 1 1210 | ] 1211 | edge 1212 | [ 1213 | source 56 1214 | target 55 1215 | value 1 1216 | ] 1217 | edge 1218 | [ 1219 | source 57 1220 | target 55 1221 | value 1 1222 | ] 1223 | edge 1224 | [ 1225 | source 57 1226 | target 41 1227 | value 1 1228 | ] 1229 | edge 1230 | [ 1231 | source 57 1232 | target 48 1233 | value 1 1234 | ] 1235 | edge 1236 | [ 1237 | source 58 1238 | target 55 1239 | value 7 1240 | ] 1241 | edge 1242 | [ 1243 | source 58 1244 | target 48 1245 | value 7 1246 | ] 1247 | edge 1248 | [ 1249 | source 58 1250 | target 27 1251 | value 6 1252 | ] 1253 | edge 1254 | [ 1255 | source 58 1256 | target 57 1257 | value 1 1258 | ] 1259 | edge 1260 | [ 1261 | source 58 1262 | target 11 1263 | value 4 1264 | ] 1265 | edge 1266 | [ 1267 | source 59 1268 | target 58 1269 | value 15 1270 | ] 1271 | edge 1272 | [ 1273 | source 59 1274 | target 55 1275 | value 5 1276 | ] 1277 | edge 1278 | [ 1279 | source 59 1280 | target 48 1281 | value 6 1282 | ] 1283 | edge 1284 | [ 1285 | source 59 1286 | target 57 1287 | value 2 1288 | ] 1289 | edge 1290 | [ 1291 | source 60 1292 | target 48 1293 | value 1 1294 | ] 1295 | edge 1296 | [ 1297 | source 60 1298 | target 58 1299 | value 4 1300 | ] 1301 | edge 1302 | [ 1303 | source 60 1304 | target 59 1305 | value 2 1306 | ] 1307 | edge 1308 | [ 1309 | source 61 1310 | target 48 1311 | value 2 1312 | ] 1313 | edge 1314 | [ 1315 | source 61 1316 | target 58 1317 | value 6 1318 | ] 1319 | edge 1320 | [ 1321 | source 61 1322 | target 60 1323 | value 2 1324 | ] 1325 | edge 1326 | [ 1327 | source 61 1328 | target 59 1329 | value 5 1330 | ] 1331 | edge 1332 | [ 1333 | source 61 1334 | target 57 1335 | value 1 1336 | ] 1337 | edge 1338 | [ 1339 | source 61 1340 | target 55 1341 | value 1 1342 | ] 1343 | edge 1344 | [ 1345 | source 62 1346 | target 55 1347 | value 9 1348 | ] 1349 | edge 1350 | [ 1351 | source 62 1352 | target 58 1353 | value 17 1354 | ] 1355 | edge 1356 | [ 1357 | source 62 1358 | target 59 1359 | value 13 1360 | ] 1361 | edge 1362 | [ 1363 | source 62 1364 | target 48 1365 | value 7 1366 | ] 1367 | edge 1368 | [ 1369 | source 62 1370 | target 57 1371 | value 2 1372 | ] 1373 | edge 1374 | [ 1375 | source 62 1376 | target 41 1377 | value 1 1378 | ] 1379 | edge 1380 | [ 1381 | source 62 1382 | target 61 1383 | value 6 1384 | ] 1385 | edge 1386 | [ 1387 | source 62 1388 | target 60 1389 | value 3 1390 | ] 1391 | edge 1392 | [ 1393 | source 63 1394 | target 59 1395 | value 5 1396 | ] 1397 | edge 1398 | [ 1399 | source 63 1400 | target 48 1401 | value 5 1402 | ] 1403 | edge 1404 | [ 1405 | source 63 1406 | target 62 1407 | value 6 1408 | ] 1409 | edge 1410 | [ 1411 | source 63 1412 | target 57 1413 | value 2 1414 | ] 1415 | edge 1416 | [ 1417 | source 63 1418 | target 58 1419 | value 4 1420 | ] 1421 | edge 1422 | [ 1423 | source 63 1424 | target 61 1425 | value 3 1426 | ] 1427 | edge 1428 | [ 1429 | source 63 1430 | target 60 1431 | value 2 1432 | ] 1433 | edge 1434 | [ 1435 | source 63 1436 | target 55 1437 | value 1 1438 | ] 1439 | edge 1440 | [ 1441 | source 64 1442 | target 55 1443 | value 5 1444 | ] 1445 | edge 1446 | [ 1447 | source 64 1448 | target 62 1449 | value 12 1450 | ] 1451 | edge 1452 | [ 1453 | source 64 1454 | target 48 1455 | value 5 1456 | ] 1457 | edge 1458 | [ 1459 | source 64 1460 | target 63 1461 | value 4 1462 | ] 1463 | edge 1464 | [ 1465 | source 64 1466 | target 58 1467 | value 10 1468 | ] 1469 | edge 1470 | [ 1471 | source 64 1472 | target 61 1473 | value 6 1474 | ] 1475 | edge 1476 | [ 1477 | source 64 1478 | target 60 1479 | value 2 1480 | ] 1481 | edge 1482 | [ 1483 | source 64 1484 | target 59 1485 | value 9 1486 | ] 1487 | edge 1488 | [ 1489 | source 64 1490 | target 57 1491 | value 1 1492 | ] 1493 | edge 1494 | [ 1495 | source 64 1496 | target 11 1497 | value 1 1498 | ] 1499 | edge 1500 | [ 1501 | source 65 1502 | target 63 1503 | value 5 1504 | ] 1505 | edge 1506 | [ 1507 | source 65 1508 | target 64 1509 | value 7 1510 | ] 1511 | edge 1512 | [ 1513 | source 65 1514 | target 48 1515 | value 3 1516 | ] 1517 | edge 1518 | [ 1519 | source 65 1520 | target 62 1521 | value 5 1522 | ] 1523 | edge 1524 | [ 1525 | source 65 1526 | target 58 1527 | value 5 1528 | ] 1529 | edge 1530 | [ 1531 | source 65 1532 | target 61 1533 | value 5 1534 | ] 1535 | edge 1536 | [ 1537 | source 65 1538 | target 60 1539 | value 2 1540 | ] 1541 | edge 1542 | [ 1543 | source 65 1544 | target 59 1545 | value 5 1546 | ] 1547 | edge 1548 | [ 1549 | source 65 1550 | target 57 1551 | value 1 1552 | ] 1553 | edge 1554 | [ 1555 | source 65 1556 | target 55 1557 | value 2 1558 | ] 1559 | edge 1560 | [ 1561 | source 66 1562 | target 64 1563 | value 3 1564 | ] 1565 | edge 1566 | [ 1567 | source 66 1568 | target 58 1569 | value 3 1570 | ] 1571 | edge 1572 | [ 1573 | source 66 1574 | target 59 1575 | value 1 1576 | ] 1577 | edge 1578 | [ 1579 | source 66 1580 | target 62 1581 | value 2 1582 | ] 1583 | edge 1584 | [ 1585 | source 66 1586 | target 65 1587 | value 2 1588 | ] 1589 | edge 1590 | [ 1591 | source 66 1592 | target 48 1593 | value 1 1594 | ] 1595 | edge 1596 | [ 1597 | source 66 1598 | target 63 1599 | value 1 1600 | ] 1601 | edge 1602 | [ 1603 | source 66 1604 | target 61 1605 | value 1 1606 | ] 1607 | edge 1608 | [ 1609 | source 66 1610 | target 60 1611 | value 1 1612 | ] 1613 | edge 1614 | [ 1615 | source 67 1616 | target 57 1617 | value 3 1618 | ] 1619 | edge 1620 | [ 1621 | source 68 1622 | target 25 1623 | value 5 1624 | ] 1625 | edge 1626 | [ 1627 | source 68 1628 | target 11 1629 | value 1 1630 | ] 1631 | edge 1632 | [ 1633 | source 68 1634 | target 24 1635 | value 1 1636 | ] 1637 | edge 1638 | [ 1639 | source 68 1640 | target 27 1641 | value 1 1642 | ] 1643 | edge 1644 | [ 1645 | source 68 1646 | target 48 1647 | value 1 1648 | ] 1649 | edge 1650 | [ 1651 | source 68 1652 | target 41 1653 | value 1 1654 | ] 1655 | edge 1656 | [ 1657 | source 69 1658 | target 25 1659 | value 6 1660 | ] 1661 | edge 1662 | [ 1663 | source 69 1664 | target 68 1665 | value 6 1666 | ] 1667 | edge 1668 | [ 1669 | source 69 1670 | target 11 1671 | value 1 1672 | ] 1673 | edge 1674 | [ 1675 | source 69 1676 | target 24 1677 | value 1 1678 | ] 1679 | edge 1680 | [ 1681 | source 69 1682 | target 27 1683 | value 2 1684 | ] 1685 | edge 1686 | [ 1687 | source 69 1688 | target 48 1689 | value 1 1690 | ] 1691 | edge 1692 | [ 1693 | source 69 1694 | target 41 1695 | value 1 1696 | ] 1697 | edge 1698 | [ 1699 | source 70 1700 | target 25 1701 | value 4 1702 | ] 1703 | edge 1704 | [ 1705 | source 70 1706 | target 69 1707 | value 4 1708 | ] 1709 | edge 1710 | [ 1711 | source 70 1712 | target 68 1713 | value 4 1714 | ] 1715 | edge 1716 | [ 1717 | source 70 1718 | target 11 1719 | value 1 1720 | ] 1721 | edge 1722 | [ 1723 | source 70 1724 | target 24 1725 | value 1 1726 | ] 1727 | edge 1728 | [ 1729 | source 70 1730 | target 27 1731 | value 1 1732 | ] 1733 | edge 1734 | [ 1735 | source 70 1736 | target 41 1737 | value 1 1738 | ] 1739 | edge 1740 | [ 1741 | source 70 1742 | target 58 1743 | value 1 1744 | ] 1745 | edge 1746 | [ 1747 | source 71 1748 | target 27 1749 | value 1 1750 | ] 1751 | edge 1752 | [ 1753 | source 71 1754 | target 69 1755 | value 2 1756 | ] 1757 | edge 1758 | [ 1759 | source 71 1760 | target 68 1761 | value 2 1762 | ] 1763 | edge 1764 | [ 1765 | source 71 1766 | target 70 1767 | value 2 1768 | ] 1769 | edge 1770 | [ 1771 | source 71 1772 | target 11 1773 | value 1 1774 | ] 1775 | edge 1776 | [ 1777 | source 71 1778 | target 48 1779 | value 1 1780 | ] 1781 | edge 1782 | [ 1783 | source 71 1784 | target 41 1785 | value 1 1786 | ] 1787 | edge 1788 | [ 1789 | source 71 1790 | target 25 1791 | value 1 1792 | ] 1793 | edge 1794 | [ 1795 | source 72 1796 | target 26 1797 | value 2 1798 | ] 1799 | edge 1800 | [ 1801 | source 72 1802 | target 27 1803 | value 1 1804 | ] 1805 | edge 1806 | [ 1807 | source 72 1808 | target 11 1809 | value 1 1810 | ] 1811 | edge 1812 | [ 1813 | source 73 1814 | target 48 1815 | value 2 1816 | ] 1817 | edge 1818 | [ 1819 | source 74 1820 | target 48 1821 | value 2 1822 | ] 1823 | edge 1824 | [ 1825 | source 74 1826 | target 73 1827 | value 3 1828 | ] 1829 | edge 1830 | [ 1831 | source 75 1832 | target 69 1833 | value 3 1834 | ] 1835 | edge 1836 | [ 1837 | source 75 1838 | target 68 1839 | value 3 1840 | ] 1841 | edge 1842 | [ 1843 | source 75 1844 | target 25 1845 | value 3 1846 | ] 1847 | edge 1848 | [ 1849 | source 75 1850 | target 48 1851 | value 1 1852 | ] 1853 | edge 1854 | [ 1855 | source 75 1856 | target 41 1857 | value 1 1858 | ] 1859 | edge 1860 | [ 1861 | source 75 1862 | target 70 1863 | value 1 1864 | ] 1865 | edge 1866 | [ 1867 | source 75 1868 | target 71 1869 | value 1 1870 | ] 1871 | edge 1872 | [ 1873 | source 76 1874 | target 64 1875 | value 1 1876 | ] 1877 | edge 1878 | [ 1879 | source 76 1880 | target 65 1881 | value 1 1882 | ] 1883 | edge 1884 | [ 1885 | source 76 1886 | target 66 1887 | value 1 1888 | ] 1889 | edge 1890 | [ 1891 | source 76 1892 | target 63 1893 | value 1 1894 | ] 1895 | edge 1896 | [ 1897 | source 76 1898 | target 62 1899 | value 1 1900 | ] 1901 | edge 1902 | [ 1903 | source 76 1904 | target 48 1905 | value 1 1906 | ] 1907 | edge 1908 | [ 1909 | source 76 1910 | target 58 1911 | value 1 1912 | ] 1913 | ] 1914 | -------------------------------------------------------------------------------- /assets/simulated_blockmodel_graph_50_nodes.tsv: -------------------------------------------------------------------------------- 1 | 1 14 1 2 | 1 27 1 3 | 1 36 1 4 | 1 17 1 5 | 1 35 1 6 | 2 12 1 7 | 2 3 1 8 | 2 14 1 9 | 2 28 1 10 | 2 45 1 11 | 2 32 1 12 | 3 15 1 13 | 3 48 1 14 | 3 18 1 15 | 3 6 1 16 | 4 45 1 17 | 4 23 1 18 | 4 20 1 19 | 4 36 1 20 | 4 7 1 21 | 4 35 1 22 | 4 24 1 23 | 4 22 1 24 | 5 26 1 25 | 5 4 1 26 | 5 25 1 27 | 5 30 1 28 | 6 31 1 29 | 6 32 1 30 | 6 48 1 31 | 6 13 1 32 | 7 24 1 33 | 7 40 1 34 | 7 1 1 35 | 7 45 1 36 | 8 9 1 37 | 8 15 1 38 | 8 14 1 39 | 8 41 1 40 | 8 16 1 41 | 9 39 1 42 | 9 32 1 43 | 9 44 1 44 | 9 3 1 45 | 9 43 1 46 | 9 8 1 47 | 10 26 1 48 | 10 27 1 49 | 10 2 1 50 | 10 21 1 51 | 10 16 1 52 | 10 32 1 53 | 10 14 1 54 | 10 50 1 55 | 11 45 1 56 | 11 24 1 57 | 11 36 1 58 | 11 42 1 59 | 12 35 1 60 | 12 3 1 61 | 12 21 1 62 | 12 48 1 63 | 12 37 1 64 | 12 8 1 65 | 12 15 1 66 | 12 18 1 67 | 13 27 1 68 | 13 28 1 69 | 13 23 1 70 | 13 12 1 71 | 13 14 1 72 | 13 37 1 73 | 14 15 1 74 | 14 21 1 75 | 14 33 1 76 | 14 6 1 77 | 15 31 1 78 | 15 23 1 79 | 15 50 1 80 | 15 32 1 81 | 15 37 1 82 | 15 11 1 83 | 15 48 1 84 | 15 3 1 85 | 15 22 1 86 | 16 32 1 87 | 16 3 1 88 | 16 14 1 89 | 16 33 1 90 | 16 25 1 91 | 16 21 1 92 | 16 26 1 93 | 16 31 1 94 | 16 2 1 95 | 16 28 1 96 | 16 50 1 97 | 16 4 1 98 | 16 37 1 99 | 16 11 1 100 | 17 14 1 101 | 17 7 1 102 | 17 40 1 103 | 17 36 1 104 | 17 45 1 105 | 17 41 1 106 | 17 18 1 107 | 18 15 1 108 | 18 8 1 109 | 18 2 1 110 | 18 5 1 111 | 19 6 1 112 | 19 5 1 113 | 19 29 1 114 | 19 39 1 115 | 19 48 1 116 | 19 46 1 117 | 19 37 1 118 | 19 12 1 119 | 20 46 1 120 | 20 36 1 121 | 20 35 1 122 | 20 39 1 123 | 20 24 1 124 | 20 1 1 125 | 20 11 1 126 | 20 41 1 127 | 20 42 1 128 | 20 40 1 129 | 20 34 1 130 | 20 17 1 131 | 20 27 1 132 | 21 14 1 133 | 21 13 1 134 | 21 2 1 135 | 21 3 1 136 | 21 20 1 137 | 21 50 1 138 | 22 35 1 139 | 22 48 1 140 | 22 7 1 141 | 22 49 1 142 | 22 1 1 143 | 22 39 1 144 | 23 47 1 145 | 23 19 1 146 | 23 14 1 147 | 23 48 1 148 | 23 34 1 149 | 24 12 1 150 | 24 30 1 151 | 24 20 1 152 | 24 31 1 153 | 25 19 1 154 | 25 34 1 155 | 25 30 1 156 | 25 48 1 157 | 26 6 1 158 | 26 50 1 159 | 26 14 1 160 | 26 15 1 161 | 26 12 1 162 | 26 10 1 163 | 26 34 1 164 | 26 37 1 165 | 26 31 1 166 | 26 32 1 167 | 26 13 1 168 | 27 37 1 169 | 27 40 1 170 | 27 20 1 171 | 27 7 1 172 | 27 32 1 173 | 27 15 1 174 | 27 34 1 175 | 27 4 1 176 | 28 31 1 177 | 28 5 1 178 | 28 10 1 179 | 28 50 1 180 | 29 46 1 181 | 29 34 1 182 | 29 30 1 183 | 29 48 1 184 | 30 29 1 185 | 30 25 1 186 | 30 5 1 187 | 30 34 1 188 | 30 43 1 189 | 31 32 1 190 | 31 33 1 191 | 31 8 1 192 | 31 13 1 193 | 31 15 1 194 | 31 16 1 195 | 32 8 1 196 | 32 15 1 197 | 32 26 1 198 | 32 27 1 199 | 32 14 1 200 | 32 43 1 201 | 32 25 1 202 | 33 12 1 203 | 33 32 1 204 | 33 16 1 205 | 33 3 1 206 | 33 8 1 207 | 34 44 1 208 | 34 9 1 209 | 34 47 1 210 | 34 5 1 211 | 34 29 1 212 | 34 49 1 213 | 34 25 1 214 | 35 38 1 215 | 35 36 1 216 | 35 15 1 217 | 35 41 1 218 | 35 4 1 219 | 35 27 1 220 | 35 11 1 221 | 35 42 1 222 | 35 20 1 223 | 36 11 1 224 | 36 35 1 225 | 36 22 1 226 | 36 42 1 227 | 36 28 1 228 | 36 17 1 229 | 36 31 1 230 | 36 20 1 231 | 36 27 1 232 | 36 47 1 233 | 37 38 1 234 | 37 14 1 235 | 37 6 1 236 | 37 26 1 237 | 37 18 1 238 | 37 8 1 239 | 37 28 1 240 | 37 21 1 241 | 37 10 1 242 | 37 3 1 243 | 37 33 1 244 | 38 25 1 245 | 38 49 1 246 | 38 34 1 247 | 38 39 1 248 | 38 9 1 249 | 38 19 1 250 | 38 48 1 251 | 38 30 1 252 | 39 38 1 253 | 39 19 1 254 | 39 48 1 255 | 39 25 1 256 | 39 34 1 257 | 39 49 1 258 | 39 23 1 259 | 40 35 1 260 | 40 8 1 261 | 40 41 1 262 | 40 36 1 263 | 40 20 1 264 | 40 29 1 265 | 40 1 1 266 | 41 35 1 267 | 41 22 1 268 | 41 36 1 269 | 41 17 1 270 | 41 48 1 271 | 42 1 1 272 | 42 30 1 273 | 42 24 1 274 | 42 36 1 275 | 43 49 1 276 | 43 34 1 277 | 43 9 1 278 | 43 25 1 279 | 43 48 1 280 | 44 47 1 281 | 44 26 1 282 | 44 14 1 283 | 44 46 1 284 | 44 39 1 285 | 44 43 1 286 | 44 5 1 287 | 44 29 1 288 | 44 30 1 289 | 45 1 1 290 | 45 24 1 291 | 45 3 1 292 | 45 41 1 293 | 46 34 1 294 | 46 25 1 295 | 46 49 1 296 | 46 27 1 297 | 47 22 1 298 | 47 44 1 299 | 47 48 1 300 | 47 43 1 301 | 48 19 1 302 | 48 27 1 303 | 48 28 1 304 | 48 25 1 305 | 48 38 1 306 | 48 33 1 307 | 48 5 1 308 | 48 13 1 309 | 49 19 1 310 | 49 23 1 311 | 49 44 1 312 | 49 26 1 313 | 50 14 1 314 | 50 33 1 315 | 50 10 1 316 | 50 44 1 317 | 50 8 1 318 | 50 31 1 319 | 50 22 1 320 | -------------------------------------------------------------------------------- /assets/simulated_blockmodel_graph_50_nodes_truePartition.tsv: -------------------------------------------------------------------------------- 1 | 1 2 2 | 2 1 3 | 3 1 4 | 4 2 5 | 5 3 6 | 6 1 7 | 7 2 8 | 8 1 9 | 9 3 10 | 10 1 11 | 11 2 12 | 12 1 13 | 13 1 14 | 14 1 15 | 15 1 16 | 16 1 17 | 17 2 18 | 18 1 19 | 19 3 20 | 20 2 21 | 21 1 22 | 22 2 23 | 23 3 24 | 24 2 25 | 25 3 26 | 26 1 27 | 27 2 28 | 28 1 29 | 29 3 30 | 30 3 31 | 31 1 32 | 32 1 33 | 33 1 34 | 34 3 35 | 35 2 36 | 36 2 37 | 37 1 38 | 38 3 39 | 39 3 40 | 40 2 41 | 41 2 42 | 42 2 43 | 43 3 44 | 44 3 45 | 45 2 46 | 46 3 47 | 47 3 48 | 48 3 49 | 49 3 50 | 50 1 51 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wno-unused-imports #-} 2 | module Algebra.Graph.IO where 3 | 4 | -- alga 5 | import qualified Algebra.Graph as G (Graph(..), edges, foldg) 6 | import qualified Algebra.Graph.Labelled as GL (Graph(..), edges, foldg) 7 | import qualified Algebra.Graph.ToGraph as G (ToGraph, toAdjacencyMap) 8 | import qualified Algebra.Graph.AdjacencyMap as GAM (AdjacencyMap, adjacencyMap, edgeList, vertexList) 9 | -- containers 10 | import qualified Data.Map as M (Map, fromList, insert) 11 | import qualified Data.Set as S (Set, fromList, insert) 12 | 13 | 14 | -- data GraphIO e a = GraphIO { 15 | -- gioNodes :: [a] 16 | -- , gioEdges :: [Edge e a] 17 | -- } 18 | -- data Edge 19 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/Datasets.hs: -------------------------------------------------------------------------------- 1 | {-# options_ghc -Wno-unused-imports -Wno-type-defaults #-} 2 | module Algebra.Graph.IO.Datasets where 3 | 4 | import Algebra.Graph (Graph) 5 | import qualified Algebra.Graph.Labelled as GL (Graph) 6 | import qualified Algebra.Graph.ToGraph as G (ToGraph(..)) 7 | import Algebra.Graph.IO.GML (GMLGraph, gmlGraphP) 8 | import qualified Algebra.Graph.IO.SV as SV (tsvSink, tsvSinkL) 9 | import Algebra.Graph.IO.Internal.Megaparsec (Parser, anyString) 10 | 11 | -- conduit 12 | import Conduit (MonadUnliftIO(..), MonadResource, runResourceT) 13 | import Data.Conduit (runConduit, ConduitT, (.|), yield, await) 14 | import qualified Data.Conduit.Combinators as C (print, sourceFile, sinkFile, map, mapM, foldM, mapWhile) 15 | -- megaparsec 16 | import Text.Megaparsec (parse) 17 | import Text.Megaparsec.Error (errorBundlePretty) 18 | import Text.Megaparsec.Char.Lexer (decimal) 19 | 20 | import Data.Text.IO (readFile) 21 | 22 | import Prelude hiding (readFile) 23 | 24 | -- | "Les Miserables" dataset 25 | -- 26 | -- from https://github.com/gephi/gephi/wiki/Datasets 27 | lesMiserables :: IO (Graph Int) 28 | lesMiserables = do 29 | t <- readFile "assets/lesmiserables.gml" 30 | case parse (gmlGraphP decimal decimal) "" t of 31 | Right gg -> pure $ G.toGraph gg 32 | Left e -> error $ errorBundlePretty e 33 | 34 | -- | "Karate club" dataset 35 | -- 36 | -- from https://github.com/gephi/gephi/wiki/Datasets 37 | karateClub :: IO (Graph Int) 38 | karateClub = do 39 | t <- readFile "assets/karate.gml" 40 | case parse (gmlGraphP decimal anyString) "" t of 41 | Right gg -> pure $ G.toGraph gg 42 | Left e -> error $ errorBundlePretty e 43 | 44 | -- | Small test dataset 45 | -- 46 | -- from https://graphchallenge.mit.edu/data-sets 47 | blockModel50 :: IO (Graph Int) 48 | blockModel50 = runResourceT $ runConduit $ 49 | C.sourceFile "assets/simulated_blockmodel_graph_50_nodes.tsv" .| 50 | SV.tsvSink 51 | 52 | -- | same as 'blockModel50' but with edge labels 53 | blockModel50L :: IO (GL.Graph [Int] Int) 54 | blockModel50L = runResourceT $ runConduit $ 55 | C.sourceFile "assets/simulated_blockmodel_graph_50_nodes.tsv" .| 56 | SV.tsvSinkL 57 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/Datasets/LINQS.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE DerivingStrategies #-} 3 | {-# LANGUAGE DeriveFunctor #-} 4 | {-# language GeneralizedNewtypeDeriving #-} 5 | {-# LANGUAGE OverloadedStrings #-} 6 | {-# LANGUAGE OverloadedStrings #-} 7 | {-# language DeriveAnyClass #-} 8 | {-# LANGUAGE DeriveGeneric #-} 9 | -- | Link-based datasets from https://linqs.soe.ucsc.edu/data 10 | {-# options_ghc -Wno-unused-imports -Wno-unused-top-binds #-} 11 | module Algebra.Graph.IO.Datasets.LINQS ( 12 | restoreContent, CitesRow(..), ContentRow(..), 13 | -- * Internal 14 | stash, 15 | sourceGraphEdges, loadGraph 16 | ) where 17 | 18 | import Control.Applicative (Alternative(..)) 19 | import Control.Monad (when) 20 | import Control.Monad.IO.Class (MonadIO(..)) 21 | import Data.Functor (($>), void) 22 | 23 | import GHC.Generics (Generic(..)) 24 | import GHC.Int (Int16) 25 | 26 | -- algebraic-graphs 27 | import qualified Algebra.Graph as G (Graph, empty, overlay, edge) 28 | -- bytestring 29 | import Data.ByteString (ByteString) 30 | import Data.ByteString.Char8 (unpack) 31 | -- binary 32 | import Data.Binary (Binary(..), encode, decode, encodeFile, decodeFileOrFail) 33 | -- binary-conduit 34 | import qualified Data.Conduit.Serialization.Binary as CB (conduitDecode, conduitEncode, ParseError(..)) 35 | -- conduit 36 | import Conduit (MonadUnliftIO(..), MonadResource, runResourceT) 37 | import Data.Conduit (runConduit, ConduitT, (.|), yield, await, runConduitRes) 38 | import qualified Data.Conduit.Combinators as C (print, sourceFile, sinkFile, map, mapM, foldM, mapWhile, mapAccumWhile, foldMap, foldl, scanl) 39 | -- containers 40 | import Data.Sequence (Seq, (|>)) 41 | import qualified Data.Map as M (Map, singleton, lookup) 42 | -- directory 43 | import System.Directory (createDirectoryIfMissing) 44 | -- exceptions 45 | import Control.Monad.Catch (MonadThrow(..)) 46 | -- filepath 47 | import System.FilePath ((), takeFileName, takeExtension) 48 | -- http-conduit 49 | import Network.HTTP.Simple (httpSource, getResponseBody, Response, Request, parseRequest, setRequestMethod) 50 | -- megaparsec 51 | import Text.Megaparsec (parse, runParserT) 52 | import Text.Megaparsec.Char (char) 53 | import Text.Megaparsec.Error (errorBundlePretty, ParseErrorBundle) 54 | -- parser.combinators 55 | import Control.Monad.Combinators (count) 56 | -- tar-conduit 57 | import Data.Conduit.Tar (Header(..), untarChunks, TarChunk, withEntries, FileInfo, filePath, withFileInfo, headerFileType, FileType(..), headerFilePath) 58 | -- text 59 | import qualified Data.Text as T (Text, unwords) 60 | 61 | 62 | 63 | import Algebra.Graph.IO.Internal (fetch, unTarGz, Parser, ParserT, ParseE, symbol, lexeme, alphaNum) 64 | import Algebra.Graph.IO.SV (parseTSV) 65 | 66 | {- 67 | CiteSeer: The CiteSeer dataset consists of 3312 scientific publications classified into one of six classes. The citation network consists of 4732 links. Each publication in the dataset is described by a 0/1-valued word vector indicating the absence/presence of the corresponding word from the dictionary. The dictionary consists of 3703 unique words. The README file in the dataset provides more details. 68 | http://www.cs.umd.edu/~sen/lbc-proj/data/citeseer.tgz 69 | 70 | Cora: The Cora dataset consists of 2708 scientific publications classified into one of seven classes. The citation network consists of 5429 links. Each publication in the dataset is described by a 0/1-valued word vector indicating the absence/presence of the corresponding word from the dictionary. The dictionary consists of 1433 unique words. The README file in the dataset provides more details. 71 | http://www.cs.umd.edu/~sen/lbc-proj/data/cora.tgz 72 | 73 | WebKB: The WebKB dataset consists of 877 scientific publications classified into one of five classes. The citation network consists of 1608 links. Each publication in the dataset is described by a 0/1-valued word vector indicating the absence/presence of the corresponding word from the dictionary. The dictionary consists of 1703 unique words. The README file in the dataset provides more details. 74 | http://www.cs.umd.edu/~sen/lbc-proj/data/WebKB.tgz 75 | 76 | -} 77 | 78 | 79 | -- | Download, decompress, parse, serialize and save the dataset to local storage 80 | stash :: (Binary c) => 81 | FilePath -- ^ directory where the data files will be saved 82 | -> String -- ^ URI of .tar.gz file 83 | -> Int -- ^ dictionary size 84 | -> Parser c -- ^ document class 85 | -> IO () 86 | stash dir uri n pc = do 87 | rq <- parseRequest uri 88 | createDirectoryIfMissing True dir 89 | runResourceT $ runConduit $ 90 | fetch rq .| 91 | unTarGz .| 92 | withFileInfo ( \fi -> do 93 | contentToFile dir n pc fi 94 | citesToFile dir fi ) 95 | 96 | -- | Load the graph node data from local storage 97 | restoreContent :: (Binary c) => FilePath -- ^ directory where the data files are saved 98 | -> IO (M.Map String (Int16, Seq Int16, c)) 99 | restoreContent dir = runResourceT $ runConduit $ 100 | contentFromFile dir .| 101 | C.foldMap ( \(CRow i k fs c) -> M.singleton k (i, fs, c) ) 102 | 103 | 104 | citesFromFile :: (MonadResource m, MonadThrow m) => FilePath -> ConduitT i (CitesRow String) m () 105 | citesFromFile dir = 106 | C.sourceFile (dir "cites") .| 107 | CB.conduitDecode 108 | 109 | -- | Reconstruct the citation graph 110 | -- 111 | -- NB : relies on the user having `stash`ed the dataset to local disk first. 112 | loadGraph :: (Binary c) => 113 | FilePath -- ^ directory where the data files were saved 114 | -> IO (G.Graph (ContentRow Int16 c)) 115 | loadGraph dir = do 116 | mm <- restoreContent dir 117 | runResourceT $ runConduit $ 118 | citesFromFile dir .| 119 | C.foldl (\gr (CitesRow b a) -> 120 | let 121 | edm = (,) <$> M.lookup a mm <*> M.lookup b mm 122 | in 123 | case edm of 124 | Nothing -> gr -- error $ show e 125 | Just ((ib, bffs, bc), (ia, affs, ac)) -> 126 | let 127 | acr = CRow ia a affs ac 128 | bcr = CRow ib b bffs bc 129 | in 130 | (acr `G.edge` bcr) `G.overlay` gr 131 | ) G.empty 132 | 133 | -- | Stream out the edges of the citation graph, in which the nodes are decorated with the document metadata. 134 | -- 135 | -- The full citation graph can be reconstructed by folding over this stream and `G.overlay`ing the graph edges as they arrive. 136 | -- 137 | -- This way the graph can be partitioned in training , test and validation subsets at the usage site 138 | sourceGraphEdges :: (MonadResource m, MonadThrow m) => 139 | FilePath -- ^ directory of data files 140 | -> M.Map String (Int16, Seq Int16, c) -- ^ 'content' data 141 | -> ConduitT i (Maybe (G.Graph (ContentRow Int16 c))) m () 142 | sourceGraphEdges dir mm = 143 | citesFromFile dir .| 144 | C.map (\(CitesRow b a) -> 145 | case (,) <$> M.lookup a mm <*> M.lookup b mm of 146 | Nothing -> Nothing 147 | Just ((ib, bffs, bc), (ia, affs, ac)) -> 148 | let 149 | acr = CRow ia a affs ac 150 | bcr = CRow ib b bffs bc 151 | in Just (acr `G.edge` bcr)) 152 | 153 | 154 | -- | Pick out the 'content' file in the archive, parse its contents and serialize to disk 155 | -- 156 | -- | NB : the integer node identifiers are serialized as Int16, so the graph can only have up to 65535 nodes. 157 | -- 158 | -- Contact customer service if you need more node IDs. 159 | contentToFile :: (MonadThrow m, MonadResource m, Binary c) => 160 | FilePath 161 | -> Int -- ^ dictionary size 162 | -> Parser c -- ^ document class 163 | -> FileInfo 164 | -> ConduitT ByteString o m () 165 | contentToFile dir n pc fi = when ((takeExtension . unpack $ filePath fi) == ".content") $ do 166 | parseTSV .| 167 | C.map T.unwords .| 168 | void (C.mapAccumWhile ( \r i -> do 169 | case parse (contentRowP i n pc) "" r of 170 | Left e -> error $ errorBundlePretty e 171 | Right x -> Right (succ i, x) ) (0 :: Int16) 172 | ) .| 173 | CB.conduitEncode .| 174 | C.sinkFile (dir "content-z") 175 | 176 | contentFromFile :: (MonadResource m, MonadThrow m, Binary c) => FilePath 177 | -> ConduitT i (ContentRow Int16 c) m () 178 | contentFromFile dir = 179 | C.sourceFile (dir "content-z") .| 180 | CB.conduitDecode 181 | 182 | 183 | -- | Who cites whom 184 | data CitesRow a = CitesRow { 185 | cirTo :: a -- ^ cited 186 | , cirFrom :: a -- ^ citing 187 | } deriving (Eq, Show, Generic, Binary) 188 | 189 | citesRowP :: Parser (CitesRow String) 190 | citesRowP = CitesRow <$> lexeme alphaNum <*> lexeme alphaNum 191 | 192 | 193 | -- | Dataset row of the .content file 194 | -- 195 | -- The .content file contains descriptions of the papers in the following format: 196 | -- 197 | -- \ \ \ 198 | -- 199 | -- The first entry in each line contains the unique string ID of the paper followed by binary values indicating whether each word in the vocabulary is present (indicated by 1) or absent (indicated by 0) in the paper. Finally, the last entry in the line contains the class label of the paper. 200 | data ContentRow i c = CRow { 201 | crId :: i -- ^ integer identifier 202 | , crIdStr :: String -- ^ identifier string 203 | , crFeatures :: Seq Int16 -- ^ features, in sparse format (without the zeros) 204 | , crClass :: c -- ^ document class label 205 | } deriving (Eq, Ord, Show, Generic, Binary) 206 | 207 | bit :: Parser Bool 208 | bit = (char '0' $> False) <|> (char '1' $> True) 209 | 210 | sparse :: Foldable t => t Bool -> Seq Int16 211 | sparse = fst . foldl (\(acc, i) b -> if b then (acc |> i, succ i) else (acc, succ i)) (mempty, 0) 212 | 213 | contentRowP :: i -- ^ node identifier 214 | -> Int -- ^ vocabulary size 215 | -> Parser c -- ^ parser for document class 216 | -> Parser (ContentRow i c) 217 | contentRowP i n dcp = do 218 | istr <- lexeme alphaNum 219 | feats <- sparse <$> count n (lexeme bit) 220 | c <- lexeme dcp 221 | pure $ CRow i istr feats c 222 | 223 | 224 | 225 | citesToFile :: (MonadThrow m, MonadIO m, MonadResource m) => 226 | FilePath 227 | -> FileInfo 228 | -> ConduitT ByteString c m () 229 | citesToFile dir fi = do 230 | let fpath = unpack $ filePath fi 231 | when (takeExtension fpath == ".cites") $ 232 | parseTSV .| 233 | C.map T.unwords .| 234 | C.map ( \r -> case parse citesRowP "" r of 235 | Left e -> error $ errorBundlePretty e 236 | Right x -> x ) .| 237 | CB.conduitEncode .| 238 | C.sinkFile (dir "cites") 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/Datasets/LINQS/Citeseer.hs: -------------------------------------------------------------------------------- 1 | {-# language DeriveAnyClass #-} 2 | {-# LANGUAGE DeriveGeneric #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# options_ghc -Wno-unused-imports -Wno-unused-top-binds #-} 5 | -- | Citeseer document classification dataset, from : 6 | -- 7 | -- Qing Lu, and Lise Getoor. "Link-based classification." ICML, 2003. 8 | -- 9 | -- https://linqs.soe.ucsc.edu/data 10 | -- 11 | -- The dataset consists of 3312 scientific publications classified into one of six classes. The citation network consists of 4732 links. Each publication in the dataset is described by a 0\/1-valued word vector indicating the absence\/presence of the corresponding word from the dictionary. The dictionary consists of 3703 unique words. 12 | module Algebra.Graph.IO.Datasets.LINQS.Citeseer ( 13 | -- * 1. Download the dataset 14 | stash 15 | -- * 2. Reconstruct the citation graph 16 | , sourceCiteseerGraphEdges, loadCiteseerGraph 17 | -- * Types 18 | ,CiteSeerDoc(..)) where 19 | 20 | import Control.Applicative (Alternative(..)) 21 | import Control.Monad (when, foldM) 22 | import Control.Monad.IO.Class (MonadIO(..)) 23 | import GHC.Generics (Generic(..)) 24 | import GHC.Int (Int16) 25 | import Data.Functor (($>)) 26 | 27 | -- algebraic-graphs 28 | import qualified Algebra.Graph as G (Graph, empty, overlay, edge) 29 | -- binary 30 | import Data.Binary (Binary(..), encode, decode, encodeFile, decodeFileOrFail) 31 | -- binary-conduit 32 | import qualified Data.Conduit.Serialization.Binary as CB (conduitDecode, conduitEncode, ParseError(..)) 33 | -- bytestring 34 | import Data.ByteString (ByteString) 35 | import Data.ByteString.Char8 (unpack) 36 | -- conduit 37 | import Conduit (MonadUnliftIO(..), MonadResource, runResourceT) 38 | import Data.Conduit (runConduit, ConduitT, (.|), yield, await) 39 | import qualified Data.Conduit.Combinators as C (print, sourceFile, sinkFile, map, mapM, foldM, foldMap, foldl, foldMapM, mapWhile) 40 | -- containers 41 | import Data.Sequence (Seq, (|>)) 42 | import qualified Data.Map as M (Map, singleton, lookup) 43 | -- directory 44 | import System.Directory (createDirectoryIfMissing) 45 | -- exceptions 46 | import Control.Monad.Catch (MonadThrow(..)) 47 | -- filepath 48 | import System.FilePath ((), takeFileName, takeExtension) 49 | -- http-conduit 50 | import Network.HTTP.Simple (httpSource, getResponseBody, Response, Request, parseRequest, setRequestMethod) 51 | -- megaparsec 52 | import Text.Megaparsec (parse, parseTest, ()) 53 | import Text.Megaparsec.Char (char) 54 | import Text.Megaparsec.Char.Lexer (decimal) 55 | import Text.Megaparsec.Error (errorBundlePretty) 56 | -- parser.combinators 57 | import Control.Monad.Combinators (count) 58 | -- primitive 59 | import Control.Monad.Primitive (PrimMonad(..)) 60 | -- tar-conduit 61 | import Data.Conduit.Tar (Header(..), untarChunks, TarChunk, withEntries, FileInfo, filePath, withFileInfo, headerFileType, FileType(..), headerFilePath) 62 | -- text 63 | import qualified Data.Text as T (Text, unwords) 64 | import qualified Data.Text.IO as T (readFile) 65 | 66 | import Algebra.Graph.IO.Internal.Conduit (fetch, unTarGz) 67 | import Algebra.Graph.IO.Internal.Megaparsec (Parser, ParseE, symbol, lexeme, alphaNum) 68 | import Algebra.Graph.IO.SV (parseTSV) 69 | import qualified Algebra.Graph.IO.Datasets.LINQS as DL (stash, sourceGraphEdges, loadGraph, restoreContent, CitesRow(..), ContentRow(..)) 70 | {- 71 | CiteSeer: The CiteSeer dataset consists of 3312 scientific publications classified into one of six classes. The citation network consists of 4732 links. Each publication in the dataset is described by a 0/1-valued word vector indicating the absence/presence of the corresponding word from the dictionary. The dictionary consists of 3703 unique words. The README file in the dataset provides more details. 72 | -} 73 | 74 | -- | See `DL.stash` 75 | stash :: FilePath -- ^ directory where the data files will be saved 76 | -> IO () 77 | stash fp = DL.stash fp "http://www.cs.umd.edu/~sen/lbc-proj/data/citeseer.tgz" 3703 docClassP 78 | 79 | -- | See `DL.sourceGraphEdges` 80 | sourceCiteseerGraphEdges :: (MonadResource m, MonadThrow m) => 81 | FilePath -- ^ directory of data files 82 | -> M.Map String (Int16, Seq Int16, CiteSeerDoc) -- ^ 'content' data 83 | -> ConduitT i (Maybe (G.Graph (DL.ContentRow Int16 CiteSeerDoc))) m () 84 | sourceCiteseerGraphEdges = DL.sourceGraphEdges 85 | 86 | -- | See `DL.loadGraph` 87 | loadCiteseerGraph :: -- (Binary ix) => 88 | FilePath -- ^ directory where the data files were saved 89 | -> IO (G.Graph (DL.ContentRow Int16 CiteSeerDoc)) 90 | loadCiteseerGraph = DL.loadGraph 91 | 92 | -- | document classes of the Citeseer dataset 93 | data CiteSeerDoc = Agents | AI | DB | IR | ML | HCI deriving (Eq, Ord, Enum, Show, Generic, Binary) 94 | 95 | docClassP :: Parser CiteSeerDoc 96 | docClassP = 97 | (symbol "Agents" $> Agents) <|> 98 | (symbol "AI" $> AI) <|> 99 | (symbol "DB" $> DB) <|> 100 | (symbol "IR" $> IR) <|> 101 | (symbol "ML" $> ML) <|> 102 | (symbol "HCI" $> HCI) 103 | 104 | 105 | {- 106 | The .cites file contains the citation graph of the corpus. Each line describes a link in the following format: 107 | 108 | 109 | 110 | Each line contains two paper IDs. The first entry is the ID of the paper being cited and the second ID stands for the paper which contains the citation. The direction of the link is from right to left. If a line is represented by "paper1 paper2" then the link is "paper2->paper1". 111 | -} 112 | -- | only process the .cites file within the archive 113 | 114 | 115 | 116 | 117 | 118 | 119 | -- test 120 | 121 | -- -- | one row of the .content file 122 | -- -- 123 | -- -- λ> content0 124 | -- -- CRow {crId = "100157", crFeatures = fromList [36,46,65,215,261,565,1162,1508,1613,1641,1662,1797,1842,1988,2025,2399,2456,2521,2597,2618,2641,2902,3016,3050,3163,3268,3272,3287,3411,3447,3669], crClass = Agents} 125 | -- content0 = do 126 | -- t <- T.readFile "src/Algebra/Graph/IO/Datasets/LINQS/c0" 127 | -- parseTest contentRowP t 128 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/Datasets/LINQS/Cora.hs: -------------------------------------------------------------------------------- 1 | {-# language DeriveAnyClass #-} 2 | {-# LANGUAGE DeriveGeneric #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# options_ghc -Wno-unused-imports -Wno-unused-top-binds #-} 5 | -- | Cora document classification dataset, from : 6 | -- 7 | -- McCallum, A. and Nigam, K., "Automating the construction of internet portals with machine learning" Information Retrieval, 2000 8 | -- 9 | -- Qing Lu, and Lise Getoor. "Link-based classification." ICML, 2003. 10 | -- 11 | -- https://linqs.soe.ucsc.edu/data 12 | -- 13 | -- The dataset consists of 2708 scientific publications classified into one of seven classes. The citation network consists of 5429 links. Each publication in the dataset is described by a 0/1-valued word vector indicating the absence/presence of the corresponding word from the dictionary. The dictionary consists of 1433 unique words. 14 | module Algebra.Graph.IO.Datasets.LINQS.Cora ( 15 | -- * 1. Download the dataset 16 | stash 17 | -- * 2. Reconstruct the citation graph 18 | , sourceCoraGraphEdges, loadCoraGraph 19 | -- * Types 20 | , CoraDoc(..) 21 | ) where 22 | 23 | import Control.Applicative (Alternative(..)) 24 | import Control.Monad (when, foldM) 25 | import Control.Monad.IO.Class (MonadIO(..)) 26 | import GHC.Generics (Generic(..)) 27 | import GHC.Int (Int16) 28 | import Data.Functor (($>)) 29 | 30 | -- algebraic-graphs 31 | import qualified Algebra.Graph as G (Graph, empty, overlay, edge) 32 | -- binary 33 | import Data.Binary (Binary(..), encode, decode, encodeFile, decodeFileOrFail) 34 | -- binary-conduit 35 | import qualified Data.Conduit.Serialization.Binary as CB (conduitDecode, conduitEncode, ParseError(..)) 36 | -- bytestring 37 | import Data.ByteString (ByteString) 38 | import Data.ByteString.Char8 (unpack) 39 | -- conduit 40 | import Conduit (MonadUnliftIO(..), MonadResource, runResourceT) 41 | import Data.Conduit (runConduit, ConduitT, (.|), yield, await) 42 | import qualified Data.Conduit.Combinators as C (print, sourceFile, sinkFile, map, mapM, foldM, foldMap, foldl, foldMapM, mapWhile) 43 | -- containers 44 | import Data.Sequence (Seq, (|>)) 45 | import qualified Data.Map as M (Map, singleton, lookup) 46 | -- directory 47 | import System.Directory (createDirectoryIfMissing) 48 | -- exceptions 49 | import Control.Monad.Catch (MonadThrow(..)) 50 | -- filepath 51 | import System.FilePath ((), takeFileName, takeExtension) 52 | -- http-conduit 53 | import Network.HTTP.Simple (httpSource, getResponseBody, Response, Request, parseRequest, setRequestMethod) 54 | -- megaparsec 55 | import Text.Megaparsec (parse, parseTest, ()) 56 | import Text.Megaparsec.Char (char) 57 | import Text.Megaparsec.Char.Lexer (decimal) 58 | import Text.Megaparsec.Error (errorBundlePretty) 59 | -- parser.combinators 60 | import Control.Monad.Combinators (count) 61 | -- primitive 62 | import Control.Monad.Primitive (PrimMonad(..)) 63 | -- tar-conduit 64 | import Data.Conduit.Tar (Header(..), untarChunks, TarChunk, withEntries, FileInfo, filePath, withFileInfo, headerFileType, FileType(..), headerFilePath) 65 | -- text 66 | import qualified Data.Text as T (Text, unwords) 67 | import qualified Data.Text.IO as T (readFile) 68 | 69 | import Algebra.Graph.IO.Internal.Megaparsec (Parser, ParseE, symbol, lexeme, alphaNum) 70 | 71 | import qualified Algebra.Graph.IO.Datasets.LINQS as DL (stash, sourceGraphEdges, loadGraph, restoreContent, CitesRow(..), ContentRow(..)) 72 | 73 | {- 74 | The Cora dataset consists of Machine Learning papers. These papers are classified into one of the following seven classes: 75 | Case_Based 76 | Genetic_Algorithms 77 | Neural_Networks 78 | Probabilistic_Methods 79 | Reinforcement_Learning 80 | Rule_Learning 81 | Theory 82 | -} 83 | 84 | -- | document classes of the Cora dataset 85 | data CoraDoc = CB | GA | NN | PM | RL | RuL | Th deriving (Eq, Show, Ord, Enum, Generic, Binary) 86 | 87 | docClassP :: Parser CoraDoc 88 | docClassP = 89 | (symbol "Case_Based" $> CB) <|> 90 | (symbol "Genetic_Algorithms" $> GA) <|> 91 | (symbol "Neural_Networks" $> NN) <|> 92 | (symbol "Probabilistic_Methods" $> PM) <|> 93 | (symbol "Reinforcement_Learning" $> RL) <|> 94 | (symbol "Rule_Learning" $> RuL) <|> 95 | (symbol "Theory" $> Th) 96 | 97 | {- 98 | The papers were selected in a way such that in the final corpus every paper cites or is cited by atleast one other paper. There are 2708 papers in the whole corpus. 99 | 100 | After stemming and removing stopwords we were left with a vocabulary of size 1433 unique words. All words with document frequency less than 10 were removed. 101 | -} 102 | 103 | -- | See `DL.stash` 104 | stash :: FilePath -> IO () 105 | stash fp = DL.stash fp "http://www.cs.umd.edu/~sen/lbc-proj/data/cora.tgz" 1433 docClassP 106 | 107 | -- | See `DL.sourceGraphEdges` 108 | sourceCoraGraphEdges :: (MonadResource m, MonadThrow m) => 109 | FilePath -- ^ directory of data files 110 | -> M.Map String (Int16, Seq Int16, CoraDoc) -- ^ 'content' data 111 | -> ConduitT i (Maybe (G.Graph (DL.ContentRow Int16 CoraDoc))) m () 112 | sourceCoraGraphEdges = DL.sourceGraphEdges 113 | 114 | -- | See `DL.loadGraph` 115 | loadCoraGraph :: FilePath -- ^ directory where the data files were saved 116 | -> IO (G.Graph (DL.ContentRow Int16 CoraDoc)) 117 | loadCoraGraph = DL.loadGraph 118 | 119 | 120 | 121 | {- 122 | 123 | 124 | THE DIRECTORY CONTAINS TWO FILES: 125 | 126 | The .content file contains descriptions of the papers in the following format: 127 | 128 | 129 | 130 | The first entry in each line contains the unique string ID of the paper followed by binary values indicating whether each word in the vocabulary is present (indicated by 1) or absent (indicated by 0) in the paper. Finally, the last entry in the line contains the class label of the paper. 131 | 132 | The .cites file contains the citation graph of the corpus. Each line describes a link in the following format: 133 | 134 | 135 | 136 | Each line contains two paper IDs. The first entry is the ID of the paper being cited and the second ID stands for the paper which contains the citation. The direction of the link is from right to left. If a line is represented by "paper1 paper2" then the link is "paper2->paper1". 137 | -} 138 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/Dot.hs: -------------------------------------------------------------------------------- 1 | {-# language OverloadedStrings #-} 2 | {-# options_ghc -Wno-unused-imports #-} 3 | module Algebra.Graph.IO.Dot where 4 | 5 | import Dot (DotGraph(..), Strictness(..), Directionality(..), Statement(..), Element(..), NodeStatement(..), EdgeStatement(..), Attribute(..), Id(..)) 6 | 7 | 8 | {- 9 | main :: IO () 10 | main = do 11 | putStrLn $ "dumping example dotgraph to " ++ target 12 | encodeToFile target example 13 | 14 | target :: FilePath 15 | target = "example/example.dot" 16 | 17 | example :: DotGraph 18 | example = DotGraph Strict Directed (Just "foobar") 19 | [ StatementNode $ NodeStatement "a1" 20 | [ Attribute "color" "blue" 21 | , Attribute "shape" "box" 22 | ] 23 | , StatementNode $ NodeStatement "a2" [] 24 | , StatementEdge $ EdgeStatement (ListTwo "a1" "a2" ["a3"]) 25 | [ Attribute "color" "red" 26 | ] 27 | ] 28 | 29 | -} 30 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/GML.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE TypeFamilies #-} 2 | {-# language OverloadedStrings #-} 3 | {-# options_ghc -Wno-unused-imports #-} 4 | -- | Mostly-complete implementation of the GML format 5 | -- 6 | -- https://en.wikipedia.org/wiki/Graph_Modelling_Language 7 | module Algebra.Graph.IO.GML (GMLGraph, gmlGraphP, GMLNode(..), GMLEdge(..)) where 8 | 9 | import Control.Applicative hiding (many, some) 10 | import Data.Char (isAlpha, isSpace) 11 | import Data.Functor (void) 12 | import Data.Void (Void) 13 | 14 | -- algebraic-graphs 15 | import qualified Algebra.Graph as G (Graph, empty, vertex, edge, overlay) 16 | import qualified Algebra.Graph.ToGraph as G (ToGraph(..)) 17 | import qualified Algebra.Graph.Labelled as GL (Graph, empty, vertex, edge, overlay) 18 | -- megaparsec 19 | import Text.Megaparsec (Parsec, parse, parseTest, satisfy, ()) 20 | import Text.Megaparsec.Char (space1) 21 | import qualified Text.Megaparsec.Char.Lexer as L 22 | -- parser-combinators 23 | import Control.Monad.Combinators (many, some, between, skipManyTill) 24 | -- text 25 | import Data.Text (Text) 26 | import Data.Text.IO (readFile) 27 | 28 | import Prelude hiding (readFile, takeWhile) 29 | 30 | import Algebra.Graph.IO.Internal.Megaparsec (Parser, lexeme, symbol, anyString) 31 | 32 | instance G.ToGraph (GMLGraph e a) where 33 | type ToVertex (GMLGraph e a) = a 34 | toGraph = gmlGraph 35 | 36 | -- | Construct a 'G.Graph' using the edge data contained in a 'GMLGraph' 37 | gmlGraph :: GMLGraph e a -> G.Graph a 38 | gmlGraph (GMLGraph _ _ es) = 39 | foldl (\gr (GMLEdge a b _ _) -> G.edge a b `G.overlay` gr) G.empty es 40 | 41 | -- | Graph entities of the GML graph format 42 | data GMLGraph e a = GMLGraph { 43 | gmlHeader :: Maybe String 44 | , gmlNodes :: [GMLNode a] 45 | , gmlEdges :: [GMLEdge e a] 46 | } deriving (Show) 47 | 48 | -- | Parser for the GML graph format 49 | gmlGraphP :: Parser a -- ^ node ids 50 | -> Parser e -- ^ node annotations 51 | -> Parser (GMLGraph e a) 52 | gmlGraphP pa pe = do 53 | header <- optional creator -- header 54 | void $ symbol "graph" 55 | sqBkts $ do 56 | ns <- many $ gmlNode pa 57 | es <- many $ gmlEdge pa pe 58 | pure $ GMLGraph header ns es 59 | 60 | creator :: Parser String 61 | creator = do 62 | void $ symbol "Creator" 63 | quoted $ many $ satisfy (/= '\"') 64 | 65 | -- | GML nodes 66 | data GMLNode a = GMLNode a (Maybe String) deriving (Show) 67 | 68 | gmlNode :: Parser a -> Parser (GMLNode a) 69 | gmlNode p = do 70 | void $ symbol "node" 71 | sqBkts $ do 72 | n <- symbol "id" *> lexeme p 73 | l <- optional gmlLabel 74 | pure $ GMLNode n l 75 | 76 | sqBkts :: Parser a -> Parser a 77 | sqBkts = between (symbol "[") (symbol "]") 78 | quoted :: Parser a -> Parser a 79 | quoted = between (symbol "\"") (symbol "\"") 80 | 81 | -- | GML edges 82 | data GMLEdge e a = GMLEdge a a (Maybe e) (Maybe String) deriving (Show) 83 | 84 | gmlEdge :: Parser a -> Parser e -> Parser (GMLEdge e a) 85 | gmlEdge pa pe = do 86 | void $ symbol "edge" 87 | sqBkts $ do 88 | a <- source pa 89 | b <- target pa 90 | v <- optional (value pe) 91 | l <- optional gmlLabel 92 | pure $ GMLEdge a b v l 93 | 94 | -- attributes 95 | 96 | source, target, value :: Parser a -> Parser a 97 | source = attr "source" 98 | target = attr "target" 99 | value = attr "value" 100 | 101 | gmlLabel :: Parser String 102 | gmlLabel = symbol "label" *> lexeme (quoted p) 103 | where 104 | p = many $ satisfy (/= '\"') 105 | 106 | attr :: Text -> Parser a -> Parser a 107 | attr str p = symbol str *> lexeme p 108 | 109 | 110 | -- gmlValue :: Parser a -> Parser a 111 | -- gmlValue p = symbol "value" *> lexeme p 112 | 113 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/Internal/Conduit.hs: -------------------------------------------------------------------------------- 1 | {-# options_ghc -Wno-unused-imports -Wno-unused-top-binds #-} 2 | -- | Miscellaneous conduit-related functionality 3 | -- 4 | -- Networking, compression 5 | module Algebra.Graph.IO.Internal.Conduit (fetchTarGz, unTarGz, fetch) where 6 | 7 | import Control.Monad (when) 8 | import Control.Monad.IO.Class (MonadIO(..)) 9 | import Data.Function ((&)) 10 | 11 | -- bytestring 12 | import Data.ByteString (ByteString) 13 | -- conduit 14 | import Conduit (MonadUnliftIO(..), MonadResource, runResourceT) 15 | import Data.Conduit (runConduit, ConduitT, (.|), yield, await) 16 | import qualified Data.Conduit.Combinators as C (print, sourceFile, sinkFile, map, mapM, foldM, mapWhile) 17 | -- conduit-extra 18 | import Data.Conduit.Zlib (ungzip) 19 | -- filepath 20 | import System.FilePath (()) 21 | -- exceptions 22 | import Control.Monad.Catch (MonadThrow(..)) 23 | -- http-conduit 24 | import Network.HTTP.Simple (httpSource, getResponseBody, Response, Request, parseRequest, setRequestMethod, setRequestSecure) 25 | -- primitive 26 | import Control.Monad.Primitive (PrimMonad(..)) 27 | -- tar-conduit 28 | import Data.Conduit.Tar (Header(..), untarChunks, TarChunk, withEntries, headerFileType, FileType(..), headerFilePath) 29 | 30 | 31 | -- | Decompress a .tar.gz stream 32 | unTarGz :: (PrimMonad m, MonadThrow m) => ConduitT ByteString TarChunk m () 33 | unTarGz = ungzip .| 34 | untarChunks 35 | 36 | -- | Download a file 37 | fetch :: MonadResource m => Request -> ConduitT i ByteString m () 38 | fetch r = httpSource (r & setRequestSecure False) getResponseBody 39 | 40 | -- | Download, decompress and save a .tar.gz archive 41 | fetchTarGz :: String -- ^ URL with the .tar.gz 42 | -> FilePath -- ^ directory where to store archive contents 43 | -> IO () 44 | fetchTarGz path fp = do 45 | rq <- parseRequest path 46 | runResourceT $ runConduit $ 47 | fetch rq .| 48 | unTarGz .| 49 | withEntries (\h -> when (headerFileType h == FTNormal) (C.sinkFile (fp headerFilePath h))) 50 | 51 | untarEntries :: MonadThrow m => 52 | (Header -> Bool) 53 | -> ConduitT ByteString o m () -- ^ process the content of each file that satisfies the predicate 54 | -> ConduitT TarChunk o m () 55 | untarEntries f p = withEntries (\h -> when (f h) p) 56 | 57 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/Internal/Megaparsec.hs: -------------------------------------------------------------------------------- 1 | {-# language OverloadedStrings #-} 2 | {-# options_ghc -Wno-unused-imports #-} 3 | module Algebra.Graph.IO.Internal.Megaparsec (Parser, ParserT, ParseE, 4 | -- ** Internal 5 | lexeme, symbol, anyString, alphaNum 6 | ) where 7 | 8 | import Control.Applicative hiding (many, some) 9 | import Data.Char (isAlpha, isSpace, isAlphaNum) 10 | import Data.Void (Void) 11 | 12 | -- megaparsec 13 | import Text.Megaparsec (Parsec, ParsecT, parseTest, satisfy, ()) 14 | import Text.Megaparsec.Char (space1) 15 | import Text.Megaparsec.Error (ParseErrorBundle) 16 | import qualified Text.Megaparsec.Char.Lexer as L 17 | -- parser-combinators 18 | import Control.Monad.Combinators (many, some, between) 19 | -- text 20 | import Data.Text (Text) 21 | 22 | type Parser = Parsec Void Text 23 | 24 | type ParserT = ParsecT Void Text 25 | 26 | type ParseE = ParseErrorBundle Text Void 27 | 28 | lexeme :: Parser a -- ^ disregard any whitespace around this parser 29 | -> Parser a 30 | lexeme = L.lexeme sc 31 | 32 | -- | Match a string 33 | symbol :: Text 34 | -> Parser Text 35 | symbol = L.symbol sc 36 | 37 | -- | space consumer 38 | sc :: Parser () 39 | sc = L.space 40 | space1 41 | (L.skipLineComment "//") 42 | (L.skipBlockComment "/*" "*/") 43 | 44 | anyString, alphaNum :: Parser String 45 | anyString = many (satisfy isAlpha) 46 | 47 | alphaNum = many (satisfy $ \c -> isAlphaNum c || c `elem` ['-', '_']) "alphanumeric string or -" 48 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/JSON.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE BangPatterns #-} 2 | {-# LANGUAGE LambdaCase #-} 3 | {-# LANGUAGE DeriveGeneric #-} 4 | {-# language OverloadedStrings #-} 5 | {-# options_ghc -Wno-unused-imports #-} 6 | {-| 7 | Module : Algebra.Graph.IO.JSON 8 | Description : 'aeson' instances for algebraic-graphs types 9 | Copyright : (c) Marco Zocca, 2022 10 | Maintainer : ocramz 11 | Stability : experimental 12 | Portability : POSIX 13 | 14 | Orphan instances for compatibility between 'algebraic-graphs' and 'aeson'. 15 | 16 | Import only if you know what you're doing. 17 | -} 18 | module Algebra.Graph.IO.JSON () where 19 | 20 | import Control.Applicative (Alternative(..)) 21 | import GHC.Generics (Generic) 22 | 23 | -- aeson 24 | import qualified Data.Aeson as A (FromJSON(..), ToJSON(..), ToJSONKey(..), FromJSONKey(..), encode, eitherDecode, Value, withArray, withText, withObject, (.:), Object) 25 | import qualified Data.Aeson.Types as A (Parser) 26 | import qualified Data.Aeson.Encoding as A (value, fromEncoding) 27 | -- alga 28 | import qualified Algebra.Graph as G (Graph(..), edges, foldg) 29 | import qualified Algebra.Graph.Labelled as GL (Graph(..), edges, foldg) 30 | import qualified Algebra.Graph.ToGraph as G (ToGraph(..), toAdjacencyMap) 31 | import qualified Algebra.Graph.AdjacencyMap as GAM (AdjacencyMap, adjacencyMap, edgeList, vertexList) 32 | 33 | 34 | g0 :: G.Graph Int 35 | g0 = G.edges [(1, 2), (3, 4), (5, 6), (1, 5), (2, 6)] 36 | 37 | 38 | 39 | instance (A.ToJSON a, A.ToJSONKey a) => A.ToJSON (GAM.AdjacencyMap a) 40 | 41 | instance (A.FromJSON a, Ord a, A.FromJSONKey a) => A.FromJSON (GAM.AdjacencyMap a) 42 | 43 | 44 | -- unlabeled edges 45 | 46 | -- instance (Ord a, A.ToJSON a, A.ToJSONKey a) => A.ToJSON (G.Graph a) where 47 | -- toJSON = A.toJSON . G.toAdjacencyMap 48 | 49 | 50 | -- | TODO benchmark against ToJSON AdjacencyMap instance 51 | instance A.ToJSON a => A.ToJSON (G.Graph a) where 52 | toJSON = graphToValue 53 | 54 | 55 | graphToValue :: (A.ToJSON t) => 56 | G.Graph t -> A.Value 57 | graphToValue = go 58 | where 59 | go G.Empty = A.toJSON Empty 60 | go (G.Vertex x) = A.toJSON (Vertex x) 61 | go (G.Overlay x y) = A.toJSON (Overlay (go x) (go y)) 62 | go (G.Connect x y) = A.toJSON (Connect (go x) (go y)) 63 | 64 | 65 | 66 | -- | TODO benchmark against FromJSON AdjacencyMap instance 67 | instance A.FromJSON a => A.FromJSON (G.Graph a) where 68 | parseJSON x = A.withObject "Graph" gObj x <|> 69 | parseE x 70 | 71 | gObj :: A.FromJSON a => A.Object -> A.Parser (G.Graph a) 72 | gObj o = parseC o <|> 73 | parseO o <|> 74 | parseV o 75 | 76 | parseE :: A.Value -> A.Parser (G.Graph a) 77 | parseE = A.withArray "empty" $ \t -> if null t then pure G.Empty else fail "cannot parse Empty" 78 | 79 | parseV :: A.FromJSON a => A.Object -> A.Parser (G.Graph a) 80 | parseV o = G.Vertex <$> o A..: "v" 81 | 82 | parseO :: A.FromJSON a => A.Object -> A.Parser (G.Graph a) 83 | parseO o = do 84 | a <- o A..: "o1" 85 | b <- o A..: "o2" 86 | pure $ G.Overlay a b 87 | 88 | parseC :: A.FromJSON a => A.Object -> A.Parser (G.Graph a) 89 | parseC o = do 90 | a <- o A..: "c1" 91 | b <- o A..: "c2" 92 | pure $ G.Connect a b 93 | 94 | 95 | 96 | 97 | instance (A.ToJSON a, A.ToJSON e) => A.ToJSON (GL.Graph e a) where 98 | toJSON = graphLToValue 99 | 100 | graphLToValue :: (A.ToJSON a, A.ToJSON e) => GL.Graph e a -> A.Value 101 | graphLToValue = go 102 | where 103 | go GL.Empty = A.toJSON Empty 104 | go (GL.Vertex x) = A.toJSON (Vertex x) 105 | go (GL.Connect e x y) = A.toJSON (LEdge e (go x) (go y)) 106 | 107 | 108 | instance (A.FromJSON e, A.FromJSON a) => A.FromJSON (GL.Graph e a) where 109 | parseJSON x = A.withObject "Graph (labeled)" gLObj x <|> 110 | parseEL x 111 | 112 | gLObj :: (A.FromJSON e, A.FromJSON a) => A.Object -> A.Parser (GL.Graph e a) 113 | gLObj o = parseCL o <|> 114 | parseVL o 115 | 116 | parseEL :: A.Value -> A.Parser (GL.Graph e a) 117 | parseEL = A.withArray "empty" $ \t -> if null t then pure GL.Empty else fail "cannot parse Empty" 118 | 119 | parseVL :: A.FromJSON a => A.Object -> A.Parser (GL.Graph e a) 120 | parseVL o = GL.Vertex <$> o A..: "v" 121 | 122 | parseCL :: (A.FromJSON e, A.FromJSON a) => 123 | A.Object -> A.Parser (GL.Graph e a) 124 | parseCL o = do 125 | e <- o A..: "l" 126 | a <- o A..: "e1" 127 | b <- o A..: "e2" 128 | pure $ GL.Connect e a b 129 | 130 | 131 | -- | Helper types 132 | 133 | -- empty 134 | data Empty = Empty deriving (Eq, Show, Generic) 135 | instance A.ToJSON Empty 136 | -- vertex 137 | newtype Vertex a = Vertex { v :: a } deriving (Eq, Show, Generic) 138 | instance A.ToJSON a => A.ToJSON (Vertex a) 139 | 140 | -- overlay 141 | data Overlay a = Overlay { o1 :: a, o2 :: a} deriving (Eq, Show, Generic) 142 | instance A.ToJSON a => A.ToJSON (Overlay a) 143 | 144 | -- connect 145 | data Connect a = Connect { c1 :: a, c2 :: a} deriving (Eq, Show, Generic) 146 | instance A.ToJSON a => A.ToJSON (Connect a) 147 | 148 | 149 | 150 | 151 | data LEdge e a = LEdge { l :: e, e1 :: a, e2 :: a } deriving (Eq, Show, Generic) 152 | instance (A.ToJSON e, A.ToJSON a) => A.ToJSON (LEdge e a) 153 | 154 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/MatrixMarket.hs: -------------------------------------------------------------------------------- 1 | {-# options_ghc -Wno-unused-imports #-} 2 | module Algebra.Graph.IO.MatrixMarket where 3 | 4 | import Data.Matrix.MatrixMarket (Matrix(..), readMatrix', nnz, dim, numDat, Array(..), readArray', dimArr, numDatArr, Structure(..), ImportError(..)) 5 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/SV.hs: -------------------------------------------------------------------------------- 1 | {-# language OverloadedStrings #-} 2 | {-# options_ghc -Wno-unused-imports #-} 3 | module Algebra.Graph.IO.SV ( 4 | -- * Parse rows of a TSV file 5 | tsvSink, 6 | -- ** Labelled graphs 7 | tsvSinkL, 8 | -- * Utilities 9 | parseTSV, 10 | ) where 11 | 12 | -- algebraic-graphs 13 | import qualified Algebra.Graph as G (Graph, edge, empty, overlay) 14 | import qualified Algebra.Graph.Labelled as GL (Graph, edge, empty, connect, overlay) 15 | -- bytestring 16 | import Data.ByteString (ByteString) 17 | -- conduit 18 | import Data.Conduit (ConduitT, (.|)) 19 | import qualified Data.Conduit.Combinators as C (map, foldM) 20 | -- csv-conduit 21 | import Data.CSV.Conduit (CSV(..), CSVSettings(..), Row) 22 | -- exceptions 23 | import Control.Monad.Catch (MonadThrow(..)) 24 | -- megaparsec 25 | import Text.Megaparsec (parse, sepBy) 26 | import Text.Megaparsec.Char.Lexer (decimal) 27 | -- text 28 | import Data.Text (Text) 29 | 30 | import Algebra.Graph.IO.Internal.Megaparsec (Parser, ParseE) 31 | 32 | 33 | -- | fetch chunks of a (uncompressed) TSV file and output the resulting graph 34 | -- 35 | -- NB The TSV is assumed to have three columns, where the first two contain the node IDs of the edges 36 | tsvSink :: (MonadThrow m) => ConduitT ByteString o m (G.Graph Int) 37 | tsvSink = parseTSV .| C.map edgeP .| accGraph 38 | 39 | -- | same as 'tsvSink', but uses the third TSV column as edge label 40 | tsvSinkL :: (MonadThrow m) => ConduitT ByteString o m (GL.Graph [Int] Int) 41 | tsvSinkL = parseTSV .| C.map edgeP .| accLGraph 42 | 43 | parseTSV :: MonadThrow m => ConduitT ByteString (Row Text) m () 44 | parseTSV = intoCSV tsvSettings 45 | 46 | edgeP :: Row Text -> Maybe (Edge [Int] Int) 47 | edgeP = rowToEdge decimal decimal 48 | 49 | rowToEdge :: Parser a -- ^ node IDs 50 | -> Parser e -- ^ edge labels 51 | -> Row Text -- ^ TSV row contents 52 | -> Maybe (Edge [e] a) 53 | rowToEdge nodeP labelP t = case t of 54 | (ta:tb:te:_) -> case (parseNode ta, parseNode tb, parseEdge te) of 55 | (Right a, Right b, Right e) -> pure (Edge a b [e]) 56 | _ -> Nothing 57 | _ -> Nothing 58 | where 59 | parseNode = parse nodeP "" 60 | parseEdge = parse labelP "" 61 | 62 | 63 | -- | Labeled graph edges 64 | data Edge e a = Edge a a e deriving (Eq, Show) 65 | 66 | accGraph :: (Monad m) => ConduitT (Maybe (Edge e a)) o m (G.Graph a) 67 | accGraph = flip C.foldM G.empty $ \acc m -> 68 | case m of 69 | Just (Edge a b _) -> pure $ (a `G.edge` b) `G.overlay` acc 70 | Nothing -> pure acc 71 | 72 | accLGraph :: (Monad m) => ConduitT (Maybe (Edge [e] a)) o m (GL.Graph [e] a) 73 | accLGraph = flip C.foldM GL.empty $ \acc m -> 74 | case m of 75 | Just (Edge a b [e]) -> pure $ (GL.edge [e] a b) `GL.overlay` acc 76 | _ -> pure acc 77 | 78 | -- | tab-separated values 79 | tsvSettings :: CSVSettings 80 | tsvSettings = CSVSettings '\t' Nothing 81 | 82 | 83 | 84 | -- playground 85 | 86 | 87 | 88 | -- test0 :: IO () -- (G.Graph Int) 89 | -- test0 = do 90 | -- rq <- parseRequest "https://graphchallenge.s3.amazonaws.com/synthetic/partitionchallenge/static/simulated_blockmodel_graph_50_nodes.tar.gz" 91 | -- runResourceT $ runConduit $ 92 | -- fetch rq .| 93 | -- unTarGz .| 94 | -- parseTarEntry fname .| 95 | -- C.print 96 | -- where 97 | -- fname :: FilePath 98 | -- fname = "simulated_blockmodel_graph_50_nodes.tsv" 99 | 100 | 101 | -- -- | Parse a single file from a .tar archive 102 | -- parseTarEntry :: (MonadThrow m) => 103 | -- FilePath -- ^ file in .tar archive 104 | -- -> ConduitT TarChunk (G.Graph Int) m () 105 | -- parseTarEntry fname = 106 | -- withEntries (\h -> when (headerFileType h == FTNormal && 107 | -- headerFilePath h == fname) tsvC) 108 | 109 | 110 | -- tsvC :: (MonadThrow m) => ConduitT ByteString (G.Graph Int) m () 111 | -- tsvC = do 112 | -- g <- tsvSink 113 | -- yield g 114 | -------------------------------------------------------------------------------- /src/Algebra/Graph/IO/Serialise.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE BangPatterns #-} 2 | {-# LANGUAGE LambdaCase #-} 3 | {-| 4 | Module : Algebra.Graph.IO.Serialise 5 | Description : 'serialise' instances for algebraic-graphs types 6 | Copyright : (c) Marco Zocca, 2022 7 | Maintainer : ocramz 8 | Stability : experimental 9 | Portability : POSIX 10 | 11 | Orphan instances for compatibility between 'algebraic-graphs' and 'serialise'. 12 | 13 | Import only if you know what you're doing. 14 | -} 15 | module Algebra.Graph.IO.Serialise () where 16 | 17 | -- alga 18 | import qualified Algebra.Graph as G (Graph(..), edges, foldg) 19 | import qualified Algebra.Graph.Labelled as GL (Graph(..), edges, foldg) 20 | -- serialise 21 | import qualified Codec.Serialise as CS (Serialise(..), serialise, serialiseIncremental, deserialiseOrFail, DeserialiseFailure) 22 | import qualified Codec.Serialise.Encoding as CS (encodeListLen, encodeListLenIndef, encodeWord) 23 | import qualified Codec.Serialise.Decoding as CS (decodeListLen, decodeListLenIndef, decodeWord) 24 | 25 | 26 | instance CS.Serialise a => CS.Serialise (G.Graph a) where 27 | encode = \case 28 | G.Empty -> CS.encodeListLen 1 <> CS.encodeWord 0 29 | G.Vertex x -> CS.encodeListLen 2 <> CS.encodeWord 1 <> CS.encode x 30 | G.Overlay a b -> CS.encodeListLen 3 <> CS.encodeWord 2 <> CS.encode a <> CS.encode b 31 | G.Connect a b -> CS.encodeListLen 3 <> CS.encodeWord 3 <> CS.encode a <> CS.encode b 32 | decode = do 33 | n <- CS.decodeListLen 34 | t <- CS.decodeWord -- constructor tag 35 | case (t, n) of 36 | (0, 1) -> pure $ G.Empty 37 | (1, 2) -> do 38 | !x <- CS.decode 39 | pure $ G.Vertex x 40 | (2, 3) -> do 41 | !x <- CS.decode 42 | !y <- CS.decode 43 | pure $ G.Overlay x y 44 | (3, 3) -> do 45 | !x <- CS.decode 46 | !y <- CS.decode 47 | pure $ G.Connect x y 48 | e -> fail $ unwords ["unknown tag", show e] 49 | 50 | instance (CS.Serialise e, CS.Serialise a) => CS.Serialise (GL.Graph e a) where 51 | encode = \case 52 | GL.Empty -> CS.encodeListLen 1 <> CS.encodeWord 0 53 | GL.Vertex x -> CS.encodeListLen 2 <> CS.encodeWord 1 <> CS.encode x 54 | GL.Connect e a b -> CS.encodeListLen 4 <> CS.encodeWord 2 <> CS.encode e <> CS.encode a <> CS.encode b 55 | decode = do 56 | n <- CS.decodeListLen 57 | t <- CS.decodeWord -- constructor tag 58 | case (t, n) of 59 | (0, 1) -> pure $ GL.Empty 60 | (1, 2) -> do 61 | !x <- CS.decode 62 | pure $ GL.Vertex x 63 | (2, 4) -> do 64 | !e <- CS.decode 65 | !x <- CS.decode 66 | !y <- CS.decode 67 | pure $ GL.Connect e x y 68 | e -> fail $ unwords ["unknown tag", show e] 69 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # 15 | # The location of a snapshot can be provided as a file or url. Stack assumes 16 | # a snapshot provided as a file might change, whereas a url resource does not. 17 | # 18 | # resolver: ./custom-snapshot.yaml 19 | # resolver: https://example.com/snapshots/2018-01-01.yaml 20 | resolver: 21 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/19/20.yaml 22 | 23 | # User packages to be built. 24 | # Various formats can be used as shown in the example below. 25 | # 26 | # packages: 27 | # - some-directory 28 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 29 | # subdirs: 30 | # - auto-update 31 | # - wai 32 | packages: 33 | - . 34 | # Dependency packages to be pulled from upstream that are not in the resolver. 35 | # These entries can reference officially published versions as well as 36 | # forks / in-progress versions pinned to a git hash. For example: 37 | # 38 | extra-deps: [] 39 | # - acme-missiles-0.3 40 | # - git: https://github.com/commercialhaskell/stack.git 41 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 42 | # 43 | # extra-deps: [] 44 | 45 | # Override default flag values for local packages and extra-deps 46 | # flags: {} 47 | 48 | # Extra package databases containing global packages 49 | # extra-package-dbs: [] 50 | 51 | # Control whether we use the GHC we find on the path 52 | # system-ghc: true 53 | # 54 | # Require a specific version of stack, using version ranges 55 | # require-stack-version: -any # Default 56 | # require-stack-version: ">=2.5" 57 | # 58 | # Override the architecture used by stack, especially useful on Windows 59 | # arch: i386 60 | # arch: x86_64 61 | # 62 | # Extra directories used by stack for building 63 | # extra-include-dirs: [/path/to/dir] 64 | # extra-lib-dirs: [/path/to/dir] 65 | # 66 | # Allow a newer minor version of GHC than the snapshot specifies 67 | # compiler-check: newer-minor 68 | -------------------------------------------------------------------------------- /test/Algebra/Graph/IO/JSONSpec.hs: -------------------------------------------------------------------------------- 1 | module Algebra.Graph.IO.JSONSpec where 2 | 3 | import Data.Either (isRight) 4 | -- aeson 5 | import qualified Data.Aeson as A (FromJSON(..), ToJSON(..), encode, eitherDecode) 6 | -- alga 7 | import qualified Algebra.Graph as G (Graph(..), edges, foldg) 8 | import qualified Algebra.Graph.Labelled as GL (Graph(..), edges, foldg) 9 | -- hspec 10 | import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy) 11 | 12 | import Algebra.Graph.IO.JSON 13 | 14 | spec :: Spec 15 | spec = describe "codec rountrip" $ do 16 | it "Graph Int" $ do 17 | g0' `shouldSatisfy` isRight 18 | it "Graph [Char] Int" $ do 19 | g1' `shouldSatisfy` isRight 20 | 21 | 22 | g0 :: G.Graph Int 23 | g0 = G.edges [(0, 1), (1, 2), (2, 3), (1, 4)] 24 | 25 | g0' :: Either String (G.Graph Int) 26 | g0' = A.eitherDecode $ A.encode g0 27 | 28 | 29 | g1 :: GL.Graph [Char] Int 30 | g1 = GL.edges [("x", 0, 1), ("y", 1, 2), ("z", 2, 3), ("w", 1, 4)] 31 | 32 | g1' :: Either String (GL.Graph [Char] Int) 33 | g1' = A.eitherDecode $ A.encode g1 34 | -------------------------------------------------------------------------------- /test/Algebra/Graph/IO/SerialiseSpec.hs: -------------------------------------------------------------------------------- 1 | module Algebra.Graph.IO.SerialiseSpec (spec) where 2 | 3 | import Data.Either (isRight) 4 | 5 | -- alga 6 | import qualified Algebra.Graph as G (Graph(..), edges, foldg) 7 | import qualified Algebra.Graph.Labelled as GL (Graph(..), edges, foldg) 8 | -- hspec 9 | import Test.Hspec (Spec, describe, it, shouldBe, shouldSatisfy) 10 | -- serialise 11 | import qualified Codec.Serialise as CS (Serialise(..), serialise, serialiseIncremental, deserialiseOrFail, DeserialiseFailure) 12 | import qualified Codec.Serialise.Encoding as CS (encodeListLen, encodeWord) 13 | import qualified Codec.Serialise.Decoding as CS (decodeListLen, decodeWord) 14 | 15 | import Algebra.Graph.IO.Serialise 16 | 17 | spec :: Spec 18 | spec = describe "codec rountrip" $ do 19 | it "Graph Int" $ do 20 | g0' `shouldSatisfy` isRight 21 | it "Graph [Char] Int" $ do 22 | g1' `shouldSatisfy` isRight 23 | 24 | g0 :: G.Graph Int 25 | g0 = G.edges [(0, 1), (1, 2), (2, 3), (1, 4)] 26 | 27 | g0' :: Either CS.DeserialiseFailure (G.Graph Int) 28 | g0' = CS.deserialiseOrFail $ CS.serialise g0 29 | 30 | 31 | 32 | g1 :: GL.Graph [Char] Int 33 | g1 = GL.edges [("x", 0, 1), ("y", 1, 2), ("z", 2, 3), ("w", 1, 4)] 34 | 35 | g1' :: Either CS.DeserialiseFailure (GL.Graph [Char] Int) 36 | g1' = CS.deserialiseOrFail $ CS.serialise g1 37 | -------------------------------------------------------------------------------- /test/Spec.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -F -pgmF hspec-discover #-} 2 | --------------------------------------------------------------------------------