Packages

Comprehensive graph file format I/O for the yog graph library - Support for GraphML, GDF, JSON (D3, Cytoscape, VisJs), TGF, LEDA, Pajek, Adjacency List/Matrix, and Matrix Market

Current section

4 Versions

Jump to

Compare versions

32 files changed
+3140 additions
-567 deletions
  @@ -289,9 +289,19 @@ let gdf_string = gdf.serialize_with(node_attr, edge_attr, options, graph)
289 289
290 290 ### JSON Format
291 291
292 - JSON format export for web visualization libraries and data exchange. Supports multiple format presets for popular visualization tools.
292 + JSON format export for web visualization libraries and data exchange. Provides multiple format presets for popular visualization tools and supports full bidirectional I/O.
293 293
294 - **Note:** The JSON module is currently write-only. Import/read functionality is not implemented. For bidirectional I/O, consider using GraphML, GDF, TGF, LEDA, or Pajek formats.
294 + ```gleam
295 + import yog/model.{Directed}
296 + import yog_io/json
297 + import gleam/dynamic/decode
298 +
299 + // Export to JSON string
300 + let json_string = json.to_json(graph, json.default_export_options())
301 +
302 + // Import from JSON string
303 + let assert Ok(graph) = json.from_json(json_string, decode.string, decode.string)
304 + ```
295 305
296 306 ```gleam
297 307 import yog/model.{Directed}
  @@ -501,7 +511,7 @@ See [test/examples/multigraph_json_example.gleam](https://github.com/code-shoily
501 511 | `yog_io/tgf` | TGF (Trivial Graph Format) serialization and parsing |
502 512 | `yog_io/leda` | LEDA format with strict validation |
503 513 | `yog_io/pajek` | Pajek (.net) format for social network analysis |
504 - | `yog_io/json` | JSON export with multiple format presets and MultiGraph support (WRITE-ONLY) |
514 + | `yog_io/json` | JSON format support with multiple presets and MultiGraph support |
505 515 | `yog_io/graphml` | Full GraphML support with custom mappers |
506 516 | `yog_io/gdf` | Full GDF support with custom mappers |
507 517
  @@ -537,7 +547,7 @@ See [test/examples/multigraph_json_example.gleam](https://github.com/code-shoily
537 547
538 548 ### JSON
539 549
540 - **Note:** JSON module is currently WRITE-ONLY. Import/read functionality is not implemented.
550 + JSON module provides comprehensive support for common web-based graph formats with full bidirectional I/O.
541 551
542 552 - âś… Generic format with full metadata
543 553 - âś… D3.js force-directed format
  @@ -545,11 +555,11 @@ See [test/examples/multigraph_json_example.gleam](https://github.com/code-shoily
545 555 - âś… vis.js network format
546 556 - âś… NetworkX node-link format
547 557 - âś… MultiGraph support with unique edge IDs
548 - - âś… Custom node and edge serializers
558 + - âś… Custom node and edge serializers/decoders
549 559 - âś… Generic type support (not limited to String)
550 - - âś… File write operations
560 + - âś… File read and write operations
551 561 - âś… Custom metadata fields
552 - - ❌ Import/read operations (not implemented)
562 + - âś… Graph import and deserialization
553 563
554 564 ### GraphML
555 565
  @@ -581,7 +591,7 @@ See [test/examples/multigraph_json_example.gleam](https://github.com/code-shoily
581 591 | GraphML | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
582 592 | GDF | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
583 593
584 - **Note:** JSON format is write-only (export only). All other formats support bidirectional read/write operations.
594 + All formats now support bidirectional read/write operations.
585 595
586 596 ## File Format Examples
587 597
  @@ -701,6 +711,38 @@ Run a specific test function:
701 711 ./test_module.sh yog_io/json_test to_json_generic_format_test
702 712 ```
703 713
714 + ### Property-Based Tests
715 +
716 + In addition to traditional example-based tests, `yog_io` includes property-based tests using [qcheck](https://hex.pm/packages/qcheck). These tests generate random graphs and verify roundtrip invariants:
717 +
718 + ```bash
719 + # Run all tests (including property tests)
720 + gleam test
721 +
722 + # Run specific property test
723 + gleam test yog_io@property_test.graphml_structural_roundtrip_property_test
724 + ```
725 +
726 + **Key Properties Verified:**
727 +
728 + | Property | Description |
729 + |----------|-------------|
730 + | Structural Equality | Complete graph topology preserved (GraphML, JSON) |
731 + | Node Count | Number of nodes unchanged after roundtrip |
732 + | Edge Count | Number of edges unchanged after roundtrip |
733 + | Graph Type | Directed/Undirected property maintained |
734 + | Undirected Symmetry | For undirected graphs, edge(u,v) implies edge(v,u) |
735 +
736 + **Format Limitations:**
737 +
738 + Not all formats support complete structural equality:
739 +
740 + - **Full Support**: GraphML, JSON (Generic format)
741 + - **Partial Support**: GDF (empty graphs), TGF (auto-node creation)
742 + - **No Support**: LEDA, Pajek (node IDs renumbered to 1, 2, 3...)
743 +
744 + See [PROPERTY_TESTS.md](PROPERTY_TESTS.md) for detailed documentation on invariants, hypotheses, and limitations.
745 +
704 746 ### Running Examples
705 747
706 748 Run all examples at once:
  @@ -1,8 +1,8 @@
1 1 name = "yog_io"
2 - version = "1.0.0"
2 + version = "1.1.0"
3 3
4 4 targets = ["erlang", "javascript"]
5 - description = "Graph file format I/O for the yog graph library - GraphML, GDF, and JSON support with multiple format presets for web visualization"
5 + description = "Comprehensive graph file format I/O for the yog graph library - Support for GraphML, GDF, JSON (D3, Cytoscape, VisJs), TGF, LEDA, Pajek, Adjacency List/Matrix, and Matrix Market"
6 6 licences = ["Apache-2.0"]
7 7 repository = { type = "github", user = "code-shoily", repo = "yog_io" }
8 8 links = [{ title = "Website", href = "https://github.com/code-shoily/yog_io" }]
  @@ -13,10 +13,11 @@ gleam_regexp = ">= 1.0.0 and < 2.0.0"
13 13 gleam_json = ">= 3.0.0 and < 4.0.0"
14 14 simplifile = ">= 1.0.0 and < 3.0.0"
15 15 xmlm = ">= 1.0.1 and < 2.0.0"
16 - yog = ">= 5.1.0 and < 6.0.0"
16 + yog = ">= 5.1.1 and < 6.0.0"
17 17
18 18 [dev_dependencies]
19 19 gleeunit = ">= 1.0.0 and < 2.0.0"
20 + qcheck = ">= 1.0.0 and < 2.0.0"
20 21
21 22 [javascript]
22 23 typescript_declarations = true
\ No newline at end of file
  @@ -1,7 +1,7 @@
1 1 {<<"name">>, <<"yog_io">>}.
2 2 {<<"app">>, <<"yog_io">>}.
3 - {<<"version">>, <<"1.0.0">>}.
4 - {<<"description">>, <<"Graph file format I/O for the yog graph library - GraphML, GDF, and JSON support with multiple format presets for web visualization"/utf8>>}.
3 + {<<"version">>, <<"1.1.0">>}.
4 + {<<"description">>, <<"Comprehensive graph file format I/O for the yog graph library - Support for GraphML, GDF, JSON (D3, Cytoscape, VisJs), TGF, LEDA, Pajek, Adjacency List/Matrix, and Matrix Market"/utf8>>}.
5 5 {<<"licenses">>, [<<"Apache-2.0">>]}.
6 6 {<<"build_tools">>, [<<"gleam">>]}.
7 7 {<<"links">>, [
  @@ -9,30 +9,30 @@
9 9 {<<"Repository">>, <<"https://github.com/code-shoily/yog_io">>}
10 10 ]}.
11 11 {<<"requirements">>, [
12 - {<<"gleam_stdlib">>, [
13 - {<<"app">>, <<"gleam_stdlib">>},
14 - {<<"optional">>, false},
15 - {<<"requirement">>, <<">= 0.69.0 and < 1.0.0">>}
16 - ]},
17 12 {<<"yog">>, [
18 13 {<<"app">>, <<"yog">>},
19 14 {<<"optional">>, false},
20 - {<<"requirement">>, <<">= 5.1.0 and < 6.0.0">>}
15 + {<<"requirement">>, <<">= 5.1.1 and < 6.0.0">>}
21 16 ]},
22 17 {<<"gleam_regexp">>, [
23 18 {<<"app">>, <<"gleam_regexp">>},
24 19 {<<"optional">>, false},
25 20 {<<"requirement">>, <<">= 1.0.0 and < 2.0.0">>}
26 21 ]},
22 + {<<"xmlm">>, [
23 + {<<"app">>, <<"xmlm">>},
24 + {<<"optional">>, false},
25 + {<<"requirement">>, <<">= 1.0.1 and < 2.0.0">>}
26 + ]},
27 27 {<<"gleam_json">>, [
28 28 {<<"app">>, <<"gleam_json">>},
29 29 {<<"optional">>, false},
30 30 {<<"requirement">>, <<">= 3.0.0 and < 4.0.0">>}
31 31 ]},
32 - {<<"xmlm">>, [
33 - {<<"app">>, <<"xmlm">>},
32 + {<<"gleam_stdlib">>, [
33 + {<<"app">>, <<"gleam_stdlib">>},
34 34 {<<"optional">>, false},
35 - {<<"requirement">>, <<">= 1.0.1 and < 2.0.0">>}
35 + {<<"requirement">>, <<">= 0.69.0 and < 1.0.0">>}
36 36 ]},
37 37 {<<"simplifile">>, [
38 38 {<<"app">>, <<"simplifile">>},
  @@ -64,6 +64,19 @@
64 64 <<"include/yog_io@leda_NodeIdOutOfRange.hrl">>,
65 65 <<"include/yog_io@leda_ReadError.hrl">>,
66 66 <<"include/yog_io@leda_WriteError.hrl">>,
67 + <<"include/yog_io@list_ListOptions.hrl">>,
68 + <<"include/yog_io@list_ParseError.hrl">>,
69 + <<"include/yog_io@list_ReadError.hrl">>,
70 + <<"include/yog_io@list_WriteError.hrl">>,
71 + <<"include/yog_io@matrix_NotSquare.hrl">>,
72 + <<"include/yog_io@matrix_ReadError.hrl">>,
73 + <<"include/yog_io@matrix_WriteError.hrl">>,
74 + <<"include/yog_io@matrix_market_InvalidHeader.hrl">>,
75 + <<"include/yog_io@matrix_market_InvalidSizeLine.hrl">>,
76 + <<"include/yog_io@matrix_market_MatrixMarketResult.hrl">>,
77 + <<"include/yog_io@matrix_market_MissingHeader.hrl">>,
78 + <<"include/yog_io@matrix_market_ReadError.hrl">>,
79 + <<"include/yog_io@matrix_market_WriteError.hrl">>,
67 80 <<"include/yog_io@pajek_InvalidArcLine.hrl">>,
68 81 <<"include/yog_io@pajek_InvalidNodeId.hrl">>,
69 82 <<"include/yog_io@pajek_InvalidSectionHeader.hrl">>,
  @@ -92,12 +105,18 @@
92 105 <<"src/yog_io/graphml.gleam">>,
93 106 <<"src/yog_io/json.gleam">>,
94 107 <<"src/yog_io/leda.gleam">>,
108 + <<"src/yog_io/list.gleam">>,
109 + <<"src/yog_io/matrix.gleam">>,
110 + <<"src/yog_io/matrix_market.gleam">>,
95 111 <<"src/yog_io/pajek.gleam">>,
96 112 <<"src/yog_io/tgf.gleam">>,
97 113 <<"src/yog_io@gdf.erl">>,
98 114 <<"src/yog_io@graphml.erl">>,
99 115 <<"src/yog_io@json.erl">>,
100 116 <<"src/yog_io@leda.erl">>,
117 + <<"src/yog_io@list.erl">>,
118 + <<"src/yog_io@matrix.erl">>,
119 + <<"src/yog_io@matrix_market.erl">>,
101 120 <<"src/yog_io@pajek.erl">>,
102 121 <<"src/yog_io@tgf.erl">>
103 122 ]}.
  @@ -0,0 +1 @@
1 + -record(list_options, {weighted :: boolean(), delimiter :: binary()}).
  @@ -0,0 +1 @@
1 + -record(parse_error, {line :: integer(), content :: binary()}).
Loading more files…