├── .gitignore
├── MANIFEST.in
├── Makefile
├── README.mkd
├── bench-opti.txt
├── data
├── H10FW_vs_H10SW.lp
├── abnormal.lp
├── bintree.lp
├── clique.lp
├── cliques.lp
├── complex-case-1.lp
├── concept-loop.lp
├── concomp.lp
├── consider-included-nodes.lp
├── ddiam.lp
├── diacli.lp
├── diamond.lp
├── disjoint-subpnodes.lp
├── double-p-groups.lp
├── double_biclique_unambiguous.lp
├── empty.lp
├── hanging-bio-notree-cc0.lp
├── horrible_data.lp
├── inclusions.lp
├── matrixdb-core27.sif
├── miRNA_mRNA_diff.sif
├── miRNA_mRNA_diff_no3019.sif
├── motif-overlapping.lp
├── multiple-optimals.lp
├── n8_d0.7.lp
├── one_edge.lp
├── order.lp
├── overlapping-bicliques.lp
├── partition.lp
├── perfectfit.lp
├── phosphatase.lp
├── pnode-to-clique.lp
├── prio_deg.lp
├── puceron-data-2018.lp
├── quasibiclique.lp
├── quoting.lp
├── recipe-option-test.lp
├── recipe-option-test.txt
├── recipe-test-inclusion.lp
├── recipe-test.lp
├── recipe-test.txt
├── single-node.lp
├── star.lp
├── structural-binding-maincc.lp
├── structural-binding-nobridge.lp
├── structural-binding.lp
├── suboptimal.lp
├── test.gml
├── test.graphml
├── testblocks.lp
├── thesis.lp
├── todel.lp
├── triplets.lp
├── typical-use-case.lp
├── unclique.lp
├── variable-name.gml
├── wiki-tree-decomposition.lp
└── zorro.lp
├── out
├── .gitignore
└── thesis
│ ├── concept-cycle-4.sif
│ ├── matrixdb-extended.sif
│ ├── matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
│ ├── matrixdb_CORE27_example__a_0-_b_0-_c_3-zoom.png.png
│ ├── matrixdb_CORE27_example__a_0-_b_0-_c_3-zoom.png.png.png
│ ├── matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
│ ├── miRNA_mRNA_diff-no3019.png
│ ├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.bbl.png
│ ├── thesis-network-triplet-zoom-a_0-_b_0-_c_0-.bbl.png
│ ├── triplet.sif
│ ├── zorro-annotated.png
│ ├── zorro-c1-c2.png
│ ├── zorro-c1-c21-annotated.png
│ ├── zorro-c1-c21-c31-c32-c22.png
│ ├── zorro-c1-c22-annotated.png
│ ├── zorro-c1-c3-annotated.png
│ ├── zorro-c1-c3-c21-annotated.png
│ ├── zorro-c1-c3-c22-annotated.png
│ ├── zorro-c1-c3.png
│ ├── zorro-c1-c31-c21-c22-c32.png
│ ├── zorro-c1.png
│ ├── zorro-compression-middle-first.png
│ ├── zorro-compression-per-concept.png
│ ├── zorro-compression-regular.png
│ ├── zorro.png
│ └── zorro.sif
├── powergrasp
├── __init__.py
├── __main__.py
├── asp.py
├── asp
│ ├── block-constraint-cpu.lp
│ ├── block-constraint-memory.lp
│ ├── extract-ccs.lp
│ ├── process-motif.lp
│ ├── scoring_powergraph.lp
│ ├── search-biclique.lp
│ ├── search-clique.lp
│ ├── search-fullbiclique.lp
│ ├── search-quasibiclique.lp
│ ├── search-star.lp
│ └── search-triplet.lp
├── cli.py
├── constants.py
├── edge_filtering.py
├── graph.py
├── metrics.py
├── motif.py
├── motif_batch.py
├── recipe.py
├── routines.py
├── searchers.py
└── utils.py
├── recipe
├── setup.cfg
├── setup.py
├── test
├── __init__.py
├── ambiguous_test_cases.py
├── definitions.py
├── powergrasp.default.cfg
├── powergrasp.manyoptions.cfg
├── powergrasp.nostarsearch.cfg
├── powergrasp.oneshot.cfg
├── test_cases.py
├── test_multishot_compression.py
├── test_recipes.py
├── test_unambiguous_compression.py
└── test_utils.py
└── triplet
├── .gitignore
├── Makefile
├── README
├── cases_cache
├── backup-incomplete
│ ├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.concepts.dat
│ ├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.dat
│ ├── miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.concepts.dat
│ └── miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.dat
├── double_biclique_unambiguous__a_0-_b_0-_c_0-.concepts.dat
├── double_biclique_unambiguous__a_0-_b_0-_c_0-.dat
├── matrixdb_CORE27_example__a_0-_b_0-_c_0-.concepts.dat
├── matrixdb_CORE27_example__a_0-_b_0-_c_0-.dat
├── matrixdb_CORE27_example__a_0-_b_0-_c_2-.concepts.dat
├── matrixdb_CORE27_example__a_0-_b_0-_c_2-.dat
├── matrixdb_CORE27_example__a_0-_b_0-_c_3-.concepts.dat
├── matrixdb_CORE27_example__a_0-_b_0-_c_3-.dat
├── matrixdb_CORE27_example__a_3-_b_3-_c_0-.concepts.dat
├── matrixdb_CORE27_example__a_3-_b_3-_c_0-.dat
├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.concepts.dat
├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.dat
├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_2-.concepts.dat
├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_2-.dat
├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_3-.concepts.dat
├── miRNA_mRNA_diff-no3019__a_0-_b_0-_c_3-.dat
├── miRNA_mRNA_diff-no3019__a_3-_b_2-_c_0-.concepts.dat
├── miRNA_mRNA_diff-no3019__a_3-_b_2-_c_0-.dat
├── miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.concepts.dat
└── miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.dat
├── combinations.py
├── compute-concepts.lp
├── out
├── number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
├── number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
├── number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
├── number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
├── number_formal_concept_per_score_allindex_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
├── number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
├── number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
├── number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
├── number_formal_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
├── number_formal_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
├── number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
├── number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
├── number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
├── number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
├── number_triplet_concept_formal_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
├── number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
├── number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
├── number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
├── number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
├── number_triplet_concept_per_score_allindex_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
├── number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
├── number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
├── number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
├── number_triplet_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
└── number_triplet_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
├── search-byconcept.lp
├── search-byenum.lp
├── search.lp
└── search.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.bbl
2 | *.csv
3 | .cache/
4 | .pytest_cache/
5 | Session.vim
6 | __pycache__/
7 | debug/
8 | dist/
9 | powergrasp.egg-info/
10 | PowerGrASP.egg-info/
11 | venv/
12 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENCE MANIFEST.in README.mkd
2 | recursive-include powergrasp *.py *.lp
3 | exclude Makefile
4 | prune data
5 | prune test
6 | prune out
7 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | INFILE_DIR=data
3 | INFILE=double_biclique_unambiguous.lp
4 | TEST_CFG_FILE=
5 |
6 | SHOW_DURATIONS=--durations=22
7 | FAILED_FIRST=--ff
8 |
9 | ## Usage and tests
10 | compress:
11 | python -m powergrasp $(INFILE_DIR)/$(INFILE) out/out.bbl
12 | python -m bubbletools validate out/out.bbl
13 |
14 | config:
15 | python -m powergrasp --config
16 |
17 | test: t
18 | t:
19 | - mv powergrasp.cfg powergrasp.cfg.bak
20 | # try different option sets
21 | $(MAKE) _test_cfg_file TEST_CFG_FILE=default # with default values
22 | $(MAKE) _test_cfg_file TEST_CFG_FILE=oneshot
23 | $(MAKE) _test_cfg_file TEST_CFG_FILE=manyoptions
24 | $(MAKE) _test_cfg_file TEST_CFG_FILE=nostarsearch
25 | rm powergrasp.cfg
26 | - mv powergrasp.cfg.bak powergrasp.cfg
27 | _pure_tests:
28 | pytest powergrasp test -x -vv --doctest-module $(SHOW_DURATIONS) $(FAILED_FIRST)
29 | _test_cfg_file:
30 | cp test/powergrasp.$(TEST_CFG_FILE).cfg powergrasp.cfg
31 | $(MAKE) _pure_tests # with many options tweaked (edges from ASP, integrity,…)
32 |
33 |
34 | ## Packaging
35 | make_dist:
36 | python setup.py sdist
37 | upload:
38 | twine upload --repository pypi dist/PowerGrASP-*.tar.gz
39 | release: fullrelease
40 | fullrelease:
41 | fullrelease
42 | install_deps:
43 | python -c "import configparser; c = configparser.ConfigParser(); c.read('setup.cfg'); print(c['options']['install_requires'])" | xargs pip install -U
44 |
45 |
46 | .PHONY: test t compress upload
47 |
48 | ## All real test cases
49 | real-puceron-mi-m-diff:
50 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=miRNA_mRNA_diff.lp
51 | real-puceron-mi-m-diff-no3019:
52 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=miRNA_mRNA_diff-no3019.lp
53 | real-puceron-mi-lnc:
54 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=miRNA_lncRNA.lp
55 | real-puceron-lnc-m:
56 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=lncRNA_mRNA.lp
57 | real-puceron-mi-m-lnc:
58 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=ternary_concepts_mi_m_lnc-edge.lp
59 | real-matrixdb-core27:
60 | $(MAKE) compress INFILE_DIR=~/data/MatrixDb/compmatrixdb/matrixdb_CORE27_example INFILE=matrixdb_CORE27_example.lp
61 | real-matrixdb-90:
62 | $(MAKE) compress INFILE_DIR=~/data/MatrixDb/compmatrixdb/matrixdb_Human_Human_171107_extended INFILE=matrixdb_CORE27_example.lp
63 | real-matrixdb-all:
64 | $(MAKE) compress INFILE_DIR=~/data/MatrixDb/compmatrixdb/matrixdb_Human_Human_171107_extended INFILE=matrixdb_Human_Human_171107_extended_0.0.lp
65 |
66 |
67 | ## All test cases
68 | abnormal:
69 | $(MAKE) compress INFILE=abnormal.lp
70 | bintree:
71 | $(MAKE) compress INFILE=bintree.lp
72 | clique:
73 | $(MAKE) compress INFILE=clique.lp
74 | cliques:
75 | $(MAKE) compress INFILE=cliques.lp
76 | concomp:
77 | $(MAKE) compress INFILE=concomp.lp
78 | concept-loop:
79 | $(MAKE) compress INFILE=concept-loop.lp
80 | ddiam:
81 | $(MAKE) compress INFILE=ddiam.lp
82 | diacli:
83 | $(MAKE) compress INFILE=diacli.lp
84 | diamond:
85 | $(MAKE) compress INFILE=diamond.lp
86 | disjoint-subpnodes:
87 | $(MAKE) compress INFILE=disjoint-subpnodes.lp
88 | double_biclique_unambiguous:
89 | $(MAKE) compress INFILE=double_biclique_unambiguous.lp
90 | double-p-groups:
91 | $(MAKE) compress INFILE=double-p-groups.lp
92 | hanging-bio-notree-cc0:
93 | $(MAKE) compress INFILE=hanging-bio-notree-cc0.lp
94 | horrible_data:
95 | $(MAKE) compress INFILE=horrible_data.lp
96 | inclusions:
97 | $(MAKE) compress INFILE=inclusions.lp
98 | consider-included-nodes:
99 | $(MAKE) compress INFILE=consider-included-nodes.lp
100 | motif-overlapping:
101 | $(MAKE) compress INFILE=motif-overlapping.lp
102 | multiple-optimals:
103 | $(MAKE) compress INFILE=multiple-optimals.lp
104 | n8_d0:
105 | $(MAKE) compress INFILE=n8_d0.7.lp
106 | one_edge:
107 | $(MAKE) compress INFILE=one_edge.lp
108 | order:
109 | $(MAKE) compress INFILE=order.lp
110 | overlapping-bicliques:
111 | $(MAKE) compress INFILE=overlapping-bicliques.lp
112 | partition:
113 | $(MAKE) compress INFILE=partition.lp
114 | perfectfit:
115 | $(MAKE) compress INFILE=perfectfit.lp
116 | phosphatase:
117 | $(MAKE) compress INFILE=phosphatase.lp
118 | pnode-to-clique:
119 | $(MAKE) compress INFILE=pnode-to-clique.lp
120 | prio_deg:
121 | $(MAKE) compress INFILE=prio_deg.lp
122 | quasibiclique:
123 | $(MAKE) compress INFILE=quasibiclique.lp
124 | quoting:
125 | $(MAKE) compress INFILE=quoting.lp
126 | single-node:
127 | $(MAKE) compress INFILE=single-node.lp
128 | star:
129 | $(MAKE) compress INFILE=star.lp
130 | structural-binding:
131 | $(MAKE) compress INFILE=structural-binding.lp
132 | structural-binding-maincc:
133 | $(MAKE) compress INFILE=structural-binding-maincc.lp
134 | structural-binding-nobridge:
135 | $(MAKE) compress INFILE=structural-binding-nobridge.lp
136 | testblocks:
137 | $(MAKE) compress INFILE=testblocks.lp
138 | test-gml:
139 | $(MAKE) compress INFILE=test.gml
140 | test-graphml:
141 | $(MAKE) compress INFILE=test.graphml
142 | thesis:
143 | $(MAKE) compress INFILE=thesis.lp
144 | todel:
145 | $(MAKE) compress INFILE=todel.lp
146 | triplets:
147 | $(MAKE) compress INFILE=triplets.lp
148 | typical:
149 | $(MAKE) compress INFILE=typical-use-case.lp
150 | unclique:
151 | $(MAKE) compress INFILE=unclique.lp
152 | variable-name:
153 | $(MAKE) compress INFILE=variable-name.gml
154 | wiki-tree-decomposition:
155 | $(MAKE) compress INFILE=wiki-tree-decomposition.lp
156 | zorro:
157 | $(MAKE) compress INFILE=zorro.lp
158 |
--------------------------------------------------------------------------------
/README.mkd:
--------------------------------------------------------------------------------
1 | # PowerGrASP
2 | Graph compression.
3 |
4 | Note that this is a full reimplementation of PowerGrASP,
5 | taking advantage of ASP and Python lifting and simplifications.
6 | For the version published in 2017, see [this repository](https://github.com/aluriak/PowerGrASP-1).
7 |
8 |
9 | ## CLI
10 |
11 | python -m powergrasp mygraph.gml -o compressed.bbl
12 |
13 | ### help !
14 |
15 | python -m powergrasp --help
16 |
17 | ## API
18 |
19 | ```python
20 | import powergrasp
21 | with open('compressed.bbl', 'w') as fd:
22 | for line in powergrasp.compress_by_cc('mygraph.gml'):
23 | fd.write(line + '\n')
24 | ```
25 |
26 | ### help !
27 | Sorry, no technical doc for the moment.
28 |
29 |
30 | ## Bubble files usage
31 | The bubble file is the main output of PowerGrASP, and describes a power graph.
32 | The bubble is a format handled by the [CyOog plugin](http://www.biotec.tu-dresden.de/research/schroeder/powergraphs/download-cytoscape-plugin.html) for [Cytoscape 2](http://cytoscape.org/), allowing one to load a bubble formatted file and to visualize the corresponding graph.
33 | As a consequence, you have to [install Cytoscape 2](http://chianti.ucsd.edu/Cyto-2%5F8%5F3/), and put the `CyOog.jar` file into `path/to/cytoscape/install/dir/plugins/`.
34 |
35 | Another way to go is to convert the bubble to other formats handling hierarchical graphs, like [gexf](https://gephi.org/gexf/format/),
36 | [dot](https://www.graphviz.org/doc/info/lang.html) or the API of [cytoscape.js](http://js.cytoscape.org/).
37 | This is a task implemented by [bubbletools](https://github.com/Aluriak/bubble-tools/).
38 |
39 |
40 | ## Configuration
41 | PowerGrASP has some [configuration values](powergrasp/constants.py),
42 | that can be overwritten by a `powergrasp.cfg` file placed in the working directory.
43 |
44 | The configuration may be printed in stdout using:
45 |
46 | python -m powergrasp --config
47 |
48 | The config file may be either in ini or json format, as shown in [`test/powergrasp.oneshot.cfg`](test/powergrasp.oneshot.cfg) and [`test/powergrasp.manyoptions.cfg`](test/powergrasp.manyoptions.cfg).
49 |
50 | Configuration allow user to define how much text will be outputed by powergrasp,
51 | and to tune the core compression and related optimizations.
52 |
53 | See complete list in the [options section](#Options).
54 |
55 |
56 | ## installation
57 |
58 | pip install powergrasp
59 |
60 | On random error, try adding `--no-cache-dir` at the end of the command,
61 | and check that you are using python 3.
62 |
63 | You must have [`clingo`](https://potassco.org/doc/start/) in your path. Depending on your OS, it might be done with a system installation,
64 | or through [downloading](https://github.com/potassco/clingo/releases) and (compilation and) manual installation.
65 |
66 |
67 | ## Changelog
68 |
69 | - 8.17
70 | - support for [recipes options](#recipes), like `breakable` or `last`
71 | - 8.11
72 | - slight bounds improvements for non-star-biclique motif
73 | - statistics/timers now provides time needed to search for each motif
74 | - support for [recipes](#recipes), that small file that allows to define the compressions to do
75 | - 8.10
76 | - option [parallel cc compression](#parallel-cc-compression) to enable parallel compression of connected components
77 | - option [bubble embeds cc](#bubble-embeds-cc), to put each cc in a dedicated powernode
78 | - option [bubble with simple edges](#bubble-with-simple-edges), to keep or discard the simple edges from output
79 | - statistics on time and compression metrics are computed by cc and for all graph. See new options.
80 | - option [cc statistic file](#cc-statistic-file), to retrieve stats and metrics for connected components.
81 | - option [global statistics](#global-statistics), to compute statistics/metrics over all connected components.
82 | - option [bubble with statistics](#bubble-with-statistics), to include stats and metrics in output bubble file
83 | - [motif type order](#motif-type-order) option, allowing user to tune the order in which motifs are searched.
84 | - [parallel motif search](#parallel-motif-search) option, making motifs search in different threads, and the overhaul compression much slower.
85 | - CLI: --config flag to print the full configuration in stdout
86 | - handling spaces instead of _ in fields names for INI format
87 | - do not filter out nodes alone in their connected component (cf [keep-single-nodes option](#keep-single-nodes))
88 | - perf gain: the first motif to reach the best score is compressed, instead of the last
89 | - improve handling of statistic files, that now contains cc idx and motif bounds
90 | - expose `__version__` attribute in the package
91 | - add a list of available options in the README
92 | - graph filtering: optimization leading to general performance gain when enabled
93 | - improvements on {low,upp}erbounds computations
94 | - 8.9
95 | - many bugfixes
96 | - CLINGO_OPTIONS can be a dict, mapping motif name to arbitrary parameters
97 | - 8.8
98 | - bugfix: lowerbound can't go under 2 (3 for cliques)
99 | - clique search: lowerbound is initially computed as the maximal degree of node of clustering coefficient equals to 1
100 | - use new versions of clyngor and bubbletools
101 | - 8.7
102 | - raise error when an invalid key is found in config file
103 | - StarSearch is dedicated to find stars, simplifying BicliqueSearch's job (enabled by default, user may use Biclique instead of both Star and NonStarBiclique)
104 | - specify a prefix for all (power) nodes with OUTPUT_NODE_PREFIX config field
105 | - arbitrary options for clingo using CLINGO_OPTIONS config field
106 | - bugfix on string options given in ini file, when unecessary quotes are kept
107 | - 8.6
108 | - bugfixes for INI format
109 | - 8.5
110 | - config may now be given in INI format
111 | - improve logging
112 | - config allow optimization target between memory or CPU (currently define a constraint implementation in ASP)
113 | - bugfix about edges yielded by ASP and multithreading parameter
114 | - no statistics file to write by default
115 | - timer for output writing
116 | - config allow user to define the number of CPU for clingo to use
117 | - simplify ASP code by removing useless arg in block/4 atoms
118 | - 8.4
119 | - replace ASP code constraint to achieve much more efficient grounding
120 | - configuration allow for improved lowerbound computation of bicliques
121 | - generated bubble allow client to give heading comments
122 | - handle keyboard interruption during search with grace
123 | - implement timers and statistics recording
124 | - enable multishot motif search by default
125 | - various bugfixes
126 |
127 |
128 | ## Recipes
129 | A recipe is a description of the motifs to compress.
130 | For instance, the [data/recipe-test.txt](data/recipe-test.txt):
131 |
132 | biclique c g h i
133 | biclique a b d e
134 | biclique a b f
135 | biclique c d e
136 |
137 | Using the `--recipe` option in CLI, user can provide a recipe for its own graph,
138 | allowing to specify prior motifs to search.
139 | The order of second and third columns does not have any influence.
140 |
141 | The first column usually contains the motif types separated by comma
142 | (altough it is not necessary), and allows to provide some options (separated by comma):
143 |
144 | - `primer`: allows the program to extend the given motif if possible
145 | - `breakable`: allows the program to compress the line with multiple motifs
146 | - `optional`: if not found, ignore instead of stopping
147 | - `last`: stop compression here (useful to avoid the compression process to take over)
148 |
149 |
150 | The recipe file will be read line by line.
151 | Once all lines have been applied, the normal compression compression takes over
152 | (unless `last` option is used).
153 |
154 | For a living example, see [data/recipe-option-test.txt](data/recipe-option-test.txt).
155 |
156 |
157 | ## Options
158 | Description of all powergrasp options.
159 | All values example are given for INI format.
160 |
161 | ### test integrity
162 | Run some integrity tests during runtime to ensure that compression is working well.
163 | May slow the compression a lot, and is mainly a debugging tool.
164 |
165 | Default value:
166 |
167 | test_integrity = true
168 |
169 | ### show story
170 | Print in stdout the main steps of compression.
171 | Default value:
172 |
173 | show_story = true
174 |
175 | ### show motif handling
176 | Print in stdout the motif transformation.
177 | Default value:
178 |
179 | show_motif_handling = false
180 |
181 | ### timers
182 | Maintain and print in stdout some timers.
183 | Default value:
184 |
185 | timers = true
186 |
187 | ### statistics file
188 | A file in which some statistics will be written in CSV format, giving among others size of compressed motifs, and their compression times (if *timers* option is enabled).
189 | Default value:
190 |
191 | statistics_file = None
192 |
193 | Example value:
194 |
195 | statistics_file = statistics.csv
196 |
197 | ### cc statistic file
198 | Filename in which the statistics about each connected component will be written.
199 | Data will contain one row for each connected component, with the following data:
200 |
201 | - connected component index
202 | - the convertion rate
203 | - the edge reduction
204 | - the compression rate
205 | - time needed to compress the connected component (if TIMERS is enabled)
206 |
207 | Default value stands for *no file*:
208 |
209 | cc_statistic_file = None
210 |
211 | Example value:
212 |
213 | cc_statistic_file = statistics-cc.csv
214 |
215 | ### global statistics
216 | When enabled, statistics and metrics computation on the overall graph will be performed at the end of compression.
217 | Default value:
218 |
219 | global_statistics = True
220 |
221 | ### bubble with statistics
222 | When enabled, output bubble will contain statistics about each connected component
223 | and the overall graph (if global_statistics is enabled).
224 | Default value:
225 |
226 | bubble_with_statistics = True
227 |
228 | ### bubble for each step
229 | Generate and save a bubble representation of the graph at each step.
230 | Mainly used for debugging.
231 | Default value:
232 |
233 | bubble_for_each_step = false
234 |
235 | ### output node prefix
236 | Prefix to add to all (power)nodes names in output.
237 | Default value:
238 |
239 | output_node_prefix = ''
240 |
241 | ### show debug
242 | Show full trace of the compression. Useful for debugging.
243 | Default value:
244 |
245 | show_debug = False
246 |
247 | ### covered edges from ASP
248 | Recover covered edges from ASP. If falsy, will ask motif searcher to compute the edges, which may be quicker.
249 | Default value:
250 |
251 | covered_edges_from_asp = False
252 |
253 | ### bubble with nodes
254 | Nodes and sets are optional in the output bubble.
255 | Default value:
256 |
257 | bubble_with_nodes = True
258 | bubble_with_sets = True
259 |
260 | ### bubble poweredge factor
261 | Edges in bubble are associated to a factor.
262 | Default value:
263 |
264 | bubble_poweredge_factor = '1.0'
265 | bubble_edge_factor = '1.0'
266 |
267 | ### bubble embeds cc
268 | If enabled, put each connected component in a dedicated powernode.
269 | Default value:
270 |
271 | bubble_embeds_cc = no
272 |
273 |
274 | ### bubble simplify quotes
275 | When possible, delete the quotes around identifiers in the bubble. May lead to node name collision.
276 | Default value:
277 |
278 | bubble_simplify_quotes = True
279 |
280 | ### bubble with simple edges
281 | If disabled, will discard simple (i.e. non-power) edges of output.
282 | Default value:
283 |
284 | bubble_with_simple_edges = True
285 |
286 | ### config file
287 | Load options found in given config file, if it exists.
288 | Default value:
289 |
290 | config_file = 'powergrasp.cfg'
291 |
292 | ### multishot motif search
293 | Search for multiple motif in a single search.
294 | Accelerate the solving for graph with lots of equivalent motifs. It is generally a good option to enable.
295 | Default value:
296 |
297 | multishot_motif_search = True
298 |
299 | ### biclique lowerbound maxnei
300 | Optimization on biclique lowerbound computation. Can be costly. Deactivate with 2. With value at n, up to n neighbors are considered.
301 | Default value:
302 |
303 | biclique_lowerbound_maxnei = 2
304 |
305 | ### clingo options
306 | Arbitrary parameters to give to clingo (note that some, like multithreading or optmode, may already be set by other options).
307 | Default value:
308 |
309 | clingo_options = {}
310 |
311 | Give a particular configuration for bicliques search:
312 |
313 | clingo_options = {'no-star-biclique': '--configuration=handy'}
314 |
315 | ### clingo multithreading
316 | Number of CPU available to clingo, or 0 for autodetect number of CPU.
317 | Default value:
318 |
319 | clingo_multithreading = 1
320 |
321 | Use as many thread there are available CPU:
322 |
323 | clingo_multithreading = 0
324 |
325 | Specify a competing search between 4 threads:
326 |
327 | clingo_multithreading = '4,compete'
328 |
329 | ### parallel cc compression
330 | To use to compress connected components in different processes.
331 | Default value (optimized in memory):
332 |
333 | parallel_cc_compression = 1
334 |
335 | Compress with 4 processes :
336 |
337 | parallel_cc_compression = 4
338 |
339 | Compress with one process for each connected component :
340 |
341 | parallel_cc_compression = 0
342 |
343 | ### use star motif
344 | Two different motifs for stars and bicliques, so the search space for bicliques is smaller. Yields good performance improvements on big graphs.
345 | Default value:
346 |
347 | use_star_motif = True
348 |
349 | ### optimize for memory
350 | When possible, prefer memory over CPU.
351 | Default value:
352 |
353 | optimize_for_memory = False
354 |
355 | ### graph filtering
356 | Ignore edges dynamically determined as impossible to compress.
357 | Default value:
358 |
359 | graph_filtering = True
360 |
361 | ### keep single nodes
362 | If a node is found to be alone in its connected component, it will be kept nonetheless.
363 | Default value:
364 |
365 | keep_single_nodes = True
366 |
367 | ### parallel motif search
368 | Use multithreading to search for all motifs in the same time, instead of sequentially.
369 | Interestingly, this yields very poor results, and it's even worse with multiprocessing.
370 |
371 | parallel_motif_search = False
372 |
373 | ### motif type order
374 | Define in which order the motifs are searched, e.g. cliques, then bicliques then stars.
375 |
376 | motif_type_order = star,clique,non-star-biclique,biclique
377 |
378 | Instead of expliciting the exact motif names and order, it is also possible to specify a bound-based order:
379 |
380 | motif_type_order = greatest-upperbound-first
381 |
382 | Or any variation with `worst`, `lowerbound` and `last`.
383 |
--------------------------------------------------------------------------------
/bench-opti.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | Benchs done with:
4 |
5 | m real-matrixdb-core27 ; tail out/out.bbl
6 |
7 | get mean and diff to vanilla:
8 | p -c "l=tuple(map(float,input().split(', ')));m=sum(l)/len(l);print(round(m,2), str(round(100*(m/51.62),2))+'%')"
9 |
10 | Run results:
11 |
12 | vanilla: 53.6, 50.62, 50.63
13 | GF: 35.39, 36.73, 34.02
14 | SM: 65.44, 64.82, 66.74
15 | PM: 45.79, 46.52, 46.13
16 | MM: 19.7, 19.68, 20.04
17 | all but SM: 17.27, 16.04, 16.64
18 | all: 14.85, 14.03, 14.79
19 |
20 | NB: MM is named parallel in the thesis… and PM is not used.
21 |
22 |
23 | vanilla config:
24 |
25 | [options]
26 | MULTISHOT_MOTIF_SEARCH=false
27 | PARALLEL_MOTIF_SEARCH=false
28 | USE_STAR_MOTIF=false
29 | GRAPH_FILTERING=false
30 |
31 | GF config:
32 |
33 | MULTISHOT_MOTIF_SEARCH=false
34 | PARALLEL_MOTIF_SEARCH=false
35 | USE_STAR_MOTIF=false
36 | GRAPH_FILTERING=true
37 |
38 |
39 | SM config:
40 |
41 | MULTISHOT_MOTIF_SEARCH=false
42 | PARALLEL_MOTIF_SEARCH=false
43 | USE_STAR_MOTIF=true
44 | GRAPH_FILTERING=false
45 |
46 | PM config:
47 |
48 | MULTISHOT_MOTIF_SEARCH=false
49 | PARALLEL_MOTIF_SEARCH=true
50 | USE_STAR_MOTIF=false
51 | GRAPH_FILTERING=false
52 |
53 | MM config:
54 |
55 | MULTISHOT_MOTIF_SEARCH=true
56 | PARALLEL_MOTIF_SEARCH=false
57 | USE_STAR_MOTIF=false
58 | GRAPH_FILTERING=false
59 |
--------------------------------------------------------------------------------
/data/abnormal.lp:
--------------------------------------------------------------------------------
1 | % This network raises an abnormal situation multiple times:
2 | edge("YNL069C","YOR063W").
3 | edge("YBL087C","YGL031C").
4 | edge("YER117W","YGL031C").
5 | edge("YIL133C","YNL067W").
6 | edge("YGL147C","YNL069C").
7 | edge("YBL087C","YOR063W").
8 | edge("YGR148C","YOR063W").
9 | edge("YNL067W","YNL069C").
10 | edge("YGL147C","YIL133C").
11 | edge("YIL133C","YOR063W").
12 | edge("YER117W","YGR148C").
13 | edge("YER117W","YOR063W").
14 | edge("YBL087C","YGR148C").
15 | edge("YGL031C","YOR063W").
16 | edge("YGL147C","YNL067W").
17 |
18 | % The same, but with letter (easier to debug):
19 | edge(b,c).
20 | edge(a,d).
21 | edge(e,d).
22 | edge(f,g).
23 | edge(h,b).
24 | edge(a,c).
25 | edge(i,c).
26 | edge(g,b).
27 | edge(h,f).
28 | edge(f,c).
29 | edge(e,i).
30 | edge(e,c).
31 | edge(a,i).
32 | edge(d,c).
33 | edge(h,g).
34 |
--------------------------------------------------------------------------------
/data/bintree.lp:
--------------------------------------------------------------------------------
1 | edge(aa,ab).
2 | edge(aa,ac).
3 | edge(ab,ad).
4 | edge(ab,ae).
5 | edge(ac,af).
6 | edge(ac,ag).
7 | edge(ad,ah).
8 | edge(ad,ai).
9 | edge(ae,aj).
10 | edge(ae,ak).
11 | edge(af,al).
12 | edge(af,am).
13 | edge(ag,an).
14 | edge(ag,ao).
15 | edge(ah,ap).
16 | edge(ah,aq).
17 | edge(ai,ar).
18 | edge(ai,as).
19 | edge(aj,at).
20 | edge(aj,au).
21 | edge(ak,av).
22 | edge(ak,aw).
23 | edge(al,ax).
24 | edge(al,ay).
25 | edge(am,az).
26 | edge(am,ba).
27 | edge(an,bb).
28 | edge(an,bc).
29 | edge(ao,bd).
30 | edge(ao,be).
31 | edge(ap,bf).
32 | edge(ap,bg).
33 | edge(aq,bh).
34 | edge(aq,bi).
35 | edge(ar,bj).
36 | edge(ar,bk).
37 | edge(as,bl).
38 | edge(as,bm).
39 | edge(at,bn).
40 | edge(at,bo).
41 | edge(au,bp).
42 | edge(au,bq).
43 | edge(av,br).
44 | edge(av,bs).
45 | edge(aw,bt).
46 | edge(aw,bu).
47 | edge(ax,bv).
48 | edge(ax,bw).
49 | edge(ay,bx).
50 | edge(ay,by).
51 | edge(az,bz).
52 | edge(az,ca).
53 | edge(ba,cb).
54 | edge(ba,cC).
55 | edge(bb,cd).
56 | edge(bb,ce).
57 | edge(bc,cf).
58 | edge(bc,cg).
59 | edge(bd,ch).
60 | edge(bd,ci).
61 | edge(be,cj).
62 | edge(be,ck).
63 | edge(bf,cl).
64 | edge(bf,cm).
65 | edge(bg,cn).
66 | edge(bg,co).
67 | edge(bh,cp).
68 | edge(bh,cq).
69 | edge(bi,cr).
70 | edge(bi,cs).
71 | edge(bj,ct).
72 | edge(bj,cu).
73 | edge(bk,cv).
74 | edge(bk,cw).
75 | edge(bl,cx).
76 | edge(bl,cy).
77 | edge(bm,cz).
78 | edge(bm,da).
79 | edge(bn,db).
80 | edge(bn,dc).
81 | edge(bo,dd).
82 | edge(bo,de).
83 | edge(bp,df).
84 | edge(bp,dg).
85 | edge(bq,dh).
86 | edge(bq,di).
87 | edge(br,dj).
88 | edge(br,dk).
89 | edge(bs,dl).
90 | edge(bs,dm).
91 | edge(bt,dn).
92 | edge(bt,do).
93 | edge(bu,dp).
94 | edge(bu,dq).
95 | edge(bv,dr).
96 | edge(bv,ds).
97 | edge(bw,dt).
98 | edge(bw,du).
99 | edge(bx,dv).
100 | edge(bx,dw).
101 | edge(by,dx).
102 | edge(by,dy).
103 | edge(bz,dz).
104 | edge(bz,ea).
105 | edge(ca,eb).
106 | edge(ca,ec).
107 | edge(cb,ed).
108 | edge(cb,ee).
109 | edge(cC,ef).
110 | edge(cC,eg).
111 | edge(cd,eh).
112 | edge(cd,ei).
113 | edge(ce,ej).
114 | edge(ce,ek).
115 | edge(cf,el).
116 | edge(cf,em).
117 | edge(cg,en).
118 | edge(cg,eo).
119 | edge(ch,ep).
120 | edge(ch,eq).
121 | edge(ci,er).
122 | edge(ci,es).
123 | edge(cj,et).
124 | edge(cj,eu).
125 | edge(ck,ev).
126 | edge(ck,ew).
127 | edge(cl,ex).
128 | edge(cl,ey).
129 | edge(cm,ez).
130 |
--------------------------------------------------------------------------------
/data/clique.lp:
--------------------------------------------------------------------------------
1 | %
2 | % Clique of 4 elements.
3 | % Must produce only one concept.
4 | %
5 | edge(a,b).
6 | edge(a,c).
7 | edge(b,c).
8 | edge(b,d).
9 | edge(c,d).
10 | edge(a,d).
11 |
12 |
--------------------------------------------------------------------------------
/data/cliques.lp:
--------------------------------------------------------------------------------
1 |
2 | % Clique of 3 elements.
3 | edge(("a";"b";"c"),("a";"b";"c")).
4 |
5 | % Clique of 4 elements.
6 | edge(a,b).
7 | edge(a,c).
8 | edge(b,c).
9 | edge(b,d).
10 | edge(c,d).
11 | edge(a,d).
12 |
13 | % Clique of 6 elements.
14 | edge((u;v;w;x;y;z),(u;v;w;x;y;z)).
15 |
16 | % % Clique of 10 elements.
17 | edge((1;2;3;4;5;6;7;8;9;0),(1;2;3;4;5;6;7;8;9;0)).
18 |
--------------------------------------------------------------------------------
/data/complex-case-1.lp:
--------------------------------------------------------------------------------
1 |
2 |
3 | edge(l,(k;p)).
4 | edge(o,(m;n)).
5 |
6 | edge((d;m),(h;i)).
7 | edge((c;k),(e;f;h)).
8 | edge((a;b;c;d),(e;f;g;h;i)).
9 |
--------------------------------------------------------------------------------
/data/concept-loop.lp:
--------------------------------------------------------------------------------
1 | % Case described ip94 of thesis livre II:
2 | edge((a;b),(c;d;e;f;i)).
3 | edge((e;f),(a;b;g;h;j)).
4 | edge((g;h),(c;d;e;f;k)).
5 | edge((c;d),(a;b;g;h;l)).
6 |
7 | % rel(X,Y) :- rel(Y,X).
8 |
--------------------------------------------------------------------------------
/data/concomp.lp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | edge((a;b;c;d),e).
5 |
6 | edge((1;6),(2;3;4;5)).
7 |
8 |
9 | edge(42,23).
10 |
--------------------------------------------------------------------------------
/data/consider-included-nodes.lp:
--------------------------------------------------------------------------------
1 | % Show that a powernode must include all nodes in the powernodes he contains.
2 | % In opposition to inclusions.lp, this case shows when the powernode can't be created.
3 | % The final star motif a×bd can't be created because b and d are in different blocks.
4 | % However, until this test case, a powernode was created, with all nodes of the score 8
5 | % motif inside, linked to a, leading falsely to the observation that a was linked
6 | % to all nodes.
7 | % This was only detected because in some case such inconsistency is rendered
8 | % very poorly in cytoscape.
9 |
10 | % If this test case must fail, probably there is no enforcement of the following rule:
11 | % if a powernode P includes a node in a block B and a node outside B,
12 | % then all nodes in B must be in P.
13 |
14 | % All nodes are in a ubiquitous powernode.
15 | edge((root;root2;root3),(a;b;c;d;e;f;g;h;i;j;l;m)). % score 36
16 |
17 | % A motif is built inside the ubiquitous powernode.
18 | edge((b;c),(d;e;f;g;h)). % score 8
19 |
20 | % The star creating (theoretically) the powernode including the motif.
21 | edge(a,(b;d)). % score 6: note the absence of e, f, g and h
22 |
--------------------------------------------------------------------------------
/data/ddiam.lp:
--------------------------------------------------------------------------------
1 | % Double biclique overlapping with a third one, unambiguous
2 |
3 | % first biclique
4 | edge((a;d;d2),(b1;b2;c;e;q;o)).
5 | edge(b1,c).
6 |
7 | % second biclique
8 | edge((i;h),(f1;f2;g;j;l;p)).
9 | edge(f1,g).
10 |
11 | % m and n are linked to l,p,q and o
12 | edge((m;m2;n),(l;p;q)).
13 |
14 | % m, m2 and n is a clique
15 | edge(n,(m;m2)).
16 |
--------------------------------------------------------------------------------
/data/diacli.lp:
--------------------------------------------------------------------------------
1 | % first biclique
2 | edge((a;d),(b;c;e;q;o)).
3 | edge(b,c).
4 |
5 | % clique
6 | edge((f;g;j;p;l;r;s),(f;g;j;p;l;r;s)).
7 |
8 | % m and n are linked to l,p,q and o
9 | edge((m;n),(l;p;s;q;o)).
10 | edge(n,m).
11 |
--------------------------------------------------------------------------------
/data/diamond.lp:
--------------------------------------------------------------------------------
1 | %*
2 | % Simple Test Graph with two components:
3 | % One on (1,2,3,4), one on (a,b,c,d)
4 | Cliques are (1,2,3) and (2,3,4)
5 | Concepts are (1,4)X(2,3), (2)X(1,3,4), (3)X(1,2,4), (b)X(a,c) and (c)X(b,d)
6 | % Solution cover : 1) (1,4)X(2,3) 2) (2,3) 3) (a,c)X(b) 4)
7 | update(1)...
8 | concept(1,1,2,2). concept(1,1,2,3). concept(1,1,1,1). concept(1,1,1,4).
9 | update(2)...
10 | clique(1,2,3). clique(1,2,2).
11 |
12 | update(1)...
13 | concept(a,1,2,b). concept(a,1,1,c). concept(a,1,1,a).
14 | update(2)...
15 | concept(a,2,2,c). concept(a,2,1,d).
16 |
17 | *%
18 | edge(i,f).
19 | edge(i,g).
20 | edge(f,g).
21 | edge(f,h).
22 | edge(g,h).
23 |
--------------------------------------------------------------------------------
/data/disjoint-subpnodes.lp:
--------------------------------------------------------------------------------
1 | % Creation of two independant powernodes inside a bigger one.
2 |
3 | edge((a;b;c;d;e;f),(a;b;c;d;e;f)).
4 |
5 | edge(g,(d;e;f)).
6 | edge(h,(b;c)).
7 |
--------------------------------------------------------------------------------
/data/double-p-groups.lp:
--------------------------------------------------------------------------------
1 | % Showcase of two powergroups with an interface.
2 |
3 | % First p-group
4 | edge((a;b;c;d),(m;l)).
5 | % Second p-group
6 | edge((e;f;g;h),(i;j)).
7 |
8 | % Interface
9 | edge(a,(h;i)).
10 | edge(m,i).
11 |
--------------------------------------------------------------------------------
/data/double_biclique_unambiguous.lp:
--------------------------------------------------------------------------------
1 | % first biclique
2 | edge((a;d;d2),(b1;b2;c;e;q;o)).
3 | edge(b1,c).
4 |
5 | % second biclique
6 | edge((i;h),(f1;f2;g;j;l;p)).
7 | edge(f1,g).
8 |
9 | % m and n are linked to l,p,q and o
10 | edge((m;m2;n),(l;p;q)).
11 |
12 | % m, m2 and n is a clique
13 | edge(n,(m;m2)).
14 |
--------------------------------------------------------------------------------
/data/empty.lp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/data/empty.lp
--------------------------------------------------------------------------------
/data/horrible_data.lp:
--------------------------------------------------------------------------------
1 | edge("[a]",("\"echo coucou\"";"'echo coucou'")).
2 | edge("[a,b]","$PYTHONPATH").
3 |
--------------------------------------------------------------------------------
/data/inclusions.lp:
--------------------------------------------------------------------------------
1 |
2 | edge((a;b;c;d;e;f;g;h;i;j;k;l),(a;b;c;d;e;f;g;h;i;k;l)).
3 | edge((d;e;f;g),(m;n;o;p)).
4 | edge((e;f;g),(q;r)).
5 | edge((f;g),(v;w;v2;w2)).
6 |
7 | edge((1;2;3;m;n;o;p),(1;2;3;m;n;o;p)).
8 | edge((q;r;5;6;7;8;9;10),(q;r;5;6;7;8;9;10)).
9 |
--------------------------------------------------------------------------------
/data/motif-overlapping.lp:
--------------------------------------------------------------------------------
1 | % Show examples of motif overlapping.
2 |
3 | % A biclique is compressed, revealing a clique of three elements.
4 | edge((a;b;c),(d;e;f)).
5 | edge((a;b;c),(a;b;c)).
6 |
7 | % Two cliques are shallowed by a star.
8 | edge((a1;a2;a3;a4;a5),(a1;a2;a3;a4;a5)). % first clique
9 | edge((b1;b2;b3;b4),(b1;b2;b3;b4)). % smaller clique
10 | edge(h,(a1;b4)). % hub
11 | edge(h,(c1;c2;c3;c4;c5;c6;c7;c8;c9)). % enough nodes to make the star the biggest motif
12 |
13 | % A clique is eaten by a biclique.
14 | edge((x1;x2;x3;x4),(x1;x2;x3;x4)). % the clique
15 | edge((x4;y1;y2),(z1;z2;z3)). % 3×3 biclique.
16 |
--------------------------------------------------------------------------------
/data/multiple-optimals.lp:
--------------------------------------------------------------------------------
1 | % Multiple bicliques of same score, to test multishot motif search.
2 |
3 | edge((a1;b1),(c1;d1;e1)).
4 | edge((a2;b2),(c2;d2;e2)).
5 | edge((a3;b3),(c3;d3;e3)).
6 |
7 | % Make them in the same cc
8 | edge(a,(a1;a2;a3)).
9 |
10 | % Show that further compression is not impacted.
11 | edge((a;b),(f1;f2)).
12 |
13 |
--------------------------------------------------------------------------------
/data/n8_d0.7.lp:
--------------------------------------------------------------------------------
1 | edge("1","5").
2 | edge("1","6").
3 | edge("1","7").
4 | edge("2","8").
5 | edge("2","3").
6 | edge("2","5").
7 | edge("2","6").
8 | edge("3","8").
9 | edge("3","4").
10 | edge("3","5").
11 | edge("3","7").
12 | edge("4","8").
13 | edge("4","5").
14 | edge("4","6").
15 | edge("4","7").
16 | edge("5","8").
17 | edge("5","6").
18 | edge("5","7").
19 | edge("6","8").
20 | edge("6","7").
21 |
--------------------------------------------------------------------------------
/data/one_edge.lp:
--------------------------------------------------------------------------------
1 |
2 | edge(a,b).
3 |
--------------------------------------------------------------------------------
/data/order.lp:
--------------------------------------------------------------------------------
1 | % This use case is about node ordering in the graph.
2 | % In ASP, nodes are ordered lexicographically,
3 | % but integers are always smaller than litterals,
4 | % that are always smaller than string.
5 |
6 | edge(10,4).
7 | edge(10,5).
8 | edge(10,6).
9 |
10 | edge(a,10).
11 | edge("b",a).
12 |
--------------------------------------------------------------------------------
/data/overlapping-bicliques.lp:
--------------------------------------------------------------------------------
1 | % a general star
2 | edge(a,(b;c;d;e;f;g;h;i;j;k)).
3 |
4 | % Small independant biclique
5 | edge((b;c),(d;e)).
6 |
7 | % a star
8 | edge(f,(g;h;i)).
9 |
10 | % overlapping with a biclique
11 | edge((f;g;h),(i;j;k)).
12 |
--------------------------------------------------------------------------------
/data/partition.lp:
--------------------------------------------------------------------------------
1 | edge((a;b;i),(e;f;g;h)).
2 | edge((g;h),(a;b;c;d)).
3 |
--------------------------------------------------------------------------------
/data/perfectfit.lp:
--------------------------------------------------------------------------------
1 |
2 | % first biclique
3 | edge((a;b;c;d),(e;f;g;h;i)).
4 |
5 | % second biclique
6 | edge((l;j),(c;d;e;f;g)).
7 |
--------------------------------------------------------------------------------
/data/pnode-to-clique.lp:
--------------------------------------------------------------------------------
1 | edge(a,c).
2 | edge(a,d).
3 | edge(a,e).
4 | edge(b,c).
5 | edge(b,d).
6 | edge(b,e).
7 | edge(f,c).
8 | edge(f,d).
9 | edge(f,e).
10 |
11 | edge(c,d).
12 | edge(c,e).
13 | edge(d,e).
14 |
--------------------------------------------------------------------------------
/data/prio_deg.lp:
--------------------------------------------------------------------------------
1 | % this is a case where the prioritization by node degree show its effect.
2 |
3 | % biclique that is normally the first motif to be compressed: {a,b,c}×{d,e}
4 | edge((a;b;c),(d;e)).
5 |
6 | % star with central node as the node with the maximal degree, overlapping the biclique.
7 | % will be compressed first if prioritization by degree is used.
8 | edge(s,(a;f;g;h)). % a is linked to 5 over nodes.
9 |
--------------------------------------------------------------------------------
/data/puceron-data-2018.lp:
--------------------------------------------------------------------------------
1 | /home/lbourneu/data/stephanie/A.pisum_data_lucas/lncRNA_classes.lp
--------------------------------------------------------------------------------
/data/quasibiclique.lp:
--------------------------------------------------------------------------------
1 | % This networks is made of multiple 2-balanced biclique.
2 |
3 |
4 | edge(a,f). edge(a,i). edge(a,j). % minobj is not linked to everyone
5 | edge(b,f). edge(b,g). edge(b,h). edge(b,i). edge(b,j).
6 | edge(c,f). edge(c,g). edge(c,h). edge(c,i). edge(c,j).
7 | edge(d,f). edge(d,g). edge(d,h). edge(d,j).
8 | edge(e,f). edge(e,g). edge(e,h). edge(e,i). edge(e,j).
9 |
10 |
11 | edge(r,q). edge(r,x). edge(r,y). edge(r,z). edge(r,p).
12 | edge(s,q). edge(s,x). edge(s,y). edge(s,p).
13 | edge(t,q). edge(t,x). edge(t,z). edge(t,p).
14 | edge(u,q). edge(u,x). edge(u,p).
15 | edge(v,q). edge(v,x). edge(v,y). edge(v,z). edge(v,p).
16 |
17 |
18 | edge(1,l). edge(1,m). edge(1,n). edge(1,o). edge(1,w).
19 | edge(2,l). edge(2,m). edge(2,n). edge(2,o). edge(2,w).
20 | edge(3,l). edge(3,m). edge(3,n). edge(3,o). edge(3,w).
21 | edge(4,l). edge(4,m). edge(4,n). edge(4,o). edge(4,w).
22 | edge(5,l). edge(5,m). edge(5,n). edge(5,o). edge(5,w).
23 |
--------------------------------------------------------------------------------
/data/quoting.lp:
--------------------------------------------------------------------------------
1 | % This use case is about the effect of quoting the litterals.
2 | % If quoting is correcly handled, it should be used on litterals
3 | % and therefore a 1×2 star should be found.
4 |
5 | edge(b,a).
6 | edge("b",c).
7 |
--------------------------------------------------------------------------------
/data/recipe-option-test.lp:
--------------------------------------------------------------------------------
1 | % Testing file for the RECIPE_FILE option.
2 | % See recipe-option-test.txt for the recipe used.
3 |
4 | % One biclique
5 | edge((a;b;c),(d;e;f)).
6 | edge(c,(g;h;i;j;k;l;m;n)).
7 | edge(a,(h;g;j;k;l;m;n)).
8 |
--------------------------------------------------------------------------------
/data/recipe-option-test.txt:
--------------------------------------------------------------------------------
1 | biclique c g h i
2 | biclique,open a b d e
3 | biclique,optional unexisting nodes are not compressible
4 | star,breakable a h g l m n
5 | biclique,last c j k
6 |
--------------------------------------------------------------------------------
/data/recipe-test-inclusion.lp:
--------------------------------------------------------------------------------
1 | % A test of recipe behavior, where an existing powernode
2 | % must be reused inside a new one.
3 |
4 | edge((a;b),(c;d;e;f)).
5 | edge((e;f),(g;h)).
6 |
7 | % With a recipe forcing ef×gh to be compressed first,
8 | % we should get a ab×cd(ef) motif on second step.
9 |
--------------------------------------------------------------------------------
/data/recipe-test.lp:
--------------------------------------------------------------------------------
1 | % Testing file for the RECIPE_FILE option.
2 | % See recipe-test.txt for the recipe used.
3 |
4 | % One biclique
5 | edge((a;b;c),(d;e;f)).
6 | edge(c,(g;h;i;j)).
7 |
--------------------------------------------------------------------------------
/data/recipe-test.txt:
--------------------------------------------------------------------------------
1 | biclique c g h i
2 | biclique a b d e
3 | biclique a b f
4 | biclique c d e
5 |
--------------------------------------------------------------------------------
/data/single-node.lp:
--------------------------------------------------------------------------------
1 | edge(a,a).
2 |
--------------------------------------------------------------------------------
/data/star.lp:
--------------------------------------------------------------------------------
1 |
2 |
3 | edge((a;b;c;d),e).
4 |
5 |
--------------------------------------------------------------------------------
/data/structural-binding-maincc.lp:
--------------------------------------------------------------------------------
1 | % The biggest connected component of the structural binding graph.
2 | % Graph comes from Royer et al, named "Interactions of SH3 Carrying Proteins".
3 | edge("YGR233C","YPL031C").
4 | edge("YDL155W","YLR210W").
5 | edge("YDL155W","YGR109C").
6 | edge("YDL155W","YGR108W").
7 | edge("YDL155W","YMR199W").
8 | edge("YDL155W","YPR119W").
9 | edge("YDL155W","YPR120C").
10 | edge("YDL155W","YPL256C").
11 | edge("YDL240W","YPR165W").
12 | edge("YBL047C","YBR109C").
13 | edge("YLR210W","YMR199W").
14 | edge("YLR210W","YPR119W").
15 | edge("YLR210W","YPR120C").
16 | edge("YLR210W","YPL256C").
17 | edge("YOR127W","YPR165W").
18 | edge("YIL063C","YLR293C").
19 | edge("YGR218W","YLR293C").
20 | edge("YBR140C","YNL098C").
21 | edge("YER009W","YOR185C").
22 | edge("YER009W","YLR293C").
23 | edge("YGR109C","YMR199W").
24 | edge("YGR109C","YPR119W").
25 | edge("YGR109C","YPR120C").
26 | edge("YGR109C","YPL256C").
27 | edge("YDR002W","YLR293C").
28 | edge("YGR108W","YMR199W").
29 | edge("YGR108W","YPR119W").
30 | edge("YGR108W","YPR120C").
31 | edge("YGR108W","YPL256C").
32 | edge("YAL040C","YPR119W").
33 | edge("YAL040C","YPR120C").
34 | edge("YJL020C","YMR032W").
35 | edge("YJL020C","YMR109W").
36 | edge("YJL020C","YKL129C").
37 | edge("YHR114W","YMR109W").
38 | edge("YHR114W","YKL129C").
39 | edge("YHR023W","YPR188C").
40 | edge("YHR023W","YMR109W").
41 | edge("YHR023W","YKL129C").
42 | edge("YHR023W","YOR326W").
43 | edge("YLR371W","YPR165W").
44 | edge("YER155C","YLR310C").
45 | edge("YDL056W","YLR182W").
46 | edge("YHR061C","YNL298W").
47 | edge("YGR250C","YIR001C").
48 | edge("YNL189W","YOR185C").
49 | edge("YGL238W","YOR185C").
50 | edge("YGL238W","YLR293C").
51 | edge("YBR109C","YML057W").
52 | edge("YBR109C","YLR433C").
53 | edge("YBR109C","YMR109W").
54 | edge("YBR109C","YKL129C").
55 | edge("YBR109C","YGL106W").
56 | edge("YBR109C","YOR326W").
57 | edge("YGL158W","YLR113W").
58 | edge("YER110C","YMR308C").
59 | edge("YER110C","YLR293C").
60 | edge("YMR199W","YPR119W").
61 | edge("YMR199W","YPL031C").
62 | edge("YMR199W","YPR120C").
63 | edge("YNL098C","YOL081W").
64 | edge("YNL098C","YOR101W").
65 | edge("YER133W","YIR006C").
66 | edge("YER133W","YLR449W").
67 | edge("YDR490C","YKL166C").
68 | edge("YBL007C","YHR016C").
69 | edge("YBL007C","YBR200W").
70 | edge("YBL007C","YCR088W").
71 | edge("YBL007C","YDR388W").
72 | edge("YJL187C","YPR119W").
73 | edge("YKL101W","YOR061W").
74 | edge("YDL159W","YGR040W").
75 | edge("YDL159W","YLR362W").
76 | edge("YGR040W","YLR362W").
77 | edge("YBR212W","YDL167C").
78 | edge("YBL085W","YBR200W").
79 | edge("YBL085W","YLR229C").
80 | edge("YNL093W","YOR370C").
81 | edge("YNL093W","YOR089C").
82 | edge("YJL005W","YOR361C").
83 | edge("YPR119W","YPR120C").
84 | edge("YNL289W","YPL031C").
85 | edge("YBL016W","YLR362W").
86 | edge("YBR028C","YHR135C").
87 | edge("YJL041W","YLR347C").
88 | edge("YHR071W","YPL031C").
89 | edge("YDR477W","YER129W").
90 | edge("YDR477W","YMR104C").
91 | edge("YJL128C","YLR362W").
92 | edge("YJL128C","YLR113W").
93 | edge("YHR086W","YIL061C").
94 | edge("YDR429C","YOR361C").
95 | edge("YNL090W","YOR089C").
96 | edge("YFL029C","YPR161C").
97 | edge("YLL016W","YLR310C").
98 | edge("YBR119W","YIL061C").
99 | edge("YBR119W","YMR268C").
100 | edge("YIR001C","YNL016W").
101 | edge("YBL004W","YJL109C").
102 | edge("YBL004W","YGL195W").
103 | edge("YJR065C","YMR109W").
104 | edge("YER136W","YKR014C").
105 | edge("YER136W","YLR262C").
106 | edge("YER136W","YFL005W").
107 | edge("YER136W","YFL038C").
108 | edge("YGL019W","YOR039W").
109 | edge("YGL019W","YOR061W").
110 | edge("YGL019W","YIL035C").
111 | edge("YOR039W","YOR061W").
112 | edge("YKL190W","YLR433C").
113 | edge("YHL007C","YPL256C").
114 | edge("YJL095W","YOR231W").
115 | edge("YHL002W","YMR109W").
116 | edge("YLR113W","YLR248W").
117 | edge("YBR200W","YER114C").
118 | edge("YBR200W","YCR088W").
119 | edge("YBR200W","YDR388W").
120 | edge("YBR017C","YHL030W").
121 | edge("YBR017C","YGL195W").
122 | edge("YBR017C","YLR293C").
123 | edge("YBR017C","YLR347C").
124 | edge("YHR030C","YOR231W").
125 | edge("YHR030C","YLR182W").
126 | edge("YDR264C","YNL154C").
127 | edge("YDR264C","YHR135C").
128 | edge("YBR260C","YKR055W").
129 | edge("YBR260C","YIL118W").
130 | edge("YNL175C","YPL043W").
131 | edge("YBL105C","YOR231W").
132 | edge("YBL105C","YPR165W").
133 | edge("YDR309C","YNL298W").
134 | edge("YDR432W","YNL016W").
135 | edge("YIL035C","YML074C").
136 | edge("YMR109W","YOR326W").
137 | edge("YLR262C","YMR235C").
138 | edge("YKL129C","YOR326W").
139 | edge("YER114C","YER118C").
140 | edge("YER114C","YLR229C").
141 | edge("YIL061C","YKL074C").
142 | edge("YIL061C","YMR268C").
143 | edge("YDL101C","YHR135C").
144 | edge("YDL051W","YNL110C").
145 | edge("YFL033C","YJL164C").
146 | edge("YAL041W","YLR229C").
147 | edge("YFL039C","YOR326W").
148 | edge("YKL074C","YMR268C").
149 | edge("YGL106W","YOR326W").
150 | edge("YFL005W","YFL038C").
151 | edge("YDL135C","YIL118W").
152 | edge("YDL135C","YPR165W").
153 | edge("YIL118W","YPR165W").
154 | edge("YDR389W","YPR165W").
155 | edge("YER031C","YFL038C").
156 | edge("YCR088W","YDR388W").
157 | edge("YLR449W","YML074C").
158 | edge("YIL007C","YPR165W").
159 | edge("YGL097W","YLR293C").
160 | edge("YER123W","YHR135C").
161 | edge("YLR293C","YLR347C").
162 | edge("YNL298W","YPL115C").
163 | edge("YAL040C","YDL155W").
164 | edge("YDL029W","YOR122C").
165 | edge("YFL039C","YOR122C").
166 | edge("YFL039C","YJL081C").
167 | edge("YGR109C","YLR210W").
168 | edge("YGR108W","YLR210W").
169 | edge("YAL040C","YLR210W").
170 | edge("YLR229C","YOR127W").
171 | edge("YHR030C","YLR096W").
172 | edge("YBR109C","YPL242C").
173 | edge("YBR109C","YOR257W").
174 | edge("YGR108W","YGR109C").
175 | edge("YAL040C","YGR109C").
176 | edge("YAL040C","YGR108W").
177 | edge("YFL039C","YOR141C").
178 | edge("YKL166C","YPL203W").
179 | edge("YJL164C","YPL203W").
180 | edge("YHR135C","YPL203W").
181 | edge("YBL007C","YHR114W").
182 | edge("YFL039C","YHR023W").
183 | edge("YGL106W","YHR023W").
184 | edge("YAL029C","YHR023W").
185 | edge("YIL061C","YIR009W").
186 | edge("YGL195W","YGL241W").
187 | edge("YER155C","YLR371W").
188 | edge("YDL108W","YPR025C").
189 | edge("YPL031C","YPR025C").
190 | edge("YGL044C","YOL123W").
191 | edge("YBL007C","YFR024C").
192 | edge("YHR030C","YHR102W").
193 | edge("YNL016W","YNL251C").
194 | edge("YDR309C","YHR061C").
195 | edge("YGL238W","YNL189W").
196 | edge("YLR335W","YNL189W").
197 | edge("YMR308C","YNL189W").
198 | edge("YLR293C","YNL189W").
199 | edge("YLR347C","YNL189W").
200 | edge("YJL031C","YPR176C").
201 | edge("YJL128C","YNR031C").
202 | edge("YAL029C","YBR109C").
203 | edge("YLR293C","YLR335W").
204 | edge("YMR235C","YOR185C").
205 | edge("YLR293C","YOR185C").
206 | edge("YLR347C","YOR185C").
207 | edge("YBL016W","YGL158W").
208 | edge("YDR477W","YGL158W").
209 | edge("YKR014C","YML064C").
210 | edge("YBR017C","YER110C").
211 | edge("YJL005W","YNL098C").
212 | edge("YLR310C","YNL098C").
213 | edge("YJL109C","YLR249W").
214 | edge("YJL164C","YKL166C").
215 | edge("YHR135C","YKL166C").
216 | edge("YJL187C","YMR001C").
217 | edge("YKL190W","YML057W").
218 | edge("YLR433C","YML057W").
219 | edge("YIL035C","YKL101W").
220 | edge("YFL029C","YPR054W").
221 | edge("YHR030C","YPR054W").
222 | edge("YLR229C","YOL113W").
223 | edge("YBL016W","YDL159W").
224 | edge("YBL016W","YGR040W").
225 | edge("YBR119W","YOR319W").
226 | edge("YIL061C","YOR319W").
227 | edge("YDL051W","YDL167C").
228 | edge("YBL085W","YMR032W").
229 | edge("YER118C","YMR032W").
230 | edge("YER136W","YNL093W").
231 | edge("YKR014C","YNL093W").
232 | edge("YJL095W","YPL140C").
233 | edge("YHR030C","YPL140C").
234 | edge("YLR085C","YNL271C").
235 | edge("YJR065C","YNL271C").
236 | edge("YDL029W","YNL271C").
237 | edge("YFL039C","YNL271C").
238 | edge("YIL159W","YNL271C").
239 | edge("YIR006C","YNL084C").
240 | edge("YPL256C","YPR119W").
241 | edge("YER136W","YGL210W").
242 | edge("YBR017C","YJL041W").
243 | edge("YNL025C","YPL042C").
244 | edge("YDR477W","YPL042C").
245 | edge("YGR092W","YPL042C").
246 | edge("YNL016W","YPL190C").
247 | edge("YNL025C","YPL031C").
248 | edge("YDL127W","YPL031C").
249 | edge("YFL033C","YPL031C").
250 | edge("YDR477W","YNL025C").
251 | edge("YHR135C","YNL025C").
252 | edge("YJL031C","YOR370C").
253 | edge("YOR089C","YOR370C").
254 | edge("YBR264C","YOR370C").
255 | edge("YFL038C","YOR370C").
256 | edge("YAR019C","YGR092W").
257 | edge("YCR073C","YJL128C").
258 | edge("YLR113W","YLR362W").
259 | edge("YER123W","YLR362W").
260 | edge("YAL041W","YGR152C").
261 | edge("YGR080W","YLL050C").
262 | edge("YCR088W","YLL050C").
263 | edge("YCR088W","YGR080W").
264 | edge("YDR432W","YHR086W").
265 | edge("YER136W","YML001W").
266 | edge("YLR262C","YML001W").
267 | edge("YER136W","YOR089C").
268 | edge("YKR014C","YOR089C").
269 | edge("YDR432W","YIR001C").
270 | edge("YDL051W","YIR001C").
271 | edge("YER165W","YIR001C").
272 | edge("YDR122W","YPR120C").
273 | edge("YPL256C","YPR120C").
274 | edge("YLR310C","YOR101W").
275 | edge("YBL004W","YMR308C").
276 | edge("YLR293C","YMR308C").
277 | edge("YLR347C","YMR308C").
278 | edge("YDL029W","YJR065C").
279 | edge("YNL154C","YPL204W").
280 | edge("YOR061W","YPL204W").
281 | edge("YLR182W","YPL204W").
282 | edge("YML074C","YPL204W").
283 | edge("YER123W","YPL204W").
284 | edge("YHR135C","YPL204W").
285 | edge("YGR159C","YKL193C").
286 | edge("YBR264C","YER136W").
287 | edge("YER031C","YER136W").
288 | edge("YER031C","YKR014C").
289 | edge("YHL034C","YMR302C").
290 | edge("YGR159C","YMR302C").
291 | edge("YDR432W","YMR302C").
292 | edge("YGR159C","YHL034C").
293 | edge("YDR432W","YHL034C").
294 | edge("YCR088W","YHR016C").
295 | edge("YDR388W","YHR016C").
296 | edge("YIL035C","YOR039W").
297 | edge("YHR082C","YJL095W").
298 | edge("YHR030C","YJL095W").
299 | edge("YHL002W","YNR006W").
300 | edge("YER118C","YHL002W").
301 | edge("YAL041W","YBR200W").
302 | edge("YER111C","YHR030C").
303 | edge("YER123W","YNL154C").
304 | edge("YHR135C","YNL154C").
305 | edge("YFL005W","YKR055W").
306 | edge("YDL135C","YKR055W").
307 | edge("YIL007C","YKR055W").
308 | edge("YOR361C","YPL043W").
309 | edge("YNL110C","YPL043W").
310 | edge("YER114C","YNL298W").
311 | edge("YLR229C","YNL298W").
312 | edge("YDR432W","YGR159C").
313 | edge("YIL035C","YOR061W").
314 | edge("YKL129C","YMR109W").
315 | edge("YDL029W","YMR109W").
316 | edge("YER118C","YMR109W").
317 | edge("YGL106W","YMR109W").
318 | edge("YAL029C","YMR109W").
319 | edge("YCR088W","YMR109W").
320 | edge("YDR388W","YMR109W").
321 | edge("YFL005W","YLR262C").
322 | edge("YLR293C","YMR235C").
323 | edge("YDL029W","YKL129C").
324 | edge("YAL029C","YKL129C").
325 | edge("YDR388W","YKL129C").
326 | edge("YAL041W","YER114C").
327 | edge("YDL101C","YPL153C").
328 | edge("YER111C","YPL153C").
329 | edge("YGL195W","YJL109C").
330 | edge("YDL051W","YNL016W").
331 | edge("YDL135C","YLR229C").
332 | edge("YAL029C","YFL039C").
333 | edge("YER165W","YGL044C").
334 | edge("YAL029C","YGL106W").
335 | edge("YAL029C","YOR326W").
336 | edge("YER111C","YLR182W").
337 | edge("YNL110C","YOR361C").
338 |
--------------------------------------------------------------------------------
/data/suboptimal.lp:
--------------------------------------------------------------------------------
1 | % Case proving that optimal compression can't always
2 | % be reached with standard heurisitic.
3 |
4 | edge((a;b),(c;d;i;e;f)).
5 | edge((c;d),(l;g;h)).
6 | edge((e;f),(j;g;h)).
7 | edge((g;h),(k;c;d)).
8 |
9 |
10 | rel(X,Y):- rel(Y,X).
11 |
--------------------------------------------------------------------------------
/data/test.gml:
--------------------------------------------------------------------------------
1 | graph [
2 | node [
3 | id 0
4 | label "d"
5 | ]
6 | node [
7 | id 1
8 | label "s"
9 | ]
10 | node [
11 | id 2
12 | label "g"
13 | ]
14 | node [
15 | id 3
16 | label "w"
17 | ]
18 | node [
19 | id 4
20 | label "c"
21 | ]
22 | node [
23 | id 5
24 | label "l"
25 | ]
26 | node [
27 | id 6
28 | label "b"
29 | ]
30 | node [
31 | id 7
32 | label "m"
33 | ]
34 | node [
35 | id 8
36 | label "f"
37 | ]
38 | node [
39 | id 9
40 | label "p"
41 | ]
42 | node [
43 | id 10
44 | label "v"
45 | ]
46 | edge [
47 | source 0
48 | target 6
49 | width 8.75
50 | ]
51 | edge [
52 | source 0
53 | target 1
54 | width 8.25
55 | ]
56 | edge [
57 | source 0
58 | target 3
59 | width 6.0
60 | ]
61 | edge [
62 | source 0
63 | target 4
64 | width 10.0
65 | ]
66 | edge [
67 | source 1
68 | target 6
69 | width 6.527777777777778
70 | ]
71 | edge [
72 | source 1
73 | target 3
74 | width 5.4
75 | ]
76 | edge [
77 | source 1
78 | target 4
79 | width 7.0
80 | ]
81 | edge [
82 | source 2
83 | target 6
84 | width 5.292397660818714
85 | ]
86 | edge [
87 | source 3
88 | target 6
89 | width 8.11111111111111
90 | ]
91 | edge [
92 | source 3
93 | target 4
94 | width 6.0
95 | ]
96 | edge [
97 | source 4
98 | target 6
99 | width 8.518518518518519
100 | ]
101 | edge [
102 | source 4
103 | target 8
104 | width 5.0925925925925934
105 | ]
106 | edge [
107 | source 5
108 | target 7
109 | width 6.923076923076923
110 | ]
111 | edge [
112 | source 5
113 | target 9
114 | width 8.787878787878787
115 | ]
116 | edge [
117 | source 5
118 | target 10
119 | width 6.190476190476191
120 | ]
121 | edge [
122 | source 6
123 | target 7
124 | width 7.3076923076923075
125 | ]
126 | edge [
127 | source 6
128 | target 8
129 | width 5.185185185185185
130 | ]
131 | edge [
132 | source 6
133 | target 10
134 | width 5.456349206349206
135 | ]
136 | edge [
137 | source 7
138 | target 10
139 | width 6.6208791208791204
140 | ]
141 | ]
142 |
--------------------------------------------------------------------------------
/data/test.graphml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/data/testblocks.lp:
--------------------------------------------------------------------------------
1 |
2 | % external clique
3 | edge(a,b).
4 | edge(a,c).
5 | edge(a,d).
6 | edge(a,e).
7 | edge(b,c).
8 | edge(b,d).
9 | edge(b,e).
10 | edge(c,d).
11 | edge(c,e).
12 | edge(d,e).
13 |
14 | % external clique 2
15 | edge((f;g;h;i;j;m),(f;g;h;i;j;m)).
16 |
17 | % biclique of 4 elements of clique 1 and 2
18 | edge(f,(a;b;c;d)).
19 |
20 | % biclique of 4 elements of previous biclique and one external element
21 | edge(l,f).
22 | edge(l,g).
23 | edge(l,h).
24 |
--------------------------------------------------------------------------------
/data/todel.lp:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | % Case study 1 for lattice-based compression
5 | % edge(a,(b;c;d;x)).
6 | % edge(b,(a;c;d;y)).
7 | % edge(c,(a;b;z)).
8 | % edge(d,(a;b;w)).
9 |
10 |
11 | % Case study 2 for lattice-based compression: overlapping 3 and 4 cliques
12 | % edge((a;b;c;d),(a;b;c;d)). % first clique
13 | % edge((d;e;f),(d;e;f)). % small clique beside the first
14 | % edge(b,g). % noise
15 |
16 |
17 | % Case study 3 for lattice-based compression: overlapping 4 cliques
18 | % edge((a;b;d;e),(a;b;d;e)). % first clique
19 | % edge((b;c;e;f),(b;c;e;f)). % second clique
20 | % edge(b,g). % noise
21 |
22 |
23 | % Case study: suboptimal with some specifics removed
24 | % suboptimal cutted-3 with some specifics removed
25 | % edge((a;b),(c;d;g)).
26 | % edge((c;d),(e;f;h)).
27 | % edge((e;f),(a;b;i)).
28 |
29 | % suboptimal cutted-4 with some specifics removed
30 | % edge((a;b),(c;d;j)).
31 | % edge((c;d),(e;f;k)).
32 | % edge((e;f),(g;h;l)).
33 | % edge((g;h),(a;b;i)).
34 |
35 | % suboptimal cutted-5 with some specifics removed
36 | % edge((a;b),(c;d;l)).
37 | % edge((c;d),(e;f;m)).
38 | % edge((e;f),(g;h;n)).
39 | % edge((g;h),(i;j;o)).
40 | % edge((i;j),(a;b;k)).
41 |
42 | % suboptimal cutted-6 with some specifics removed
43 | % edge((a;b),(c;d;n)).
44 | % edge((c;d),(e;f)).
45 | % edge((e;f),(g;h)).
46 | % edge((g;h),(i;j;q)).
47 | % edge((i;j),(k;l)).
48 | % edge((k;l),(a;b)).
49 |
--------------------------------------------------------------------------------
/data/triplets.lp:
--------------------------------------------------------------------------------
1 | % Example of triplet concepts
2 |
3 |
4 | edge((a;b;c),(d;e;f)).
5 | edge((a;b;c;d;e;f),(g;h;i)).
6 | edge((g;h;i),(g;h;i)).
7 |
8 | edge((b;c),(l;m;n;o;p;q)).
9 | edge((e;f),(l;m;n;o;p;q)).
10 | edge((l;m;n;o;p;q),(l;m;n;o;p;q)).
11 |
--------------------------------------------------------------------------------
/data/typical-use-case.lp:
--------------------------------------------------------------------------------
1 | % The typical use case for the lattice-based search space formalization.
2 |
3 | edge((a;b;c),(e;d)).
4 | edge(g,e).
5 | edge((d;i;j;k),(e;f;g;h)).
6 |
7 | % motifs to compress (for ~/pocs/generalist_compression)
8 | motif(1,e,(a;b;c;d;g;i;j;k)). % concept 1
9 | motif(2,g,(d;i;j;k)). % concept 2
10 | motif(3,(d;i;j;k),(f;g;h)). % concept 3
11 | motif(4,d,(a;b;c)). % concept 7
12 |
13 |
--------------------------------------------------------------------------------
/data/unclique.lp:
--------------------------------------------------------------------------------
1 | % non maximal clique
2 | edge((a;b;c;d),(a;b;c;d)).
3 |
4 | % maximal biclique
5 | edge((c;e;f;g),(h;i)).
6 |
--------------------------------------------------------------------------------
/data/variable-name.gml:
--------------------------------------------------------------------------------
1 | graph [
2 | node [
3 | id 0
4 | label "B"
5 | ]
6 | node [
7 | id 1
8 | label "C"
9 | ]
10 | node [
11 | id 2
12 | label "D"
13 | ]
14 | node [
15 | id 3
16 | label "E"
17 | ]
18 | node [
19 | id 4
20 | label ""A""
21 | ]
22 | edge [
23 | source 0
24 | target 1
25 | ]
26 | edge [
27 | source 0
28 | target 2
29 | ]
30 | edge [
31 | source 0
32 | target 4
33 | ]
34 | edge [
35 | source 3
36 | target 4
37 | ]
38 | ]
39 |
--------------------------------------------------------------------------------
/data/wiki-tree-decomposition.lp:
--------------------------------------------------------------------------------
1 | % Graph from wikipedia's tree decomposition page.
2 | % https://en.wikipedia.org/wiki/Tree_decomposition
3 |
4 | edge(a,(b;c)).
5 | edge(d,(c;e)).
6 | edge(f,(b;g)).
7 | edge(h,(e;g)).
8 |
9 | edge(b,(c;e;g)).
10 | edge(e,(c;b;g)).
11 |
--------------------------------------------------------------------------------
/data/zorro.lp:
--------------------------------------------------------------------------------
1 |
2 | % graph
3 | edge((a1;a2;a3;a4),(b1;b2;b3;b4;b5;b6)).
4 | edge((c1;c2;e3;e4),(b1;b2;d1;d2;d3)).
5 | edge((e1;e2;e3;e4),(f1;f2;f3;f4)).
6 |
7 | % motifs to compress (for ~/pocs/generalist_compression)
8 | motif(1,(a1;a2;a3;a4),(b1;b2;b3;b4;b5;b6)).
9 | motif(2,(c1;c2;e3;e4),(b1;b2;d1;d2;d3)).
10 | motif(3,(e1;e2;e3;e4),(f1;f2;f3;f4)).
11 |
--------------------------------------------------------------------------------
/out/.gitignore:
--------------------------------------------------------------------------------
1 | stats.csv
2 | *.bbl
3 |
--------------------------------------------------------------------------------
/out/thesis/concept-cycle-4.sif:
--------------------------------------------------------------------------------
1 | a Edge j
2 | b Edge j
3 | a Edge c
4 | a Edge d
5 | a Edge g
6 | a Edge h
7 | b Edge c
8 | b Edge d
9 | b Edge g
10 | b Edge h
11 |
12 | e Edge l
13 | f Edge l
14 | e Edge c
15 | e Edge d
16 | e Edge g
17 | e Edge h
18 | f Edge c
19 | f Edge d
20 | f Edge g
21 | f Edge h
22 |
23 | g Edge i
24 | h Edge i
25 |
26 | c Edge k
27 | d Edge k
28 |
--------------------------------------------------------------------------------
/out/thesis/matrixdb_CORE27_example__a_0-_b_0-_c_3-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
--------------------------------------------------------------------------------
/out/thesis/matrixdb_CORE27_example__a_0-_b_0-_c_3-zoom.png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/matrixdb_CORE27_example__a_0-_b_0-_c_3-zoom.png.png
--------------------------------------------------------------------------------
/out/thesis/matrixdb_CORE27_example__a_0-_b_0-_c_3-zoom.png.png.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/matrixdb_CORE27_example__a_0-_b_0-_c_3-zoom.png.png.png
--------------------------------------------------------------------------------
/out/thesis/matrixdb_CORE27_example__a_3-_b_3-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
--------------------------------------------------------------------------------
/out/thesis/miRNA_mRNA_diff-no3019.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/miRNA_mRNA_diff-no3019.png
--------------------------------------------------------------------------------
/out/thesis/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.bbl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.bbl.png
--------------------------------------------------------------------------------
/out/thesis/thesis-network-triplet-zoom-a_0-_b_0-_c_0-.bbl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/thesis-network-triplet-zoom-a_0-_b_0-_c_0-.bbl.png
--------------------------------------------------------------------------------
/out/thesis/triplet.sif:
--------------------------------------------------------------------------------
1 | node12 DirectedEdge node10
2 | node12 DirectedEdge node9
3 | node12 DirectedEdge node8
4 | node12 DirectedEdge node7
5 | node11 DirectedEdge node10
6 | node11 DirectedEdge node9
7 | node11 DirectedEdge node8
8 | node11 DirectedEdge node7
9 | node11 DirectedEdge node12
10 | node8 DirectedEdge node10
11 | node8 DirectedEdge node9
12 | node7 DirectedEdge node10
13 | node7 DirectedEdge node9
14 |
--------------------------------------------------------------------------------
/out/thesis/zorro-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-annotated.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c2.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c21-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c21-annotated.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c21-c31-c32-c22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c21-c31-c32-c22.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c22-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c22-annotated.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c3-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c3-annotated.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c3-c21-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c3-c21-annotated.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c3-c22-annotated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c3-c22-annotated.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c3.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1-c31-c21-c22-c32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1-c31-c21-c22-c32.png
--------------------------------------------------------------------------------
/out/thesis/zorro-c1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-c1.png
--------------------------------------------------------------------------------
/out/thesis/zorro-compression-middle-first.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-compression-middle-first.png
--------------------------------------------------------------------------------
/out/thesis/zorro-compression-per-concept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-compression-per-concept.png
--------------------------------------------------------------------------------
/out/thesis/zorro-compression-regular.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro-compression-regular.png
--------------------------------------------------------------------------------
/out/thesis/zorro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/out/thesis/zorro.png
--------------------------------------------------------------------------------
/out/thesis/zorro.sif:
--------------------------------------------------------------------------------
1 | a1 DirectedEdge b1
2 | a1 DirectedEdge b2
3 | a1 DirectedEdge b3
4 | a1 DirectedEdge b4
5 | a1 DirectedEdge b5
6 | a1 DirectedEdge b6
7 | a2 DirectedEdge b1
8 | a2 DirectedEdge b2
9 | a2 DirectedEdge b3
10 | a2 DirectedEdge b4
11 | a2 DirectedEdge b5
12 | a2 DirectedEdge b6
13 | a3 DirectedEdge b1
14 | a3 DirectedEdge b2
15 | a3 DirectedEdge b3
16 | a3 DirectedEdge b4
17 | a3 DirectedEdge b5
18 | a3 DirectedEdge b6
19 | a4 DirectedEdge b1
20 | a4 DirectedEdge b2
21 | a4 DirectedEdge b3
22 | a4 DirectedEdge b4
23 | a4 DirectedEdge b5
24 | a4 DirectedEdge b6
25 |
26 |
27 | c1 DirectedEdge b5
28 | c1 DirectedEdge b6
29 | c1 DirectedEdge d1
30 | c1 DirectedEdge d2
31 | c1 DirectedEdge d3
32 | c2 DirectedEdge b5
33 | c2 DirectedEdge b6
34 | c2 DirectedEdge d1
35 | c2 DirectedEdge d2
36 | c2 DirectedEdge d3
37 |
38 | e1 DirectedEdge b5
39 | e1 DirectedEdge b6
40 | e2 DirectedEdge b5
41 | e2 DirectedEdge b6
42 |
43 | e1 DirectedEdge d1
44 | e1 DirectedEdge d2
45 | e1 DirectedEdge d3
46 | e2 DirectedEdge d1
47 | e2 DirectedEdge d2
48 | e2 DirectedEdge d3
49 |
50 |
51 | e1 DirectedEdge f1
52 | e1 DirectedEdge f2
53 | e1 DirectedEdge f3
54 | e1 DirectedEdge f4
55 | e2 DirectedEdge f1
56 | e2 DirectedEdge f2
57 | e2 DirectedEdge f3
58 | e2 DirectedEdge f4
59 | e3 DirectedEdge f1
60 | e3 DirectedEdge f2
61 | e3 DirectedEdge f3
62 | e3 DirectedEdge f4
63 | e4 DirectedEdge f1
64 | e4 DirectedEdge f2
65 | e4 DirectedEdge f3
66 | e4 DirectedEdge f4
67 |
--------------------------------------------------------------------------------
/powergrasp/__init__.py:
--------------------------------------------------------------------------------
1 | import pkg_resources
2 | ASP_FILES = {
3 | fname: pkg_resources.resource_filename(__name__, 'asp/' + fname + '.lp')
4 | for fname in ('search-triplet', 'search-fullbiclique', 'search-biclique', 'search-quasibiclique', 'search-star', 'search-clique', 'process-motif', 'block-constraint-cpu', 'block-constraint-memory', 'scoring_powergraph')
5 | }
6 |
7 | from .routines import compress_by_cc
8 |
9 | __version__ = '0.8.19.dev0'
10 |
--------------------------------------------------------------------------------
/powergrasp/__main__.py:
--------------------------------------------------------------------------------
1 | """PowerGrASP package.
2 |
3 | """
4 |
5 | from . import cli
6 | from .routines import compress_by_cc
7 | from .constants import print_config
8 |
9 |
10 | def run_cli():
11 | args = cli.parse_args(__doc__)
12 |
13 | if args.show_config:
14 | print_config()
15 | exit()
16 | elif args.infile:
17 | with open(args.outfile, 'w') as fd:
18 | for line in compress_by_cc(args.infile, args.recipe):
19 | fd.write(line + '\n')
20 | else:
21 | print('Nothing to do.')
22 |
23 |
24 | if __name__ == "__main__":
25 | run_cli()
26 |
--------------------------------------------------------------------------------
/powergrasp/asp.py:
--------------------------------------------------------------------------------
1 | """Search for motif using clyngor/clingo/ASP.
2 |
3 | Function solve_motif_search is defined according to global constants.
4 |
5 | """
6 |
7 | import math
8 | import clyngor
9 | from powergrasp.constants import (COVERED_EDGES_FROM_ASP, SHOW_STORY, SHOW_DEBUG,
10 | MULTISHOT_MOTIF_SEARCH, CLINGO_MULTITHREADING)
11 |
12 |
13 | def _build_solver(step:int, lowerbound:int, upperbound:int, files:iter, graph:str, options:str) -> iter:
14 | """Return iterator over found models"""
15 | constants = {'k': step, 'lowerbound': lowerbound, 'upperbound': upperbound}
16 | if COVERED_EDGES_FROM_ASP:
17 | constants['covered_edges_from_asp'] = 1
18 | options += CLINGO_MULTITHREADING
19 | models = clyngor.solve(files=tuple(files), inline=str(graph), constants=constants, stats=False, options=options)
20 | if SHOW_STORY:
21 | print('SOLVE', models.command)
22 | return models.by_predicate.careful_parsing
23 |
24 |
25 | def oneshot_motif_search(step:int, lowerbound:int, upperbound:int, files:iter, graph:str, options:str='') -> iter:
26 | """Return iterable over the generator of the one best model
27 | containing atoms found in best model"""
28 | model = None
29 | for model in _build_solver(step, lowerbound, upperbound, files, graph, options):
30 | pass # get the last one
31 | if model:
32 | yield model
33 |
34 |
35 | def multishot_motif_search(step:int, lowerbound:int, upperbound:int, files:iter, graph:str, options:str='') -> iter:
36 | """Yield atoms found in bests models"""
37 | all_models = _build_solver(step, lowerbound, upperbound, files, graph, options='--opt-mode=optN ' + options)
38 | best_opt, models = math.inf, []
39 | for model, opt in all_models.with_optimization:
40 | if SHOW_DEBUG:
41 | print('OPT, MODEL:', opt[0], model)
42 | if opt[0] < best_opt: # smaller is best
43 | best_opt, models = opt[0], [] # model will be given again as last model, so no need to include it twice
44 | else:
45 | models.append(model)
46 | yield from models
47 |
48 |
49 | # define the default behavior
50 | if MULTISHOT_MOTIF_SEARCH:
51 | solve_motif_search = multishot_motif_search
52 | else:
53 | solve_motif_search = oneshot_motif_search
54 |
--------------------------------------------------------------------------------
/powergrasp/asp/block-constraint-cpu.lp:
--------------------------------------------------------------------------------
1 | % Implementation of the block overlap constraint, optimized for CPU.
2 | % If a powernode contains a node in a block and a node not in the block,
3 | % all nodes in block must be contained.
4 | includedblock(L,U,T,X):- newconcept(T,X) ; block(L,U,X) ; % X is in the block
5 | newconcept(T,Z) ; not block(L,U,Z). % Z is outside
6 | :- includedblock(L,U,T,X); not newconcept(T,Y) ;
7 | block(L,U,Y) ; X!=Y. % Y is not in the pnode
8 |
9 | % NB: this constraint may be written in many ways.
10 | % See debug/overlap-block-constraint for further investigations.
11 |
--------------------------------------------------------------------------------
/powergrasp/asp/block-constraint-memory.lp:
--------------------------------------------------------------------------------
1 | % Implementation of the block overlap constraint, optimized for memory.
2 | % If a powernode contains a node in a block and a node not in the block,
3 | % all nodes in block must be contained.
4 | includedblock(L,U,T):- newconcept(T,X) ; block(L,U,X). % X is in the block
5 | strictlyincludedblock(L,U,T):-
6 | includedblock(L,U,T) ; newconcept(T,Z) ; not block(L,U,Z). % Z is outside
7 | :- strictlyincludedblock(L,U,T) ; not newconcept(T,Y) ; block(L,U,Y). % Y is not in the pnode
8 |
9 | % NB: this constraint may be written in many ways.
10 | % See debug/overlap-block-constraint for further investigations.
11 |
--------------------------------------------------------------------------------
/powergrasp/asp/extract-ccs.lp:
--------------------------------------------------------------------------------
1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 | % Extraction of graph data from edge information.
3 | %
4 | % initial version: 09/12/2014 J. Nicolas
5 | % revised version: 29/04/2014 L. Bourneuf
6 | %
7 | % Extract the connected components of the graph
8 | % Input:
9 | % - edge(X,Y): there exists an edge between X and Y in the graph.
10 | % Output (one model per connected component):
11 | % - cc(CC): CC is the minimal element of a connected component.
12 | % - membercc(X,Y): there exists a path from X to Y, X P == B): done
73 | % else: (<=> P is included in B)
74 | % create include_block(k,B,P)
75 | % for each block C in B:
76 | % assert(C is included in P OR C inter P = {0})
77 | % if C included in P:
78 | % don't create include_block(k,B,C)
79 | % create include_block(k,P,C)
80 | % else: (C inter P = {0})
81 | % do nothing else
82 | %
83 | % in a more logical way:
84 | % - powernode P is included in its parent block B
85 | hierarchy_add(KP,TP,k,T):- parent_block(KP,TP,T) ; not star(T).
86 | % - all blocks included in B that are in P are included in P instead of B
87 | change_hierarchy(KP,TP,k,T,L,U):-
88 | parent_block(KP,TP,T) ; % the parent block…
89 | include_block(KP,TP,L,U) ; L=lowerbound ; clique ; edgecover(1,N1).
45 | score(N):- N<=upperbound; N>=lowerbound; star(T) ; edgecover(3-T,N) ; biclique ; not quasibiclique.
46 | score(N):- N=N1*N2; N<=upperbound; N>=lowerbound; biclique ; not star(1); not star(2);
47 | edgecover(1,N1) ; edgecover(2,N2).
48 | % score(N):- N={covered_edge(_,_)} ; quasibiclique; N<=upperbound; N>=lowerbound.
49 |
50 | % Minimize score
51 | #minimize{(upperbound-S)@1,S:score(S)}.
52 |
53 | % Discard cases when no score is generated
54 | :- not score(_).
55 | #show score/1. % Warning, Python will use this and consider that the edge cover is the score.
56 |
--------------------------------------------------------------------------------
/powergrasp/asp/search-biclique.lp:
--------------------------------------------------------------------------------
1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 | % Find best biclique in input graph.
3 | % A biclique here can't be a star, so there is at least two elements in each set.
4 | %
5 | % initial version: 09/12/2014 J. Nicolas
6 | % revised version: 29/04/2015 L. Bourneuf
7 | % revised version: 08/07/2015 L. Bourneuf
8 | % revised version: 26/03/2018 J. Nicolas
9 | %
10 | % Constants:
11 | % - k: current step of treatment (first step is 1).
12 | % - lowerbound: the minimal cover to reach.
13 | % - upperbound: the maximal reachable cover.
14 | % - max_set_size: the maximal reachable size of a set.
15 | % Input:
16 | % - edge(X,Y): there exist a edge linking X and Y in cc (X=2.
31 | MS { newconcept(1,Y): inter(Hub,Y) } upperbound :- newconcept(2,Hub) ; min_size(MS).
32 |
33 |
34 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 | %%%%%% CONCEPT PROPERTIES %%%%%%%%
36 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
37 | % A star is a biclique with one powernode composed of only one node.
38 | star(2).
39 |
40 | biclique.
41 |
--------------------------------------------------------------------------------
/powergrasp/asp/search-triplet.lp:
--------------------------------------------------------------------------------
1 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 | % Find best triplet concept in input graph.
3 | %
4 | % Constants:
5 | % - k: current step of treatment (first step is 1).
6 | % - lowerbound: the minimal cover to reach.
7 | % - upperbound: the maximal reachable cover.
8 | % - max_set_size: the maximal reachable size of a set.
9 | % Input:
10 | % - edge(X,Y): there exist a edge linking X and Y in cc (X dict:
10 | return cli_parser(description).parse_args(args)
11 |
12 | def existant_file(filepath:str) -> str:
13 | """Argparse type, raising an error if given file does not exists"""
14 | if filepath is None: return None
15 | if not os.path.exists(filepath):
16 | raise argparse.ArgumentTypeError("file {} doesn't exists".format(filepath))
17 | return filepath
18 |
19 | def writable_file(filepath:str) -> str:
20 | """Argparse type, raising an error if given file is not writable.
21 | Will delete the file !
22 |
23 | """
24 | try:
25 | with open(filepath, 'a') as fd:
26 | pass
27 | os.remove(filepath)
28 | return filepath
29 | except (PermissionError, IOError):
30 | raise argparse.ArgumentTypeError("file {} is not writable.".format(filepath))
31 |
32 |
33 | def cli_parser(description:str) -> argparse.ArgumentParser:
34 | # main parser
35 | parser = argparse.ArgumentParser(description=description.strip())
36 |
37 |
38 | parser.add_argument('infile', type=existant_file, nargs='?',
39 | help="Name of the input file to compress")
40 | parser.add_argument('outfile', nargs='?',
41 | type=writable_file, default='out.bbl',
42 | help="Name of the bubble file to produce")
43 | parser.add_argument('--config', '-c', action='store_true', dest='show_config',
44 | help="Print detected configuration, then quit")
45 | parser.add_argument('--recipe', '-r', default=None, type=str,
46 | help="Use content of given filename as a recipe")
47 |
48 | return parser
49 |
--------------------------------------------------------------------------------
/powergrasp/constants.py:
--------------------------------------------------------------------------------
1 | """Some constants to tune the compression behavior.
2 |
3 | Final user may want to override it by writing in 'powergrasp.cfg' file in
4 | either JSON or INI format.
5 |
6 | """
7 | import os
8 | import ast
9 | import json
10 | import itertools
11 | import configparser
12 | import multiprocessing
13 | from collections import ChainMap
14 | from . import utils
15 |
16 | # Default values
17 | constants = {
18 | # Run some integrity tests at some points. May slow the compression a lot.
19 | 'TEST_INTEGRITY': True,
20 |
21 | # Show main steps of the compression.
22 | 'SHOW_STORY': True,
23 |
24 | # Show motifs transformation into powergraph.
25 | 'SHOW_MOTIF_HANDLING': False,
26 |
27 | # Timers.
28 | 'TIMERS': True,
29 |
30 | # Statistics file to write during compression.
31 | 'STATISTIC_FILE': None,
32 |
33 | # Connected components statistics file to write at the end of each cc.
34 | 'CC_STATISTIC_FILE': None,
35 |
36 | # Compute statistics/metrics over all connected components.
37 | 'GLOBAL_STATISTICS': True,
38 |
39 | # Include some statistics in output bubble.
40 | 'BUBBLE_WITH_STATISTICS': True,
41 |
42 | # Generate and save a bubble representation of the graph at each step.
43 | 'BUBBLE_FOR_EACH_STEP': False,
44 |
45 | # Keep simple edges in output.
46 | 'BUBBLE_WITH_SIMPLE_EDGES': True,
47 |
48 | # Prefix to add to all (power)nodes names in output.
49 | 'OUTPUT_NODE_PREFIX': '',
50 |
51 | # Show full trace of the compression. Useful for debugging.
52 | 'SHOW_DEBUG': False,
53 |
54 | # Recover covered edges from ASP. If falsy, will ask motif searcher to compute the edges, which may be quicker.
55 | 'COVERED_EDGES_FROM_ASP': False,
56 |
57 | # Nodes and sets are optional in the output bubble.
58 | 'BUBBLE_WITH_NODES': True,
59 | 'BUBBLE_WITH_SETS': True,
60 |
61 | # Put each connected component in a dedicated powernode.
62 | 'BUBBLE_EMBEDS_CC': False,
63 |
64 | # Edges in bubble are associated to a factor
65 | 'BUBBLE_POWEREDGE_FACTOR': '1.0',
66 | 'BUBBLE_EDGE_FACTOR': '1.0',
67 |
68 | # When possible, delete the quotes around identifiers in the bubble. May lead to node name collision.
69 | 'BUBBLE_SIMPLIFY_QUOTES': True,
70 |
71 | # Change them according to config file
72 | 'CONFIG_FILE': 'powergrasp.cfg',
73 |
74 | # Search for multiple motif in a single search. Accelerate the solving for graph with lots of equivalent motifs.
75 | 'MULTISHOT_MOTIF_SEARCH': True,
76 |
77 | # Optimization on biclique lowerbound computation. Can be costly. Deactivate with 2. With value at n, up to n neighbors are considered.
78 | 'BICLIQUE_LOWERBOUND_MAXNEI': 2,
79 |
80 | # Arbitrary parameters to give to clingo (note that some, like multithreading or optmode, may already be set by other options).
81 | 'CLINGO_OPTIONS': {},
82 |
83 | # Number of CPU available to clingo (or a string like '2,join' or '48,compete'), or 0 for autodetect number of CPU.
84 | 'CLINGO_MULTITHREADING': 1,
85 |
86 | # Do not search for cliques
87 | 'ONLY_BICLIQUES': False,
88 |
89 | # Two different motifs for stars and bicliques, so the work of biclique is lighter.
90 | 'USE_STAR_MOTIF': True,
91 |
92 | # Use quasibicliques instead of cliques.
93 | 'QUASIBICLIQUES': False,
94 |
95 | # Search for triplets.
96 | 'TRIPLETS': False,
97 |
98 | # Use only triplet motifs.
99 | 'ONLY_TRIPLETS': False,
100 |
101 | # Mu is the value for constraints for quasibicliques.
102 | 'QUASIBICLIQUE_MU': 2,
103 |
104 | # When a choice is given, prefer memory over CPU.
105 | 'OPTIMIZE_FOR_MEMORY': False,
106 |
107 | # If set, will keep nodes connected to nothing. Else, will discard them.
108 | 'KEEP_SINGLE_NODES': True,
109 |
110 | # Ignore edges dynamically determined as impossible to compress.
111 | 'GRAPH_FILTERING': True,
112 |
113 | # Perform the search for motifs in different process instead of sequentially.
114 | 'PARALLEL_MOTIF_SEARCH': False,
115 |
116 | # Number of processes to work on connected components. Zero to get one per cc. One to deactivate.
117 | 'PARALLEL_CC_COMPRESSION': 1,
118 |
119 | # Define in which order the motifs are tested.
120 | 'MOTIF_TYPE_ORDER': 'star,clique,non-star-biclique,quasi-biclique,biclique,triplet',
121 |
122 | # TODO Detect and postpone compression of terminal tree subgraphs
123 | # 'TERMINAL_TREES_POSTPONING': True,
124 | # TODO Detect, delete and restore bridges
125 | # 'BRIDGES_CUT': True,
126 | # TODO Detect and if available run specialized compression routine for: trees, triangle-free graphs, cactii.
127 | # Will not do anything on a graph that does not belong to those classes.
128 | # 'SPECIAL_CASES_DETECTION': True,
129 | }
130 |
131 | _derived_constants = {
132 | 'KEEP_NX_GRAPH': lambda c: c['GRAPH_FILTERING'],
133 | }
134 |
135 |
136 | def make_key(key:str) -> str:
137 | """Return the well formed key for constants dictionary"""
138 | return key.upper().replace(' ', '_')
139 |
140 | def make_value_from_ini(found_key:str, real_key:str, section,
141 | config:configparser.ConfigParser):
142 | """Return the int, str, dict, float or tuple equivalent to value
143 | found for given section and key.
144 |
145 | """
146 | assert real_key in constants, f"{real_key} key found in config file is unexpected"
147 | assert section in config
148 | assert found_key in config[section], (config[section], found_key)
149 | default = constants[real_key]
150 | if isinstance(default, bool):
151 | return config.getboolean(section, found_key)
152 | elif isinstance(default, type(None)):
153 | try:
154 | return ast.literal_eval(config[section][found_key])
155 | except ValueError as err: # it's not a python literal, so it's a string
156 | return config[section][found_key]
157 | elif isinstance(default, (int, float, tuple, list, dict, set, frozenset)):
158 | value = config[section][found_key] or '""' # cast empty value to empty string
159 | try:
160 | return type(default)(ast.literal_eval(value))
161 | except ValueError as err: # it's not a python literal, so it's a string
162 | return config[section][found_key]
163 | elif isinstance(default, str):
164 | return config[section][found_key]
165 | else:
166 | raise ValueError("Non-handled option type {} for field {}"
167 | "".format(type(default), real_key))
168 |
169 | def open_config_file(fname:str) -> dict:
170 | """Try reading file in INI, and if it do not works, try JSON"""
171 | try:
172 | # read file, take all available sections
173 | config = configparser.ConfigParser()
174 | config.read(fname)
175 | if config.sections():
176 | return {
177 | key: make_value_from_ini(found_key, key, section, config)
178 | for section in config.sections()
179 | for found_key, key in map(lambda k:(k, make_key(k)), config[section])
180 | }
181 | else:
182 | print('ERROR input config do not have any section.')
183 | except configparser.MissingSectionHeaderError as err:
184 | ini_err = err.args[0]
185 | try:
186 | with open(fname) as fd:
187 | return {make_key(k): v for k, v in json.load(fd).items()}
188 | except json.decoder.JSONDecodeError as err:
189 | json_err = err.args[0]
190 | print("ERROR input config file is not a valid json, nor a valid ini."
191 | "\nINI error: {}\nJSON error: {}".format(ini_err, json_err))
192 |
193 |
194 | try:
195 | cfg = open_config_file(constants['CONFIG_FILE'])
196 | except FileNotFoundError:
197 | cfg = None
198 | if cfg:
199 | for field, value in cfg.items():
200 | if field in constants:
201 | constants[field] = value
202 | elif constants['SHOW_STORY'] or constants['SHOW_DEBUG']:
203 | raise ValueError("field '{}' is not a valid field for configuration."
204 | "".format(field))
205 | if constants['SHOW_STORY'] or constants['SHOW_DEBUG']:
206 | print('INFO: config file {} loaded.'.format(constants['CONFIG_FILE']))
207 | elif constants['SHOW_STORY'] or constants['SHOW_DEBUG']:
208 | print("INFO no config file")
209 |
210 |
211 | def _convert_parallel_mode_option(value:str or int) -> str:
212 | """Return the option to give to clingo to handle given number of CPU.
213 |
214 | >>> _convert_parallel_mode_option(0)
215 | ' --parallel-mode=4'
216 | >>> _convert_parallel_mode_option(1)
217 | ''
218 | >>> _convert_parallel_mode_option('2')
219 | ' --parallel-mode=2'
220 | >>> _convert_parallel_mode_option(' 2')
221 | ' --parallel-mode=2'
222 | >>> _convert_parallel_mode_option('0,split')
223 | ' --parallel-mode=4,split'
224 | >>> _convert_parallel_mode_option("'0,split'")
225 | ' --parallel-mode=4,split'
226 | >>> _convert_parallel_mode_option('"0,split"')
227 | ' --parallel-mode=4,split'
228 |
229 | """
230 | if isinstance(value, str):
231 | value = value.strip(' "\'\t\n')
232 | if value.isnumeric():
233 | return str(_convert_parallel_mode_option(int(value)))
234 | elif value and value.startswith('0'):
235 | if value[1:] not in {',compete', ',split'}:
236 | raise ValueError("Multithreading option value is not valid: {}"
237 | "".format(value))
238 | nb_cpu = str(multiprocessing.cpu_count())
239 | return _convert_parallel_mode_option(nb_cpu + value.lstrip('0'))
240 | elif value:
241 | return ' --parallel-mode=' + value
242 | elif value == 0:
243 | return _convert_parallel_mode_option(multiprocessing.cpu_count())
244 | elif value > 1:
245 | return ' --parallel-mode=' + str(value)
246 | return ''
247 |
248 |
249 | def _convert_clingo_options(value:dict or str) -> dict:
250 | """Return a map from motif name to clingo options."""
251 | # NB: can't access MotifSearchers subclass at this point, since constants
252 | # are computed at import time. We therefore can't verify the keys of the dict.
253 | # convert string to dict
254 | if isinstance(value, str):
255 | value = {None: value}
256 | # validate and return the dict
257 | value = {None if motif is None else motif.lower().replace(' ', '-').replace('_', '-'): options
258 | for motif, options in value.items()}
259 | value.setdefault(None, '')
260 | return value
261 |
262 |
263 | def _convert_motif_type_order(value:str) -> callable:
264 | """Return a function returning sorted searchers according to the given value.
265 |
266 | For instance, if input is 'worst-upperbound-first', the function returned
267 | will sort input searchers according to their upperbound, smallest first.
268 |
269 | """
270 | error = lambda m: ValueError("Invalid value for option MOTIF TYPE ORDER: {} ({})".format(value, m))
271 | value = value.lower().replace(' ', '-').replace('_', '-')
272 | if ',' in value:
273 | values = (val for val in map(str.strip, value.split(',')) if val)
274 | order = {name: idx for idx, name in enumerate(values)}
275 | def ordered(searchers, *, order=order) -> iter:
276 | """Yield given searchers according to their priority"""
277 | return sorted(searchers, key=lambda s: order[s.name])
278 | elif '-' in value:
279 | which, bound, where = value = value.split('-')
280 | greatests = {'greatest', 'biggest'}
281 | smallests = {'worst', 'smallest'}
282 | if len(value) != 3:
283 | raise error("{} groups instead of 3, like 'greatest-lowerbound-first'".format(len(value)))
284 | if which not in greatests | smallests:
285 | raise error("the which value must be something like worst or greatest".format(len(value)))
286 | reverse = (where == 'first') != (which in smallests)
287 | if bound == 'lowerbound':
288 | key = lambda s: s.lowerbound
289 | elif bound == 'upperbound':
290 | key = lambda s: s.upperbound
291 | else:
292 | raise error("the bound value must be upperbound or lowerbound".format(len(value)))
293 | def ordered(searchers, *, reverse=reverse, key=key) -> iter:
294 | return sorted(searchers, reverse=reverse, key=key)
295 | elif value == 'random': # funny, but not really useful
296 | def ordered(searchers) -> iter:
297 | import random
298 | searchers = list(searchers)
299 | random.shuffle(searchers)
300 | return searchers
301 | else:
302 | raise error("not a list of elements or groups, like 'star,clique,non-star-biclique,biclique' or 'greatest-lowerbound-first'")
303 | return ordered
304 |
305 | def _convert_erased_file(fname:str) -> str:
306 | """A type to give to convertion, where value is a file to erase."""
307 | if fname is not None:
308 | with open(fname, 'w') as fd:
309 | pass # just erase it
310 | return fname # conserve it untouched
311 |
312 |
313 | # Apply the value convertion, if any.
314 | _CONVERTIONS = {
315 | 'CLINGO_MULTITHREADING': _convert_parallel_mode_option,
316 | 'BICLIQUE_LOWERBOUND_MAXNEI': int,
317 | 'CLINGO_OPTIONS': _convert_clingo_options,
318 | 'MOTIF_TYPE_ORDER': _convert_motif_type_order,
319 | 'CC_STATISTIC_FILE': _convert_erased_file,
320 | 'COMPRESSION_STATISTIC_FILE': _convert_erased_file,
321 | 'STATISTIC_FILE': _convert_erased_file,
322 | }
323 | constants = {f: _CONVERTIONS.get(f, lambda x:x)(v) for f, v in constants.items()}
324 | # add the derived ones
325 | constants.update({
326 | field: make_value(constants)
327 | for field, make_value in _derived_constants.items()
328 | })
329 |
330 | # verifications about clingo options
331 | if constants['CLINGO_MULTITHREADING']:
332 | for motif, options in constants['CLINGO_OPTIONS'].items():
333 | if '--parallel-mode=' in options:
334 | raise ValueError("Invalid option value: --parallel-mode given by both"
335 | " CLINGO_OPTIONS ({}) and CLINGO_MULTITHREADING ({})."
336 | "".format(options, constants['CLINGO_MULTITHREADING']))
337 |
338 |
339 | # Put them in global access
340 | globals().update(constants)
341 |
342 |
343 | def print_config():
344 | """Print configuration in stdout"""
345 | # name_width = max(len(name) for name, _ in uplets)
346 | # value_width = max(len(repr(value)) for _, value in uplets)
347 | uplets = sorted(tuple(OPTIONS_CATEGORIES.items()))
348 | name_width = max(len(name) for _, names in uplets for name in names)
349 | for category, options in uplets:
350 | print('[{}]'.format(category))
351 | for option in options:
352 | value = repr(constants[option]).strip('"\'')
353 | option = option.lower().replace('_', ' ').ljust(name_width)
354 | print('{} = {}'.format(option, value))
355 | print()
356 |
357 |
358 |
359 | OPTIONS_CATEGORIES = utils.reverse_dict({
360 | 'TEST_INTEGRITY': 'debug',
361 | 'SHOW_STORY': 'debug',
362 | 'SHOW_MOTIF_HANDLING': 'debug',
363 | 'TIMERS': 'statistics',
364 | 'STATISTIC_FILE': 'statistics',
365 | 'CC_STATISTIC_FILE': 'statistics',
366 | 'GLOBAL_STATISTICS': 'statistics',
367 | 'BUBBLE_WITH_STATISTICS': 'statistics',
368 | 'BUBBLE_FOR_EACH_STEP': 'debug',
369 | 'BUBBLE_WITH_SIMPLE_EDGES': 'output',
370 | 'OUTPUT_NODE_PREFIX': 'output',
371 | 'SHOW_DEBUG': 'debug',
372 | 'COVERED_EDGES_FROM_ASP': 'optimization',
373 | 'BUBBLE_WITH_NODES': 'output',
374 | 'BUBBLE_WITH_SETS': 'output',
375 | 'BUBBLE_POWEREDGE_FACTOR': 'output',
376 | 'BUBBLE_EDGE_FACTOR': 'output',
377 | 'BUBBLE_EMBEDS_CC': 'output',
378 | 'BUBBLE_SIMPLIFY_QUOTES': 'input',
379 | 'CONFIG_FILE': 'input',
380 | 'MULTISHOT_MOTIF_SEARCH': 'optimization',
381 | 'BICLIQUE_LOWERBOUND_MAXNEI': 'optimization',
382 | 'CLINGO_OPTIONS': 'clingo',
383 | 'CLINGO_MULTITHREADING': 'clingo',
384 | 'USE_STAR_MOTIF': 'optimization',
385 | 'ONLY_BICLIQUES': 'output',
386 | 'QUASIBICLIQUES': 'output',
387 | 'QUASIBICLIQUE_MU': 'output',
388 | 'TRIPLETS': 'output',
389 | 'ONLY_TRIPLETS': 'output',
390 | 'OPTIMIZE_FOR_MEMORY': 'optimization',
391 | 'KEEP_SINGLE_NODES': 'output',
392 | 'KEEP_NX_GRAPH': 'optimization',
393 | 'GRAPH_FILTERING': 'optimization',
394 | 'PARALLEL_MOTIF_SEARCH': 'optimization',
395 | 'PARALLEL_CC_COMPRESSION': 'optimization',
396 | 'MOTIF_TYPE_ORDER': 'optimization',
397 |
398 | # 'TERMINAL_TREES_POSTPONING': 'optimization',
399 | # 'BRIDGES_CUT': 'optimization',
400 | # 'SPECIAL_CASES_DETECTION': 'optimization',
401 | })
402 | categorized_options = frozenset(itertools.chain.from_iterable(OPTIONS_CATEGORIES.values()))
403 | assert all(option in constants for option in categorized_options)
404 | for constant in constants:
405 | if constant not in categorized_options:
406 | raise ValueError("Option {} has no category".format(constant))
407 |
--------------------------------------------------------------------------------
/powergrasp/edge_filtering.py:
--------------------------------------------------------------------------------
1 | """Routines implementing edge filtering on input graphs,
2 | knowing the bounds on the size of the motif to search in it.
3 |
4 | All routines follow the following interface:
5 | - arguments are the graph and the bounds
6 | - yield valid edges as pairs of nodes
7 |
8 | """
9 |
10 | import networkx as nx
11 |
12 |
13 | def for_biclique(graph:nx.Graph, lowerbound:int, upperbound:int) -> [(str, str)]:
14 | """
15 | Remove any edge that the product of its nodes degrees is inferior to lowerbound.
16 | """
17 | def ok_to_go(edge:tuple) -> bool:
18 | one, two = edge
19 | degone, degtwo = map(graph.degree, edge)
20 | return degone * degtwo >= lowerbound
21 | for edge in graph.edges:
22 | if ok_to_go(edge):
23 | yield edge
24 |
25 |
26 | def for_star(graph:nx.Graph, lowerbound:int, upperbound:int) -> [(str, str)]:
27 | """
28 | """
29 | def ok_to_go(edge:tuple) -> bool:
30 | one, two = edge
31 | degone, degtwo = map(graph.degree, edge)
32 | return not all((
33 | degone < lowerbound,
34 | degtwo < lowerbound,
35 | ))
36 | for edge in graph.edges:
37 | if ok_to_go(edge):
38 | yield edge
39 |
40 |
41 | def for_clique(graph:nx.Graph, lowerbound:int, upperbound:int) -> [(str, str)]:
42 | """
43 | Remove an edge when one participating node has a clustering coefficient equal to 0.
44 | """
45 | clusterings = {} # node -> clustering coefficient
46 | def clustering_of(node:str) -> float:
47 | if node in clusterings:
48 | return clusterings[node]
49 | else:
50 | return clusterings.setdefault(node, nx.clustering(graph, node))
51 | for edge in graph.edges:
52 | if any(clustering_of(node) > 0. for node in edge):
53 | yield edge
54 |
--------------------------------------------------------------------------------
/powergrasp/metrics.py:
--------------------------------------------------------------------------------
1 | def conversion_rate(initial_edge, final_edge, poweredge, powernode):
2 | """Compute conversion rate"""
3 | try:
4 | edge = initial_edge
5 | poweredge = final_edge + poweredge
6 | return (edge - poweredge) / powernode
7 | except ZeroDivisionError:
8 | return 1.
9 |
10 | def edge_reduction(initial_edge, final_edge, poweredge, _):
11 | """Compute edge reduction (percentage)"""
12 | try:
13 | edge = initial_edge
14 | poweredge = final_edge + poweredge
15 | return ((edge - poweredge) / edge) * 100
16 | except ZeroDivisionError:
17 | return 100.
18 |
19 | def compression_ratio(initial_edge, final_edge, poweredge, _):
20 | """Compute data compression ratio"""
21 | try:
22 | return initial_edge / (final_edge + poweredge)
23 | except ZeroDivisionError:
24 | return 1.
25 |
26 |
27 | def compression_metrics(initial_edge, final_edge, poweredge, powernode) -> [(str, float)]:
28 | """Yield pairs (name, value) describing measures and their value.
29 |
30 | initial_edge -- number of edge in initial graph
31 | final_edge -- number of (non power) edge in final graph
32 | poweredge -- number of poweredge in final graph
33 | powernode -- number of powernode in final graph
34 |
35 | """
36 | payload = initial_edge, final_edge, poweredge, powernode
37 | for func in (conversion_rate, edge_reduction, compression_ratio):
38 | yield func.__name__.replace('_', ' '), func(*payload)
39 |
--------------------------------------------------------------------------------
/powergrasp/motif.py:
--------------------------------------------------------------------------------
1 |
2 | from powergrasp.constants import TEST_INTEGRITY, SHOW_DEBUG, COVERED_EDGES_FROM_ASP
3 |
4 |
5 | class Motif:
6 | """A motif found by a motif searcher.
7 |
8 | The motif is a proxy to the ASP model, with a large set of properties
9 | that Graph and Searchers will use to determine the Motif effect
10 | on the Graph.
11 |
12 | """
13 | def __init__(self, typename:str, atoms:dict, maximal:bool, step:int, searcher:object):
14 | self.typename, self.atoms, self.ismaximal, self.step = str(typename), dict(atoms), bool(maximal), int(step)
15 | self.step_modifier = 0
16 | self._searcher = searcher
17 | if SHOW_DEBUG:
18 | from pprint import pprint
19 | print('ATOMS FOR MOTIF {}:'.format(typename))
20 | pprint(self.atoms)
21 | self._score = None
22 |
23 |
24 | @property
25 | def name(self) -> str: return self.typename
26 | @property
27 | def uid(self) -> int: return self.step + self.step_modifier
28 | @property
29 | def type(self): return self._searcher
30 |
31 |
32 | @property
33 | def score(self) -> int:
34 | if self._score is None:
35 | self._score = self._compute_score()
36 | return self._score
37 | @property
38 | def edge_cover(self) -> int:
39 | return self.score
40 |
41 | def _compute_score(self) -> int:
42 | """Compute and return the score of this motif"""
43 | score_atoms = tuple(self.atoms.get('score', ()))
44 | if not score_atoms:
45 | raise ASPModelError("No atom `score` found")
46 | if len(score_atoms) > 1:
47 | raise ASPModelError("Multiple atom `score` found: {}".format(len(score_atoms)))
48 | score_atom_args = score_atoms[0]
49 | if len(score_atom_args) != 1:
50 | raise ASPModelError("Atom `score` got a non-valid number of arguments: {}".format(score_atom_args))
51 | score = score_atom_args[0]
52 | if not isinstance(score, int):
53 | raise ASPModelError("Atom `score` got a non-int argument of type {}: {}".format(type(score), score))
54 | assert isinstance(score, int)
55 | return score
56 |
57 | def increase_step(self, step_diff:int):
58 | """Increase internally the step of self"""
59 | self.step_modifier += int(step_diff)
60 |
61 | @property
62 | def new_nodes(self) -> iter:
63 | yield from (node for numset, node in self.atoms.get('new_powernode', ()))
64 | yield from self.stars
65 | @property
66 | def new_powernodes(self) -> iter:
67 | yield from self.atoms.get('new_powernode', ())
68 | @property
69 | def powernodes(self) -> iter:
70 | _ = lambda s: s + (self.step_modifier if s == self.step else 0)
71 | yield from (
72 | (_(step), numset) for step, numset in self.atoms.get('powernode', ())
73 | )
74 | @property
75 | def stars(self) -> iter:
76 | yield from (args[0] for args in self.atoms.get('star', ()))
77 | @property
78 | def new_poweredge(self) -> iter:
79 | _ = lambda s: s + (self.step_modifier if s == self.step else 0)
80 | for args in self.atoms.get('poweredge', ()):
81 | if len(args) == 4:
82 | step_a, set_a, step_b, set_b = args
83 | yield (_(step_a), set_a), (_(step_b), set_b)
84 | elif len(args) == 3:
85 | step_a, set_a, node = args
86 | yield (_(step_a), set_a), node
87 | @property
88 | def hierachy_added(self) -> iter:
89 | _ = lambda s: s + (self.step_modifier if s == self.step else 0)
90 | yield from (
91 | (_(step1), numset, _(step2), numset2)
92 | for step1, numset, step2, numset2
93 | in self.atoms.get('hierarchy_add', ()))
94 | @property
95 | def hierachy_removed(self) -> iter:
96 | _ = lambda s: s + (self.step_modifier if s == self.step else 0)
97 | yield from (
98 | (_(step1), numset, _(step2), numset2)
99 | for step1, numset, step2, numset2
100 | in self.atoms.get('hierarchy_remove', ()))
101 |
102 |
103 | def edges_covered(self, sets:[frozenset]=None) -> iter:
104 | """If sets are given, the computation of edges covered by the motif
105 | is delegated to the searcher object.
106 |
107 | This allow to avoid a costly output from ASP.
108 |
109 | """
110 | if sets: # give that to the searcher
111 | yield from frozenset(self.type.covered_edges(tuple(sets)))
112 | else: # ASP provide us with the data
113 | yield from map(frozenset, self.atoms.get('covered_edge', ()))
114 |
--------------------------------------------------------------------------------
/powergrasp/motif_batch.py:
--------------------------------------------------------------------------------
1 | """Batch of concurrent motif on a graph.
2 |
3 | Offers an API useful for compression routine (access to score),
4 | internal integrity test (equivalency of all contained motifs),
5 | and non-overlapping subset selection.
6 |
7 | """
8 |
9 | from itertools import islice
10 | from powergrasp.motif import Motif
11 | from powergrasp.constants import TEST_INTEGRITY
12 |
13 |
14 | class MotifBatch:
15 | """Container of Motif"""
16 |
17 | def __init__(self, motifs:iter):
18 | self.motifs = tuple(motifs)
19 | self.score = self.motifs[0].score if self.motifs else None
20 | if TEST_INTEGRITY and self.score:
21 | scores = set(m.score for m in self.motifs)
22 | assert len(scores) == 1, "Multiple different scores in motifs: " + str(scores)
23 |
24 | def __bool__(self) -> bool:
25 | return bool(self.motifs)
26 |
27 | @property
28 | def empty(self) -> bool:
29 | return not self
30 | @property
31 | def name(self) -> str: return self.motifs[0].typename
32 | @property
33 | def ismaximal(self) -> bool: return self.motifs[0].ismaximal
34 | @property
35 | def count(self) -> int: return len(self.motifs)
36 |
37 |
38 | def non_overlapping_subset(self) -> iter:
39 | """Yield motifs contained in self that are non overlapping between them.
40 |
41 | These motifs can all be compressed at the same time because they
42 | do not share any node.
43 |
44 | """
45 | if self.empty: return # nothing to yield
46 | motifs = iter(self.motifs)
47 | first = next(motifs)
48 | nodes = frozenset(first.new_nodes)
49 | yield first
50 | for motif in motifs:
51 | motif_nodes = frozenset(motif.new_nodes)
52 | if motif_nodes.isdisjoint(nodes):
53 | nodes |= motif_nodes
54 | yield motif
55 |
--------------------------------------------------------------------------------
/powergrasp/recipe.py:
--------------------------------------------------------------------------------
1 | """Definition of a Recipe.
2 |
3 | """
4 |
5 | import itertools
6 |
7 |
8 | class RecipeError(ValueError):
9 | pass
10 |
11 |
12 | class Recipe:
13 | RecipeError = RecipeError
14 |
15 | """Basically, a list of RecipeEntry"""
16 |
17 | def __init__(self, lines:iter):
18 | self._lines = tuple(RecipeEntry(*line) for line in lines)
19 |
20 | def __iter__(self):
21 | yield from self._lines
22 |
23 | def __len__(self):
24 | return len(self._lines)
25 |
26 | def __getitem__(self, idx):
27 | return self._lines[idx]
28 |
29 | def __str__(self):
30 | nb_entry = len(self._lines)
31 | return f" 1 else ''}>"
32 |
33 | def works_on(self, graph) -> bool:
34 | "True if the graph has a node referenced in self."
35 | for entry in self:
36 | if f'"{entry.one_node}"' in graph.nodes:
37 | return True
38 | return False
39 |
40 |
41 | @staticmethod
42 | def from_(obj:object) -> object:
43 | "Build a Recipe instance from a filename, its raw data, or an iterable of lines"
44 | if isinstance(obj, Recipe):
45 | return obj
46 | if isinstance(obj, str):
47 | if '\t' in obj:
48 | return Recipe.from_string(obj)
49 | # must be a file
50 | return Recipe.from_file(obj)
51 | return Recipe.from_lines(obj)
52 |
53 | @staticmethod
54 | def from_string(string:str) -> object:
55 | return Recipe.from_lines(string.splitlines(False))
56 |
57 | @staticmethod
58 | def from_lines(lines:[str]) -> object:
59 | """Build a Recipe instance from given file"""
60 | def motif_from_line(line:str) -> (str, [str], [str]) or None:
61 | if not line or line.count('\t') != 2:
62 | return None # not a Motif ; it needs to be looked by itself
63 | typemotif, seta, setb = line.split('\t')
64 | return set(typemotif.split(',')), set(seta.split(' ')), set(setb.split(' '))
65 |
66 | motifs = tuple(motif for motif in
67 | map(motif_from_line, map(str.strip, lines)) if motif)
68 | return Recipe(
69 | (typemotifs, seta, setb) for typemotifs, seta, setb in motifs
70 | )
71 |
72 | @staticmethod
73 | def from_file(fname:str) -> object:
74 | """Build a Recipe instance from given file"""
75 | if fname is None: return Recipe(())
76 | with open(fname) as fd:
77 | return Recipe.from_lines(fd)
78 |
79 |
80 | class RecipeEntry:
81 | RecipeError = RecipeError
82 | def __init__(self, typenames:set, seta:set, setb:set):
83 | self.typenames = frozenset(typenames)
84 | self.seta = frozenset(seta)
85 | self.setb = frozenset(setb)
86 | if self.isextendable and self.isbreakable:
87 | raise NotImplementedError("Recipe is both extendable (open/primer option)"
88 | " and breakable.")
89 |
90 | @property
91 | def isextendable(self) -> bool:
92 | return bool({'open', 'primer'} & self.typenames)
93 | @property
94 | def isbreakable(self) -> bool:
95 | return 'breakable' in self.typenames
96 | @property
97 | def islast(self) -> bool:
98 | return 'last' in self.typenames
99 | @property
100 | def isrequired(self) -> bool:
101 | return 'optional' not in self.typenames
102 | @property
103 | def one_node(self) -> str:
104 | """Return one node found in sets"""
105 | return next(iter(self.seta), None) or next(iter(self.setb))
106 | @property
107 | def is_clique(self) -> str:
108 | """True if describes a clique"""
109 | return 'clique' in self.typenames
110 | @property
111 | def is_star(self) -> bool:
112 | """True if describes a star"""
113 | return 'star' in self.typenames or ('biclique' in self.typenames and any(len(s) == 1 for s in self.sets))
114 |
115 | @property
116 | def covered_edges(self) -> {frozenset((str, str))}:
117 | "Return edges covered by the recipe"
118 | quoted = lambda a: '"' + a + '"'
119 | return frozenset(map(frozenset, itertools.product(map(quoted, self.seta), map(quoted, self.setb))))
120 |
121 | def __iter__(self):
122 | return iter((self.typenames, self.seta, self.setb))
123 |
124 | def __str__(self):
125 | return f""
126 |
127 | def as_asp(self, is_star:bool):
128 | """Return ASP atoms translating the given recipe line"""
129 | typenames, seta, setb = self
130 | if min(setb) < min(seta): # minimal element must be in seta
131 | setb, seta = seta, setb
132 | if is_star and len(seta) == 1: # if it's a star, then single element must be in setb
133 | setb, seta = seta, setb
134 | return '\n'.join((
135 | ' '.join(f'newconcept(1,"{element}").' for element in seta),
136 | '' if self.is_clique else ' '.join(f'newconcept(2,"{element}").' for element in setb),
137 | # '|'.join(typenames) + '.'
138 | ))
139 |
140 | @property
141 | def sets(self) -> [set]:
142 | """Return the sets composing the motif described by the entry"""
143 | return frozenset([self.seta]) if self.is_clique else frozenset([self.seta, self.setb])
144 |
145 | def accept(self, searcher):
146 | if searcher.name == 'star':
147 | return self.is_star
148 | return searcher.motif_name in self.typenames
149 |
--------------------------------------------------------------------------------
/powergrasp/routines.py:
--------------------------------------------------------------------------------
1 | """Definition of high level functions operating the compression.
2 |
3 | """
4 |
5 | import csv
6 | from .searchers import CliqueSearcher, BicliqueSearcher, StarSearcher, NonStarBicliqueSearcher, QuasiBicliqueSearcher, TripletSearcher
7 | from .utils import get_time
8 | from .graph import Graph
9 | from .recipe import Recipe
10 | from . import constants as const
11 | from .constants import MULTISHOT_MOTIF_SEARCH, BUBBLE_FOR_EACH_STEP, TIMERS, SHOW_STORY, SHOW_DEBUG, STATISTIC_FILE, USE_STAR_MOTIF, ONLY_BICLIQUES, QUASIBICLIQUES, TRIPLETS, ONLY_TRIPLETS
12 | from .motif_batch import MotifBatch
13 | from multiprocessing.dummy import Pool as ThreadPool # dummy here to use the threading backend, not process
14 | from multiprocessing import Pool as ProcessPool
15 |
16 |
17 | if TIMERS and STATISTIC_FILE:
18 | # function to fill the file during compression
19 | def save_stats(*args):
20 | """Fill statistic file with given data"""
21 | with open(STATISTIC_FILE, 'a') as fd:
22 | fd.write(','.join(map(str, args)) + '\n')
23 |
24 |
25 | def search_best_motifs_sequentially(searchers, step, recipe) -> MotifBatch:
26 | """Return a MotifBatch instance containing the best motifs
27 | found by given searchers."""
28 | score_to_beat = 0
29 | best_motifs, best_motifs_score = None, 0
30 | ordered_searchers = const.MOTIF_TYPE_ORDER(searchers)
31 | for searcher in ordered_searchers:
32 | if recipe and not recipe.accept(searcher):
33 | print(f'Recipe {recipe} cannot be fulfilled with searcher {searcher.name}')
34 | continue
35 | motifs = MotifBatch(searcher.search(step, score_to_beat, recipe=recipe))
36 | if motifs:
37 | searcher.on_new_found_motif(motifs)
38 | if motifs.score > best_motifs_score:
39 | best_motifs, best_motifs_score = motifs, motifs.score
40 | score_to_beat = best_motifs_score
41 | return best_motifs
42 |
43 | def search_best_motifs_in_parallel(searchers, step, recipe) -> MotifBatch:
44 | """Return a MotifBatch instance containing the best motifs
45 | found by given searchers."""
46 | with ThreadPool(len(searchers)) as pool:
47 | founds = pool.starmap(search_best_motifs_sequentially, (([s], step, recipe) for s in searchers))
48 | return max(founds, key=lambda f: 0 if f is None else f.score)
49 |
50 | if const.PARALLEL_MOTIF_SEARCH:
51 | search_best_motifs = search_best_motifs_in_parallel
52 | else:
53 | search_best_motifs = search_best_motifs_sequentially
54 |
55 |
56 | def create_searchers(graph):
57 | """Return a list of searchers on given graph, depending on the global parameters"""
58 | if ONLY_TRIPLETS:
59 | return [TripletSearcher(graph)]
60 | searchers = [] if ONLY_BICLIQUES else [CliqueSearcher(graph)]
61 | if USE_STAR_MOTIF and QUASIBICLIQUES:
62 | # searchers.extend([QuasiBicliqueSearcher(graph), BicliqueSearcher(graph), StarSearcher(graph)])
63 | searchers.extend([QuasiBicliqueSearcher(graph), StarSearcher(graph)])
64 | elif USE_STAR_MOTIF:
65 | searchers.extend([NonStarBicliqueSearcher(graph), StarSearcher(graph)])
66 | elif QUASIBICLIQUES:
67 | searchers.extend([QuasiBicliqueSearcher(graph), BicliqueSearcher(graph)])
68 | else:
69 | searchers.append(BicliqueSearcher(graph))
70 | if TRIPLETS:
71 | searchers.append(TripletSearcher(graph))
72 | return searchers
73 |
74 |
75 | def compress(graph:Graph, *, cc_idx=None, recipe:[Recipe]=None) -> [str]:
76 | """Yield bubble lines found in graph"""
77 | if TIMERS:
78 | timer_start = get_time()
79 | timer_last = timer_start
80 | searchers = create_searchers(graph)
81 | if SHOW_STORY:
82 | print('INFO searchers: ' + ', '.join(s.name for s in searchers))
83 | print(f"INFO recipe: {recipe}")
84 | recipe_lines, recipe_line, recipe_completed = iter(recipe or ()), None, False
85 | step = 0
86 | complete_compression = False
87 | while True:
88 | if recipe_line and recipe_line.isbreakable and not recipe_completed:
89 | pass # reuse the same recipe line
90 | else: # everything normal
91 | recipe_line = next(recipe_lines, None)
92 | recipe_completed = False
93 | if (SHOW_STORY and recipe_line) or SHOW_DEBUG: print('INFO recipe:', recipe_line)
94 | step += 1
95 | try:
96 | best_motifs = search_best_motifs(searchers, step, recipe=recipe_line)
97 | except KeyboardInterrupt:
98 | print('WARNING interrupted search. Graph compression aborted. Output will be written.')
99 | best_motifs = None
100 | break
101 | if best_motifs: # let's compress it
102 | step += graph.compress_all(best_motifs.non_overlapping_subset())
103 | for searcher in searchers:
104 | searcher.on_new_compressed_motif(best_motifs)
105 | if BUBBLE_FOR_EACH_STEP:
106 | graph.output('out/out_k{}_s{}.bbl'.format(step, best_motif.score))
107 | if SHOW_STORY:
108 | print('INFO {} {} motif of score {} compressed'.format(best_motifs.count, best_motifs.name, best_motifs.score))
109 | if TIMERS:
110 | now = get_time()
111 | timers = round(now - timer_start, 2), round(now - timer_last, 2)
112 | if SHOW_STORY:
113 | print("TIMER since start: {}s\t\tsince last motif: {}s"
114 | "".format(*timers))
115 | timer_last = now
116 | for searcher in searchers: # timer per motif search
117 | timers = timers + ('{}:{}'.format(searcher.name, round(searcher.last_search_time, 2)) if searcher.last_search_time is not None else 'none',)
118 | if STATISTIC_FILE:
119 | bounds = [
120 | '{}:[{};{}]'.format(searcher.name, searcher.lowerbound, searcher.upperbound)
121 | for searcher in searchers
122 | ]
123 | if not TIMERS:
124 | timers = 'none', 'none'
125 | save_stats(cc_idx, *timers, best_motifs.name, best_motifs.score, *bounds)
126 | else:
127 | if recipe_line: # the recipe failed, or is optional
128 | recipe_completed = True
129 | if recipe_line.isbreakable:
130 | if SHOW_DEBUG:
131 | print(f"DEBUG breakable recipe {recipe_line} exhausted.")
132 | elif recipe_line.isrequired:
133 | raise recipe_line.RecipeError(f"Recipe {recipe_line} failed, but was necessary.")
134 | else: # it is optional
135 | if SHOW_STORY:
136 | print(f"INFO optional recipe {recipe_line} failed.")
137 | else: # no recipe, so it's a normal ending of compression
138 | complete_compression = True
139 | break # nothing to compress
140 | # finish compression if the recipe asks so (and is not an uncompleted breakable).
141 | if recipe_line and recipe_line.islast and not (recipe_line.isbreakable and not recipe_completed):
142 | break
143 | if TIMERS:
144 | timer_output = get_time()
145 |
146 | # write the bubble
147 | head_comment = ''
148 | if not complete_compression:
149 | head_comment = 'Warning: incomplete compression (stopped at step {})'.format(step)
150 | yield from graph.bubble_repr(head_comment=head_comment, given_uid=cc_idx)
151 |
152 | # timers
153 | if TIMERS:
154 | now = get_time()
155 | timers = round(now - timer_start, 2), round(now - timer_output, 2)
156 | if SHOW_STORY:
157 | print("TIMER since start: {}s\t\toutput generation: {}s"
158 | "".format(*timers))
159 |
160 | # compute the statistics
161 | compression_statistics = tuple(graph.compression_metrics())
162 | if cc_idx:
163 | compression_statistics = (('connected component', cc_idx), *compression_statistics)
164 | if TIMERS:
165 | compression_statistics = (*compression_statistics, ('time since start', timers[0]))
166 | # write the statistics where available
167 | if const.CC_STATISTIC_FILE and cc_idx:
168 | with open(const.CC_STATISTIC_FILE, 'a') as fd:
169 | csv.writer(fd).writerow(v for _, v in compression_statistics)
170 | if const.BUBBLE_WITH_STATISTICS:
171 | yield from _gen_metrics(compression_statistics)
172 |
173 |
174 | def compress_by_cc(fname:str, recipe_files:[str]=None) -> [str]:
175 | """Yield bubble lines from compression of each cc found in given filename
176 |
177 | recipe_files -- iterable of filenames or raw recipe, or Recipe objects
178 |
179 | """
180 | if TIMERS and const.BUBBLE_WITH_STATISTICS:
181 | timer = get_time()
182 | if recipe_files:
183 | if SHOW_STORY:
184 | print('INFO recipe files:', recipe_files)
185 | recipe_files = tuple([recipe_files] if isinstance(recipe_files, str) else recipe_files)
186 | recipes = tuple(map(Recipe.from_, recipe_files))
187 | def recipe_for(graph:Graph, recipes=recipes) -> Recipe:
188 | "Return the first recipe using a node found in given graph"
189 | for recipe in recipes:
190 | if recipe.works_on(graph):
191 | return recipe
192 | else:
193 | def recipe_for(*_, **__): return None # no recipe available
194 |
195 | graphs = enumerate(Graph.ccs_from_file(fname), start=1)
196 | stats = None
197 | if const.PARALLEL_CC_COMPRESSION == 1: # simple case, allowing for global stats
198 | for idx, graph in graphs:
199 | if idx > 1: yield ''
200 | yield '# CONNECTED COMPONENT {}'.format(idx)
201 | yield from compress(graph, cc_idx=idx, recipe=recipe_for(graph))
202 | if const.GLOBAL_STATISTICS:
203 | stats = _build_global_stats(stats, graph.compression_metrics_data())
204 | else: # many processes imply a more complex system
205 | nb_process = const.PARALLEL_CC_COMPRESSION
206 | if nb_process == 0:
207 | graphs = ((idx, gr, recipe_for(gr)) for idx, gr in graphs)
208 | graphs = tuple(graphs) # RIP memory
209 | nb_process = len(graphs) or 1
210 | with ProcessPool(nb_process) as pool:
211 | for lines, stats_data in pool.starmap(_func_on_graph, graphs):
212 | yield from lines
213 | if const.GLOBAL_STATISTICS:
214 | stats = _build_global_stats(stats, stats_data)
215 |
216 | # show global stats
217 | if stats is not None and const.BUBBLE_WITH_STATISTICS:
218 | yield '# TOTAL GRAPH METRICS'
219 | yield from _gen_metrics(Graph.compression_metrics_from_data(stats))
220 | if TIMERS and const.BUBBLE_WITH_STATISTICS:
221 | yield '# total compression time: {}'.format(round(get_time() - timer, 2))
222 |
223 |
224 | def _func_on_graph(idx, graph, recipe):
225 | """Function used by multiprocessing compression of cc. Needs to be global
226 | to be pickled."""
227 | lines = (
228 | '# CONNECTED COMPONENT {}'.format(idx),
229 | *compress(graph, cc_idx=idx, recipe=recipe)
230 | )
231 | return lines, graph.compression_metrics_data()
232 |
233 |
234 | def _build_global_stats(prev:tuple, current:tuple):
235 | """Return the addition of previous and current statistics values"""
236 | if prev is None:
237 | return current
238 | return tuple(one + two for one, two in zip(prev, current))
239 |
240 |
241 | def _gen_metrics(metrics:[(str, float)]) -> str:
242 | """Yield lines describing given iterable of metric and their value."""
243 | metrics = tuple(metrics)
244 | metric_name_size = max((len(m) for m, _ in metrics))
245 | for metric, value in metrics:
246 | if isinstance(value, float): value = round(value, 2)
247 | yield '# {}: {}'.format(metric.ljust(metric_name_size), value)
248 |
--------------------------------------------------------------------------------
/powergrasp/utils.py:
--------------------------------------------------------------------------------
1 |
2 | import re
3 | import math
4 | import time
5 | import phasme
6 |
7 |
8 | def get_time() -> float: return time.time()
9 |
10 |
11 |
12 | def maximal_clique_size(nb_edge:int) -> int:
13 | """Maximal number of nodes implied in a clique when there is given
14 | number of edges between neighbors of a node.
15 |
16 | nb_edge -- number of edges between neighbors of a node n
17 | return -- maximal number of node implied in a clique implying node n
18 |
19 | The number of edges covered by a clique of n nodes is given by function k:
20 | k(n) = n(n-1) / 2
21 |
22 | The contraposition of k, giving the number of nodes n in a clique of k(n) edges is:
23 | n = (1 + √(1+8*k(n))) / 2
24 |
25 | Proof: function k may be expressed as:
26 | n^2 - n - 2k(n) = 0
27 | The discriminant Δ = (-1)^2 - 4 * 1 * (-2k(n)) = 1 + 8k(n)
28 | Roots are (1±√Δ)/2, only the positive one is relevant (unless antimatter is involved),
29 | therefore root of k is (1 + √(1+8*k(n))) / 2 □
30 |
31 | For any node n, the maximal clique MC implying node n is constitued of n
32 | and its neighbors having a link between them.
33 | An upperbound of that number of node is given by
34 | considering that neighbors link are optimally placed to create a clique.
35 |
36 | There is therefore, in the best case, NMC(n) nodes in the maximal
37 | clique implying n:
38 | NMC(n) = 1 + ⌊ (1 + √(1+8*v(n))) / 2 ⌋
39 |
40 | With v(n) the number of edges between the neighbors of n.
41 |
42 | NMC(n) may then be used to compute the upperbound of clique search,
43 | by taking the maximal NMC(n) for all n of the graph in which a clique
44 | is searched.
45 |
46 | >>> maximal_clique_size(0) # when no edge between neighbors: the node and one neighbor make the clique
47 | 2
48 | >>> maximal_clique_size(1) # when 1 edge between neighbors of n, the maximal clique imply two neighbors and n
49 | 3
50 | >>> maximal_clique_size(2) # when 2 edges between neighbors of n, the maximal clique imply two neighbors and n
51 | 3
52 | >>> maximal_clique_size(3)
53 | 4
54 | >>> maximal_clique_size(4)
55 | 4
56 | >>> maximal_clique_size(5)
57 | 4
58 | >>> maximal_clique_size(6)
59 | 5
60 |
61 | """
62 | return 1 + int((1 + math.sqrt(1 + 8 * nb_edge)) / 2)
63 |
64 |
65 | def quoted(string:str) -> str:
66 | """Return the given string, quoted, and with internal quotes escaped.
67 |
68 | Existing quotes will be escaped.
69 |
70 | >>> quoted('a')
71 | '"a"'
72 | >>> quoted('"a"')
73 | '"\\\\"a\\\\""'
74 | >>> quoted('"a')
75 | '"\\\\"a"'
76 |
77 | """
78 | # space is here to allow the regex to match for the first char
79 | return '"' + re.sub(r'([^\\])"', r'\1\\"', ' ' + string)[1:] + '"'
80 |
81 |
82 | def unquoted(string:str) -> str:
83 | """Return the given string, unquoted.
84 |
85 | >>> unquoted('a')
86 | 'a'
87 | >>> unquoted('"a"')
88 | 'a'
89 | >>> unquoted('"a')
90 | '"a'
91 |
92 | """
93 | if string[0] == '"' and string[-1] == '"':
94 | return string[1:-1]
95 | return string
96 |
97 |
98 | def normalized_name(string:str) -> str:
99 | """Return the normalized string, that consist of a quoted string
100 | with special characters replaced by their ord number.
101 |
102 | >>> normalized_name('a')
103 | '"a"'
104 | >>> normalized_name('A')
105 | '"A"'
106 | >>> normalized_name('"a"')
107 | '"a"'
108 | >>> normalized_name('"a')
109 | '"_c34_a"'
110 |
111 | """
112 | return quoted(phasme.commons.fixed_name(unquoted(string)))
113 |
114 |
115 | def reverse_dict(indict:dict) -> dict:
116 | """Return a dict containing values of input dict as keys,
117 | and a set of associated keys as values.
118 |
119 | >>> reverse_dict({1: 2, 3: 2})
120 | {2: {1, 3}}
121 | >>> reverse_dict({1: 2, 3: 4})
122 | {2: {1}, 4: {3}}
123 |
124 | """
125 | ret = {}
126 | for key, val in indict.items():
127 | ret.setdefault(val, set()).add(key)
128 | return ret
129 |
--------------------------------------------------------------------------------
/recipe:
--------------------------------------------------------------------------------
1 | biclique a1 a2 a3 a4 b1 b2 b3 b4 b5 b6
2 | biclique,breakable e3 e4 d1 d2 d3 b1 b2 f1 f2 f3 f4
3 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = powergrasp
3 | version = attr: powergrasp.__version__
4 | description = compress graphs with answer-set-programming
5 | long_description = file: README.mkd
6 | author = Lucas Bourneuf
7 | author_email = lucas.bourneuf@inria.fr
8 | url = https://github.com/aluriak/powergrasp
9 | license = GPL
10 | keywords = graph, Answer Set Programming
11 | classifiers =
12 | Development Status :: 2 - Pre-Alpha
13 | Intended Audience :: Science/Research
14 | License :: OSI Approved :: GNU General Public License (GPL)
15 | Natural Language :: English
16 | Programming Language :: Python :: 3
17 | Programming Language :: Python :: 3.5
18 | Programming Language :: ASP
19 | Topic :: Software Development :: Libraries :: Python Modules
20 |
21 | [options]
22 | zip_safe = False
23 | include_package_data = True
24 | packages = find:
25 | install_requires =
26 | bubbletools>=0.6.1
27 | clyngor>=0.3.12
28 | networkx>=2.1
29 | phasme>=0.0.16
30 | pytest>=3.5.0
31 |
32 | [zest.releaser]
33 | create-wheel = yes
34 | python-file-with-version = powergrasp/__init__.py
35 |
36 |
37 | [options.packages.find]
38 | exclude =
39 | test
40 |
41 | [options.entry_points]
42 | console_scripts=
43 | powergrasp = powergrasp.__main__:run_cli
44 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup()
4 |
--------------------------------------------------------------------------------
/test/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/test/__init__.py
--------------------------------------------------------------------------------
/test/ambiguous_test_cases.py:
--------------------------------------------------------------------------------
1 | """List of compression cases.
2 |
3 | """
4 |
5 |
6 | cases = {} # filename: {expected bubble}
7 |
8 | # Expected results of tested cases
9 | cases['multiple-optimals.lp'] = {
10 | 'common': """
11 | NODE\ta
12 | NODE\ta1
13 | NODE\ta2
14 | NODE\ta3
15 | NODE\tb
16 | NODE\tb1
17 | NODE\tb2
18 | NODE\tb3
19 | NODE\tc1
20 | NODE\tc2
21 | NODE\tc3
22 | NODE\td1
23 | NODE\td2
24 | NODE\td3
25 | NODE\te1
26 | NODE\te2
27 | NODE\te3
28 | NODE\tf1
29 | NODE\tf2
30 | SET\tPWRN-a-1-1\t1.0
31 | SET\tPWRN-a-1-2\t1.0
32 | SET\tPWRN-a-2-1\t1.0
33 | SET\tPWRN-a-2-2\t1.0
34 | SET\tPWRN-a-3-1\t1.0
35 | SET\tPWRN-a-3-2\t1.0
36 | SET\tPWRN-a-4-1\t1.0
37 | SET\tPWRN-a-4-2\t1.0
38 | IN\tf1\tPWRN-a-4-2
39 | IN\tf2\tPWRN-a-4-2
40 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
41 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
42 | EDGE\tPWRN-a-3-1\tPWRN-a-3-2\t1.0
43 | EDGE\tPWRN-a-4-1\tPWRN-a-4-2\t1.0
44 | EDGE\ta\ta1\t1.0
45 | EDGE\ta\ta2\t1.0
46 | EDGE\ta\ta3\t1.0
47 | """,
48 | 'values': (1, 2, 3),
49 | 'variant': """
50 | IN\ta1\tPWRN-a-{a}-1
51 | IN\ta2\tPWRN-a-{b}-1
52 | IN\ta3\tPWRN-a-{c}-1
53 | IN\ta\tPWRN-a-4-1
54 | IN\tb1\tPWRN-a-{a}-1
55 | IN\tb2\tPWRN-a-{b}-1
56 | IN\tb3\tPWRN-a-{c}-1
57 | IN\tb\tPWRN-a-4-1
58 | IN\tc1\tPWRN-a-{a}-2
59 | IN\tc2\tPWRN-a-{b}-2
60 | IN\tc3\tPWRN-a-{c}-2
61 | IN\td1\tPWRN-a-{a}-2
62 | IN\td2\tPWRN-a-{b}-2
63 | IN\td3\tPWRN-a-{c}-2
64 | IN\te1\tPWRN-a-{a}-2
65 | IN\te2\tPWRN-a-{b}-2
66 | IN\te3\tPWRN-a-{c}-2
67 | IN\tf1\tPWRN-a-4-2
68 | IN\tf2\tPWRN-a-4-2
69 | """,
70 | }
71 |
72 | cases['test.gml'] = {
73 | 'common': """
74 | NODE\tb
75 | NODE\tc
76 | NODE\td
77 | NODE\tf
78 | NODE\tg
79 | NODE\tl
80 | NODE\tm
81 | NODE\tp
82 | NODE\ts
83 | NODE\tv
84 | NODE\tw
85 | """,
86 | 'values': (1, 2),
87 | 'variant': """
88 | IN\tc\tPWRN-b-1-1
89 | IN\tb\tPWRN-b-1-1
90 | IN\tw\tPWRN-b-1-1
91 | IN\td\tPWRN-b-1-1
92 | IN\ts\tPWRN-b-1-1
93 | IN\tg\tPWRN-b-2-{a}
94 | IN\tf\tPWRN-b-2-{a}
95 | IN\tPWRN-b-3-{a}\tPWRN-b-2-{a}
96 | IN\tv\tPWRN-b-3-{a}
97 | IN\tm\tPWRN-b-3-{a}
98 | SET\tPWRN-b-1-1\t1.0
99 | SET\tPWRN-b-2-{a}\t1.0
100 | SET\tPWRN-b-3-{a}\t1.0
101 | EDGE\tPWRN-b-2-{a}\tb\t1.0
102 | EDGE\tc\tf\t1.0
103 | EDGE\tPWRN-b-1-1\tPWRN-b-1-1\t1.0
104 | EDGE\tm\tv\t1.0
105 | EDGE\tl\tp\t1.0
106 | EDGE\tPWRN-b-3-{a}\tl\t1.0
107 | """,
108 | }
109 |
--------------------------------------------------------------------------------
/test/definitions.py:
--------------------------------------------------------------------------------
1 | """Various definitions needed by testing routines.
2 |
3 | """
4 |
5 | import os
6 | from powergrasp import constants
7 |
8 |
9 | def unified_bubble(bubble_lines):
10 | """Return the set of comparable lines found in given bubble lines"""
11 | filtered = ['#']
12 | if not constants.BUBBLE_WITH_NODES:
13 | filtered.append('NODE')
14 | if not constants.BUBBLE_WITH_SETS:
15 | filtered.append('SET')
16 | filtered = tuple(filtered)
17 |
18 | # filter out comments and blank lines
19 | bubble_lines = (line.strip() for line in bubble_lines)
20 | return set(line for line in bubble_lines
21 | if not line.startswith(filtered) and len(line) > 0)
22 |
23 |
24 | def gen_test_functions(cases:dict, template_test_function:callable):
25 | for fname, bubble in cases.items():
26 | func = template_test_function('data/' + fname, bubble)
27 | yield 'test_' + os.path.basename(fname).replace('-', '_'), func
28 |
--------------------------------------------------------------------------------
/test/powergrasp.default.cfg:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/test/powergrasp.manyoptions.cfg:
--------------------------------------------------------------------------------
1 | [many options]
2 | COVERED_EDGES_FROM_ASP = yes
3 | BICLIQUE_LOWERBOUND_MAXNEI = 5
4 | CLINGO_MULTITHREADING = 0,split
5 | CLINGO_OPTIONS = {'non-star-biclique': '--configuration=handy', 'clique': '--configuration=frumpy'}
6 | STATISTIC_FILE = out/stats.csv
7 | CC_STATISTIC_FILE = out/stats-cc.csv
8 | GLOBAL_STATISTICS = no
9 | BUBBLE_WITH_STATISTICS = no
10 | SHOW_DEBUG = yes
11 | TEST_INTEGRITY = no
12 | SHOW_STORY = no
13 | SHOW_MOTIF_HANDLING = yes
14 | OPTIMIZE_FOR_MEMORY = yes
15 | GRAPH_FILTERING = no
16 | KEEP_SINGLE_NODES = no
17 | MOTIF_TYPE_ORDER = worst-lowerbound-last
18 | PARALLEL_CC_COMPRESSION = 0
19 |
--------------------------------------------------------------------------------
/test/powergrasp.nostarsearch.cfg:
--------------------------------------------------------------------------------
1 | {
2 | "use star motif": false,
3 | "biclique lowerbound maxnei": 5
4 | }
5 |
--------------------------------------------------------------------------------
/test/powergrasp.oneshot.cfg:
--------------------------------------------------------------------------------
1 | {
2 | "multishot motif search": false
3 | }
4 |
--------------------------------------------------------------------------------
/test/test_cases.py:
--------------------------------------------------------------------------------
1 | """List of compression cases.
2 |
3 | """
4 |
5 | from powergrasp import constants
6 |
7 | cases = {} # filename: expected bubble
8 |
9 | # Expected results of tested cases
10 | cases['diacli.lp'] = """
11 | NODE\tn
12 | NODE\tr
13 | NODE\tp
14 | NODE\tq
15 | NODE\te
16 | NODE\tb
17 | NODE\tm
18 | NODE\ta
19 | NODE\to
20 | NODE\tc
21 | NODE\tj
22 | NODE\td
23 | NODE\tg
24 | NODE\tl
25 | NODE\ts
26 | NODE\tf
27 | IN\tPWRN-a-3-1\tPWRN-a-1-1
28 | IN\tPWRN-a-4-2\tPWRN-a-2-2
29 | IN\ta\tPWRN-a-2-1
30 | IN\tb\tPWRN-a-2-2
31 | IN\tc\tPWRN-a-2-2
32 | IN\td\tPWRN-a-2-1
33 | IN\te\tPWRN-a-2-2
34 | IN\tf\tPWRN-a-1-1
35 | IN\tg\tPWRN-a-1-1
36 | IN\tj\tPWRN-a-1-1
37 | IN\tl\tPWRN-a-3-1
38 | IN\tm\tPWRN-a-3-2
39 | IN\tn\tPWRN-a-3-2
40 | IN\to\tPWRN-a-4-2
41 | IN\tp\tPWRN-a-3-1
42 | IN\tq\tPWRN-a-4-2
43 | IN\tr\tPWRN-a-1-1
44 | IN\ts\tPWRN-a-3-1
45 | SET\tPWRN-a-1-1\t1.0
46 | SET\tPWRN-a-2-1\t1.0
47 | SET\tPWRN-a-2-2\t1.0
48 | SET\tPWRN-a-3-1\t1.0
49 | SET\tPWRN-a-3-2\t1.0
50 | SET\tPWRN-a-3-2\t1.0
51 | SET\tPWRN-a-4-2\t1.0
52 | EDGE\tPWRN-a-3-1\tPWRN-a-3-2\t1.0
53 | EDGE\tPWRN-a-1-1\tPWRN-a-1-1\t1.0
54 | EDGE\tPWRN-a-3-2\tPWRN-a-4-2\t1.0
55 | EDGE\tb\tc\t1.0
56 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
57 | EDGE\tm\tn\t1.0
58 | """
59 |
60 | cases['partition.lp'] = """
61 | NODE\ta
62 | NODE\tb
63 | NODE\tc
64 | NODE\td
65 | NODE\te
66 | NODE\tf
67 | NODE\tg
68 | NODE\th
69 | NODE\ti
70 | IN\tc\tPWRN-a-2-1
71 | IN\td\tPWRN-a-2-1
72 | IN\tg\tPWRN-a-2-2
73 | IN\th\tPWRN-a-2-2
74 | IN\ta\tPWRN-a-1-1
75 | IN\tb\tPWRN-a-1-1
76 | IN\ti\tPWRN-a-1-1
77 | IN\te\tPWRN-a-1-2
78 | IN\tPWRN-a-2-2\tPWRN-a-1-2
79 | IN\tf\tPWRN-a-1-2
80 | SET\tPWRN-a-1-1\t1.0
81 | SET\tPWRN-a-1-2\t1.0
82 | SET\tPWRN-a-2-1\t1.0
83 | SET\tPWRN-a-2-2\t1.0
84 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
85 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
86 | """
87 |
88 | cases['double_biclique_unambiguous.lp'] = """
89 | NODE\ta
90 | NODE\tb1
91 | NODE\tb2
92 | NODE\tc
93 | NODE\td
94 | NODE\td2
95 | NODE\te
96 | NODE\tf1
97 | NODE\tf2
98 | NODE\tg
99 | NODE\th
100 | NODE\ti
101 | NODE\tj
102 | NODE\tl
103 | NODE\tm
104 | NODE\tm2
105 | NODE\tn
106 | NODE\to
107 | NODE\tp
108 | NODE\tq
109 | IN\tPWRN-a-3-1\tPWRN-a-2-1
110 | IN\tPWRN-a-5-1\tPWRN-a-3-2
111 | IN\ta\tPWRN-a-1-1
112 | IN\tb1\tPWRN-a-1-2
113 | IN\tb2\tPWRN-a-1-2
114 | IN\tc\tPWRN-a-1-2
115 | IN\td2\tPWRN-a-1-1
116 | IN\td\tPWRN-a-1-1
117 | IN\te\tPWRN-a-1-2
118 | IN\tf1\tPWRN-a-2-1
119 | IN\tf2\tPWRN-a-2-1
120 | IN\tg\tPWRN-a-2-1
121 | IN\th\tPWRN-a-2-2
122 | IN\ti\tPWRN-a-2-2
123 | IN\tj\tPWRN-a-2-1
124 | IN\tl\tPWRN-a-3-1
125 | IN\tm2\tPWRN-a-5-1
126 | IN\tm\tPWRN-a-5-1
127 | IN\tn\tPWRN-a-3-2
128 | IN\to\tPWRN-a-1-2
129 | IN\tp\tPWRN-a-3-1
130 | IN\tq\tPWRN-a-1-2
131 | SET\tPWRN-a-1-1\t1.0
132 | SET\tPWRN-a-1-2\t1.0
133 | SET\tPWRN-a-2-1\t1.0
134 | SET\tPWRN-a-2-2\t1.0
135 | SET\tPWRN-a-3-1\t1.0
136 | SET\tPWRN-a-3-2\t1.0
137 | SET\tPWRN-a-5-1\t1.0
138 | EDGE\tPWRN-a-5-1\tn\t1.0
139 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
140 | EDGE\tf1\tg\t1.0
141 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
142 | EDGE\tPWRN-a-3-1\tPWRN-a-3-2\t1.0
143 | EDGE\tPWRN-a-3-2\tq\t1.0
144 | EDGE\tb1\tc\t1.0
145 | """
146 |
147 |
148 | cases['testblocks.lp'] = """
149 | NODE\ta
150 | NODE\tb
151 | NODE\tc
152 | NODE\td
153 | NODE\te
154 | NODE\tf
155 | NODE\tg
156 | NODE\th
157 | NODE\ti
158 | NODE\tj
159 | NODE\tl
160 | NODE\tm
161 | IN\te\tPWRN-a-2-1
162 | IN\tPWRN-a-3-1\tPWRN-a-2-1
163 | IN\ta\tPWRN-a-3-1
164 | IN\tc\tPWRN-a-3-1
165 | IN\tb\tPWRN-a-3-1
166 | IN\td\tPWRN-a-3-1
167 | IN\tg\tPWRN-a-4-1
168 | IN\tf\tPWRN-a-4-1
169 | IN\th\tPWRN-a-4-1
170 | IN\ti\tPWRN-a-1-1
171 | IN\tj\tPWRN-a-1-1
172 | IN\tPWRN-a-4-1\tPWRN-a-1-1
173 | IN\tm\tPWRN-a-1-1
174 | SET\tPWRN-a-1-1\t1.0
175 | SET\tPWRN-a-2-1\t1.0
176 | SET\tPWRN-a-3-1\t1.0
177 | SET\tPWRN-a-4-1\t1.0
178 | EDGE\tPWRN-a-2-1\tPWRN-a-2-1\t1.0
179 | EDGE\tPWRN-a-3-1\tf\t1.0
180 | EDGE\tPWRN-a-4-1\tl\t1.0
181 | EDGE\tPWRN-a-1-1\tPWRN-a-1-1\t1.0
182 | """
183 |
184 | cases['pnode-to-clique.lp'] = """
185 | NODE\tb
186 | NODE\td
187 | NODE\ta
188 | NODE\tf
189 | NODE\te
190 | NODE\tc
191 | IN\te\tPWRN-a-1-2
192 | IN\tc\tPWRN-a-1-2
193 | IN\td\tPWRN-a-1-2
194 | IN\ta\tPWRN-a-1-1
195 | IN\tf\tPWRN-a-1-1
196 | IN\tb\tPWRN-a-1-1
197 | SET\tPWRN-a-1-2\t1.0
198 | SET\tPWRN-a-1-1\t1.0
199 | EDGE\tPWRN-a-1-2\tPWRN-a-1-2\t1.0
200 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
201 | """
202 |
203 | cases['clique.lp'] = """
204 | NODE\tc
205 | NODE\tb
206 | NODE\ta
207 | NODE\td
208 | IN\tc\tPWRN-a-1-1
209 | IN\tb\tPWRN-a-1-1
210 | IN\ta\tPWRN-a-1-1
211 | IN\td\tPWRN-a-1-1
212 | SET\tPWRN-a-1-1\t1.0
213 | EDGE\tPWRN-a-1-1\tPWRN-a-1-1\t1.0
214 | """
215 |
216 | cases['concomp.lp'] = """
217 | NODE\t23
218 | NODE\t42
219 | EDGE\t23\t42\t1.0
220 | NODE\t1
221 | NODE\t2
222 | NODE\t3
223 | NODE\t4
224 | NODE\t5
225 | NODE\t6
226 | IN\t1\tPWRN-1-1-1
227 | IN\t2\tPWRN-1-1-2
228 | IN\t3\tPWRN-1-1-2
229 | IN\t4\tPWRN-1-1-2
230 | IN\t5\tPWRN-1-1-2
231 | IN\t6\tPWRN-1-1-1
232 | SET\tPWRN-1-1-1\t1.0
233 | SET\tPWRN-1-1-2\t1.0
234 | EDGE\tPWRN-1-1-1\tPWRN-1-1-2\t1.0
235 | NODE\ta
236 | NODE\tb
237 | NODE\tc
238 | NODE\td
239 | NODE\te
240 | IN\ta\tPWRN-a-1-1
241 | IN\tb\tPWRN-a-1-1
242 | IN\tc\tPWRN-a-1-1
243 | IN\td\tPWRN-a-1-1
244 | SET\tPWRN-a-1-1\t1.0
245 | EDGE\tPWRN-a-1-1\te\t1.0
246 | """
247 |
248 | cases['prio_deg.lp'] = """
249 | NODE\ta
250 | NODE\tb
251 | NODE\tc
252 | NODE\td
253 | NODE\te
254 | NODE\tf
255 | NODE\tg
256 | NODE\th
257 | NODE\ts
258 | IN\tg\tPWRN-a-2-1
259 | IN\tf\tPWRN-a-2-1
260 | IN\th\tPWRN-a-2-1
261 | IN\tc\tPWRN-a-1-1
262 | IN\tb\tPWRN-a-1-1
263 | IN\ta\tPWRN-a-1-1
264 | IN\te\tPWRN-a-1-2
265 | IN\td\tPWRN-a-1-2
266 | SET\tPWRN-a-1-1\t1.0
267 | SET\tPWRN-a-1-2\t1.0
268 | SET\tPWRN-a-2-1\t1.0
269 | EDGE\tPWRN-a-2-1\ts\t1.0
270 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
271 | EDGE\ta\ts\t1.0
272 | """
273 |
274 | cases['perfectfit.lp'] = """
275 | NODE\ta
276 | NODE\tb
277 | NODE\tc
278 | NODE\td
279 | NODE\te
280 | NODE\tf
281 | NODE\tg
282 | NODE\th
283 | NODE\ti
284 | NODE\tj
285 | NODE\tl
286 | IN\tPWRN-a-2-1\tPWRN-a-1-2
287 | IN\tb\tPWRN-a-1-1
288 | IN\th\tPWRN-a-1-2
289 | IN\ti\tPWRN-a-1-2
290 | IN\tPWRN-a-3-1\tPWRN-a-1-1
291 | IN\ta\tPWRN-a-1-1
292 | IN\tc\tPWRN-a-3-1
293 | IN\td\tPWRN-a-3-1
294 | IN\te\tPWRN-a-2-1
295 | IN\tf\tPWRN-a-2-1
296 | IN\tg\tPWRN-a-2-1
297 | IN\tj\tPWRN-a-2-2
298 | IN\tl\tPWRN-a-2-2
299 | SET\tPWRN-a-1-1\t1.0
300 | SET\tPWRN-a-1-2\t1.0
301 | SET\tPWRN-a-2-1\t1.0
302 | SET\tPWRN-a-2-2\t1.0
303 | SET\tPWRN-a-3-1\t1.0
304 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
305 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
306 | EDGE\tPWRN-a-2-2\tPWRN-a-3-1\t1.0
307 | """
308 |
309 | cases['variable-name.gml'] = """
310 | NODE\tA
311 | NODE\tB
312 | NODE\tC
313 | NODE\tD
314 | NODE\tE
315 | SET\tPWRN-A-1-1\t1.0
316 | IN\tA\tPWRN-A-1-1
317 | IN\tC\tPWRN-A-1-1
318 | IN\tD\tPWRN-A-1-1
319 | EDGE\tB\tPWRN-A-1-1\t1.0
320 | EDGE\tA\tE\t1.0
321 | """
322 |
323 | cases['unclique.lp'] = """
324 | NODE\ta
325 | NODE\tb
326 | NODE\tc
327 | NODE\td
328 | NODE\te
329 | NODE\tf
330 | NODE\tg
331 | NODE\th
332 | NODE\ti
333 | IN\tc\tPWRN-a-1-1
334 | IN\te\tPWRN-a-1-1
335 | IN\tf\tPWRN-a-1-1
336 | IN\tg\tPWRN-a-1-1
337 | IN\th\tPWRN-a-1-2
338 | IN\ti\tPWRN-a-1-2
339 | IN\td\tPWRN-a-2-1
340 | IN\tb\tPWRN-a-2-1
341 | IN\ta\tPWRN-a-2-1
342 | SET\tPWRN-a-1-1\t1.0
343 | SET\tPWRN-a-1-2\t1.0
344 | SET\tPWRN-a-2-1\t1.0
345 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
346 | EDGE\tPWRN-a-2-1\tc\t1.0
347 | EDGE\tPWRN-a-2-1\tPWRN-a-2-1\t1.0
348 | """
349 |
350 | cases['order.lp'] = """
351 | NODE\tb
352 | NODE\t10
353 | NODE\t4
354 | NODE\t5
355 | NODE\t6
356 | NODE\ta
357 | IN\t4\tPWRN-a-1-1
358 | IN\ta\tPWRN-a-1-1
359 | IN\t6\tPWRN-a-1-1
360 | IN\t5\tPWRN-a-1-1
361 | SET\tPWRN-a-1-1\t1.0
362 | EDGE\ta\tb\t1.0
363 | EDGE\t10\tPWRN-a-1-1\t1.0
364 | """
365 |
366 | cases['quoting.lp'] = """
367 | NODE\ta
368 | NODE\tb
369 | NODE\tc
370 | IN\tc\tPWRN-a-1-1
371 | IN\ta\tPWRN-a-1-1
372 | SET\tPWRN-a-1-1\t1.0
373 | EDGE\tPWRN-a-1-1\tb\t1.0
374 | """
375 |
376 | cases['single-node.lp'] = """
377 | NODE\ta
378 | """ if constants.KEEP_SINGLE_NODES else ""
379 |
380 | cases['empty.lp'] = """
381 | """
382 |
383 | cases['star.lp'] = """
384 | NODE\ta
385 | NODE\tb
386 | NODE\tc
387 | NODE\td
388 | NODE\te
389 | IN\ta\tPWRN-a-1-1
390 | IN\tb\tPWRN-a-1-1
391 | IN\tc\tPWRN-a-1-1
392 | IN\td\tPWRN-a-1-1
393 | SET\tPWRN-a-1-1\t1.0
394 | EDGE\tPWRN-a-1-1\te\t1.0
395 | """
396 |
397 | cases['test.graphml'] = """
398 | NODE\t1
399 | NODE\t2
400 | NODE\t3
401 | NODE\t4
402 | IN\t1\tPWRN-1-1-1
403 | IN\t4\tPWRN-1-1-1
404 | IN\t3\tPWRN-1-1-2
405 | IN\t2\tPWRN-1-1-2
406 | SET\tPWRN-1-1-1\t1.0
407 | SET\tPWRN-1-1-2\t1.0
408 | EDGE\tPWRN-1-1-1\tPWRN-1-1-2\t1.0
409 | """
410 |
411 | cases['one_edge.lp'] = """
412 | NODE\ta
413 | NODE\tb
414 | EDGE\ta\tb\t1.0
415 | """
416 |
417 | cases['horrible_data.lp'] = """
418 | NODE\t_c92__c34_echo_c32_coucou_c92__c34_
419 | NODE\t_c91_a_c93_
420 | NODE\t_c39_echo_c32_coucou_c39_
421 | SET\tPWRN-_c39_echo_c32_coucou_c39_-1-1\t1.0
422 | IN\t_c92__c34_echo_c32_coucou_c92__c34_\tPWRN-_c39_echo_c32_coucou_c39_-1-1
423 | IN\t_c39_echo_c32_coucou_c39_\tPWRN-_c39_echo_c32_coucou_c39_-1-1
424 | EDGE\tPWRN-_c39_echo_c32_coucou_c39_-1-1\t_c91_a_c93_\t1.0
425 | NODE\t_c91_a_c44_b_c93_
426 | NODE\t_c36_PYTHONPATH
427 | EDGE\t_c36_PYTHONPATH\t_c91_a_c44_b_c93_\t1.0
428 | """
429 |
430 | cases['inclusions.lp'] = """
431 | NODE\t1
432 | NODE\t2
433 | NODE\t3
434 | NODE\t5
435 | NODE\t6
436 | NODE\t7
437 | NODE\t8
438 | NODE\t9
439 | NODE\t10
440 | NODE\ta
441 | NODE\tb
442 | NODE\tc
443 | NODE\td
444 | NODE\te
445 | NODE\tf
446 | NODE\tg
447 | NODE\th
448 | NODE\ti
449 | NODE\tj
450 | NODE\tk
451 | NODE\tl
452 | NODE\tm
453 | NODE\tn
454 | NODE\to
455 | NODE\tp
456 | NODE\tq
457 | NODE\tr
458 | NODE\tv
459 | NODE\tv2
460 | NODE\tw
461 | NODE\tw2
462 | IN\tPWRN-a-6-2\tPWRN-a-2-1
463 | IN\tPWRN-a-6-1\tPWRN-a-4-1
464 | IN\tPWRN-a-5-1\tPWRN-a-6-1
465 | IN\tPWRN-a-4-1\tPWRN-a-1-1
466 | IN\tPWRN-a-4-2\tPWRN-a-3-1
467 | IN\t10\tPWRN-a-2-1
468 | IN\t1\tPWRN-a-3-1
469 | IN\t2\tPWRN-a-3-1
470 | IN\t3\tPWRN-a-3-1
471 | IN\t5\tPWRN-a-2-1
472 | IN\t6\tPWRN-a-2-1
473 | IN\t7\tPWRN-a-2-1
474 | IN\t8\tPWRN-a-2-1
475 | IN\t9\tPWRN-a-2-1
476 | IN\ta\tPWRN-a-1-1
477 | IN\tb\tPWRN-a-1-1
478 | IN\tc\tPWRN-a-1-1
479 | IN\td\tPWRN-a-4-1
480 | IN\te\tPWRN-a-6-1
481 | IN\tf\tPWRN-a-5-1
482 | IN\tg\tPWRN-a-5-1
483 | IN\th\tPWRN-a-1-1
484 | IN\ti\tPWRN-a-1-1
485 | IN\tj\tPWRN-a-1-1
486 | IN\tk\tPWRN-a-1-1
487 | IN\tl\tPWRN-a-1-1
488 | IN\tm\tPWRN-a-4-2
489 | IN\tn\tPWRN-a-4-2
490 | IN\to\tPWRN-a-4-2
491 | IN\tp\tPWRN-a-4-2
492 | IN\tq\tPWRN-a-6-2
493 | IN\tr\tPWRN-a-6-2
494 | IN\tv2\tPWRN-a-5-2
495 | IN\tv\tPWRN-a-5-2
496 | IN\tw2\tPWRN-a-5-2
497 | IN\tw\tPWRN-a-5-2
498 | SET\tPWRN-a-1-1\t1.0
499 | SET\tPWRN-a-2-1\t1.0
500 | SET\tPWRN-a-3-1\t1.0
501 | SET\tPWRN-a-4-1\t1.0
502 | SET\tPWRN-a-5-1\t1.0
503 | SET\tPWRN-a-6-1\t1.0
504 | SET\tPWRN-a-4-2\t1.0
505 | SET\tPWRN-a-5-2\t1.0
506 | SET\tPWRN-a-6-2\t1.0
507 | EDGE\tPWRN-a-1-1\tPWRN-a-1-1\t1.0
508 | EDGE\tPWRN-a-2-1\tPWRN-a-2-1\t1.0
509 | EDGE\tPWRN-a-3-1\tPWRN-a-3-1\t1.0
510 | EDGE\tPWRN-a-4-1\tPWRN-a-4-2\t1.0
511 | EDGE\tPWRN-a-5-1\tPWRN-a-5-2\t1.0
512 | EDGE\tPWRN-a-6-1\tPWRN-a-6-2\t1.0
513 | """
514 |
515 | cases['disjoint-subpnodes.lp'] = """
516 | NODE\ta
517 | NODE\tb
518 | NODE\tc
519 | NODE\td
520 | NODE\te
521 | NODE\tf
522 | NODE\tg
523 | NODE\th
524 | IN\ta\tPWRN-a-1-1
525 | IN\tb\tPWRN-a-3-1
526 | IN\tc\tPWRN-a-3-1
527 | IN\td\tPWRN-a-2-1
528 | IN\te\tPWRN-a-2-1
529 | IN\tf\tPWRN-a-2-1
530 | IN\tPWRN-a-2-1\tPWRN-a-1-1
531 | IN\tPWRN-a-3-1\tPWRN-a-1-1
532 | SET\tPWRN-a-1-1\t1.0
533 | SET\tPWRN-a-2-1\t1.0
534 | SET\tPWRN-a-3-1\t1.0
535 | EDGE\tPWRN-a-1-1\tPWRN-a-1-1\t1.0
536 | EDGE\tPWRN-a-2-1\tg\t1.0
537 | EDGE\tPWRN-a-3-1\th\t1.0
538 | """
539 |
540 |
541 | cases['consider-included-nodes.lp'] = """
542 | NODE\ta
543 | NODE\tb
544 | NODE\tc
545 | NODE\td
546 | NODE\te
547 | NODE\tf
548 | NODE\tg
549 | NODE\th
550 | NODE\ti
551 | NODE\tj
552 | NODE\tl
553 | NODE\tm
554 | NODE\troot
555 | NODE\troot2
556 | NODE\troot3
557 | SET\tPWRN-a-1-1\t1.0
558 | SET\tPWRN-a-1-2\t1.0
559 | SET\tPWRN-a-2-1\t1.0
560 | SET\tPWRN-a-2-2\t1.0
561 | IN\tPWRN-a-2-1\tPWRN-a-1-1
562 | IN\tPWRN-a-2-2\tPWRN-a-1-1
563 | IN\ta\tPWRN-a-1-1
564 | IN\tb\tPWRN-a-2-1
565 | IN\tc\tPWRN-a-2-1
566 | IN\td\tPWRN-a-2-2
567 | IN\te\tPWRN-a-2-2
568 | IN\tf\tPWRN-a-2-2
569 | IN\tg\tPWRN-a-2-2
570 | IN\th\tPWRN-a-2-2
571 | IN\ti\tPWRN-a-1-1
572 | IN\tj\tPWRN-a-1-1
573 | IN\tl\tPWRN-a-1-1
574 | IN\tm\tPWRN-a-1-1
575 | IN\troot2\tPWRN-a-1-2
576 | IN\troot3\tPWRN-a-1-2
577 | IN\troot\tPWRN-a-1-2
578 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
579 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
580 | EDGE\ta\tb\t1.0
581 | EDGE\ta\td\t1.0
582 | """
583 |
584 | cases['motif-overlapping.lp'] = """
585 | # CONNECTED COMPONENT 1
586 | NODE\tc
587 | NODE\te
588 | NODE\tb
589 | NODE\tf
590 | NODE\td
591 | NODE\ta
592 | SET\tPWRN-a-1-2\t1.0
593 | SET\tPWRN-a-1-1\t1.0
594 | IN\te\tPWRN-a-1-2
595 | IN\td\tPWRN-a-1-2
596 | IN\tf\tPWRN-a-1-2
597 | IN\tc\tPWRN-a-1-1
598 | IN\tb\tPWRN-a-1-1
599 | IN\ta\tPWRN-a-1-1
600 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
601 | EDGE\tPWRN-a-1-1\tPWRN-a-1-1\t1.0
602 | # CONNECTED COMPONENT 2
603 | NODE\ta3
604 | NODE\tb4
605 | NODE\tc1
606 | NODE\tc9
607 | NODE\ta5
608 | NODE\ta4
609 | NODE\tc8
610 | NODE\tb2
611 | NODE\tc4
612 | NODE\tb3
613 | NODE\tc5
614 | NODE\tc7
615 | NODE\tc3
616 | NODE\th
617 | NODE\tb1
618 | NODE\ta2
619 | NODE\tc2
620 | NODE\tc6
621 | NODE\ta1
622 | SET\tPWRN-a1-4-1\t1.0
623 | SET\tPWRN-a1-1-1\t1.0
624 | SET\tPWRN-a1-2-1\t1.0
625 | IN\ta1\tPWRN-a1-1-1
626 | IN\ta2\tPWRN-a1-2-1
627 | IN\ta3\tPWRN-a1-2-1
628 | IN\ta4\tPWRN-a1-2-1
629 | IN\ta5\tPWRN-a1-2-1
630 | IN\tb1\tPWRN-a1-4-1
631 | IN\tb2\tPWRN-a1-4-1
632 | IN\tb3\tPWRN-a1-4-1
633 | IN\tb4\tPWRN-a1-1-1
634 | IN\tc1\tPWRN-a1-1-1
635 | IN\tc2\tPWRN-a1-1-1
636 | IN\tc3\tPWRN-a1-1-1
637 | IN\tc4\tPWRN-a1-1-1
638 | IN\tc5\tPWRN-a1-1-1
639 | IN\tc6\tPWRN-a1-1-1
640 | IN\tc7\tPWRN-a1-1-1
641 | IN\tc8\tPWRN-a1-1-1
642 | IN\tc9\tPWRN-a1-1-1
643 | EDGE\tPWRN-a1-4-1\tb4\t1.0
644 | EDGE\tPWRN-a1-4-1\tPWRN-a1-4-1\t1.0
645 | EDGE\tPWRN-a1-1-1\th\t1.0
646 | EDGE\tPWRN-a1-2-1\ta1\t1.0
647 | EDGE\tPWRN-a1-2-1\tPWRN-a1-2-1\t1.0
648 | # CONNECTED COMPONENT 3
649 | NODE\tx2
650 | NODE\tz2
651 | NODE\tx1
652 | NODE\tx4
653 | NODE\tz1
654 | NODE\ty1
655 | NODE\tx3
656 | NODE\tz3
657 | NODE\ty2
658 | SET\tPWRN-x1-1-2\t1.0
659 | SET\tPWRN-x1-1-1\t1.0
660 | SET\tPWRN-x1-2-1\t1.0
661 | IN\tz1\tPWRN-x1-1-2
662 | IN\tz3\tPWRN-x1-1-2
663 | IN\tz2\tPWRN-x1-1-2
664 | IN\ty1\tPWRN-x1-1-1
665 | IN\ty2\tPWRN-x1-1-1
666 | IN\tx1\tPWRN-x1-2-1
667 | IN\tx2\tPWRN-x1-2-1
668 | IN\tx3\tPWRN-x1-2-1
669 | IN\tx4\tPWRN-x1-1-1
670 | EDGE\tPWRN-x1-1-1\tPWRN-x1-1-2\t1.0
671 | EDGE\tPWRN-x1-2-1\tx4\t1.0
672 | EDGE\tPWRN-x1-2-1\tPWRN-x1-2-1\t1.0
673 | """
674 |
--------------------------------------------------------------------------------
/test/test_multishot_compression.py:
--------------------------------------------------------------------------------
1 | """Test of ambiguous and multishot graph compression.
2 |
3 | Note that the test_* functions are generated
4 | from the template_test_function function.
5 |
6 | """
7 | import os
8 | import itertools
9 |
10 | from powergrasp.routines import compress_by_cc
11 | from .ambiguous_test_cases import cases
12 | from .definitions import unified_bubble, gen_test_functions
13 |
14 |
15 | def template_test_function(file:str, all_bubblelines:set):
16 | def test_function():
17 | found = unified_bubble(compress_by_cc(file))
18 | variants_keys = itertools.permutations(all_bubblelines['values'])
19 | variants = {all_bubblelines['common'] + '\n'
20 | + all_bubblelines['variant'].format(**dict(zip('abcde', keys)))
21 | for keys in variants_keys}
22 | expecteds = tuple(unified_bubble(variant.splitlines(keepends=False))
23 | for variant in variants)
24 | # The following lines are here to get a proper understanding of the data
25 | # in case of error.
26 | for expected in expecteds:
27 | if found == expected:
28 | return
29 | print('FOUND:', found)
30 | for idx, expected in enumerate(expecteds, start=1):
31 | print('EXPECTED', idx, ':', expected - found)
32 | assert found in expecteds, "Expected not found"
33 | return test_function
34 |
35 |
36 | for name, func in gen_test_functions(cases, template_test_function):
37 | globals()[name] = func
38 |
--------------------------------------------------------------------------------
/test/test_recipes.py:
--------------------------------------------------------------------------------
1 | """Test of recipe'd graph compression.
2 |
3 | Note that the test_* functions are generated
4 | from the template_test_function function.
5 |
6 | """
7 |
8 | import pytest
9 | from powergrasp.constants import USE_STAR_MOTIF
10 | from powergrasp.routines import compress_by_cc
11 | from powergrasp.recipe import RecipeError
12 | from .definitions import unified_bubble, gen_test_functions
13 |
14 |
15 | def test_recipe_build_from_generator():
16 | "Same as test_simple_recipe, but with initialization from generator"
17 | def gen_recipe():
18 | yield 'biclique c g h i'
19 | yield 'biclique a b d e'
20 | yield 'biclique a b f'
21 | yield 'biclique c d e'
22 | found = unified_bubble(compress_by_cc('data/recipe-test.lp', recipe_files=[gen_recipe()]))
23 | expected = unified_bubble(BUBBLELINES_SIMPLE.splitlines(keepends=False))
24 | with open('out/out.bbl', 'w') as fd:
25 | fd.write('\n'.join(found))
26 | assert found == expected
27 |
28 |
29 | def test_simple_recipe():
30 | found = unified_bubble(compress_by_cc('data/recipe-test.lp', recipe_files='data/recipe-test.txt'))
31 | expected = unified_bubble(BUBBLELINES_SIMPLE.splitlines(keepends=False))
32 | # with open('out/out.bbl', 'w') as fd:
33 | # fd.write('\n'.join(found))
34 | assert found == expected
35 |
36 | def test_recipe_options():
37 | found = unified_bubble(compress_by_cc('data/recipe-option-test.lp', recipe_files='data/recipe-option-test.txt'))
38 | expected = unified_bubble(BUBBLELINES_OPTIONS.splitlines(keepends=False))
39 | # with open('out/out.bbl', 'w') as fd:
40 | # fd.write('\n'.join(found))
41 | assert found == expected
42 |
43 | def test_recipe_error_because_of_unknow_nodes():
44 | with pytest.raises(RecipeError):
45 | unified_bubble(compress_by_cc('data/recipe-option-test.lp', recipe_files="""
46 | biclique c g h i
47 | biclique,open a b d e
48 | biclique unexisting nodes are not compressible
49 | """))
50 |
51 | def test_recipe_error_because_of_impossible_compression():
52 | with pytest.raises(RecipeError):
53 | unified_bubble(compress_by_cc('data/recipe-option-test.lp', recipe_files="""
54 | biclique c g h i
55 | biclique a b d e c
56 | """))
57 |
58 | def test_recipe_error_because_of_overlapping_entries():
59 | with pytest.raises(RecipeError):
60 | unified_bubble(compress_by_cc('data/recipe-option-test.lp', recipe_files="""
61 | biclique c g h i
62 | biclique a b d e
63 | biclique a b c d e f
64 | """))
65 |
66 | def _test_recipe_use_of_already_defined_powernodes():
67 | found = unified_bubble(compress_by_cc('data/suboptimal.lp', recipe_files="""
68 | biclique,star a b i e f
69 | biclique,star c d a b l
70 | biclique,star e f g h j
71 | biclique,star,last g h k c d
72 | """))
73 | expected = unified_bubble(BUBBLELINES_POWERNODE.splitlines(keepends=False))
74 | # with open('out/out.bbl', 'w') as fd:
75 | # fd.write('\n'.join(found))
76 | assert found == expected
77 |
78 | @pytest.mark.skip(reason="this is a core bug that should be fixed ASAP")
79 | def test_after_recipe_use_of_already_defined_powernode():
80 | found = unified_bubble(compress_by_cc('data/recipe-test-inclusion.lp', recipe_files="""
81 | biclique,star e f g h
82 | """))
83 | expected = unified_bubble(BUBBLELINES_POWERNODE_REUSE.splitlines(keepends=False))
84 | with open('out/out.bbl', 'w') as fd:
85 | fd.write('\n'.join(found))
86 | assert found == expected
87 |
88 |
89 | BUBBLELINES_POWERNODE_REUSE = """
90 | NODE\ta
91 | NODE\tb
92 | NODE\tc
93 | NODE\td
94 | NODE\te
95 | NODE\tf
96 | NODE\tg
97 | NODE\th
98 | SET\tPWRN-a-1-1\t1.0
99 | SET\tPWRN-a-1-2\t1.0
100 | SET\tPWRN-a-2-1\t1.0
101 | SET\tPWRN-a-2-2\t1.0
102 | IN\ta\tPWRN-a-2-1
103 | IN\tb\tPWRN-a-2-1
104 | IN\tc\tPWRN-a-2-2
105 | IN\td\tPWRN-a-2-2
106 | IN\te\tPWRN-a-1-1
107 | IN\tf\tPWRN-a-1-1
108 | IN\tg\tPWRN-a-1-2
109 | IN\th\tPWRN-a-1-2
110 | IN\tPWRN-a-1-1\tPWRN-a-2-2
111 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
112 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
113 | """
114 |
115 | BUBBLELINES_POWERNODE = """
116 | NODE\ta
117 | NODE\tb
118 | NODE\tc
119 | NODE\td
120 | NODE\te
121 | NODE\tf
122 | NODE\tg
123 | NODE\th
124 | NODE\ti
125 | NODE\tj
126 | NODE\tk
127 | NODE\tl
128 | SET\tPWRN-a-1-1\t1.0
129 | SET\tPWRN-a-1-2\t1.0
130 | SET\tPWRN-a-2-1\t1.0
131 | SET\tPWRN-a-2-2\t1.0
132 | SET\tPWRN-a-3-1\t1.0
133 | SET\tPWRN-a-3-2\t1.0
134 | SET\tPWRN-a-4-1\t1.0
135 | SET\tPWRN-a-4-2\t1.0
136 | IN\ta\tPWRN-a-1-1
137 | IN\tb\tPWRN-a-1-1
138 | IN\ti\tPWRN-a-1-2
139 | IN\tc\tPWRN-a-2-1
140 | IN\td\tPWRN-a-2-1
141 | IN\tl\tPWRN-a-2-2
142 | IN\te\tPWRN-a-3-1
143 | IN\tf\tPWRN-a-3-1
144 | IN\tj\tPWRN-a-3-2
145 | IN\tg\tPWRN-a-4-1
146 | IN\th\tPWRN-a-4-1
147 | IN\tk\tPWRN-a-4-2
148 | IN\tPWRN-a-1-1\tPWRN-a-2-2
149 | IN\tPWRN-a-3-1\tPWRN-a-1-2
150 | IN\tPWRN-a-4-1\tPWRN-a-3-2
151 | IN\tPWRN-a-2-1\tPWRN-a-4-2
152 | EDGE\tPWRN-a-1-1\tPWRN-a-1-2\t1.0
153 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
154 | EDGE\tPWRN-a-3-1\tPWRN-a-3-2\t1.0
155 | EDGE\tPWRN-a-4-1\tPWRN-a-4-2\t1.0
156 | """
157 |
158 | if USE_STAR_MOTIF:
159 | BUBBLELINES_SIMPLE = """
160 | NODE\ta
161 | NODE\tb
162 | NODE\tc
163 | NODE\td
164 | NODE\te
165 | NODE\tf
166 | NODE\tg
167 | NODE\th
168 | NODE\ti
169 | NODE\tj
170 | SET\tPWRN-a-1-1\t1.0
171 | SET\tPWRN-a-2-1\t1.0
172 | SET\tPWRN-a-2-2\t1.0
173 | SET\tPWRN-a-5-1\t1.0
174 | IN\ta\tPWRN-a-2-1
175 | IN\tb\tPWRN-a-2-1
176 | IN\td\tPWRN-a-2-2
177 | IN\te\tPWRN-a-2-2
178 | IN\tf\tPWRN-a-5-1
179 | IN\tg\tPWRN-a-1-1
180 | IN\th\tPWRN-a-1-1
181 | IN\ti\tPWRN-a-1-1
182 | IN\tj\tPWRN-a-5-1
183 | EDGE\tPWRN-a-1-1\tc\t1.0
184 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
185 | EDGE\tPWRN-a-2-1\tf\t1.0
186 | EDGE\tPWRN-a-2-2\tc\t1.0
187 | EDGE\tPWRN-a-5-1\tc\t1.0
188 | """
189 | BUBBLELINES_OPTIONS = """
190 | NODE\ta
191 | NODE\tb
192 | NODE\tc
193 | NODE\td
194 | NODE\te
195 | NODE\tf
196 | NODE\tg
197 | NODE\th
198 | NODE\ti
199 | NODE\tj
200 | NODE\tk
201 | NODE\tl
202 | NODE\tm
203 | NODE\tn
204 | SET\tPWRN-a-1-1\t1.0
205 | SET\tPWRN-a-2-1\t1.0
206 | SET\tPWRN-a-2-2\t1.0
207 | SET\tPWRN-a-4-1\t1.0
208 | SET\tPWRN-a-5-1\t1.0
209 | SET\tPWRN-a-7-1\t1.0
210 | IN\tPWRN-a-5-1\tPWRN-a-1-1
211 | IN\ta\tPWRN-a-2-1
212 | IN\tb\tPWRN-a-2-1
213 | IN\tc\tPWRN-a-2-1
214 | IN\td\tPWRN-a-2-2
215 | IN\te\tPWRN-a-2-2
216 | IN\tf\tPWRN-a-2-2
217 | IN\tg\tPWRN-a-5-1
218 | IN\th\tPWRN-a-5-1
219 | IN\ti\tPWRN-a-1-1
220 | IN\tj\tPWRN-a-7-1
221 | IN\tk\tPWRN-a-7-1
222 | IN\tl\tPWRN-a-4-1
223 | IN\tm\tPWRN-a-4-1
224 | IN\tn\tPWRN-a-4-1
225 | EDGE\tPWRN-a-1-1\tc\t1.0
226 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
227 | EDGE\tPWRN-a-4-1\ta\t1.0
228 | EDGE\tPWRN-a-5-1\ta\t1.0
229 | EDGE\tPWRN-a-7-1\tc\t1.0
230 | EDGE\ta\tj\t1.0
231 | EDGE\ta\tk\t1.0
232 | EDGE\tc\tl\t1.0
233 | EDGE\tc\tm\t1.0
234 | EDGE\tc\tn\t1.0
235 | """
236 | else:
237 | # in such case, the stars are handled as bicliques, and in this case,
238 | # node c being smaller than its neighbor, it is placed in set 1 instead of 2.
239 | BUBBLELINES_SIMPLE = """
240 | NODE\ta
241 | NODE\tb
242 | NODE\tc
243 | NODE\td
244 | NODE\te
245 | NODE\tf
246 | NODE\tg
247 | NODE\th
248 | NODE\ti
249 | NODE\tj
250 | SET\tPWRN-a-2-1\t1.0
251 | SET\tPWRN-a-2-2\t1.0
252 | # Sets modifications:
253 | SET\tPWRN-a-1-2\t1.0
254 | SET\tPWRN-a-5-2\t1.0
255 | IN\ta\tPWRN-a-2-1
256 | IN\tb\tPWRN-a-2-1
257 | IN\td\tPWRN-a-2-2
258 | IN\te\tPWRN-a-2-2
259 | # Inclusions modifications:
260 | IN\tf\tPWRN-a-5-2
261 | IN\tg\tPWRN-a-1-2
262 | IN\th\tPWRN-a-1-2
263 | IN\ti\tPWRN-a-1-2
264 | IN\tj\tPWRN-a-5-2
265 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
266 | EDGE\tPWRN-a-2-1\tf\t1.0
267 | EDGE\tPWRN-a-2-2\tc\t1.0
268 | # Edges modifications:
269 | EDGE\tPWRN-a-1-2\tc\t1.0
270 | EDGE\tPWRN-a-5-2\tc\t1.0
271 | """
272 | BUBBLELINES_OPTIONS = """
273 | NODE\ta
274 | NODE\tb
275 | NODE\tc
276 | NODE\td
277 | NODE\te
278 | NODE\tf
279 | NODE\tg
280 | NODE\th
281 | NODE\ti
282 | NODE\tj
283 | NODE\tk
284 | NODE\tl
285 | NODE\tm
286 | NODE\tn
287 | SET\tPWRN-a-2-1\t1.0
288 | SET\tPWRN-a-2-2\t1.0
289 | # Sets modifications:
290 | SET\tPWRN-a-1-2\t1.0
291 | SET\tPWRN-a-4-2\t1.0
292 | SET\tPWRN-a-5-2\t1.0
293 | SET\tPWRN-a-7-2\t1.0
294 | IN\ta\tPWRN-a-2-1
295 | IN\tb\tPWRN-a-2-1
296 | IN\tc\tPWRN-a-2-1
297 | IN\td\tPWRN-a-2-2
298 | IN\te\tPWRN-a-2-2
299 | IN\tf\tPWRN-a-2-2
300 | # Inclusions modifications:
301 | IN\tPWRN-a-5-2\tPWRN-a-1-2
302 | IN\ti\tPWRN-a-1-2
303 | IN\tl\tPWRN-a-4-2
304 | IN\tm\tPWRN-a-4-2
305 | IN\tn\tPWRN-a-4-2
306 | IN\tg\tPWRN-a-5-2
307 | IN\th\tPWRN-a-5-2
308 | IN\tj\tPWRN-a-7-2
309 | IN\tk\tPWRN-a-7-2
310 | EDGE\tPWRN-a-2-1\tPWRN-a-2-2\t1.0
311 | EDGE\ta\tj\t1.0
312 | EDGE\ta\tk\t1.0
313 | EDGE\tc\tl\t1.0
314 | EDGE\tc\tm\t1.0
315 | EDGE\tc\tn\t1.0
316 | # Edges modifications:
317 | EDGE\tPWRN-a-1-2\tc\t1.0
318 | EDGE\tPWRN-a-4-2\ta\t1.0
319 | EDGE\tPWRN-a-5-2\ta\t1.0
320 | EDGE\tPWRN-a-7-2\tc\t1.0
321 | """
322 |
--------------------------------------------------------------------------------
/test/test_unambiguous_compression.py:
--------------------------------------------------------------------------------
1 | """Test of graph compression.
2 |
3 | Note that the test_* functions are generated
4 | from the template_test_function function.
5 |
6 | """
7 |
8 | from powergrasp.routines import compress_by_cc
9 | from .test_cases import cases
10 | from .definitions import unified_bubble, gen_test_functions
11 |
12 |
13 | def template_test_function(file:str, bubblelines:str):
14 | def test_function():
15 | found = unified_bubble(compress_by_cc(file))
16 | expected = unified_bubble(bubblelines.splitlines(keepends=False))
17 | assert found == expected
18 | return test_function
19 |
20 |
21 | for name, func in gen_test_functions(cases, template_test_function):
22 | globals()[name] = func
23 |
--------------------------------------------------------------------------------
/test/test_utils.py:
--------------------------------------------------------------------------------
1 |
2 | from powergrasp.utils import quoted
3 |
4 | def test_quoted():
5 | """Hard to test in docstring because of all the backslashs"""
6 | assert quoted('"a') == r'"\"a"'
7 | assert quoted('a"b') == r'"a\"b"'
8 | assert len(quoted('a"b')) == 6
9 | assert quoted(r'"\"a"') == r'"\"\"a\""'
10 | assert quoted(r'"\"a\""') == r'"\"\"a\"\""'
11 | assert len(quoted('"a')) == 5
12 | assert '{}'.format(quoted('"a')) == r'"\"a"'
13 | assert len('{}'.format(quoted('"a'))) == 5
14 | assert quoted(r'"\"a"') == r'"\"\"a\""'
15 |
--------------------------------------------------------------------------------
/triplet/.gitignore:
--------------------------------------------------------------------------------
1 | .cache/
2 | .pytest_cache/
3 | Session.vim
4 | __pycache__/
5 | debug/
6 | dist/
7 | powergrasp.egg-info/
8 | PowerGrASP.egg-info/
9 | venv/
10 |
--------------------------------------------------------------------------------
/triplet/Makefile:
--------------------------------------------------------------------------------
1 |
2 | METHOD=search-byenum.lp
3 | # METHOD=search-byconcept.lp
4 | INFILE_DIR=../data
5 | INFILE=double_biclique_unambiguous.lp
6 | OPTIONS=
7 |
8 | ## Usage and tests
9 | compress:
10 | # clingo 0 --opt-mode=optN -t 4 $(INFILE_DIR)/$(INFILE) $(METHOD)
11 | python search.py $(INFILE_DIR)/$(INFILE) $(METHOD) $(OPTIONS)
12 |
13 | thesis:
14 | - rm out/*.png out/*.bbl
15 | $(MAKE) real-puceron-mi-m-diff-no3019 OPTIONS=""
16 | $(MAKE) real-puceron-mi-m-diff-no3019 OPTIONS="-na 3 -nb 3"
17 | $(MAKE) real-puceron-mi-m-diff-no3019 OPTIONS="-nc 2"
18 | $(MAKE) real-puceron-mi-m-diff-no3019 OPTIONS="-nc 3"
19 | $(MAKE) real-matrixdb-core27 OPTIONS=""
20 | $(MAKE) real-matrixdb-core27 OPTIONS="-na 3 -nb 3"
21 | $(MAKE) real-matrixdb-core27 OPTIONS="-nc 2"
22 | $(MAKE) real-matrixdb-core27 OPTIONS="-nc 3"
23 |
24 | clear-cache:
25 | rm cases_cache/*.dat
26 |
27 | ## All real test cases
28 | real-puceron-mi-m-diff:
29 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=miRNA_mRNA_diff.lp
30 | real-puceron-mi-m-diff-no3019:
31 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=miRNA_mRNA_diff-no3019.lp
32 | real-puceron-mi-lnc:
33 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=miRNA_lncRNA.lp
34 | real-puceron-lnc-m:
35 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=lncRNA_mRNA.lp
36 | real-puceron-mi-m-lnc:
37 | $(MAKE) compress INFILE_DIR=~/data/puceron/data-playing/output INFILE=ternary_concepts_mi_m_lnc-edge.lp
38 | real-matrixdb-core27:
39 | $(MAKE) compress INFILE_DIR=~/data/MatrixDb/compmatrixdb/matrixdb_CORE27_example INFILE=matrixdb_CORE27_example.lp
40 | real-matrixdb-all:
41 | $(MAKE) compress INFILE_DIR=~/data/MatrixDb/compmatrixdb/matrixdb_Human_Human_171107_extended INFILE=matrixdb_Human_Human_171107_extended_0.0.lp
42 |
43 |
44 | ## All test cases
45 | abnormal:
46 | $(MAKE) compress INFILE=abnormal.lp
47 | bintree:
48 | $(MAKE) compress INFILE=bintree.lp
49 | clique:
50 | $(MAKE) compress INFILE=clique.lp
51 | cliques:
52 | $(MAKE) compress INFILE=cliques.lp
53 | concomp:
54 | $(MAKE) compress INFILE=concomp.lp
55 | concept-loop:
56 | $(MAKE) compress INFILE=concept-loop.lp
57 | ddiam:
58 | $(MAKE) compress INFILE=ddiam.lp
59 | diacli:
60 | $(MAKE) compress INFILE=diacli.lp
61 | diamond:
62 | $(MAKE) compress INFILE=diamond.lp
63 | disjoint-subpnodes:
64 | $(MAKE) compress INFILE=disjoint-subpnodes.lp
65 | double_biclique_unambiguous:
66 | $(MAKE) compress INFILE=double_biclique_unambiguous.lp
67 | double-p-groups:
68 | $(MAKE) compress INFILE=double-p-groups.lp
69 | hanging-bio-notree-cc0:
70 | $(MAKE) compress INFILE=hanging-bio-notree-cc0.lp
71 | horrible_data:
72 | $(MAKE) compress INFILE=horrible_data.lp
73 | inclusions:
74 | $(MAKE) compress INFILE=inclusions.lp
75 | consider-included-nodes:
76 | $(MAKE) compress INFILE=consider-included-nodes.lp
77 | motif-overlapping:
78 | $(MAKE) compress INFILE=motif-overlapping.lp
79 | multiple-optimals:
80 | $(MAKE) compress INFILE=multiple-optimals.lp
81 | n8_d0:
82 | $(MAKE) compress INFILE=n8_d0.7.lp
83 | one_edge:
84 | $(MAKE) compress INFILE=one_edge.lp
85 | order:
86 | $(MAKE) compress INFILE=order.lp
87 | overlapping-bicliques:
88 | $(MAKE) compress INFILE=overlapping-bicliques.lp
89 | partition:
90 | $(MAKE) compress INFILE=partition.lp
91 | perfectfit:
92 | $(MAKE) compress INFILE=perfectfit.lp
93 | phosphatase:
94 | $(MAKE) compress INFILE=phosphatase.lp
95 | pnode-to-clique:
96 | $(MAKE) compress INFILE=pnode-to-clique.lp
97 | prio_deg:
98 | $(MAKE) compress INFILE=prio_deg.lp
99 | quasibiclique:
100 | $(MAKE) compress INFILE=quasibiclique.lp
101 | quoting:
102 | $(MAKE) compress INFILE=quoting.lp
103 | single-node:
104 | $(MAKE) compress INFILE=single-node.lp
105 | star:
106 | $(MAKE) compress INFILE=star.lp
107 | structural-binding:
108 | $(MAKE) compress INFILE=structural-binding.lp
109 | structural-binding-maincc:
110 | $(MAKE) compress INFILE=structural-binding-maincc.lp
111 | structural-binding-nobridge:
112 | $(MAKE) compress INFILE=structural-binding-nobridge.lp
113 | testblocks:
114 | $(MAKE) compress INFILE=testblocks.lp
115 | test-gml:
116 | $(MAKE) compress INFILE=test.gml
117 | test-graphml:
118 | $(MAKE) compress INFILE=test.graphml
119 | todel:
120 | $(MAKE) compress INFILE=todel.lp
121 | triplets:
122 | $(MAKE) compress INFILE=triplets.lp
123 | typical:
124 | $(MAKE) compress INFILE=typical-use-case.lp
125 | unclique:
126 | $(MAKE) compress INFILE=unclique.lp
127 | variable-name:
128 | $(MAKE) compress INFILE=variable-name.gml
129 | wiki-tree-decomposition:
130 | $(MAKE) compress INFILE=wiki-tree-decomposition.lp
131 | zorro:
132 | $(MAKE) compress INFILE=zorro.lp
133 |
--------------------------------------------------------------------------------
/triplet/README:
--------------------------------------------------------------------------------
1 | Distribution of triplet concepts, computation of graphics
2 |
--------------------------------------------------------------------------------
/triplet/cases_cache/backup-incomplete/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/backup-incomplete/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/backup-incomplete/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/backup-incomplete/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/backup-incomplete/miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/backup-incomplete/miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/backup-incomplete/miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/backup-incomplete/miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/double_biclique_unambiguous__a_0-_b_0-_c_0-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/double_biclique_unambiguous__a_0-_b_0-_c_0-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/double_biclique_unambiguous__a_0-_b_0-_c_0-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/double_biclique_unambiguous__a_0-_b_0-_c_0-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_0-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_0-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_0-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_0-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_2-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_2-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_2-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_2-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_3-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_3-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_3-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/matrixdb_CORE27_example__a_0-_b_0-_c_3-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/matrixdb_CORE27_example__a_3-_b_3-_c_0-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/matrixdb_CORE27_example__a_3-_b_3-_c_0-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/matrixdb_CORE27_example__a_3-_b_3-_c_0-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/matrixdb_CORE27_example__a_3-_b_3-_c_0-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_2-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_2-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_2-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_2-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_3-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_3-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_3-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_0-_b_0-_c_3-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_3-_b_2-_c_0-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_3-_b_2-_c_0-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_3-_b_2-_c_0-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_3-_b_2-_c_0-.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.concepts.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.concepts.dat
--------------------------------------------------------------------------------
/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/cases_cache/miRNA_mRNA_diff-no3019__a_3-_b_3-_c_0-.dat
--------------------------------------------------------------------------------
/triplet/combinations.py:
--------------------------------------------------------------------------------
1 | """Create a table of triplet state combinations"""
2 |
3 |
4 | import itertools
5 | CROSS = r'\x'
6 | EMPTY = r' '
7 |
8 | A_CASES, B_CASES, C_CASES = (0, 1, 2), (0, 1, 2), (0, 1, 2)
9 | COLSEP = '&'
10 |
11 |
12 | def nb_edge_of(a, b, c):
13 | return int(a * b + a * c + b * c + (c*(c-1))/2)
14 | def is_canonical(a, b, c):
15 | if a == 1 or b == 1:
16 | return False
17 | if (not a) and b: # a empty, but not b
18 | return False
19 | return nb_edge_of(a, b, c) > 0
20 |
21 | def desc_other_forms(a, b, c):
22 | P002 = '$7$', 'equivalent to $A=0, B=0, C=2$'
23 | if nb_edge_of(a, b, c) == 0: return
24 | if a == 0 and b == 0:
25 | if c == 2:
26 | return r'$1+\frac{|C|!}{(|C|-2)!}+2\times |C|$', r'\newline$\forall x,y \in C$: $A=\{x\} \land B=\{y\}$' # permutation writing
27 | elif c == 1:
28 | return r'$3$', r'$A=\{x\} or B=\{x\} or C=\{x\}$'
29 | elif a == 0:
30 | if b == 1:
31 | return P002
32 | else:
33 | assert b == 2
34 | if c == 1:
35 | return r'$2 \times 2$', 'move $C$ in $A$ and $A\leftrightharpoons B$'
36 | elif c == 2:
37 | return r'$1 + |C| \times 2$', 'move $c\in C$ in $A$ and $A\leftrightharpoons B$'
38 | elif b == 0:
39 | if a == 1:
40 | return P002
41 | else:
42 | assert a == 2
43 | if c == 1:
44 | return r'$2 \times 2$', 'move $C$ in $B$ and $A\leftrightharpoons B$'
45 | elif c == 2:
46 | return r'$1 + |C| \times 2$', 'move $c\in C$ in $B$ and $A\leftrightharpoons B$'
47 | if a == 1 and b == 1:
48 | if c == 0:
49 | return P002
50 | elif c == 1:
51 | return P002
52 | else:
53 | return P002
54 | elif a == 1:
55 | assert b == 2
56 | return r'2', r'$A=\emptyset \land C=C\cup A$'
57 | elif b == 1:
58 | assert a == 2
59 | return r'2', r'$B=\emptyset \land C=C\cup B$'
60 | elif a == 2 and b == 2:
61 | return r'2', r'$A\leftrightharpoons B$'
62 | assert False, f'MISSING CASE: ({a}, {b}, {c})'
63 |
64 | columns = {
65 | r'$A_0$': lambda a, b, c: a == 0,
66 | r'$A_1$': lambda a, b, c: a == 1,
67 | r'$A_{2+}$': lambda a, b, c: a == 2,
68 | r'$B_0$': lambda a, b, c: b == 0,
69 | r'$B_1$': lambda a, b, c: b == 1,
70 | r'$B_{2+}$': lambda a, b, c: b == 2,
71 | r'$C_0$': lambda a, b, c: c == 0,
72 | r'$C_1$': lambda a, b, c: c == 1,
73 | r'$C_{2+}$': lambda a, b, c: c == 2,
74 | # 'has edge': lambda a, b, c: nb_edge_of(a, b, c) > 0,
75 | # 'canonical': is_canonical,
76 | 'edge': lambda a, b, c: nb_edge_of(a, b, c) > 0,
77 | 'can.': is_canonical,
78 | r'\# of form': desc_other_forms,
79 | }
80 | def render_desc_other_forms(segments):
81 | if segments is None: return '' # 'no edges' # clearer when empty ?
82 | return ' $~\LeftRightArrow~$ '.join(v for v in segments if v)
83 |
84 | renderer = {
85 | r'\# of form': render_desc_other_forms,
86 | }
87 | default_renderer = lambda x: CROSS if x else EMPTY # used if column not in renderer dict
88 |
89 | lambda segs: ' ' + ' + '.join(segs) + ' '
90 | print(r"""
91 | \begin{table}
92 | \centering\footnotesize\tabcolsep=0.11cm
93 | \begin{tabular}{|c|c|c||c|c|c||c|c|c||c|c||c|}
94 | \cline{1-9}
95 | \multicolumn{9}{|c|}{Triplet composition (in node number)} & \multicolumn{3}{c}{} \\\hline
96 | """)
97 | print(r'\begin{tabular}{|' + '|'.join('c' for _ in columns) + '|}')
98 | print(' ' + f' {COLSEP} '.join(columns) + r' \\\hline\hline')
99 | for a, b, c in itertools.product(A_CASES, B_CASES, C_CASES):
100 | if is_canonical(a, b, c) or True:
101 | line = COLSEP.join((
102 | str(renderer.get(col, default_renderer)(call(a, b, c))).center(len(col)+2)
103 | for col, call in columns.items()
104 | ))
105 | print(line + r'\\\hline')
106 | print(r"""
107 | \end{tabular}
108 | \caption{Canonicity and number of alternative writing of all triplets according to their node composition. The rightmost column indicates the number of alternative writing, based on the number of node in set $C$, and on the permutation of sets $A$ and $B$, noted $A\leftrightharpoons B$.}
109 | % see ~/packages/powergrasp/triplet/combinations.py for generation of that table (it has been edited directly here since)
110 | \label{tab:triplet-concept:canonicity-distribution}
111 | \end{table} % Table tab:triplet-concept:canonicity-distribution
112 | """)
113 |
--------------------------------------------------------------------------------
/triplet/compute-concepts.lp:
--------------------------------------------------------------------------------
1 | edge(X,Y) :- edge(Y,X). % add symmetry
2 | edge(X,X) :- edge(X,_). % add reflexivity
3 | ext(X) :- edge(X,_) ; edge(X,Y): int(Y).
4 | int(Y) :- edge(_,Y) ; edge(X,Y): ext(X).
5 | #show ext/1.
6 | #show int/1.
7 |
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_allindex_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_allindex_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_allindex_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_formal_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_formal_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_formal_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_formal_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_formal_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_allindex_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_allindex_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_allindex_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_2-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_matrixdb_CORE27_example__a_0-_b_0-_c_3-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_matrixdb_CORE27_example__a_3-_b_3-_c_0-.png
--------------------------------------------------------------------------------
/triplet/out/number_triplet_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Aluriak/PowerGrASP/2b601e40c483dcc1dc1eb27ac31d76bbc99e7e94/triplet/out/number_triplet_concept_per_score_miRNA_mRNA_diff-no3019__a_0-_b_0-_c_0-.png
--------------------------------------------------------------------------------
/triplet/search-byconcept.lp:
--------------------------------------------------------------------------------
1 | % Search for a maximal triplet, using concept method.
2 | % input: edge(X,Y): X is linked to Y
3 | rel(X,Y) :- edge(X,Y).
4 |
5 | % Maximization:
6 | % nb(a,Na) :- Na = {a(_)}.
7 | % nb(b,Nb) :- Nb = {b(_)}.
8 | % nb(c,Nc) :- Nc = {c(_)}.
9 | % cover(ab,N) :- N=Na*Nb ; nb(a,Na) ; nb(b,Nb).
10 | % cover(ac,N) :- N=Na*Nc ; nb(a,Na) ; nb(c,Nc).
11 | % cover(bc,N) :- N=Nb*Nc ; nb(b,Nb) ; nb(c,Nc).
12 | % cover(cc,N) :- N=(Nc*(Nc-1))/2 ; nb(c,Nc).
13 |
14 | % cover(N) :- N=#sum{X:cover(_,X)}.
15 | % cover(N) :- N=(Na*Nb)+(Na*Nc)+(Nb*Nc)+(Nc*(Nc-1))/2 ; Na = {a(_)} ; Nb = {b(_)} ; Nc = {c(_)}.
16 |
17 | % #maximize{N:cover(N)}.
18 | #show a/1.
19 | #show b/1.
20 | #show c/1.
21 |
22 |
23 | #const min_a=0.
24 | #const min_b=0.
25 | #const min_c=0.
26 | #const max_a=#sup.
27 | #const max_b=#sup.
28 | #const max_c=#sup.
29 |
30 | :- {a(_)}min_a-1.
31 | :- {b(_)}min_b-1.
32 | :- {c(_)}min_c-1.
33 | :- max_a+1{a(_)}.
34 | :- max_b+1{b(_)}.
35 | :- max_c+1{c(_)}.
36 |
37 |
38 | % Mining of triplet concepts, by deriving a triplet from each formal concept
39 | % found in the graph context with all reflexive edges.
40 | % Don't care positions to yield triplet concepts seems to not be necessary.
41 |
42 |
43 | % Ensure symmetry of the input relation.
44 | rel(X,Y) :- rel(Y,X).
45 | rel(X,X) :- rel(X,_). % use all reflexive edges
46 |
47 | % Mine formal concepts.
48 | obj(O) :- rel(O,_) ; rel(O,A): att(A).
49 | att(A) :- rel(A,_) ; rel(O,A): obj(O).
50 |
51 | % Convert formal concept as triplet.
52 | a(O) :- obj(O) ; not att(O).
53 | b(A) :- att(A) ; not obj(A).
54 | c(C) :- obj(C) ; att(C).
55 |
56 | % break symmetry
57 | :- b(A) ; A