From 2b5214dbdaccf49aaaeea91436e639a5d4a6645c Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 21 Nov 2022 14:35:39 +0100 Subject: [PATCH 01/12] Update for new version --- .drone.jsonnet | 19 +- .drone.yml | 2344 ++++++++++++++++- .gitignore | 5 +- Brewfile | 1 + CMakeLists.txt | 12 +- ChangeLog.rst | 121 + bindep.txt | 3 + bootstrap.sh | 21 +- cmake/MODUtils.cmake | 2 +- conda/build.Dockerfile | 2 +- conda/conda_build_config.yaml | 5 +- conda/meta.yaml | 2 + conda/test.py | 4 +- doc/CMakeLists.txt | 3 +- doc/makeDocs.sh | 96 +- doc/source/compiling.rst | 11 +- doc/source/conf.py | 111 +- doc/source/dataDesc/graph.rst | 193 -- doc/source/dataDesc/molEnc.rst | 39 - doc/source/dataDesc/rule.rst | 19 - doc/source/dataDesc/term.rst | 35 - doc/source/dgStrat/{dgStrat.rst => index.rst} | 0 doc/source/epim/{epim.rst => index.rst} | 0 .../modWrapper.rst => exe/index.rst} | 2 +- doc/source/formats/dfs.rst | 135 + doc/source/{dataDesc => formats}/dg.rst | 3 - doc/source/formats/dot.rst | 6 + .../dataDesc.rst => formats/gml.rst} | 44 +- doc/source/formats/index.rst | 23 + doc/source/formats/mdl.rst | 31 + doc/source/formats/smiles.rst | 82 + doc/source/formats/tikz.rst | 19 + doc/source/graphModel/index.rst | 272 ++ doc/source/index.rst | 53 + doc/source/installation.rst | 2 +- doc/source/libmod/api.rst | 2 +- doc/source/libmod/{libmod.rst => index.rst} | 2 + doc/source/postmod/{postmod.rst => index.rst} | 4 +- doc/source/pymod/api.rst | 2 +- doc/source/pymod/{pymod.rst => index.rst} | 3 + docker/Arch.Dockerfile | 3 +- docker/Fedora.Dockerfile | 6 +- docker/Ubuntu.Dockerfile | 5 +- examples/CMakeLists.txt | 2 +- .../000_hello.py} | 4 +- .../010_graphLoading.py} | 0 .../011_graphPrinting.py} | 0 .../012_graphInterface.py} | 0 .../013_graphMorphisms.py} | 0 .../030_ruleLoading.py} | 0 .../031_ruleMorphisms.py} | 0 .../050_formoseGrammar.py} | 0 examples/py/000_basics/051_fileInclusion.py | 9 + examples/py/000_basics/meta.json | 4 + examples/py/0051_fileInclusion.py | 9 - .../100_rcGraphOps.py} | 8 +- .../101_rcParallel.py} | 2 +- .../102_rcSuper.py} | 2 +- .../120_rcFormose.py} | 2 +- examples/py/010_rc/meta.json | 4 + .../210_dgFixed.py} | 2 +- .../211_dgRepeat.py} | 2 +- .../212_dgPredicate.py} | 4 +- .../250_dgPrinting.py} | 4 +- .../260_dpoPrinting.py} | 2 +- examples/py/020_dg/meta.json | 4 + .../320_aconitase.py} | 0 .../330_tartaric.py} | 0 .../{0340_tree.py => 030_stereo/340_tree.py} | 0 examples/py/030_stereo/meta.json | 4 + .../0_encoding_simple.py} | 0 .../1_encoding_restriction.py} | 0 .../2_encoding_recursive.py} | 0 .../3_encoding_config.py} | 0 .../4_exec_space_simple.py} | 0 .../5_exec_space_recursive.py} | 0 .../6_hospital.py} | 0 examples/py/900_epim/meta.json | 4 + examples/py/fileRenames.json | 63 + examples/py/sections.json | 7 - examples/pymod_extension/CMakeLists.txt | 2 +- libs/jla_boost/CMakeLists.txt | 22 +- .../graph/EdgeIndexedAdjacencyList.hpp | 25 +- .../include/jla_boost/graph/dpo/IO.hpp | 17 - .../include/jla_boost/graph/dpo/Rule.hpp | 94 - .../graph/morphism/AsPropertyMap.hpp | 2 +- .../jla_boost/graph/morphism/Concepts.hpp | 160 ++ .../jla_boost/graph/morphism/VertexMap.hpp | 107 - .../graph/morphism/callbacks/Filter.hpp | 2 +- .../graph/morphism/callbacks/Limit.hpp | 2 +- .../graph/morphism/callbacks/Store.hpp | 2 +- .../graph/morphism/callbacks/Transform.hpp | 2 +- .../graph/morphism/callbacks/Unwrapper.hpp | 2 +- .../graph/morphism/finders/CommonSubgraph.hpp | 31 +- .../finders/mcgregor_common_subgraphs.hpp | 1126 ++++++++ .../graph/morphism/models/Inverted.hpp | 2 +- .../morphism/models/InvertibleAdaptor.hpp | 2 +- .../morphism/models/InvertibleVector.hpp | 256 ++ .../graph/morphism/models/PropertyMap.hpp | 26 +- .../morphism/models/PropertyVertexMap.hpp | 2 +- .../graph/morphism/models/Vector.hpp | 205 +- libs/jla_boost/src/graph/dpo/IO.cpp | 17 - libs/libmod/CMakeLists.txt | 2 +- libs/libmod/src/mod/Chem.cpp | 8 +- libs/libmod/src/mod/Config.cpp | 26 +- libs/libmod/src/mod/Config.hpp | 111 +- libs/libmod/src/mod/Derivation.hpp | 8 +- libs/libmod/src/mod/Error.cpp | 2 +- libs/libmod/src/mod/Function.cpp | 12 - libs/libmod/src/mod/GraphConcepts.hpp | 2 +- libs/libmod/src/mod/Misc.cpp | 6 +- libs/libmod/src/mod/Post.cpp | 64 +- libs/libmod/src/mod/Post.hpp | 75 +- libs/libmod/src/mod/Term.cpp | 21 +- libs/libmod/src/mod/VertexMap.hpp | 9 +- libs/libmod/src/mod/dg/Builder.cpp | 22 +- libs/libmod/src/mod/dg/Builder.hpp | 17 + libs/libmod/src/mod/dg/DG.cpp | 16 +- libs/libmod/src/mod/dg/ForwardDecl.hpp | 20 +- libs/libmod/src/mod/dg/GraphInterface.cpp | 6 +- libs/libmod/src/mod/dg/GraphInterface.hpp | 6 +- libs/libmod/src/mod/dg/Printer.cpp | 33 +- libs/libmod/src/mod/dg/Printer.hpp | 39 +- libs/libmod/src/mod/graph/Graph.cpp | 209 +- libs/libmod/src/mod/graph/Graph.hpp | 108 +- libs/libmod/src/mod/graph/GraphInterface.cpp | 4 +- libs/libmod/src/mod/graph/Printer.cpp | 31 +- libs/libmod/src/mod/graph/Printer.hpp | 34 +- libs/libmod/src/mod/graph/Union.cpp | 13 +- libs/libmod/src/mod/graph/Union.hpp | 2 + libs/libmod/src/mod/graph/internal/Graph.cpp | 4 +- .../mod/lib/Algorithm/ConnectedComponents.hpp | 55 + .../src/mod/lib/Algorithm/Container.hpp | 23 + .../MultiDimSelector.hpp} | 36 +- libs/libmod/src/mod/lib/Algorithm/Point.hpp | 27 + libs/libmod/src/mod/lib/Chem/MDL.cpp | 1881 +++++++++++++ libs/libmod/src/mod/lib/Chem/MDL.hpp | 19 + libs/libmod/src/mod/lib/Chem/MoleculeUtil.cpp | 10 + libs/libmod/src/mod/lib/Chem/MoleculeUtil.hpp | 12 +- libs/libmod/src/mod/lib/Chem/OBabel.cpp | 195 +- libs/libmod/src/mod/lib/Chem/OBabel.hpp | 52 +- libs/libmod/src/mod/lib/Chem/Smiles.hpp | 9 +- libs/libmod/src/mod/lib/Chem/SmilesRead.cpp | 46 +- libs/libmod/src/mod/lib/DG/Dump.cpp | 16 +- libs/libmod/src/mod/lib/DG/Hyper.cpp | 10 +- libs/libmod/src/mod/lib/DG/Hyper.hpp | 35 +- .../mod/lib/{IO/DGRead.cpp => DG/IO/Read.cpp} | 120 +- libs/libmod/src/mod/lib/DG/IO/Read.hpp | 52 + .../lib/{IO/DGWrite.cpp => DG/IO/Write.cpp} | 300 ++- .../mod/lib/{IO/DG.hpp => DG/IO/Write.hpp} | 332 ++- .../IO/WriteDerivation.cpp} | 77 +- .../IO/WriteDetail.hpp} | 26 +- libs/libmod/src/mod/lib/DG/NonHyper.cpp | 6 +- libs/libmod/src/mod/lib/DG/NonHyper.hpp | 6 +- .../libmod/src/mod/lib/DG/NonHyperBuilder.cpp | 114 +- .../libmod/src/mod/lib/DG/NonHyperBuilder.hpp | 2 +- .../src/mod/lib/DG/RuleApplicationUtils.hpp | 17 +- .../libmod/src/mod/lib/DG/Strategies/Rule.cpp | 55 +- libs/libmod/src/mod/lib/DPO/CombinedRule.cpp | 129 + libs/libmod/src/mod/lib/DPO/CombinedRule.hpp | 157 ++ libs/libmod/src/mod/lib/DPO/Concepts.hpp | 66 + .../mod/lib/DPO}/FilteredGraphProjection.hpp | 46 +- libs/libmod/src/mod/lib/DPO/Membership.cpp | 19 + libs/libmod/src/mod/lib/DPO/Membership.hpp | 28 + libs/libmod/src/mod/lib/DPO/Traits.hpp | 15 + libs/libmod/src/mod/lib/Graph/Collection.cpp | 2 +- libs/libmod/src/mod/lib/Graph/DFSEncoding.cpp | 623 ----- libs/libmod/src/mod/lib/Graph/DFSEncoding.hpp | 19 - .../Depiction.cpp => IO/DepictionData.cpp} | 131 +- .../Depiction.hpp => IO/DepictionData.hpp} | 43 +- libs/libmod/src/mod/lib/Graph/IO/Read.cpp | 524 ++++ libs/libmod/src/mod/lib/Graph/IO/Read.hpp | 52 + libs/libmod/src/mod/lib/Graph/IO/Write.cpp | 665 +++++ libs/libmod/src/mod/lib/Graph/IO/Write.hpp | 52 + .../src/mod/lib/Graph/LabelledGraph.cpp | 2 +- .../src/mod/lib/Graph/LabelledGraph.hpp | 1 + .../src/mod/lib/Graph/Properties/Molecule.cpp | 13 +- .../src/mod/lib/Graph/Properties/Term.cpp | 4 +- libs/libmod/src/mod/lib/Graph/Single.cpp | 107 +- libs/libmod/src/mod/lib/Graph/Single.hpp | 15 +- .../GraphMorphism/CommonSubgraphFinder.hpp | 3 +- .../GraphMorphism/Constraints/AllVisitor.hpp | 20 +- .../Constraints/CheckVisitor.hpp | 32 +- .../GraphMorphism/Constraints/Constraint.hpp | 7 +- .../Constraints/ShortestPath.hpp | 53 +- .../Constraints/VertexAdjacency.hpp | 68 +- .../lib/GraphMorphism/Constraints/Visitor.hpp | 17 +- .../src/mod/lib/GraphMorphism/Finder.hpp | 13 +- .../lib/GraphMorphism/IO/WriteConstraints.hpp | 152 ++ .../mod/lib/GraphMorphism/StereoVertexMap.hpp | 2 +- .../mod/lib/GraphMorphism/TermVertexMap.hpp | 52 +- libs/libmod/src/mod/lib/GraphPimpl.hpp | 19 +- libs/libmod/src/mod/lib/IO/Config.hpp | 2 +- libs/libmod/src/mod/lib/IO/DFS.cpp | 412 +++ libs/libmod/src/mod/lib/IO/DFS.hpp | 103 + libs/libmod/src/mod/lib/IO/Derivation.hpp | 28 - .../src/mod/lib/IO/{GMLUtil.cpp => GML.cpp} | 2 +- .../src/mod/lib/IO/{GMLUtils.hpp => GML.hpp} | 11 +- libs/libmod/src/mod/lib/IO/Graph.hpp | 254 -- libs/libmod/src/mod/lib/IO/GraphRead.cpp | 282 -- libs/libmod/src/mod/lib/IO/GraphWrite.cpp | 335 +-- libs/libmod/src/mod/lib/IO/GraphWrite.hpp | 155 ++ ...hWriteDetail.hpp => GraphWriteGeneric.hpp} | 207 +- libs/libmod/src/mod/lib/IO/IO.cpp | 21 +- libs/libmod/src/mod/lib/IO/IO.hpp | 5 +- .../mod/lib/IO/{JsonUtils.cpp => Json.cpp} | 2 +- .../mod/lib/IO/{JsonUtils.hpp => Json.hpp} | 6 +- .../src/mod/lib/IO/MorphismConstraints.hpp | 144 - .../lib/IO/{ParsingUtil.hpp => Parsing.hpp} | 31 +- libs/libmod/src/mod/lib/IO/Result.hpp | 4 +- libs/libmod/src/mod/lib/IO/Rule.hpp | 81 - libs/libmod/src/mod/lib/IO/RuleRead.cpp | 538 ---- libs/libmod/src/mod/lib/IO/RuleWrite.cpp | 832 ------ libs/libmod/src/mod/lib/IO/Stereo.hpp | 43 - libs/libmod/src/mod/lib/IO/StereoWrite.cpp | 581 ---- libs/libmod/src/mod/lib/IO/Term.cpp | 306 --- libs/libmod/src/mod/lib/IO/Term.hpp | 27 - libs/libmod/src/mod/lib/LabelledGraph.hpp | 13 +- libs/libmod/src/mod/lib/RC/Compose.hpp | 22 +- .../libmod/src/mod/lib/RC/ComposeRuleReal.cpp | 23 - .../libmod/src/mod/lib/RC/ComposeRuleReal.hpp | 6 + .../src/mod/lib/RC/ComposeRuleRealCommon.cpp | 10 + .../src/mod/lib/RC/ComposeRuleRealGeneric.hpp | 34 +- .../mod/lib/RC/ComposeRuleRealParallel.cpp | 10 + .../src/mod/lib/RC/ComposeRuleRealSub.cpp | 10 + .../src/mod/lib/RC/ComposeRuleRealSuper.cpp | 10 + libs/libmod/src/mod/lib/RC/Evaluator.cpp | 4 +- .../mod/lib/{IO/RC.cpp => RC/IO/Write.cpp} | 98 +- .../mod/lib/{IO/RC.hpp => RC/IO/Write.hpp} | 18 +- .../src/mod/lib/RC/LabelledComposition.hpp | 44 +- libs/libmod/src/mod/lib/RC/MatchBuilder.cpp | 16 +- libs/libmod/src/mod/lib/RC/MatchBuilder.hpp | 6 +- .../src/mod/lib/RC/MatchMaker/Common.hpp | 4 +- .../lib/RC/MatchMaker/ComponentWiseUtil.hpp | 3 - .../mod/lib/RC/MatchMaker/LabelledMatch.hpp | 75 +- .../src/mod/lib/RC/MatchMaker/Parallel.hpp | 21 +- libs/libmod/src/mod/lib/RC/MatchMaker/Sub.hpp | 12 +- .../src/mod/lib/RC/MatchMaker/Super.hpp | 12 +- libs/libmod/src/mod/lib/RC/Result.hpp | 67 +- .../src/mod/lib/RC/Visitor/Compound.hpp | 196 +- .../mod/lib/RC/Visitor/MatchConstraints.hpp | 109 +- libs/libmod/src/mod/lib/RC/Visitor/Stereo.hpp | 415 +-- libs/libmod/src/mod/lib/RC/Visitor/String.hpp | 195 +- libs/libmod/src/mod/lib/RC/Visitor/Term.hpp | 242 +- .../mod/lib/RC/detail/CompositionHelper.hpp | 247 +- .../src/mod/lib/Rules/ConnectedComponent.hpp | 18 +- .../src/mod/lib/Rules/GraphAsRuleCache.cpp | 8 +- .../src/mod/lib/Rules/GraphAsRuleCache.hpp | 7 +- libs/libmod/src/mod/lib/Rules/GraphDecl.hpp | 52 +- libs/libmod/src/mod/lib/Rules/GraphToRule.hpp | 45 +- .../src/mod/lib/Rules/IO/DepictionData.cpp | 614 +++++ .../src/mod/lib/Rules/IO/DepictionData.hpp | 138 + libs/libmod/src/mod/lib/Rules/IO/Read.cpp | 984 +++++++ libs/libmod/src/mod/lib/Rules/IO/Read.hpp | 31 + libs/libmod/src/mod/lib/Rules/IO/Write.cpp | 984 +++++++ libs/libmod/src/mod/lib/Rules/IO/Write.hpp | 65 + .../libmod/src/mod/lib/Rules/LabelledRule.cpp | 342 ++- .../libmod/src/mod/lib/Rules/LabelledRule.hpp | 207 +- .../mod/lib/Rules/Properties/Depiction.cpp | 527 ---- .../mod/lib/Rules/Properties/Depiction.hpp | 80 - .../src/mod/lib/Rules/Properties/Molecule.cpp | 80 +- .../src/mod/lib/Rules/Properties/Molecule.hpp | 32 +- .../src/mod/lib/Rules/Properties/Property.cpp | 43 +- .../src/mod/lib/Rules/Properties/Property.hpp | 535 ++-- .../src/mod/lib/Rules/Properties/Stereo.hpp | 165 +- .../src/mod/lib/Rules/Properties/String.cpp | 80 +- .../src/mod/lib/Rules/Properties/String.hpp | 32 +- .../src/mod/lib/Rules/Properties/Term.cpp | 138 +- .../src/mod/lib/Rules/Properties/Term.hpp | 40 +- libs/libmod/src/mod/lib/Rules/Real.cpp | 110 +- libs/libmod/src/mod/lib/Rules/Real.hpp | 36 +- .../src/mod/lib/Stereo/Configuration/Any.cpp | 8 +- .../src/mod/lib/Stereo/Configuration/Any.hpp | 14 +- .../Stereo/Configuration/Configuration.cpp | 8 +- .../Stereo/Configuration/Configuration.hpp | 33 +- .../mod/lib/Stereo/Configuration/Linear.cpp | 8 +- .../mod/lib/Stereo/Configuration/Linear.hpp | 14 +- .../lib/Stereo/Configuration/Tetrahedral.cpp | 8 +- .../lib/Stereo/Configuration/Tetrahedral.hpp | 14 +- .../Stereo/Configuration/TrigonalPlanar.cpp | 8 +- .../Stereo/Configuration/TrigonalPlanar.hpp | 15 +- .../src/mod/lib/Stereo/EmbeddingEdge.cpp | 10 +- .../src/mod/lib/Stereo/EmbeddingEdge.hpp | 15 +- .../{IO/StereoRead.cpp => Stereo/IO/Read.cpp} | 46 +- libs/libmod/src/mod/lib/Stereo/IO/Read.hpp | 25 + libs/libmod/src/mod/lib/Stereo/IO/Write.cpp | 133 + libs/libmod/src/mod/lib/Stereo/IO/Write.hpp | 13 + .../mod/lib/Stereo/IO/WriteConfiguration.cpp | 109 + .../mod/lib/Stereo/IO/WriteConfiguration.hpp | 305 +++ libs/libmod/src/mod/lib/Stereo/Inference.hpp | 171 +- libs/libmod/src/mod/lib/Term/IO/Read.cpp | 113 + libs/libmod/src/mod/lib/Term/IO/Read.hpp | 20 + libs/libmod/src/mod/lib/Term/IO/Write.cpp | 192 ++ libs/libmod/src/mod/lib/Term/IO/Write.hpp | 25 + libs/libmod/src/mod/lib/Term/WAM.hpp | 2 +- libs/libmod/src/mod/rule/CompositionMatch.cpp | 16 +- libs/libmod/src/mod/rule/GraphInterface.cpp | 425 +-- libs/libmod/src/mod/rule/Rule.cpp | 49 +- libs/libmod/src/mod/rule/Rule.hpp | 16 +- libs/libmod/src/mod/rule/internal/Rule.cpp | 37 +- libs/libmod/src/mod/rule/internal/Rule.hpp | 23 +- libs/post_mod/bin/mod_post | 86 +- libs/post_mod/share/mod/figureTemplate.tex | 15 +- libs/pymod/CMakeLists.txt | 25 +- libs/pymod/bin/mod.in | 14 +- libs/pymod/lib/mod/__init__.py | 170 +- libs/pymod/lib/mod/latex.py | 14 +- libs/pymod/lib/mod/libpymod.pyi | 106 +- libs/pymod/redirector/mod/__init__.py.in | 6 + libs/pymod/redirector/pyproject.toml | 3 + libs/pymod/redirector/setup.cfg.in | 10 + libs/pymod/share/mod/python.supp | 17 + libs/pymod/src/mod/py/Chem.cpp | 5 +- libs/pymod/src/mod/py/Collections.cpp | 3 +- libs/pymod/src/mod/py/Config.cpp | 125 + libs/pymod/src/mod/py/Function.cpp | 16 +- libs/pymod/src/mod/py/Post.cpp | 89 +- libs/pymod/src/mod/py/dg/Builder.cpp | 16 + libs/pymod/src/mod/py/dg/DG.cpp | 2 +- libs/pymod/src/mod/py/dg/Printer.cpp | 29 + libs/pymod/src/mod/py/graph/Graph.cpp | 139 +- libs/pymod/src/mod/py/graph/Printer.cpp | 172 +- libs/pymod/src/mod/py/graph/Union.cpp | 1 + libs/pymod/src/mod/py/rule/Rule.cpp | 18 +- libs/pymodutils/CMakeLists.txt | 33 + .../src/mod/py/Common.hpp | 25 +- .../src/mod/py/Function.hpp | 8 +- .../src/mod/py/VertexMap.hpp | 0 scripts/checkJsonVisibility.sh | 6 +- scripts/flake8.sh | 2 +- scripts/makePyExamples.py | 100 + scripts/mypy.sh | 2 +- scripts/printDepGraph.py | 1 - test/CMakeLists.txt | 5 + test/cpp/dg/printer.cpp | 31 +- test/cpp/graph/graph.cpp | 19 + test/py/chem.py | 107 +- test/py/config.py | 9 +- test/py/derivation.py | 2 +- test/py/dg/021_build_addHyperEdge.py | 27 + test/py/dg/030_build_apply.py | 9 + test/py/dg/035_build_apply_nonProper.py | 15 + test/py/dg/121_build_execute_rule.py | 3 + test/py/dg/401_printer_using.py | 77 +- test/py/dg/402_printer_latex_escape.py | 25 + test/py/dg/411_printData_using.py | 10 +- test/py/dg/449_printNonHyper.py | 2 +- ...450_print_dpo.py => 450_print_dpo_fail.py} | 18 +- test/py/dg/451_print_dpo_no_openbabel.py | 45 - test/py/dg/451_print_dpo_open_babel.py | 2 + test/py/dg/452_print_dpo_graphviz.py | 2 + test/py/formoseCommon/test.py | 21 + test/py/function.py | 8 +- test/py/graph/010_basic_loading.py | 63 +- test/py/graph/020_graphDFS.py | 53 - test/py/graph/020_graphDFS/common.py | 39 + test/py/graph/020_graphDFS/dot.py | 34 + test/py/graph/020_graphDFS/other.py | 7 + test/py/graph/020_graphDFS/tests.py | 28 + test/py/graph/020_graphDFS/topo.py | 80 + test/py/graph/020_graphDFS/whitespace.py | 8 + test/py/graph/030_smiles/abstract.py | 5 +- test/py/graph/030_smiles/components.py | 2 +- .../030_smiles/mass/genSymbolJumpTable.py | 2 +- test/py/graph/030_smiles/mass/problematic.py | 2 +- test/py/graph/030_smiles/mass/rhea.py | 2 +- test/py/graph/030_smiles/mass/smiles.py | 6 +- .../mass/smilesMetacycReactionSmiles.py | 2 +- .../mass/smiles_cansmi_roundtrip.py | 2 +- test/py/graph/030_smiles/mass/smiles_nci.py | 2 +- test/py/graph/050_mdl/allSmiles.py | 104 + test/py/graph/050_mdl/mol2000.py | 384 +++ test/py/graph/050_mdl/mol3000.py | 640 +++++ test/py/graph/050_mdl/molMult.py | 15 + test/py/graph/050_mdl/sd.py | 34 + test/py/graph/050_mdl/sdMulti.py | 37 + test/py/graph/201_morphisms.py | 67 + test/py/graph/{130_aut.py => 250_aut.py} | 2 +- test/py/graph/301_image.py | 11 + test/py/graph/800_printer.py | 88 + test/py/graph/801_printer_graphviz.py | 24 + test/py/graph/900_union.py | 3 + test/py/graph/graph.py | 23 - .../matchConstraints/vertexAdjacency/main.py | 2 +- .../vertexAdjacency/termConversion.py | 8 +- test/py/molDepiction.py | 12 +- test/py/papers/21_tcs/calc.py | 20 +- test/py/post.py | 39 + test/py/rc/000_rules.py | 363 --- test/py/rc/010_base_test.py | 62 + test/py/rc/500_compositionMatch.py | 43 +- test/py/rc/checks.py | 358 --- test/py/rc/test.py | 57 - test/py/rc/xxx_base_checks.py | 595 +++++ test/py/rcEval/common.py | 11 +- test/py/rcEval/composeCommon.py | 2 +- test/py/rcEval/convertGraph.py | 2 +- ..._basic_loading.py => 001_gml_interface.py} | 2 +- .../{001_loadFail.py => 002_gml_loadFail.py} | 42 +- test/py/rule/002_loadTopoFail.py | 29 - test/py/rule/003_gml_loadTopoFail.py | 53 + ...Fail.py => 004_gml_loadConstraintsFail.py} | 4 +- test/py/rule/010_gml_basic_structure.py | 83 + test/py/rule/030_gml_stereo_loadFail.py | 96 + .../{110_extId.py => 049_gml_externalIds.py} | 4 +- test/py/rule/100_ruleInterface.py | 146 - test/py/rule/101_dfs_basic_loading.py | 11 + test/py/rule/102_dfs_loadFail.py | 4 + test/py/rule/103_dfs_topo.py | 26 + test/py/rule/110_dfs_basic_structure.py | 121 + test/py/rule/111_dfs_load_implicit.py | 6 + test/py/rule/112_dfs_whitespace.py | 37 + test/py/rule/149_dfs_externalIds.py | 24 + test/py/rule/{010_basic.py => 500_basic.py} | 9 - test/py/rule/501_numComponents.py | 22 + test/py/rule/510_ruleInterface.py | 234 ++ test/py/rule/520_inverse.py | 88 + test/py/rule/800_morphism.py | 42 + ...ftRight.py => 850_isomorphismLeftRight.py} | 7 +- test/py/rule/{500_print.py => 900_print.py} | 2 +- test/py/rule/901_print_graphviz.py | 20 + test/py/rule/910_depict.py | 42 + test/py/rule/911_depict_hydrogen_collapse.py | 16 + test/py/rule/920_depict_stereo.py | 28 + test/py/rule/rule.gml | 27 - test/py/rule/rule.py | 53 - test/py/rule/xxx_gml_stereo.py | 68 + test/py/rule/xxx_helpers.py | 42 +- test/py/stereo/00_geometryAndEmbedding.py | 8 +- test/py/stereo/01_geometry.py | 8 +- test/py/stereo/02_embedding.py | 8 +- test/py/stereo/03_completeDeduce.py | 4 +- test/py/stereo/04_graphFix.py | 12 +- test/py/stereo/20_morphismGraph.py | 14 +- test/py/stereo/graphDepiction.py | 8 +- test/py/stereo/ruleDepiction.py | 10 +- test/py/stereo/smiles.py | 16 +- test/py/stereo/smilesAll.py | 2 +- test/py/stereo/testTetraRule.py | 6 +- test/py/term/counter.py | 13 +- test/py/term/makeVars.py | 8 +- test/py/term/parsing.py | 56 +- test/py/term/relations.py | 19 +- test/py/uniqueFilePrefix.py | 5 - test/py/xxx_graphInterface.py | 2 +- test/py/xxx_helpers.py | 16 +- 446 files changed, 22558 insertions(+), 11274 deletions(-) delete mode 100644 doc/source/dataDesc/graph.rst delete mode 100644 doc/source/dataDesc/molEnc.rst delete mode 100644 doc/source/dataDesc/rule.rst delete mode 100644 doc/source/dataDesc/term.rst rename doc/source/dgStrat/{dgStrat.rst => index.rst} (100%) rename doc/source/epim/{epim.rst => index.rst} (100%) rename doc/source/{modWrapper/modWrapper.rst => exe/index.rst} (99%) create mode 100644 doc/source/formats/dfs.rst rename doc/source/{dataDesc => formats}/dg.rst (94%) create mode 100644 doc/source/formats/dot.rst rename doc/source/{dataDesc/dataDesc.rst => formats/gml.rst} (76%) create mode 100644 doc/source/formats/index.rst create mode 100644 doc/source/formats/mdl.rst create mode 100644 doc/source/formats/smiles.rst create mode 100644 doc/source/formats/tikz.rst create mode 100644 doc/source/graphModel/index.rst create mode 100644 doc/source/index.rst rename doc/source/libmod/{libmod.rst => index.rst} (91%) rename doc/source/postmod/{postmod.rst => index.rst} (95%) rename doc/source/pymod/{pymod.rst => index.rst} (94%) rename examples/py/{0000_hello.py => 000_basics/000_hello.py} (88%) rename examples/py/{0010_graphLoading.py => 000_basics/010_graphLoading.py} (100%) rename examples/py/{0011_graphPrinting.py => 000_basics/011_graphPrinting.py} (100%) rename examples/py/{0012_graphInterface.py => 000_basics/012_graphInterface.py} (100%) rename examples/py/{0013_graphMorphisms.py => 000_basics/013_graphMorphisms.py} (100%) rename examples/py/{0030_ruleLoading.py => 000_basics/030_ruleLoading.py} (100%) rename examples/py/{0031_ruleMorphisms.py => 000_basics/031_ruleMorphisms.py} (100%) rename examples/py/{0050_formoseGrammar.py => 000_basics/050_formoseGrammar.py} (100%) create mode 100644 examples/py/000_basics/051_fileInclusion.py create mode 100644 examples/py/000_basics/meta.json delete mode 100644 examples/py/0051_fileInclusion.py rename examples/py/{0100_rcGraphOps.py => 010_rc/100_rcGraphOps.py} (81%) rename examples/py/{0101_rcParallel.py => 010_rc/101_rcParallel.py} (88%) rename examples/py/{0102_rcSuper.py => 010_rc/102_rcSuper.py} (85%) rename examples/py/{0120_rcFormose.py => 010_rc/120_rcFormose.py} (92%) create mode 100644 examples/py/010_rc/meta.json rename examples/py/{0210_dgFixed.py => 020_dg/210_dgFixed.py} (93%) rename examples/py/{0211_dgRepeat.py => 020_dg/211_dgRepeat.py} (85%) rename examples/py/{0212_dgPredicate.py => 020_dg/212_dgPredicate.py} (83%) rename examples/py/{0250_dgPrinting.py => 020_dg/250_dgPrinting.py} (89%) rename examples/py/{0260_dpoPrinting.py => 020_dg/260_dpoPrinting.py} (81%) create mode 100644 examples/py/020_dg/meta.json rename examples/py/{0320_aconitase.py => 030_stereo/320_aconitase.py} (100%) rename examples/py/{0330_tartaric.py => 030_stereo/330_tartaric.py} (100%) rename examples/py/{0340_tree.py => 030_stereo/340_tree.py} (100%) create mode 100644 examples/py/030_stereo/meta.json rename examples/py/{5000_encoding_simple.py => 900_epim/0_encoding_simple.py} (100%) rename examples/py/{5001_encoding_restriction.py => 900_epim/1_encoding_restriction.py} (100%) rename examples/py/{5002_encoding_recursive.py => 900_epim/2_encoding_recursive.py} (100%) rename examples/py/{5003_encoding_config.py => 900_epim/3_encoding_config.py} (100%) rename examples/py/{5004_exec_space_simple.py => 900_epim/4_exec_space_simple.py} (100%) rename examples/py/{5005_exec_space_recursive.py => 900_epim/5_exec_space_recursive.py} (100%) rename examples/py/{5006_hospital.py => 900_epim/6_hospital.py} (100%) create mode 100644 examples/py/900_epim/meta.json create mode 100644 examples/py/fileRenames.json delete mode 100644 examples/py/sections.json delete mode 100644 libs/jla_boost/include/jla_boost/graph/dpo/IO.hpp delete mode 100644 libs/jla_boost/include/jla_boost/graph/dpo/Rule.hpp create mode 100644 libs/jla_boost/include/jla_boost/graph/morphism/Concepts.hpp delete mode 100644 libs/jla_boost/include/jla_boost/graph/morphism/VertexMap.hpp create mode 100644 libs/jla_boost/include/jla_boost/graph/morphism/finders/mcgregor_common_subgraphs.hpp create mode 100644 libs/jla_boost/include/jla_boost/graph/morphism/models/InvertibleVector.hpp delete mode 100644 libs/jla_boost/src/graph/dpo/IO.cpp delete mode 100644 libs/libmod/src/mod/Function.cpp create mode 100644 libs/libmod/src/mod/lib/Algorithm/ConnectedComponents.hpp create mode 100644 libs/libmod/src/mod/lib/Algorithm/Container.hpp rename libs/libmod/src/mod/lib/{Algorithm.hpp => Algorithm/MultiDimSelector.hpp} (86%) create mode 100644 libs/libmod/src/mod/lib/Algorithm/Point.hpp create mode 100644 libs/libmod/src/mod/lib/Chem/MDL.cpp create mode 100644 libs/libmod/src/mod/lib/Chem/MDL.hpp rename libs/libmod/src/mod/lib/{IO/DGRead.cpp => DG/IO/Read.cpp} (52%) create mode 100644 libs/libmod/src/mod/lib/DG/IO/Read.hpp rename libs/libmod/src/mod/lib/{IO/DGWrite.cpp => DG/IO/Write.cpp} (78%) rename libs/libmod/src/mod/lib/{IO/DG.hpp => DG/IO/Write.hpp} (64%) rename libs/libmod/src/mod/lib/{IO/Derivation.cpp => DG/IO/WriteDerivation.cpp} (80%) rename libs/libmod/src/mod/lib/{IO/DGWriteDetail.hpp => DG/IO/WriteDetail.hpp} (85%) create mode 100644 libs/libmod/src/mod/lib/DPO/CombinedRule.cpp create mode 100644 libs/libmod/src/mod/lib/DPO/CombinedRule.hpp create mode 100644 libs/libmod/src/mod/lib/DPO/Concepts.hpp rename libs/{jla_boost/include/jla_boost/graph/dpo => libmod/src/mod/lib/DPO}/FilteredGraphProjection.hpp (82%) create mode 100644 libs/libmod/src/mod/lib/DPO/Membership.cpp create mode 100644 libs/libmod/src/mod/lib/DPO/Membership.hpp create mode 100644 libs/libmod/src/mod/lib/DPO/Traits.hpp delete mode 100644 libs/libmod/src/mod/lib/Graph/DFSEncoding.cpp delete mode 100644 libs/libmod/src/mod/lib/Graph/DFSEncoding.hpp rename libs/libmod/src/mod/lib/Graph/{Properties/Depiction.cpp => IO/DepictionData.cpp} (84%) rename libs/libmod/src/mod/lib/Graph/{Properties/Depiction.hpp => IO/DepictionData.hpp} (70%) create mode 100644 libs/libmod/src/mod/lib/Graph/IO/Read.cpp create mode 100644 libs/libmod/src/mod/lib/Graph/IO/Read.hpp create mode 100644 libs/libmod/src/mod/lib/Graph/IO/Write.cpp create mode 100644 libs/libmod/src/mod/lib/Graph/IO/Write.hpp create mode 100644 libs/libmod/src/mod/lib/GraphMorphism/IO/WriteConstraints.hpp create mode 100644 libs/libmod/src/mod/lib/IO/DFS.cpp create mode 100644 libs/libmod/src/mod/lib/IO/DFS.hpp delete mode 100644 libs/libmod/src/mod/lib/IO/Derivation.hpp rename libs/libmod/src/mod/lib/IO/{GMLUtil.cpp => GML.cpp} (90%) rename libs/libmod/src/mod/lib/IO/{GMLUtils.hpp => GML.hpp} (89%) delete mode 100644 libs/libmod/src/mod/lib/IO/Graph.hpp delete mode 100644 libs/libmod/src/mod/lib/IO/GraphRead.cpp create mode 100644 libs/libmod/src/mod/lib/IO/GraphWrite.hpp rename libs/libmod/src/mod/lib/IO/{GraphWriteDetail.hpp => GraphWriteGeneric.hpp} (80%) rename libs/libmod/src/mod/lib/IO/{JsonUtils.cpp => Json.cpp} (98%) rename libs/libmod/src/mod/lib/IO/{JsonUtils.hpp => Json.hpp} (88%) delete mode 100644 libs/libmod/src/mod/lib/IO/MorphismConstraints.hpp rename libs/libmod/src/mod/lib/IO/{ParsingUtil.hpp => Parsing.hpp} (72%) delete mode 100644 libs/libmod/src/mod/lib/IO/Rule.hpp delete mode 100644 libs/libmod/src/mod/lib/IO/RuleRead.cpp delete mode 100644 libs/libmod/src/mod/lib/IO/RuleWrite.cpp delete mode 100644 libs/libmod/src/mod/lib/IO/Stereo.hpp delete mode 100644 libs/libmod/src/mod/lib/IO/StereoWrite.cpp delete mode 100644 libs/libmod/src/mod/lib/IO/Term.cpp delete mode 100644 libs/libmod/src/mod/lib/IO/Term.hpp delete mode 100644 libs/libmod/src/mod/lib/RC/ComposeRuleReal.cpp create mode 100644 libs/libmod/src/mod/lib/RC/ComposeRuleRealCommon.cpp create mode 100644 libs/libmod/src/mod/lib/RC/ComposeRuleRealParallel.cpp create mode 100644 libs/libmod/src/mod/lib/RC/ComposeRuleRealSub.cpp create mode 100644 libs/libmod/src/mod/lib/RC/ComposeRuleRealSuper.cpp rename libs/libmod/src/mod/lib/{IO/RC.cpp => RC/IO/Write.cpp} (71%) rename libs/libmod/src/mod/lib/{IO/RC.hpp => RC/IO/Write.hpp} (73%) create mode 100644 libs/libmod/src/mod/lib/Rules/IO/DepictionData.cpp create mode 100644 libs/libmod/src/mod/lib/Rules/IO/DepictionData.hpp create mode 100644 libs/libmod/src/mod/lib/Rules/IO/Read.cpp create mode 100644 libs/libmod/src/mod/lib/Rules/IO/Read.hpp create mode 100644 libs/libmod/src/mod/lib/Rules/IO/Write.cpp create mode 100644 libs/libmod/src/mod/lib/Rules/IO/Write.hpp delete mode 100644 libs/libmod/src/mod/lib/Rules/Properties/Depiction.cpp delete mode 100644 libs/libmod/src/mod/lib/Rules/Properties/Depiction.hpp rename libs/libmod/src/mod/lib/{IO/StereoRead.cpp => Stereo/IO/Read.cpp} (54%) create mode 100644 libs/libmod/src/mod/lib/Stereo/IO/Read.hpp create mode 100644 libs/libmod/src/mod/lib/Stereo/IO/Write.cpp create mode 100644 libs/libmod/src/mod/lib/Stereo/IO/Write.hpp create mode 100644 libs/libmod/src/mod/lib/Stereo/IO/WriteConfiguration.cpp create mode 100644 libs/libmod/src/mod/lib/Stereo/IO/WriteConfiguration.hpp create mode 100644 libs/libmod/src/mod/lib/Term/IO/Read.cpp create mode 100644 libs/libmod/src/mod/lib/Term/IO/Read.hpp create mode 100644 libs/libmod/src/mod/lib/Term/IO/Write.cpp create mode 100644 libs/libmod/src/mod/lib/Term/IO/Write.hpp create mode 100644 libs/pymod/redirector/mod/__init__.py.in create mode 100644 libs/pymod/redirector/pyproject.toml create mode 100644 libs/pymod/redirector/setup.cfg.in create mode 100644 libs/pymodutils/CMakeLists.txt rename libs/{pymod => pymodutils}/src/mod/py/Common.hpp (52%) rename libs/{pymod => pymodutils}/src/mod/py/Function.hpp (95%) rename libs/{pymod => pymodutils}/src/mod/py/VertexMap.hpp (100%) create mode 100755 scripts/makePyExamples.py create mode 100644 test/cpp/graph/graph.cpp create mode 100644 test/py/dg/021_build_addHyperEdge.py create mode 100644 test/py/dg/402_printer_latex_escape.py rename test/py/dg/{450_print_dpo.py => 450_print_dpo_fail.py} (70%) delete mode 100644 test/py/dg/451_print_dpo_no_openbabel.py create mode 100644 test/py/dg/451_print_dpo_open_babel.py create mode 100644 test/py/dg/452_print_dpo_graphviz.py create mode 100644 test/py/formoseCommon/test.py delete mode 100644 test/py/graph/020_graphDFS.py create mode 100644 test/py/graph/020_graphDFS/common.py create mode 100644 test/py/graph/020_graphDFS/dot.py create mode 100644 test/py/graph/020_graphDFS/other.py create mode 100644 test/py/graph/020_graphDFS/tests.py create mode 100644 test/py/graph/020_graphDFS/topo.py create mode 100644 test/py/graph/020_graphDFS/whitespace.py create mode 100644 test/py/graph/050_mdl/allSmiles.py create mode 100644 test/py/graph/050_mdl/mol2000.py create mode 100644 test/py/graph/050_mdl/mol3000.py create mode 100644 test/py/graph/050_mdl/molMult.py create mode 100644 test/py/graph/050_mdl/sd.py create mode 100644 test/py/graph/050_mdl/sdMulti.py create mode 100644 test/py/graph/201_morphisms.py rename test/py/graph/{130_aut.py => 250_aut.py} (96%) create mode 100644 test/py/graph/301_image.py create mode 100644 test/py/graph/800_printer.py create mode 100644 test/py/graph/801_printer_graphviz.py create mode 100644 test/py/post.py delete mode 100644 test/py/rc/000_rules.py create mode 100644 test/py/rc/010_base_test.py delete mode 100644 test/py/rc/checks.py delete mode 100644 test/py/rc/test.py create mode 100644 test/py/rc/xxx_base_checks.py rename test/py/rule/{004_basic_loading.py => 001_gml_interface.py} (96%) rename test/py/rule/{001_loadFail.py => 002_gml_loadFail.py} (52%) delete mode 100644 test/py/rule/002_loadTopoFail.py create mode 100644 test/py/rule/003_gml_loadTopoFail.py rename test/py/rule/{003_loadConstraintsFail.py => 004_gml_loadConstraintsFail.py} (72%) create mode 100644 test/py/rule/010_gml_basic_structure.py create mode 100644 test/py/rule/030_gml_stereo_loadFail.py rename test/py/rule/{110_extId.py => 049_gml_externalIds.py} (90%) delete mode 100644 test/py/rule/100_ruleInterface.py create mode 100644 test/py/rule/101_dfs_basic_loading.py create mode 100644 test/py/rule/102_dfs_loadFail.py create mode 100644 test/py/rule/103_dfs_topo.py create mode 100644 test/py/rule/110_dfs_basic_structure.py create mode 100644 test/py/rule/111_dfs_load_implicit.py create mode 100644 test/py/rule/112_dfs_whitespace.py create mode 100644 test/py/rule/149_dfs_externalIds.py rename test/py/rule/{010_basic.py => 500_basic.py} (62%) create mode 100644 test/py/rule/501_numComponents.py create mode 100644 test/py/rule/510_ruleInterface.py create mode 100644 test/py/rule/520_inverse.py create mode 100644 test/py/rule/800_morphism.py rename test/py/rule/{200_isomorphismLeftRight.py => 850_isomorphismLeftRight.py} (74%) rename test/py/rule/{500_print.py => 900_print.py} (79%) create mode 100644 test/py/rule/901_print_graphviz.py create mode 100644 test/py/rule/910_depict.py create mode 100644 test/py/rule/911_depict_hydrogen_collapse.py create mode 100644 test/py/rule/920_depict_stereo.py delete mode 100644 test/py/rule/rule.gml delete mode 100644 test/py/rule/rule.py create mode 100644 test/py/rule/xxx_gml_stereo.py delete mode 100644 test/py/uniqueFilePrefix.py diff --git a/.drone.jsonnet b/.drone.jsonnet index 18bcf22..cbb6b47 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,7 +1,7 @@ local image = "localhost:5000/jla/mod"; local boostArg(v) = "-DBOOST_ROOT=/opt/boost/%s" % v; -local CoverageStep(withCoverage, compiler, boost) = if !withCoverage then null else { +local CoverageStep(withCoverage, compiler, boost) = if !withCoverage then [] else [{ name: "coverage", image: image, environment: { @@ -10,9 +10,10 @@ local CoverageStep(withCoverage, compiler, boost) = if !withCoverage then null e CXXFLAGS: "-Werror", }, commands: [ + "bindep testing", "mkdir covBuild", "cd covBuild", - "cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_TESTING=on -DBUILD_COVERAGE=on %s" % [boostArg(boost)], + "cmake ../ -DENABLE_IPO=off -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_TESTING=on -DBUILD_COVERAGE=on %s" % [boostArg(boost)], "make", "make install", "make tests", @@ -33,7 +34,7 @@ local CoverageStep(withCoverage, compiler, boost) = if !withCoverage then null e path: "/www", }, ], -}; +}]; local Volumes(withCoverage) = if !withCoverage then [] else [ { @@ -63,9 +64,10 @@ local Configure(compiler, boost, dep=false) = { CXXFLAGS: "-Werror", }, commands: [ + "bindep testing", "mkdir build", "cd build", - "cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off %s" % [boostArg(boost)], + "cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off %s" % [boostArg(boost)], ], [ if dep then "depends_on"]: [ "bootstrap" ], }; @@ -136,8 +138,7 @@ local Pipeline(withCoverage, compiler, boost) = { ] }, }, - CoverageStep(withCoverage, compiler, boost), - ], + ] + CoverageStep(withCoverage, compiler, boost), volumes: Volumes(withCoverage), }; @@ -232,9 +233,11 @@ local Pipeline(withCoverage, compiler, boost) = { Pipeline(boost == "1_74_0" && compiler == "g++-9", compiler, boost) for compiler in [ "g++-8", "g++-9", "g++-10", "g++-11", - "clang++-8", "clang++-9", "clang++-10", "clang++-11", "clang++-12", + "clang++-8", + #"clang++-9", + "clang++-10", "clang++-11", "clang++-12", ] for boost in [ - "1_73_0", "1_74_0", "1_75_0", "1_76_0", + "1_73_0", "1_74_0", "1_75_0", "1_76_0", "1_77_0", "1_78_0", "1_79_0", "1_80_0", ] ] diff --git a/.drone.yml b/.drone.yml index a5b0f59..95cf984 100644 --- a/.drone.yml +++ b/.drone.yml @@ -47,9 +47,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 environment: CXX: g++ CXXFLAGS: -Werror @@ -115,9 +116,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 environment: CXX: g++-8 CXXFLAGS: -Werror @@ -187,9 +189,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 environment: CXX: g++-8 CXXFLAGS: -Werror @@ -259,9 +262,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 environment: CXX: g++-8 CXXFLAGS: -Werror @@ -331,9 +335,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: CXX: g++-8 CXXFLAGS: -Werror @@ -384,6 +389,298 @@ steps: - refs/heads/master - refs/tags/v* +--- +kind: pipeline +name: g++-8, Boost 1_77_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + environment: + CXX: g++-8 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-8, Boost 1_78_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + environment: + CXX: g++-8 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-8, Boost 1_79_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + environment: + CXX: g++-8 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-8, Boost 1_80_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + environment: + CXX: g++-8 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + --- kind: pipeline name: g++-9, Boost 1_73_0 @@ -403,18 +700,1656 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + environment: + CXX: g++-9 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-9, Boost 1_74_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + environment: + CXX: g++-9 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +- name: coverage + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir covBuild + - cd covBuild + - cmake ../ -DENABLE_IPO=off -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_TESTING=on -DBUILD_COVERAGE=on -DBOOST_ROOT=/opt/boost/1_74_0 + - make + - make install + - make tests + - make coverage_collect + - make coverage_build + - /copyCoverage.sh + environment: + CTEST_OUTPUT_ON_FAILURE: 1 + CXX: g++-9 + CXXFLAGS: -Werror + volumes: + - name: www + path: /www + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +volumes: +- name: www + host: + path: /www/results/mod + +--- +kind: pipeline +name: g++-9, Boost 1_75_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + environment: + CXX: g++-9 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-9, Boost 1_76_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + environment: + CXX: g++-9 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-9, Boost 1_77_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + environment: + CXX: g++-9 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-9, Boost 1_78_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + environment: + CXX: g++-9 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-9, Boost 1_79_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + environment: + CXX: g++-9 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-9, Boost 1_80_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + environment: + CXX: g++-9 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-10, Boost 1_73_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + environment: + CXX: g++-10 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-10, Boost 1_74_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + environment: + CXX: g++-10 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-10, Boost 1_75_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + environment: + CXX: g++-10 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-10, Boost 1_76_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + environment: + CXX: g++-10 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-10, Boost 1_77_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + environment: + CXX: g++-10 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-10, Boost 1_78_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + environment: + CXX: g++-10 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-10, Boost 1_79_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + environment: + CXX: g++-10 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-10, Boost 1_80_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + environment: + CXX: g++-10 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-11, Boost 1_73_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + environment: + CXX: g++-11 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-11, Boost 1_74_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + environment: + CXX: g++-11 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-11, Boost 1_75_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + environment: + CXX: g++-11 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-11, Boost 1_76_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + environment: + CXX: g++-11 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-11, Boost 1_77_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + environment: + CXX: g++-11 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-11, Boost 1_78_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + environment: + CXX: g++-11 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: g++-11, Boost 1_79_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: g++-9 + CXX: g++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -458,7 +2393,7 @@ steps: --- kind: pipeline -name: g++-9, Boost 1_74_0 +name: g++-11, Boost 1_80_0 platform: os: linux @@ -475,18 +2410,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: g++-9 + CXX: g++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -528,39 +2464,155 @@ steps: - refs/heads/master - refs/tags/v* -- name: coverage +--- +kind: pipeline +name: clang++-8, Boost 1_73_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap image: localhost:5000/jla/mod commands: - - mkdir covBuild - - cd covBuild - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_TESTING=on -DBUILD_COVERAGE=on -DBOOST_ROOT=/opt/boost/1_74_0 + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + environment: + CXX: clang++-8 + CXXFLAGS: -Werror + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build - make tests - - make coverage_collect - - make coverage_build - - /copyCoverage.sh + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +--- +kind: pipeline +name: clang++-8, Boost 1_74_0 + +platform: + os: linux + arch: amd64 + +steps: +- name: bootstrap + image: localhost:5000/jla/mod + commands: + - git fetch --tags + - git submodule update --init --recursive + - ./bootstrap.sh + +- name: configure + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir build + - cd build + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 environment: - CTEST_OUTPUT_ON_FAILURE: 1 - CXX: g++-9 + CXX: clang++-8 CXXFLAGS: -Werror - volumes: - - name: www - path: /www + +- name: build + image: localhost:5000/jla/mod + commands: + - cd build + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - make + +- name: install + image: localhost:5000/jla/mod + commands: + - cd build + - make install + +- name: build-test + image: localhost:5000/jla/mod + commands: + - cd build + - make tests + +- name: test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -E cmake_add_subdirectory_build + +- name: simple test + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - cd ../ + - mod -e "smiles('O').print()" + +- name: test subdirectory build + image: localhost:5000/jla/mod + commands: + - cd build + - make install + - ctest --output-on-failure -R cmake_add_subdirectory_build when: ref: - refs/heads/develop - refs/heads/master - refs/tags/v* -volumes: -- name: www - host: - path: /www/results/mod - --- kind: pipeline -name: g++-9, Boost 1_75_0 +name: clang++-8, Boost 1_75_0 platform: os: linux @@ -577,11 +2629,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 environment: - CXX: g++-9 + CXX: clang++-8 CXXFLAGS: -Werror - name: build @@ -632,7 +2685,7 @@ steps: --- kind: pipeline -name: g++-9, Boost 1_76_0 +name: clang++-8, Boost 1_76_0 platform: os: linux @@ -649,11 +2702,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: g++-9 + CXX: clang++-8 CXXFLAGS: -Werror - name: build @@ -704,7 +2758,7 @@ steps: --- kind: pipeline -name: g++-10, Boost 1_73_0 +name: clang++-8, Boost 1_77_0 platform: os: linux @@ -721,18 +2775,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: g++-10 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -776,7 +2831,7 @@ steps: --- kind: pipeline -name: g++-10, Boost 1_74_0 +name: clang++-8, Boost 1_78_0 platform: os: linux @@ -793,18 +2848,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: g++-10 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -848,7 +2904,7 @@ steps: --- kind: pipeline -name: g++-10, Boost 1_75_0 +name: clang++-8, Boost 1_79_0 platform: os: linux @@ -865,18 +2921,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: g++-10 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -920,7 +2977,7 @@ steps: --- kind: pipeline -name: g++-10, Boost 1_76_0 +name: clang++-8, Boost 1_80_0 platform: os: linux @@ -937,18 +2994,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: g++-10 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -992,7 +3050,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_73_0 +name: clang++-10, Boost 1_73_0 platform: os: linux @@ -1009,11 +3067,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 environment: - CXX: g++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build @@ -1064,7 +3123,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_74_0 +name: clang++-10, Boost 1_74_0 platform: os: linux @@ -1081,11 +3140,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 environment: - CXX: g++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build @@ -1136,7 +3196,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_75_0 +name: clang++-10, Boost 1_75_0 platform: os: linux @@ -1153,11 +3213,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 environment: - CXX: g++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build @@ -1208,7 +3269,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_76_0 +name: clang++-10, Boost 1_76_0 platform: os: linux @@ -1225,11 +3286,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: g++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build @@ -1280,7 +3342,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_73_0 +name: clang++-10, Boost 1_77_0 platform: os: linux @@ -1297,18 +3359,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: clang++-8 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -1352,7 +3415,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_74_0 +name: clang++-10, Boost 1_78_0 platform: os: linux @@ -1369,18 +3432,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: clang++-8 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -1424,7 +3488,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_75_0 +name: clang++-10, Boost 1_79_0 platform: os: linux @@ -1441,18 +3505,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: clang++-8 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -1496,7 +3561,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_76_0 +name: clang++-10, Boost 1_80_0 platform: os: linux @@ -1513,18 +3578,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: clang++-8 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -1568,7 +3634,7 @@ steps: --- kind: pipeline -name: clang++-9, Boost 1_73_0 +name: clang++-11, Boost 1_73_0 platform: os: linux @@ -1585,11 +3651,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 environment: - CXX: clang++-9 + CXX: clang++-11 CXXFLAGS: -Werror - name: build @@ -1640,7 +3707,7 @@ steps: --- kind: pipeline -name: clang++-9, Boost 1_74_0 +name: clang++-11, Boost 1_74_0 platform: os: linux @@ -1657,11 +3724,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 environment: - CXX: clang++-9 + CXX: clang++-11 CXXFLAGS: -Werror - name: build @@ -1712,7 +3780,7 @@ steps: --- kind: pipeline -name: clang++-9, Boost 1_75_0 +name: clang++-11, Boost 1_75_0 platform: os: linux @@ -1729,11 +3797,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 environment: - CXX: clang++-9 + CXX: clang++-11 CXXFLAGS: -Werror - name: build @@ -1784,7 +3853,7 @@ steps: --- kind: pipeline -name: clang++-9, Boost 1_76_0 +name: clang++-11, Boost 1_76_0 platform: os: linux @@ -1801,11 +3870,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: clang++-9 + CXX: clang++-11 CXXFLAGS: -Werror - name: build @@ -1856,7 +3926,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_73_0 +name: clang++-11, Boost 1_77_0 platform: os: linux @@ -1873,18 +3943,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: clang++-10 + CXX: clang++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -1928,7 +3999,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_74_0 +name: clang++-11, Boost 1_78_0 platform: os: linux @@ -1945,18 +4016,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: clang++-10 + CXX: clang++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -2000,7 +4072,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_75_0 +name: clang++-11, Boost 1_79_0 platform: os: linux @@ -2017,18 +4089,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: clang++-10 + CXX: clang++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -2072,7 +4145,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_76_0 +name: clang++-11, Boost 1_80_0 platform: os: linux @@ -2089,18 +4162,19 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: clang++-10 + CXX: clang++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -2144,7 +4218,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_73_0 +name: clang++-12, Boost 1_73_0 platform: os: linux @@ -2161,11 +4235,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 environment: - CXX: clang++-11 + CXX: clang++-12 CXXFLAGS: -Werror - name: build @@ -2216,7 +4291,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_74_0 +name: clang++-12, Boost 1_74_0 platform: os: linux @@ -2233,11 +4308,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 environment: - CXX: clang++-11 + CXX: clang++-12 CXXFLAGS: -Werror - name: build @@ -2288,7 +4364,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_75_0 +name: clang++-12, Boost 1_75_0 platform: os: linux @@ -2305,11 +4381,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 environment: - CXX: clang++-11 + CXX: clang++-12 CXXFLAGS: -Werror - name: build @@ -2360,7 +4437,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_76_0 +name: clang++-12, Boost 1_76_0 platform: os: linux @@ -2377,11 +4454,12 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: clang++-11 + CXX: clang++-12 CXXFLAGS: -Werror - name: build @@ -2432,7 +4510,7 @@ steps: --- kind: pipeline -name: clang++-12, Boost 1_73_0 +name: clang++-12, Boost 1_77_0 platform: os: linux @@ -2449,9 +4527,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: CXX: clang++-12 CXXFLAGS: -Werror @@ -2460,7 +4539,7 @@ steps: image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -2504,7 +4583,7 @@ steps: --- kind: pipeline -name: clang++-12, Boost 1_74_0 +name: clang++-12, Boost 1_78_0 platform: os: linux @@ -2521,9 +4600,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: CXX: clang++-12 CXXFLAGS: -Werror @@ -2532,7 +4612,7 @@ steps: image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -2576,7 +4656,7 @@ steps: --- kind: pipeline -name: clang++-12, Boost 1_75_0 +name: clang++-12, Boost 1_79_0 platform: os: linux @@ -2593,9 +4673,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: CXX: clang++-12 CXXFLAGS: -Werror @@ -2604,7 +4685,7 @@ steps: image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -2648,7 +4729,7 @@ steps: --- kind: pipeline -name: clang++-12, Boost 1_76_0 +name: clang++-12, Boost 1_80_0 platform: os: linux @@ -2665,9 +4746,10 @@ steps: - name: configure image: localhost:5000/jla/mod commands: + - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: CXX: clang++-12 CXXFLAGS: -Werror @@ -2676,7 +4758,7 @@ steps: image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install diff --git a/.gitignore b/.gitignore index 61a2842..c328bd2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,12 +13,9 @@ __pycache__ /test/py/**/out/ /test/py/**/summary/ /test/py/**/.gdb_history -/test/py/graph/myGraph.gml +/test/py/graph/myGraph* /test/py/rule/myRule.gml -/doc/source/index.rst -/doc/source/references.rst -/doc/source/toc.rst /doc/source/libmod/[A-Z]*.rst /doc/source/pymod/[A-Z]*.rst /doc/source/libmod/[^/]*/ diff --git a/Brewfile b/Brewfile index f4fd291..fe66d5c 100644 --- a/Brewfile +++ b/Brewfile @@ -1,3 +1,4 @@ +brew "cmake" brew "boost" brew "boost-python3" brew "open-babel" diff --git a/CMakeLists.txt b/CMakeLists.txt index 20d8987..9095263 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,9 @@ string(STRIP "${PROJECT_VERSION}" PROJECT_VERSION) # remove the newline project(mod VERSION ${PROJECT_VERSION}) set(mod_VERSION ${PROJECT_VERSION} CACHE INTERNAL "" FORCE) set(PNAME_FILE mod) # how the project name should be written in file names -set(CPACK_PACKAGE_CONTACT "Jakob Lykke Andersen ") +set(mod_AUTHOR "Jakob Lykke Andersen") +set(mod_AUTHOR_EMAIL "jlandersen@imada.sdu.dk") +set(CPACK_PACKAGE_CONTACT "${mod_AUTHOR} <${mod_AUTHOR_EMAIL}>") set(CMAKE_CXX_FLAGS_OPTDEBUG "-g -O3") @@ -34,6 +36,7 @@ option(BUILD_DOC "Enable documentation building." OFF) option(BUILD_POST_MOD "Enable building of the post processor." ON) option(BUILD_POST_MOD_FMT "Enable building of the post processor Latex format files." ON) option(BUILD_PY_MOD "Enable building of the Python bindings." ON) +option(BUILD_PY_MOD_PIP "If BUILD_PY_MOD, then install the bindings also with pip in the default location." ON) option(BUILD_EPIM "Enable building of the EpiM extension (requires PyMØD)." ON) if(BUILD_EPIM AND NOT BUILD_PY_MOD) @@ -88,7 +91,7 @@ set(libmod_config_find_files "FindPackageHandleStandardArgs.cmake;FindPackageMes # ------------------------------------------------------------------------- set(v 1.73.0) if(BUILD_PY_MOD) - foreach(PY 3 34 35 36 37 38 39) + foreach(PY 3 34 35 36 37 38 39 310 311 312 313 314 315 316 317 318 319) set(lib "python${PY}") find_package(Boost ${v} QUIET COMPONENTS ${lib}) if(Boost_FOUND) @@ -98,8 +101,8 @@ if(BUILD_PY_MOD) endif() endforeach() if(NOT Boost_FOUND) - find_package(Boost ${v} REQUIRED COMPONENTS python3) - message(FATAL_ERROR "Could not find Boost.Python for Python 3. Tried 'python' wih suffixes 3, 34, 35, 36, 37, 38, and 39.") + find_package(Boost ${v} COMPONENTS python3) + message(FATAL_ERROR "Could not find Boost.Python for Python 3. Tried 'python' wih suffixes 3, 34 to 39, and 310 to 319.") endif() endif() find_package(Boost ${v} REQUIRED COMPONENTS graph iostreams) @@ -291,6 +294,7 @@ add_subdirectory(libs/epim) add_subdirectory(libs/gml) add_subdirectory(libs/jla_boost) add_subdirectory(libs/libmod) +add_subdirectory(libs/pymodutils) add_subdirectory(libs/pymod) add_subdirectory(libs/post_mod) add_subdirectory(doc) diff --git a/ChangeLog.rst b/ChangeLog.rst index 0da9f4e..e9d07a6 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -4,6 +4,127 @@ Changes ####### +develop +======= + +Incompatible Changes +-------------------- + +Doc, several pages have changed URL: + +- ``libmod/libmod.html`` to ``libmod/index.html`` +- ``pymod/pymod.html`` to ``pymod/index.html`` +- ``postmod/postmod.html`` to ``postmod/index.html`` +- ``modWrapper/modWrapper.html`` to ``exe/index.html`` +- ``epim/epim.html`` to ``epim/index.html`` +- ``dataDesc/dataDesc.html`` to ``formats/index.html`` +- ``dgStrat/dgStrat.html`` to ``dgStrat/index.html`` +- As default, a Python package is now installed with ``pip`` which enables + import of ``mod`` without extra ``PYTHONPATH`` manipulation. + This may potentially clash with other packages of the same name. + Use ``-DBUILD_PY_MOD_PIP=off`` for CMake to disable this + (see :ref:`compiling`). +- Due to a change in escaping of ``#`` characters in DG vertex/hyperedge labels + when printing, some custom labels with additional escaping may need + adjustment. See also the bug fix entry regarding this. + + +New Features +------------ + +- Doc, a new section, :ref:`graph-model`, and restructuring of + :ref:`formats`. +- The :ref:`GraphDFS format ` now supports disconnected graphs + through ``.``-edges, simiilar to :ref:`SMILES `. + The graph loading functions + :cpp:func:`graph::Graph::fromDFSMulti` and + :py:func:`Graph.fromDFSMulti` has been added to load disconnected graphs. +- Added :cpp:func:`rule::Rule::fromDFS`/:py:func:`Rule.fromDFS` for loading + rules from a :ref:`RuleDFS ` string, a new line-notation for + rules based on :ref:`GraphDFS `. +- Added support for :ref:`MOL and SD ` formats for loading graphs. + The loadeing can be done through the functions + + - :cpp:func:`graph::Graph::fromMOLString`/:py:func:`Graph.fromMOLString`, + - :cpp:func:`graph::Graph::fromMOLFile`/:py:func:`Graph.fromMOLFile`, + - :cpp:func:`graph::Graph::fromMOLStringMulti`/:py:func:`Graph.fromMOLStringMulti`, + - :cpp:func:`graph::Graph::fromMOLFileMulti`/:py:func:`Graph.fromMOLFileMulti`, + - :cpp:func:`graph::Graph::fromSDString`/:py:func:`Graph.fromSDString`, + - :cpp:func:`graph::Graph::fromSDFile`/:py:func:`Graph.fromSDFile`, + - :cpp:func:`graph::Graph::fromSDStringMulti`/:py:func:`Graph.fromSDStringMulti`, and + - :cpp:func:`graph::Graph::fromSDFileMulti`/:py:func:`Graph.fromSDFileMulti`, +- PyMØD: add installation of the bindings via ``pip``. + See the setting ``-DBUILD_PY_MOD_PIP=on`` in :ref:`compiling`. +- Added :cpp:func:`dg::Builder::addHyperEdge`/:py:meth:`DGBuilder.addHyperEdge`. +- Added :cpp:func:`graph::Printer::setRaiseIsotopes`/:cpp:func:`graph::Printer::getRaiseIsotopes`/:py:attr:`GraphPrinter.raiseIsotopes`. + It was previously only available in the internal interface. +- Added :cpp:func:`graph::Printer::setWithGraphvizCoords`/:cpp:func:`graph::Printer::getWithGraphvizCoords`/:py:attr:`GraphPrinter.withGraphvizCoords`. +- Added :cpp:func:`graph::Printer::setGraphvizPrefix`/:cpp:func:`graph::Printer::getGraphvizPrefix`/:py:attr:`GraphPrinter.graphvizPrefix`. +- Whitespace is now allowed inside :ref:`format-dfs` strings. +- Make :option:`mod --memcheck` cause Valgrind to return non-zero on problems. + Additionally add an ``atexit`` handler in Python to delete remaining global + objects as this is not guaranteed otherwise. +- Several undocumented post-processing functions are now documented, + and several internal functions are now exposed. + See :ref:`cpp-Post`/:ref:`py-Post`. +- Added :cpp:func:`graph::Graph::enumerateMonomorphisms`/:py:meth:`Graph.enumerateMonomorphisms`. +- Added :cpp:func:`dg::Printer::setImageOverwrite`/:py:meth:`DGPrinter.setImageOverwrite`. + +Bugs Fixed +---------- + +- Rule GML loading, check for edges dangling due to wrong vertex membership. +- :cpp:func:`dg::Builder::execute`/:py:meth:`DGBuilder.execute` and + :cpp:func:`dg::Builder::apply`/:py:meth:`DGBuilder.apply`, + properly ignore direct derivations with empty right-hand sides, + instead of crashing. +- :cpp:func:`dg::DG::load`/:py:meth:`DG.load` and + :cpp:func:`dg::Builder::load`/:py:meth:`DGBuilder.load`, + reenable loading of very old dump formats. +- Fix critical bugs in + :cpp:class:`rule::CompositionMatch`/:py:class:`RCMatch`. +- Doc, added missing ``cd mod`` step in :ref:`compiling`. +- Doc, add missing ``"`` in usage description for the Docker image. +- Doc, fix typo (:math:`C_3` to :math:`C_4`) in :ref:`format-graphDFS`, + and improve description of ring-closure semantics. +- Fix :cpp:func:`graph::Graph::getGraphDFS`/:py:attr:`Graph.graphDFS` + and :cpp:func:`graph::Graph::getGraphDFSWithIds`/:py:attr:`Graph.graphDFSWithIds` + to not produce a :token:`~graphDFS:defRingId` directly followed by a + :token:`~graphDFS:ringClosure` which is indistinguishable from just a + :token:`~graphDFS:defRingId` when parsing the string again. +- Check for loop edges and parallel edges when loading graphs from DFS. +- :ref:`PostMØD `, avoid use of inline ``sed`` in ``compileTikz`` + to make it work on macOS. +- For compiling from source on macOS, add ``cmake`` to ``Brewfile``. +- Check for Boost.Python compiled against Python 3.10 through 3.20 as well. +- Py, use :py:class:`collections.abc.Iterable` instead of the deprecated/removed + ``collections.Iterable``. +- Py, use :py:func:`inspect.getfullargspec` instead of the deprecated/removed + ``inspect.getargspec()``. +- ``mod_post`` scrub more unreproducible meta-info from figure PDFs. +- Fix memory leaks in :cpp:func:`dg::Builder::apply`/:py:meth:`DGBuilder.apply`. +- Fix colour on changed stereo-information in the right-side graph when printing + rules and direct derivations. +- Stop recreating vertex-orders for connected components of rule sides, + thereby speeding up rule application (5-6% reduced run-time observed). +- Fix missing coordinates for rule depiction in rare non-chemical cases with + vertices with label "H". +- Fix rule composition with :cpp:any:`LabelType::Term`/:py:obj:`LabelType.Term`, + when two vertices are overlapping and there is an edge in the left side of the + second rule, but not in the right side of the first rule. +- Fix Tikz coordinate node names in rule and stereo depictions to always include + ``\modIdPrefix``, to allow post-printing namespacing of node names. +- :cpp:func:`graph::Graph::fromSMILES`/:py:meth:`Graph.fromSMILES`, properly parse + abstract labels when multiple nests of balanced brackets are present. +- Fix handling of null pointers: + + - :cpp:func:`graph::Graph::isomorphism`/:py:meth:`Graph.isomorphism`. + - :cpp:func:`graph::Graph::monomorphism`/:py:meth:`Graph.monomorphism`. + - :cpp:func:`graph::Union::Union`/:py:meth:`UnionGraph.__init__`. +- Fix escaping of ``#`` characters in DG vertex/hyperedge labels when printing + using a :cpp:class:`dg::Printer`/:py:class:`DGPrinter` with + ``labelsAsLatexMath=True`` (the default). + v0.13.0 (2021-07-08) ==================== diff --git a/bindep.txt b/bindep.txt index fd30ae9..b4ee322 100644 --- a/bindep.txt +++ b/bindep.txt @@ -20,6 +20,9 @@ python3-devel [platform:rpm] openbabel [platform:arch] libopenbabel-dev [platform:dpkg] openbabel-devel [platform:rpm] +python-openbabel [platform:arch testing] +python3-openbabel [platform:dpkg testing] +python3-openbabel [platform:rpm testing] texlive-science [platform:arch] texlive-pictures [platform:arch] diff --git a/bootstrap.sh b/bootstrap.sh index c642849..0376512 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -28,10 +28,12 @@ function doDir { find $inc -iname "*.hpp" | indentAndDir libs/$1 echo ")" echo "" - echo "set(mod_$1_SRC_FILES" - find src -iname "*.cpp" | indentAndDir libs/$1 - echo ")" - echo "" + if test -d src; then + echo "set(mod_$1_SRC_FILES" + find src -iname "*.cpp" | indentAndDir libs/$1 + echo ")" + echo "" + fi if test -d test; then echo "set(mod_$1_TEST_CPP_FILES" find test -iname "*.cpp" | sed "s/^test\/\(.*\)\.cpp$/\1/" | indent @@ -50,6 +52,7 @@ function gen_file_lists { doDir gml doDir jla_boost doDir libmod src + doDir pymodutils src doDir pymod src echo "set(mod_pymod_PY_FILES" find libs/pymod/lib -iname "*.py" | indentAndDir @@ -64,6 +67,12 @@ function gen_file_lists { echo "set(mod_EXAMPLES_PY_FILES" find examples/py -iname "*.py" | sed -e "s!^examples/!!" -e 's!\.py$!!' | indent echo ")" + # this doesn't cover every single file, but it should be enough for normal edit-compile usage + echo "set(mod_DOC_FILES" + echo '${CMAKE_CURRENT_LIST_DIR}/doc/source/conf.py' | indent + echo '${CMAKE_CURRENT_LIST_DIR}/ChangeLog.rst' | indent + find doc/source -iname "*.rst" | sed -e 's!^!${CMAKE_CURRENT_LIST_DIR}/!' | indent + echo ")" } echo "VERSION" @@ -72,10 +81,10 @@ echo "VERSION" v=$(git describe --tags --always --exclude "archive/*" | sed -e "s/^v//" -e 's/priv-\([0-9]*\.[0-9]*\)\.0/\1.1/' -e "s/-g.*$//" -e "s/-/./") echo $v > VERSION cat VERSION -echo "CMakeFiles.txt" -gen_file_lists > CMakeFiles.txt echo "Docs" doc/makeDocs.sh . || exit 1 +echo "CMakeFiles.txt" +gen_file_lists > CMakeFiles.txt echo "Recursing in external/graph_canon" echo "---------------------------------" diff --git a/cmake/MODUtils.cmake b/cmake/MODUtils.cmake index 24b3a99..b7d39be 100644 --- a/cmake/MODUtils.cmake +++ b/cmake/MODUtils.cmake @@ -7,6 +7,6 @@ function(make_py_test fileName testName extraEnv) COMMAND ${CMAKE_INSTALL_FULL_BINDIR}/mod -f ${CMAKE_CURRENT_LIST_DIR}/${fileName}.py WORKING_DIRECTORY ${workDir}) set_tests_properties(${testName} PROPERTIES - ENVIRONMENT "MOD_NUM_POST_THREADS=1${extraEnv}") + ENVIRONMENT "MOD_NUM_POST_THREADS=1;PYTHONWARNINGS=error${extraEnv}") add_coverage_case(${testName}) endfunction() diff --git a/conda/build.Dockerfile b/conda/build.Dockerfile index a9474fe..1fc53cc 100644 --- a/conda/build.Dockerfile +++ b/conda/build.Dockerfile @@ -7,7 +7,7 @@ ENV PATH /opt/conda/bin:$PATH RUN apt-get update --fix-missing \ && apt-get install -y wget bzip2 ca-certificates curl git -RUN wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-4.5.11-Linux-x86_64.sh -O ~/miniconda.sh && \ +RUN wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-py38_4.12.0-Linux-x86_64.sh -O ~/miniconda.sh && \ /bin/bash ~/miniconda.sh -b -p /opt/conda && \ rm ~/miniconda.sh && \ /opt/conda/bin/conda clean -tipsy && \ diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml index 8d04a71..156fdc3 100644 --- a/conda/conda_build_config.yaml +++ b/conda/conda_build_config.yaml @@ -1,11 +1,14 @@ python: - - 3.6 - 3.7 - 3.8 - 3.9 + - 3.10 boost: - 1.74 - 1.76 + - 1.77 + - 1.78 + - 1.80 pin_run_as_build: python: x.x boost: x.x diff --git a/conda/meta.yaml b/conda/meta.yaml index 8ca3571..b5af349 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -32,6 +32,8 @@ requirements: - networkx - parse - typing_extensions + # Add openbabel here to get pybel for test. + - openbabel host: # lib packages - boost {{ boost }} diff --git a/conda/test.py b/conda/test.py index 0771d02..5fd7ba0 100644 --- a/conda/test.py +++ b/conda/test.py @@ -1,5 +1,5 @@ -g = smiles('C') -r = ruleGMLString("""rule [ +g = Graph.fromSMILES('C') +r = Rule.fromGMLString("""rule [ left [ node [ id 1 label "C" ] node [ id 2 label "H" ] diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 35ada88..af544c3 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -6,7 +6,8 @@ set(output ${CMAKE_CURRENT_BINARY_DIR}/html) set(doctrees ${CMAKE_CURRENT_BINARY_DIR}/doctrees) add_custom_command( OUTPUT ${output} - COMMAND ${SPHINX} ${CMAKE_CURRENT_SOURCE_DIR}/source -n -b html ${output} -d ${doctrees}) + COMMAND ${SPHINX} ${CMAKE_CURRENT_SOURCE_DIR}/source -n -b html ${output} -d ${doctrees} + DEPENDS ${mod_DOC_FILES}) if(TARGET doc) add_dependencies(doc ${output}) else() diff --git a/doc/makeDocs.sh b/doc/makeDocs.sh index a070593..a88f375 100755 --- a/doc/makeDocs.sh +++ b/doc/makeDocs.sh @@ -136,81 +136,6 @@ function getPyModCPPs { done; } -function makeIndex { - function indexFiles { - cat << "EOF" - installation - compiling - libmod/libmod - pymod/pymod - postmod/postmod - modWrapper/modWrapper - epim/epim - dataDesc/dataDesc - dgStrat/dgStrat - examples/index - - knownIssues - changes - references -EOF - } - function data { - cat << "EOF" -################################################ -MØD -################################################ - -.. toctree:: - :maxdepth: 1 - :numbered: - -EOF - indexFiles -cat << "EOF" - - -.. _overview: - -Overview -======== - -This is the documentation for the MØD software package. -The sources can be found in the GitHub repository: ``_. -For additional information see the webpage: ``_. - -The package contains the following components. - -* libMØD, shared library (``mod``) - The main library. -* PyMØD, Python 3 module (``mod``) - Python bindings for the library and extra functionality for easier usage of - many features. - It additionally include the submodule :ref:`epim`. -* PostMØD, Bash Script (``mod_post``) - The post processor for compiling figures and summaries. -* The wrapper script, Bash Script (``mod``) - A convenience wrapper for invoking a virtual machine (e.g., ``python3``) - with user-supplied code. - The wrapper handles output and invokes the post processor. - Additionally, it can run the chosen virtual machine through ``gdb`` - and/or ``valgrind`` (either normal memcheck or callgrind). - - -Contributors -============ - -* `Jakob Lykke Andersen `__: main author. -* `Nikolai Nøjgaard `__: author of :ref:`EpiM`. - -EOF -cat << "EOF" - -EOF - } - data | outputRST index -} - function makeLibMod { function data { local f=$1 @@ -269,7 +194,7 @@ EOF echo "" getFolders | sed 's/$/\/index/' | sed 's/^/ /' } - dataToc | outputRST libmod/Libmodtoc + dataToc | outputRST libmod/Toc if [ ${PIPESTATUS[0]} -ne 0 ]; then return 1 fi @@ -333,15 +258,18 @@ EOF echo "" getFolders | sed 's/$/\/index/' | sed 's/^/ /' } - dataToc | outputRST pymod/Pymodtoc + dataToc | outputRST pymod/Toc +} + +function makeExamples { + rm -rf $topSrcDir/doc/source/_static/examples + mkdir -p $topSrcDir/doc/source/_static/examples + cp -a $topSrcDir/examples/libmod_cmake $topSrcDir/doc/source/_static/examples/ + cp -a $topSrcDir/examples/pymod_extension $topSrcDir/doc/source/_static/examples/ + cp -a $topSrcDir/examples/py $topSrcDir/doc/source/_static/examples/ + $topSrcDir/scripts/makePyExamples.py $topSrcDir rst } -makeIndex || exit 1 makeLibMod || exit 1 makePyMod || exit 1 - -rm -rf $topSrcDir/doc/source/_static/examples -mkdir -p $topSrcDir/doc/source/_static/examples -cp -a $topSrcDir/examples/libmod_cmake $topSrcDir/doc/source/_static/examples/ -cp -a $topSrcDir/examples/pymod_extension $topSrcDir/doc/source/_static/examples/ -cp -a $topSrcDir/examples/py $topSrcDir/doc/source/_static/examples/ +makeExamples || exit 1 diff --git a/doc/source/compiling.rst b/doc/source/compiling.rst index b5409cd..84ea677 100644 --- a/doc/source/compiling.rst +++ b/doc/source/compiling.rst @@ -19,6 +19,7 @@ First, get MØD and install the easy dependencies: .. code-block:: bash git clone --recursive https://github.com/jakobandersen/mod.git + cd mod ./bootstrap.sh pip3 install -r requirements.txt # may need --user to install in home folder instead of system folders # Ubuntu: @@ -139,6 +140,13 @@ See also :ref:`dependencies` for elaboration on some of them. :option:`mod_post --install-format`/:option:`mod_post --install-format-sudo` options. - ``-DBUILD_PY_MOD=on``, whether to build the Python bindings or not. +- ``-DBUILD_PY_MOD_PIP=on``, whether to install the Python bindings via pip or + not. The bindings are always installed in the ``/lib`` folder, so + a normal ``import`` in Python will probably not find the module. + Having this setting on will enable a build of a fake Python package to be + installed via ``pip`` in the default system folder. This fake package will + redirect the import to the real location. + This package can be uninstalled with ``pip uninstall mod-jakobandersen``. - ``-DBUILD_TESTING=off``, whether to allow test building or not. This is forced to ``off`` when used via ``add_subdirectory``. When ``on`` the tests can be build with ``make tests`` and run with ``ctest``. @@ -207,7 +215,8 @@ related to them. This is fulfilled via a Git submodule (make sure to do ``git submodule update --init --recursive``), but if another source is needed, set ``-DUSE_NESTED_NLOHMANN_JSON=off``. - - (optional) `Open Babel`_ dev, >= 2.3.2 (``-DWITH_OPENBABEL=on``). + - (optional) `Open Babel `__ dev, >= 2.3.2 + (``-DWITH_OPENBABEL=on``). - PyMØD (``-DBUILD_PY_MOD=on``): diff --git a/doc/source/conf.py b/doc/source/conf.py index b1c6dd5..613c870 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -22,101 +22,8 @@ #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ - -refLinks = { - "Boost" : "http://boost.org", - "ChemFig" : "http://www.ctan.org/pkg/chemfig", - "GGL" : "http://www.tbi.univie.ac.at/software/GGL", - "Graphviz" : "http://www.graphviz.org", - "Open Babel" : "http://openbabel.org", - "Sphinx" : "http://sphinx-doc.org", - "Tikz" : "http://www.ctan.org/pkg/pgf", -} -rst_prolog = ""; -with open("references.rst", "w") as f: - f.write(""" -********** -References -********** - -""") - for name, link in sorted(refLinks.items(), key=lambda x: x[0].lower()): - f.write("* " + name + " " + link + "\n") - rst_prolog += ".. _" + name + ": " + link + "\n" - rst_prolog += "\n" - import sys import os -import shlex - -def generateExamples(): - import json - with open("_static/examples/py/sections.json") as j: - sections = json.load(j) - os.makedirs("examples", exist_ok=True) - with open("examples/index.rst", "w") as f: - f.truncate() - f.write(".. Autogenerated in conf.py\n\n") - f.write(".. _examples:\n\n") - f.write("Examples\n########\n\n") - f.write(".. toctree::\n\n") - for s in sections: - f.write("\t{}\n".format(s["id"])) - s["f"] = open("examples/{}.rst".format(s["id"]), "w", encoding='utf-8') - s["f"].truncate() - s["f"].write(".. Autogenerated in conf.py\n\n") - s["f"].write(".. _examples-{}:\n\n".format(s["id"])) - if "future" in s and s["future"]: - s["f"].write("Upcomming Feature: {}\n".format(s["title"])) - else: - s["f"].write("{}\n".format(s["title"])) - s["f"].write(80*"#" + "\n\n") - - for fName in sorted(os.listdir("_static/examples/py/")): - if not fName.endswith(".py"): - continue - assert fName[4] == '_' - id_ = int(fName[:4]) - s = next(s for s in sections - if id_ >= int(s["first"]) and id_ < int(s["last"])) - - with open("_static/examples/py/{}".format(fName), encoding='utf-8') as f: - endLine = 1 - for line in f.readlines(): - if not line.startswith("# rst"): - endLine += 1 - continue - line = line[5:] - if line.startswith("-name: "): - dash = line.find(" --- ") - if dash >= 0: - title = line[7:dash] - else: - title = line[7:-1] - s["f"].write("\n\n%s\n" % (line[7:-1])) - s["f"].write(80*"^" + "\n\n") - else: - assert line.startswith(": ") - s["f"].write(line[2:]) - s["f"].write(""" -`Explore in the playground -`__. - -.. literalinclude:: /_static/examples/py/{} - :language: python - :linenos: - :lines: 1-{} - :tab-width: 3 -""".format(fName, fName, endLine - 1)) - - for s in sections: - s["f"].close() - -generateExamples() - - - - # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '3.5.0' @@ -188,13 +95,17 @@ def generateExamples(): # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [ - 'libmod/Libmodtoc.rst', - 'pymod/Pymodtoc.rst', - 'dataDesc/molEnc.rst', - 'dataDesc/graph.rst', - 'dataDesc/rule.rst', - 'dataDesc/term.rst', - 'dataDesc/dg.rst', + 'libmod/Toc.rst', + 'pymod/Toc.rst', + 'graphModel/molEnc.rst', + 'formats/dfs.rst', + 'formats/dg.rst', + 'formats/dot.rst', + 'formats/gml.rst', + 'formats/graphDFS.rst', + 'formats/mdl.rst', + 'formats/smiles.rst', + 'formats/tikz.rst', ] # The name of the Pygments (syntax highlighting) style to use. diff --git a/doc/source/dataDesc/graph.rst b/doc/source/dataDesc/graph.rst deleted file mode 100644 index db14fd8..0000000 --- a/doc/source/dataDesc/graph.rst +++ /dev/null @@ -1,193 +0,0 @@ -.. _graph-tikz: - -Tikz (Graph) -############ - -Graphs are visualised using generated `Tikz`_ code. -The coordinates for the layout is either generated using `Open Babel`_ or `Graphviz`_. -The visualisation style is controlled by passing instances of the classes -:cpp:class:`mod::graph::Printer` (C++) and :py:class:`mod.GraphPrinter` (Python) -to the printing functions. -The drawing style is inspired by `ChemFig`_ and `Open Babel`_. -See also :doc:`/postmod/postmod`. - - -.. _graph-dot: - -DOT (Graph) -########### - -The DOT format (from `Graphviz`_) is used for generating vertex coordinates for the Tikz format, -when `Open Babel`_ can not be used. - - -.. _graph-smiles: - -SMILES -###### - -The `Simplified molecular-input line-entry system` is a line notation for -molecules. MØD can load most SMILES strings, and converts them internally to -labelled graphs. For graphs that are sufficiently molecule-like, a SMILES -string can be generated. The generated strings are canonical in the sense that -the same version of MØD will print the same SMILES string for isomorphic -molecules. - -The reading of SMILES strings is based on the `OpenSMILES -`_ specification, but with the following -notes/changes. - -- Only single SMILES strings are accepted, i.e., not multiple strings separated - by white-space. -- Up and down bonds are regarded as implicit bonds, i.e., they might represent - either a sngle bond or an aromatic bond. The stereo information is ignored. -- Atom classes are (mostly) ignored. They can be used to specify unique IDs to - atoms. -- Wildcard atoms (specified with ``*``) are converted to vertices with label - ``*``. When inside brakcets, only the hydrogen count and atom class is then - permitted. -- Abstract vertex labels can be specified inside brakcets. The bracket must in - that case only contain the label and an optional class label. - The label must be a non-empty string without ``:`` and with balanced square - brackets. -- Charges of magnitude 2 and 3 may be specified with repeated ``-`` and ``+``. -- The bond type ``$`` is currently not allowed. -- Aromaticity can only be specified using the bond type ``:`` - or using the special lower case atoms. - I.e., ``c1ccccc1`` and ``C1:C:C:C:C:C:1`` represent the same molecule, - but ``C1=CC=CC=C1`` is a different molecule. -- Ring-bonds and branches may appear in mixed order. The normal order is to - have all ring-bonds first and all branches, e.g., ``C123(O)(N)``. - The parser accepts them in mixed order, e.g., ``C1(O)2(N)3``. -- The final graph will conform to the molecule encoding scheme described below. -- Implicit hydrogens are added following a more complicated procedure. -- A bracketed atom can have a radical by writing a dot (``.``) between the - position of the charge and the position of the class. - -The written SMILES strings are intended to be canonical and may not conform to any "prettyness" standards. - -Implicit Hydrogen Atoms ------------------------ - -When SMILES strings are written they will use implicit hydrogens whenever they -can be inferred when reading the string. -For the purposes of implicit hydrogens we use the following definition of -valence for an atom. -The valence of an atom is the weighted sum of its incident edges, where single -(``-``) and aromatic (``:``) bonds have weight 1, double bounds (``=``) have -weight 2, and triple bonds (``#``) have weight 3. -If an atom has an incident aromatic bond, its valence is increased by 1. -The atoms that can have implicit hydrogens are B, C, N, O, P, S, F, Cl, Br, and I. -Each have a set of so-called "normal" valences as shown in the following table. -The atoms N and S additionally have certain sets of incident edges that are -also considered "normal", which are also listed in the table. - -============= ===================================================================== -Atom Normal Valences and Neighbourhoods -============= ===================================================================== -B 3 -C 4 -N 3, 5, :math:`\{-, :, :\}`, :math:`\{-, -, =\}`, :math:`\{:, :, :\}` -O 2 -P 3, 5 -S 2, 4, 6, :math:`\{:, :\}` -F, Cl, Br, I 1 -============= ===================================================================== - -If the set of incident edges is listed in the table, then no hydrogens are added. -If the valence is higher than the highest normal valence, then no hydrogens are -added. -Otherwise, hydrogens are added until the valence is at the next higher normal -valence. - -When writing SMILES strings the inverse procedure is used. - - -.. _graph-graphDFS: - -GraphDFS -######## - -The GraphDFS format is intended to provide a convenient line notation for -general undirected labelled graphs. Thus it is in many aspects similar to -SMILES strings, but a string being both a valid SMILES string and GraphDFS -string **may not represent the same graph**. -The semantics of ring-closures/back-edges are in particular not the same. - -Grammar -------- - -.. productionlist:: graphDFS - graphDFS: `chain` - chain: `vertex` `evPair`* - vertex: (`labelVertex` | `ringClosure`) `branch`* - evPair: `edge` `vertex` - labelVertex: '[' bracketEscapedString ']' [ `defRingId` ] - : `implicitHydrogenVertexLabels` [ `defRingId` ] - implicitHydrogenVertexLabels: 'B' | 'C' | 'N' | 'O' | 'P' | 'S' | 'F' \ - | 'Cl' | 'Br' | 'I' - defRingId: unsignedInt - ringClosure: unsignedInt - edge: '{' braceEscapedString '}' - : `shorthandEdgeLabels` - shorthandEdgeLabels: '-' | ':' | '=' | '#' | '' - branch: '(' `evPair`+ ')' - -A ``bracketEscapedString`` and ``braceEscapedString`` are zero or more -characters except respectively ``]`` and ``}``. To have these characters in -each of their strings they must be escaped, i.e., ``\]`` and ``\}`` -respectively. - -The parser additionally enforces that a :token:`~graphDFS:defRingId` may not be -a number which has previously been used. -Similarly, a :token:`~graphDFS:ringClosure` may only be a number which has -previously occured in a :token:`~graphDFS:defRingId`. - -A vertex specified via the :token:`~graphDFS:implicitHydrogenVertexLabels` rule -will potentially have ekstra neighbours added after parsning. The rules are the -exact same as for implicit hydrogen atoms in :ref:`graph-smiles`. - - -Semantics ---------- - -A GraphDFS string is, like the SMILES strings, an encoding of a depth-first traversal of the -graph it encodes. -Vertex labels are enclosed in square brackets and edge labels are enclosed in curly brackets. -However, a special set of labels can be specified without the enclosing brackets. -An edge label may additionally be completely omitted as a shorthand for a dash (``-``). - -A vertex can have a numeric identifier, defined by the -:token:`~graphDFS:defRingId` non-terminal. -At a later stage this identifier can be used as a vertex specification to -specify a back-edge in the depth-first traversal. -Example: ``[v1]1-[v2]-[v3]-[v4]-1``, specifies a labelled :math:`C_3` -(which equivalently can be specified shorter as ``[v1]1[v2][v3][v4]1``). - -A :token:`~graphDFS:vertex` being a :token:`~graphDFS:ringClosure` can never be -the first vertex in a string, and is thus preceded with a -:token:`~graphDFS:edge`. As in a depth-first traversal, such a back-edge is a -kind of degenerated branch. Example: ``[v1]1[v2][v3][v4]1[v5][v6]1``, this -specifies a graph which is two fused :math:`C_4` with a common edge (and not -just a common vertex). - -.. warning:: The semantics of back-edges/ring closures are **not** the same as in SMILES strings. - In SMILES, a pair of matching numeric identifiers denote the individual back-edges. - -A branch in the depth-first traversal is enclosed in parentheses. - -Abstracted Molecules --------------------- - -The short-hand labels for vertices and edges makes it easier to specify partial molecules -than using :ref:`GML ` files. - -As example, consider modelling Acetyl-CoA in which we wish to abstract most of the CoA part. -The GraphDFS string ``CC(=O)S[CoA]`` can be used and we let the library add missing hydrogen -atoms to the vertices which encode atoms. A plain CoA molecule would in this modelling be -``[CoA]S``, or a bit more verbosely as ``[CoA]S[H]``. - -The format can also be used to create completely abstract structures -(it can encode any undirected labelled graph), e.g., RNA strings. -Note that in this case it may not be appropriate to add "missing" hydrogen atoms. -This can be controlled by an optional parameter to the loading function. diff --git a/doc/source/dataDesc/molEnc.rst b/doc/source/dataDesc/molEnc.rst deleted file mode 100644 index 10c3366..0000000 --- a/doc/source/dataDesc/molEnc.rst +++ /dev/null @@ -1,39 +0,0 @@ - -.. _mol-enc: - -Molecule Encoding -################# - -There is no strict requirement that graphs encode molecules, however several optimizations -are in place when they do. -The following describes how to encode molecules as undirected, simple, labelled graphs and thus -when the library assumes a graph is a molecule. - -Edges / Bonds -------------- - -An edge encodes a chemical bond if and only if its label is listed in the table below. - -====== ================== -Label Interpretation -====== ================== -``-`` Single bond -``:`` "Aromatic" bond -``=`` Double bond -``#`` Triple bond -====== ================== - -Vertices / Atoms ----------------- - -A vertex encodes an atom with a charge if and only if its label conforms to the following grammar. - -.. productionlist:: molecularVertexLabel - vertexLabel: [ isotope ] `atomSymbol` [ `charge` ] [ `radical` ] - isotope: unsignedInt - charge: singleDigit ('-' | '+') - radical: '.' - atomSymbol: an atom symbol with the first letter capitalised - -Currently there are no valence requirements for a graph being recognised as a -molecule. diff --git a/doc/source/dataDesc/rule.rst b/doc/source/dataDesc/rule.rst deleted file mode 100644 index 2e09944..0000000 --- a/doc/source/dataDesc/rule.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _rule-tikz: - -Tikz (Rule) -########### - -This format is used for visualising rules similarly to how the :ref:`graph-tikz` format is used -for graphs. A rule is depicted as its span :math:`(L\leftarrow K\rightarrow R)` with the vertex -positions in the plane indicating the embedding of :math:`K` in :math:`L` and :math:`R`. -Additionally, :math:`L\backslash K` and :math:`R\backslash K` are shown in different colour in -:math:`L` and :math:`R` respectively. - - -.. _rule-dot: - -DOT (Rule) -########## - -The DOT format (from `Graphviz`_) is used for generating vertex coordinates for the Tikz format, -when `Open Babel`_ can not be used. diff --git a/doc/source/dataDesc/term.rst b/doc/source/dataDesc/term.rst deleted file mode 100644 index e230b85..0000000 --- a/doc/source/dataDesc/term.rst +++ /dev/null @@ -1,35 +0,0 @@ - -.. py:currentmodule:: mod -.. cpp:namespace:: mod - -.. _term-desc: - -First-Order Terms -################# - -Vertex/edge labels on graphs/rules can be interpreted either as text strings -or as `firt-order terms `__. -Additionally, for first-order terms there is a choice in which type of relation between terms -should be required in morphisms. -This can be controlled in each algorithm through label settings objects -(C++: :cpp:class:`LabelSettings`, Python: :py:class:`LabelSettings`). - -A constant or function symbol is a word that can be matched -by the regex ``[A-Za-z0-9=#:.+-][A-Za-z0-9=#:.+-_]*``. -This means that all strings that are usually considered "molecular" can be reinterpreted -as constant symbols. - -A variable symbol is a word that can be matched -by the regex ``_[A-Za-z0-9=#:.+-][A-Za-z0-9=#:.+-_]*``. -That is, variable is like a constant/function symbol, but with a ``_`` prepended. -An unnamed variable can be specified by the special wildcard symbol ``*``. - -.. note:: Variable names matched by the regex ``_[HT][0-9][0-9]*`` may be generated - when printing out graphs/rules. Any original variable names are not saved. - -Function terms start with a function symbol followed by -a parenthesis with a comma-separated list of terms. -They may contain white-space. - -If parsing of terms fails a specific exception is thrown -(C++: :cpp:class:`TermParsingError`, Python: :py:class:`TermParsingError`). diff --git a/doc/source/dgStrat/dgStrat.rst b/doc/source/dgStrat/index.rst similarity index 100% rename from doc/source/dgStrat/dgStrat.rst rename to doc/source/dgStrat/index.rst diff --git a/doc/source/epim/epim.rst b/doc/source/epim/index.rst similarity index 100% rename from doc/source/epim/epim.rst rename to doc/source/epim/index.rst diff --git a/doc/source/modWrapper/modWrapper.rst b/doc/source/exe/index.rst similarity index 99% rename from doc/source/modWrapper/modWrapper.rst rename to doc/source/exe/index.rst index 2ddcbe5..2339b11 100644 --- a/doc/source/modWrapper/modWrapper.rst +++ b/doc/source/exe/index.rst @@ -5,7 +5,7 @@ .. _mod-wrapper: **************************** -The Wrapper Script (``mod``) +The Wrapper Script **************************** .. program:: mod diff --git a/doc/source/formats/dfs.rst b/doc/source/formats/dfs.rst new file mode 100644 index 0000000..4f22a80 --- /dev/null +++ b/doc/source/formats/dfs.rst @@ -0,0 +1,135 @@ +.. _format-dfs: + +DFS Line Notation +################# + +The DFS formats are intended to provide a convenient line +notation for general undirected labelled graphs and rules. +Thus it is in many aspects similar to SMILES strings and reaction SMILES +strings, but a string being both a valid (reaction SMILES) string and +GraphDFS/RuleDFS string **does not mean they represent the same objects**. +In particular, the semantics of ring-closures/back-edges are not the same. + +.. _format-graphDFS: + +GraphDFS +======== + +.. productionlist:: graphDFS + graphDFS: `chain` + chain: `vertex` `evPair`* + vertex: (`labelVertex` | `ringClosure`) `branch`* + evPair: `edge` `vertex` + labelVertex: '[' bracketEscapedString ']' [ `defRingId` ] + : `implicitHydrogenVertexLabels` [ `defRingId` ] + implicitHydrogenVertexLabels: 'B' | 'C' | 'N' | 'O' | 'P' | 'S' | 'F' \ + | 'Cl' | 'Br' | 'I' + defRingId: unsignedInt + ringClosure: unsignedInt + edge: '{' braceEscapedString '}' + : `shorthandEdgeLabel` + shorthandEdgeLabel: '-' | ':' | '=' | '#' | '.' | '' + branch: '(' `evPair`+ ')' + +A ``bracketEscapedString`` and ``braceEscapedString`` are zero or more +characters except respectively ``]`` and ``}``. To have these characters in +each of their strings they must be escaped, i.e., ``\]`` and ``\}`` +respectively. + +Whitespace is ignored, except inside ``bracketEscapedString`` and +``braceEscapedString``. + +The parser additionally enforces that a :token:`~graphDFS:defRingId` may not be +a number which has previously been used. +Similarly, a :token:`~graphDFS:ringClosure` may only be a number which has +previously occured in a :token:`~graphDFS:defRingId`. + +A vertex specified via the :token:`~graphDFS:implicitHydrogenVertexLabels` rule +will potentially have ekstra neighbours added after parsning. The rules are the +exact same as for implicit hydrogen atoms in :ref:`graph-smiles`. + +Semantics +--------- + +A GraphDFS string is, like the SMILES strings, an encoding of a depth-first +traversal of the graph it encodes. Vertex labels are enclosed in square +brackets and edge labels are enclosed in curly brackets. However, a special +set of labels can be specified without the enclosing brackets. +An edge label may additionally be completely omitted as a shorthand for a dash +(``-``). + +A vertex can have a numeric identifier, defined by the +:token:`~graphDFS:defRingId` non-terminal. +At a later stage this identifier can be used as a vertex specification to +specify a back-edge in the depth-first traversal. +Example: ``[v1]1-[v2]-[v3]-[v4]-1``, specifies a labelled :math:`C_4` +(which equivalently can be specified shorter as ``[v1]1[v2][v3][v4]1``). + +A :token:`~graphDFS:vertex` being a :token:`~graphDFS:ringClosure` can never be +the first vertex in a string, and is thus preceded with a +:token:`~graphDFS:edge`. As in a depth-first traversal, such a back-edge is a +kind of degenerated branch. Example: ``[v1]1[v2][v3][v4]1[v5][v6]1``, this +specifies a graph which is two fused :math:`C_4`, +:math:`v_1, v_2, v_3, v_4` and :math:`v_4, v_5, v_6, v_1`, +with a common edge, :math:`(v_1, v_4)`. + +.. warning:: The semantics of back-edges/ring closures are **not** the same as + in SMILES strings. In SMILES, a pair of matching numeric identifiers denote + the individual back-edges. + +A branch in the depth-first traversal is enclosed in parentheses. + +The :token:`~graphDFS:shorthandEdgeLabel` ``.`` indicates a non-edge, +i.e., a jump to a new vertex without creating an edge. +For example ``[v1].[v2]`` encodes a graph with two vertices and no edges, +while ``[v1]{.}[v2]`` encodes a graph with two vertcies connected with an edge +with label ``.``. + +Abstracted Molecules +-------------------- + +The short-hand labels for vertices and edges makes it easier to specify partial +molecules than using :ref:`GML ` files. + +As example, consider modelling Acetyl-CoA in which we wish to abstract most of +the CoA part. The GraphDFS string ``CC(=O)S[CoA]`` can be used and we let the +library add missing hydrogen atoms to the vertices which encode atoms. A plain +CoA molecule would in this modelling be ``[CoA]S``, or a bit more verbosely as +``[CoA]S[H]``. + +The format can also be used to create completely abstract structures +(it can encode any undirected labelled graph), e.g., RNA strings. +Note that in this case it may not be appropriate to add "missing" hydrogen +atoms. This can be controlled by an optional parameter to the loading function. + + +.. _format-ruleDFS: + +RuleDFS +======= + +The rule format builds on the graph format by using two GraphDFS strings to +encode a rule: + +.. productionlist:: graphDFS + ruleDFS: [ `graphDFS` ] '>>' [ `graphDFS` ] + +The two (possibly empty) GraphDFS strings encode the left-hand and right-hand +side of a rule, with the vertex IDs being used to relate them. +That is, a pair of vertices in the left side and right side with the same ID +will be identified and the vertex put in the context graph of the rule as well. +A similar pair of edges where both end-points are in the context graph will be +put in the context graph as well. + +Examples: + +- ``>>``: the empty rule. +- ``[A]>>``: a rule with a single vertex in :math:`L`, and empty :math:`K` and + :math:`R`. +- ``[A]>>[B]``: a rule with empty :math:`K` but with a vertex in :math:`L` + which is removed by the rule, and a vertex in :math:`R` being created by the + rule. +- ``[A]1>>[B]1``: a rule with a vertex changing label from "A" to "B". + +.. note:: Currently it is not possible to use vertices with implicit hydrogens + in RuleDFS. diff --git a/doc/source/dataDesc/dg.rst b/doc/source/formats/dg.rst similarity index 94% rename from doc/source/dataDesc/dg.rst rename to doc/source/formats/dg.rst index 61e7c5e..30c37e7 100644 --- a/doc/source/dataDesc/dg.rst +++ b/doc/source/formats/dg.rst @@ -1,6 +1,3 @@ -.. py:currentmodule:: mod -.. cpp:namespace:: mod - .. _dg_abstract-desc: Abstract Derivation Graphs diff --git a/doc/source/formats/dot.rst b/doc/source/formats/dot.rst new file mode 100644 index 0000000..7955ead --- /dev/null +++ b/doc/source/formats/dot.rst @@ -0,0 +1,6 @@ +DOT (Graphviz) +############## + +The DOT format (from `Graphviz `__) is used for +generating vertex coordinates for the Tikz format, when Open Babel can not be +used. diff --git a/doc/source/dataDesc/dataDesc.rst b/doc/source/formats/gml.rst similarity index 76% rename from doc/source/dataDesc/dataDesc.rst rename to doc/source/formats/gml.rst index 8ef4d3a..af51f54 100644 --- a/doc/source/dataDesc/dataDesc.rst +++ b/doc/source/formats/gml.rst @@ -1,10 +1,3 @@ -************ -Data Formats -************ - -MØD utilises several data formats and encoding schemes. - - .. _gml: GML @@ -36,7 +29,7 @@ They are ignored during parsing. .. _graph-gml: Graph ------ +===== A graph can be specified as :ref:`GML` by giving a list of vertices and edges with the key ``graph``. @@ -53,7 +46,7 @@ Note though that list elements can appear in any order. .. _rule-gml: Rule ----- +==== A rule :math:`(L\leftarrow K\rightarrow R)` in :ref:`GML` format is specified as three graph fragments; ``left``, ``context``, and ``right``. @@ -92,12 +85,35 @@ The key-value structure is exemplified by the following grammar. Note though that list elements can appear in any order. -.. include:: rule.rst +A Note on Term Labels +--------------------- + +As described in :ref:`graph-model-terms` it is possible to interpret the +ordinary vertex and edges labels as first-order terms. +When using the label ``*`` it will be interpreted as an unnamed term variable. +Consider the rule:: + + rule [ + left [ node [ id 0 label "*" ] ] + right [ node [ id 0 label "*" ] ] + ] + +In string mode this is simply an identity rule, but in term mode each ``*`` +is interpreted as an unnamed variable. Be careful that in this case the +two labels are interepreted as *the same* variable. That is, it is equivalent +to:: + + rule [ + left [ node [ id 0 label "_A" ] ] + right [ node [ id 0 label "_A" ] ] + ] -.. include:: graph.rst +If you wish to replace any vertex label with an explicit new variable, you can +write it as:: -.. include:: molEnc.rst + rule [ + left [ node [ id 0 label "_A" ] ] + right [ node [ id 0 label "_B" ] ] + ] -.. include:: term.rst -.. include:: dg.rst diff --git a/doc/source/formats/index.rst b/doc/source/formats/index.rst new file mode 100644 index 0000000..47072b8 --- /dev/null +++ b/doc/source/formats/index.rst @@ -0,0 +1,23 @@ +.. py:currentmodule:: mod +.. cpp:namespace:: mod + +.. _formats: + +************ +Data Formats +************ + +This page gives an overview of the *external* data formats that MØD works with. +For the formats that describe graphs and rules there are details on how +the external format is interpreted when creating the corresponding in-memmory +data structure. The in-memory model is described in :ref:`graph-model`. + + +.. include:: gml.rst +.. include:: smiles.rst +.. include:: dfs.rst +.. include:: mdl.rst +.. include:: dg.rst + +.. include:: tikz.rst +.. include:: dot.rst diff --git a/doc/source/formats/mdl.rst b/doc/source/formats/mdl.rst new file mode 100644 index 0000000..fcdc4b5 --- /dev/null +++ b/doc/source/formats/mdl.rst @@ -0,0 +1,31 @@ +.. _graph-mdl: + +MOL and SD +########## + +MØD can load graphs stored in the +`CT File `__ formats +MOL (single structure) and SD (multiple structures). +See the loading functions in :cpp:class:`graph::Graph`/:py:class:`Graph` for +the API. +The loaded structures are converted to labelled graphs according to a specific +:ref:`molecule encoding `. + +The reading of structures is based on the +`published specification `__, +but with the following notes/changes. + +- the :cpp:class:`MDLOptions`/:py:class:`MDLOptions` can be used to customize + the loading procedure. +- radical value 2 is converted to ``.`` in the vertex labels. +- the atom symbols "LP" and "L" are used as is, as an atom with an abstract + label. +- the atom symbols "A", "Q", and "*" are all considered as wildcard atoms and + are converted to vertices with label ``*``. + See the use of :ref:`first-order terms ` as labels. +- the bond orders 5, 6, and 7 for constrained wildcard bonds are converted + to edges with labels starting with ``_Q``, i.e., term variables. + See the use of :ref:`first-order terms ` as labels. +- the bond order 8 for unconstrianed bonds are converted to edges with label + ``*``. + See the use of :ref:`first-order terms ` as labels. diff --git a/doc/source/formats/smiles.rst b/doc/source/formats/smiles.rst new file mode 100644 index 0000000..e8e01ad --- /dev/null +++ b/doc/source/formats/smiles.rst @@ -0,0 +1,82 @@ +.. _graph-smiles: + +SMILES +###### + +The `Simplified molecular-input line-entry system` is a line notation for +molecules. MØD can load most SMILES strings, and converts them internally to +labelled graphs according to a specific :ref:`molecule encoding `. +For graphs that are sufficiently molecule-like, a SMILES string can be +generated. The generated strings are canonical in the sense that the same +version of MØD will print the same SMILES string for isomorphic molecules. + +The reading of SMILES strings is based on the `OpenSMILES +`_ specification, but with the following +notes/changes. + +- Only single SMILES strings are accepted, i.e., not multiple strings separated + by white-space. +- Up and down bonds are regarded as implicit bonds, i.e., they might represent + either a sngle bond or an aromatic bond. The stereo information is ignored. +- Atom classes are (mostly) ignored. They can be used to specify unique IDs to + atoms. +- Wildcard atoms (specified with ``*``) are converted to vertices with label + ``*``. When inside brakcets, only the hydrogen count and atom class is then + permitted. +- Abstract vertex labels can be specified inside brakcets. The bracket must in + that case only contain the label and an optional class label. + The label must be a non-empty string without ``:`` and with balanced square + brackets. +- Charges of magnitude 2 and 3 may be specified with repeated ``-`` and ``+``. +- The bond type ``$`` is currently not allowed. +- Aromaticity can only be specified using the bond type ``:`` + or using the special lower case atoms. + I.e., ``c1ccccc1`` and ``C1:C:C:C:C:C:1`` represent the same molecule, + but ``C1=CC=CC=C1`` is a different molecule. + The lower-case atoms are converted to normal case when used as a label. +- Ring-bonds and branches may appear in mixed order. The normal order is to + have all ring-bonds first and all branches, e.g., ``C123(O)(N)``. + The parser accepts them in mixed order, e.g., ``C1(O)2(N)3``. +- Implicit hydrogens are added following a more complicated procedure + (see below). +- A bracketed atom can have a radical by writing a dot (``.``) between the + position of the charge and the position of the class. + +The written SMILES strings are intended to be canonical and may not conform to +any "prettyness" standards. + +Implicit Hydrogen Atoms +======================= + +When SMILES strings are written they will use implicit hydrogens whenever they +can be inferred when reading the string back in. +For the purposes of implicit hydrogens we use the following definition of +valence for an atom. +The valence of an atom is the weighted sum of its incident edges, where single +(``-``) and aromatic (``:``) bonds have weight 1, double bounds (``=``) have +weight 2, and triple bonds (``#``) have weight 3. +If an atom has an incident aromatic bond, its valence is increased by 1. +The atoms that can have implicit hydrogens are +B, C, N, O, P, S, F, Cl, Br, and I. +Each have a set of so-called "normal" valences as shown in the following table. +The atoms N and S additionally have certain sets of incident edges that are +also considered "normal", which are also listed in the table. + +============= ===================================================================== +Atom Normal Valences and Neighbourhoods +============= ===================================================================== +B 3 +C 4 +N 3, 5, :math:`\{-, :, :\}`, :math:`\{-, -, =\}`, :math:`\{:, :, :\}` +O 2 +P 3, 5 +S 2, 4, 6, :math:`\{:, :\}` +F, Cl, Br, I 1 +============= ===================================================================== + +If the set of incident edges is listed in the table, then no hydrogens are +added. If the valence is higher than the highest normal valence, then no +hydrogens are added. Otherwise, hydrogens are added until the valence is at the +next higher normal valence. + +When writing SMILES strings the inverse procedure is used. diff --git a/doc/source/formats/tikz.rst b/doc/source/formats/tikz.rst new file mode 100644 index 0000000..60808fe --- /dev/null +++ b/doc/source/formats/tikz.rst @@ -0,0 +1,19 @@ +Tikz +#### + +Both graphs and rules are visualized through :ref:`PostMØD ` by the +library generating `Tikz `__ code and compiling it +with Latex. + +The visualisation style is controlled by passing instances of +:cpp:class:`mod::graph::Printer`/:py:class:`mod.GraphPrinter` +to the printing functions. +The drawing style is inspired by `ChemFig `__ +and `Open Babel `__. + +The coordinates for the layout is either generated using Open Babel when the +graphs a chemical enough, but otherwise `Graphviz `__ +is invoked to generate coordinates. + +For visualizing a rule or DPO diagram, the position of vertices is used to +indicate how morphisms map vertices to each other. diff --git a/doc/source/graphModel/index.rst b/doc/source/graphModel/index.rst new file mode 100644 index 0000000..7fcafbc --- /dev/null +++ b/doc/source/graphModel/index.rst @@ -0,0 +1,272 @@ +.. cpp:namespace:: mod +.. py:currentmodule:: mod + +.. _graph-model: + +******************************* +Graph, Rule, and Molecule Model +******************************* + +A central component of MØD is the :cpp:class:`graph::Graph`/:py:class:`Graph` +which simplified represents simple graphs with labels on vertices and edges. +However, how the labels are interepreted and how graph morphisms are defined +depends which :cpp:class:`LabelSettings`/:py:class:`LabelSettings` object you +pass to various algorithms. +Another complication is that in the formal mathematics for describing +graph transformation through a :cpp:class:`rule::Rule`/:py:class:`Rule` classes, +we need need a slightly more complicated description of the graphs in order +to make the mathematics work. + +We therefore start with the :ref:`simplified graph model ` +with the labels being opaque types. +:ref:`Then ` we explore the different label settings +which switches the mathematical category of the graphs. +Afterwards the :ref:`rule model ` is explained where we +get into the mathematical details for label-encoding which is necessary to +formally make the graph transformation composable. +Lastly we see how MØD interprets graphs as molecule (fragments) in order to +create depictions that resembles ordinary molecule depictions. + + +.. _graph-model-basic: + +Simple Graphs with String Labels +################################ + +In the simplified model, a graph +:math:`G = (V, E, l\colon V\cup E\rightarrow \Omega)` +is undirected, and neither has loop edges nor parallel edges. +The function :math:`l` assigns a label to each vertex and edge. +A morphism between two graphs :math:`m\colon G\rightarrow H` is a graph morphism +which commutes with the labelling functions. +That is, + +- graph morphism: for every edge :math:`(u, v) \in E_G` we must have an + corresponding edge + :math:`(m(u), m(v)) \in E_H` (:math:`m` is a graph morphism), and +- vertex labels: each vertex :math:`v \in V_G` is mapped to a vertex with the + same label, :math:`l_G(v) = l_H(m(v))`. +- edge labels: each edge :math:`e \in E_G` is mapped to a vertex with the + same label, :math:`l_G(e) = l_H(m(e))`. + +The only requirement of :math:`\Omega` is thus that the elements can be +compared for equality. +In MØD this correspons to using +:cpp:any:`LabelType::String`/:py:obj:`LabelType.String` +in the a :cpp:class:`LabelSettings`/:py:class:`LabelSettings` object, +and in the API of PyMØD, this is the default. + +.. note:: When using :cpp:any:`LabelType::String`/:py:obj:`LabelType.String` + the :cpp:any:`LabelRelation`/:py:obj:`LabelRelation` in + :cpp:class:`LabelSettings`/:py:class:`LabelSettings` doesn't matter, + +.. note:: Technically, in this basic model the stereo setting in + :cpp:class:`LabelSettings`/:py:class:`LabelSettings` should be disabled as + well. Though, the stereo model is in an experimental stage and we will + therefore not explore it further. + +A :cpp:class:`graph::Graph`/:py:class:`Graph` is further required to be +connected. + + +.. _graph-model-terms: + +Switching Category --- First-Order Terms as Labels +################################################## + +We can imbue the label set :math:`Omega` with extra structure by using +:cpp:any:`LabelType::Term`/:py:obj:`LabelType.Term` instead of +:cpp:any:`LabelType::String`/:py:obj:`LabelType.String`. +The labels are now interpreted as `firt-order terms `__, +and formally the graph/objects are now in a different +`category `__. + +In the graph/rule objects it is the same text strings being used either verbatim +when in string mode or being parsed when in term mode. + +The following describes how the strings are parsed in term mode. + +- A constant or function symbol is a word that can be matched by the regex + ``[A-Za-z0-9=#:.+-][A-Za-z0-9=#:.+-_]*``. + This means that all strings that are :ref:`considered "molecular" ` + can be reinterpreted as constant symbols. +- A variable symbol is a word that can be matched by the regex + ``_[A-Za-z0-9=#:.+-][A-Za-z0-9=#:.+-_]*``. + That is, variable is like a constant/function symbol, but with a ``_`` + prepended. + An unnamed variable can be specified by the special wildcard symbol ``*``. + + The variable symbols matched by the regex ``_[HT][0-9][0-9]*`` are + discouraged, as they are reserved for converting terms back into strings. +- Function terms start with a function symbol followed by a parenthesis with a + comma-separated list of terms. They may contain white-space which is ignored. + +If parsing of terms fails an exception of type +:cpp:class:`TermParsingError`/:py:class:`TermParsingError` +is thrown. Thus, essentially any function that takes a +:cpp:class:`LabelSettings`/:py:class:`LabelSettings` +object may throw such an exception. + +If a graph/rule was created in term mode and then is used in string mode, +the first-order terms are serialized back into strings: + +- Constant/function symbols are serialized verbatim. +- Variable names are generated and will be matchable by the regex + ``_[HT][0-9][0-9]*``. Any original variable names are not saved. +- Function terms will be serialized with a single space character after each + comma. + + +Morphisms +========= + +First-order terms are partially ordered, and we thus have additional choice in +how to define morphisms. +This choice is in the API selected through the +:cpp:any:`LabelRelation`/:py:obj:`LabelRelation` objects which offers the three +choices "unification", "specialisation", and "isomorphism". +Given a graph morphism :math:`m\colon G\rightarrow H` +let :math:`V_G = \{v_1, v_2, \dots, v_n\}` be the vertices of :math:`G` +and :math:`E_G = \{e_1, e_2, \dots, e_p\}` be the edges of :math:`G`. +Consider the following two terms: + +.. math:: + + t_G &= \texttt{assoc}(l_G(v_1), l_G(v_2), \dots, l_G(v_n), + l_G(e_1), l_G(e_2), \dots, l_G(e_p)) \\ + t_H &= \texttt{assoc}(l_H(m(v_1)), l_H(m(v_2)), \dots, l_H(m(v_n)), + l_H(m(e_1)), l_H(m(e_2)), \dots, l_H(m(e_p))) + +They lay out all the labels of :math:`G` in :math:`t_G` and then the labels +of the corresponding vertices and edges in :math:`H` in :math:`t_H`. + +The graph morphism :math:`m` is in term mode a valid morphism if + +- with + :cpp:any:`LabelRelation::Unification`/:py:obj:`LabelRelation.Unification` + there exists a most-general unifier between :math:`t_G` and :math:`t_H`. +- with + :cpp:any:`LabelRelation::Specialisation`/:py:obj:`LabelRelation.Specialisation` + there exists a most-general unifier between :math:`t_G` and :math:`t_H`, + and it only binds variables in :math:`t_G` but not :math:`t_H`. + That is, :math:`t_H` is a specialisation of :math:`t_G`. +- with + :cpp:any:`LabelRelation::Isomorphism`/:py:obj:`LabelRelation.Isomorphism` + there exists a most-general unifier between :math:`t_G` and :math:`t_H`, + and it only binds variables to variables, and that variable mapping is + a bijection. + That is, the only difference between :math:`t_G` and :math:`t_H` is the naming + of the variables they use. + +In most cases you should use +:cpp:any:`LabelRelation::Specialisation`/:py:obj:`LabelRelation.Specialisation` +though, when for example using +:cpp:func:`graph::Graph::isomorphism`/:py:meth:`Graph.isomorphism` +to check if two graphs encode the same mathematical object, you should use +:cpp:any:`LabelRelation::Isomorphism`/:py:obj:`LabelRelation.Isomorphism`. +The funtion name thus refers to the type of the *graph morphism* being found, +while the label settings describes the additional requirements for the labels. + +The third option, +:cpp:any:`LabelRelation::Unification`/:py:obj:`LabelRelation.Unification`, +should be used very carefully. +For example, when performing graph transofrmation, e.g., through a +:cpp:class:`dg::DG`/:py:class:`DG`, it may give results that are unexpected. +One use-case is when calling +:cpp:func:`graph::Graph::isomorphism`/:py:meth:`Graph.isomorphism`, +where using +:cpp:any:`LabelRelation::Unification`/:py:obj:`LabelRelation.Unification` +means that you will find graph isomorphisms for which the labels on both graphs +can be specialized such that the graph objects becomes isomorphic. + +.. note:: For the graph transformation is it necessary to define a distinguished + set of morphisms :math:`\mathcal{M}` for the term mode. + These morphisms are those that are graph monomorphisms, but are label + isomorphisms on the label part. + + +.. _graph-model-rules: + +Rule Model +########## + +A :cpp:class:`rule::Rule`/:py:class:`Rule` represents a +`Double Pushout `__ +(DPO) graph transformation rule :math:`p = (L\xleftarrow{l}K\xrightarrow{r}R)`. +Specifically, they are in the DPO variant with all morphisms being at least +graph monomorphisms. + +Label Change +============ + +The rules allow both for vertices and edges to change labels, but only the +latter is well-defined in traditional DPO transformation. +Thus, mathematically it is not immediately possible to define the graphs as +above. Consider a vertex changing label in a rule, which label should it +have in :math:`K`? +However, as our graphs are not allowed to have loop edges we can as a trick +simply pretend that whenever a vertex would have a label, we attach a loop edge +to the vertex, and put the "vertex" label on that edge. +Note that all morphisms are required to be at least graph monomorphisms, so a +loop edge can not be created inadvertently. + +Avoiding Parallel Edges +======================= + +Our graphs are defined to be without parallel edges, which presents a +mathematical problem in that certain pushouts are not allowed. +In a DPO direct derivation we can avoid parallel edges by simply introducing +algorithmic ad-hoc constraint, that if the second pushout would result in +parallel edges in the result graph, then the direct derviation is not defined. + +However, in the big picture we can avoid this ad-hoc solution and think of the +rules as having implicit negative application conditions (NACs): + +- For each pair of vertices :math:`u, v\in V_K` + which are not connected in :math:`L`, :math:`(l(u), l(v))\not\in E_L`, + but are connected in :math:`R`, :math:`(r(u), r(v))\in E_R`, + there is a NAC on :math:`L` preventing edges :math:`(l(u), l(v))`. +- And symmetrically, for each pair of vertices :math:`u, v\in V_K` + which are connected in :math:`L`, :math:`(l(u), l(v))\not\in E_L`, + but are not connected in :math:`R`, :math:`(r(u), r(v))\in E_R`, + there is a NAC on :math:`R` preventing edges :math:`(l(u), l(v))`. + + +.. _mol-enc: + +Molecule Encoding +################# + +There is no strict requirement that graphs/rules encode molecule (fragments), +however several optimizations are in place when they do, and depictions are +prettified based on "moleculeness". +The following describes the encoding of molecules in the +:ref:`basic graph model `, that MØD recognizes. + +Edges / Bonds +============= + +An edge encodes a chemical bond if and only if its label is listed in the table below. + +====== ================== +Label Interpretation +====== ================== +``-`` Single bond +``:`` "Aromatic" bond +``=`` Double bond +``#`` Triple bond +====== ================== + +Vertices / Atoms +================ + +A vertex encodes an atom if and only if its label conforms to the following grammar. + +.. productionlist:: molecularVertexLabel + vertexLabel: [ `isotope` ] `atomSymbol` [ `charge` ] [ `radical` ] + isotope: unsignedInt + charge: [ singleDigit ] ('-' | '+') + radical: '.' + atomSymbol: an atom symbol with the first letter capitalised + +.. note:: In the basic model there are no valence requirements for a graph being recognised as a molecule. diff --git a/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 0000000..ce57f53 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,53 @@ +################################################ +MØD +################################################ + +This is the documentation for the MØD software package. +The sources can be found in the GitHub repository: +``_. +For additional information see the webpage: ``_. + +The package contains the following components. + +* :ref:`libmod`, a C++ shared library (``mod``) + The main library. +* :ref:`pymod`, a Python module (``mod``) + Python bindings for the library and extra functionality for easier usage of + many features. + It additionally includes the submodule :ref:`epim`, +* :ref:`mod_post`, a Bash script (``mod_post``) + The post-processor for compiling figures and summaries. +* :ref:`mod-wrapper`, a Bash script (``mod``) + A convenience wrapper for invoking ``python3`` with user-supplied code. + The wrapper handles output and invokes the post-processor. + Additionally, it can run ``python3`` through ``gdb`` + and/or ``valgrind`` (either normal memcheck or callgrind). + + +Contents +======== + +.. toctree:: + :maxdepth: 1 + :numbered: + + installation + compiling + libmod/index + pymod/index + postmod/index + exe/index + graphModel/index + formats/index + dgStrat/index + examples/index + + knownIssues + changes + + +Contributors +============ + +* `Jakob Lykke Andersen `__: main author. +* `Nikolai Nøjgaard `__: author of :ref:`EpiM`. diff --git a/doc/source/installation.rst b/doc/source/installation.rst index bdaf41a..c136b70 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -85,7 +85,7 @@ We thus arrive at the following full command docker run -it --rm -v $(pwd):/workdir -u $(id -u):$(id -g) jakobandersen/mod -As a quick test, you can try running ``mod -e "smiles('O').print()`` inside +As a quick test, you can try running ``mod -e "smiles('O').print()"`` inside the container. When it's done, then outside the container you should now be able to open the printed summary ``summary/summary.pdf``. You can thus use the ``mod`` command inside the container, but otherwise diff --git a/doc/source/libmod/api.rst b/doc/source/libmod/api.rst index b4f480f..d700919 100644 --- a/doc/source/libmod/api.rst +++ b/doc/source/libmod/api.rst @@ -6,4 +6,4 @@ declarations directly in the ``mod`` namespace. The nested namespace ``mod::lib` contains the actual implementation, and you probably do not want to use anything from it directly. -.. include:: Libmodtoc.rst +.. include:: Toc.rst diff --git a/doc/source/libmod/libmod.rst b/doc/source/libmod/index.rst similarity index 91% rename from doc/source/libmod/libmod.rst rename to doc/source/libmod/index.rst index 944b445..0e59716 100644 --- a/doc/source/libmod/libmod.rst +++ b/doc/source/libmod/index.rst @@ -1,3 +1,5 @@ +.. _libmod: + ****** libMØD ****** diff --git a/doc/source/postmod/postmod.rst b/doc/source/postmod/index.rst similarity index 95% rename from doc/source/postmod/postmod.rst rename to doc/source/postmod/index.rst index 99ef336..775e9a4 100644 --- a/doc/source/postmod/postmod.rst +++ b/doc/source/postmod/index.rst @@ -1,7 +1,7 @@ .. _mod_post: **************************** -PostMØD (``mod_post``) +PostMØD **************************** .. program:: mod_post @@ -13,7 +13,7 @@ invoked by the wrapper script. The script does the following: #. Clear the folder ``summary/``. If it does not exist, it is created. -#. Source the file ``out/post.sh`` and create ``summary/Makefile`` +#. Source the command file ``out/post.sh`` and create ``summary/Makefile`` (and a bunch of additional Makefile files it includes). #. Run ``make -f summary/Makefile all`` (with a few more arguments). The Makefile will compile figures and generate a Latex file. diff --git a/doc/source/pymod/api.rst b/doc/source/pymod/api.rst index 194b4eb..bf43f60 100644 --- a/doc/source/pymod/api.rst +++ b/doc/source/pymod/api.rst @@ -1,4 +1,4 @@ API Reference ============= -.. include:: Pymodtoc.rst +.. include:: Toc.rst diff --git a/doc/source/pymod/pymod.rst b/doc/source/pymod/index.rst similarity index 94% rename from doc/source/pymod/pymod.rst rename to doc/source/pymod/index.rst index e1d96e2..f25eb9b 100644 --- a/doc/source/pymod/pymod.rst +++ b/doc/source/pymod/index.rst @@ -1,5 +1,7 @@ .. py:module:: mod +.. _pymod: + ***** PyMØD ***** @@ -14,4 +16,5 @@ which automatically sets ``PYTHONPATH`` and inserts a small preamble. :maxdepth: 2 api + ../epim/index extensions diff --git a/docker/Arch.Dockerfile b/docker/Arch.Dockerfile index aebe980..30fc819 100644 --- a/docker/Arch.Dockerfile +++ b/docker/Arch.Dockerfile @@ -9,11 +9,12 @@ RUN pacman -Suy --noconfirm \ python-pip \ && pip3 install -r requirements_nodoc.txt \ && pacman -Suy --noconfirm \ - $(bindep -b | tr '\n' ' ') \ + $(bindep -b testing | tr '\n' ' ') \ && rm -rf /var/cache/pacman WORKDIR /opt/mod/build +ENV CXXFLAGS=-Werror -Wno-error=maybe-uninitialized RUN cmake ../ -DBUILD_DOC=no \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING_SANITIZERS=off \ diff --git a/docker/Fedora.Dockerfile b/docker/Fedora.Dockerfile index cc2ed31..027cf31 100644 --- a/docker/Fedora.Dockerfile +++ b/docker/Fedora.Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:34 +FROM fedora:36 ARG j=7 WORKDIR /opt/mod @@ -9,12 +9,14 @@ RUN dnf install -y \ python3-pip \ && pip3 install -r requirements_nodoc.txt \ && dnf install -y \ - $(bindep -b | tr '\n' ' ') \ + $(bindep -b testing | tr '\n' ' ') \ && dnf clean all \ && rm -rf /var/cache/yum WORKDIR /opt/mod/build +ENV BABEL_LIBDIR=/usr/lib64/openbabel3 +ENV CXXFLAGS=-Werror RUN cmake ../ -DBUILD_DOC=no \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_TESTING_SANITIZERS=off \ diff --git a/docker/Ubuntu.Dockerfile b/docker/Ubuntu.Dockerfile index 4776af9..25ded52 100644 --- a/docker/Ubuntu.Dockerfile +++ b/docker/Ubuntu.Dockerfile @@ -23,11 +23,11 @@ RUN apt-get update -qq \ && pip3 install -r requirements_nodoc.txt \ && DEBIAN_FRONTEND=noninteractive \ apt install --no-install-recommends -y \ - $(bindep -b | tr '\n' ' ') \ + $(bindep -b testing | tr '\n' ' ') \ librsvg2-dev libpango1.0-dev \ && DEBIAN_FRONTEND=noninteractive \ apt install --no-install-recommends -y \ - vim \ + vim less \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* @@ -61,6 +61,7 @@ RUN \ WORKDIR /opt/mod/build +ENV CXXFLAGS=-Werror RUN cmake ../ -DBUILD_DOC=no \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_MODULE_LINKER_FLAGS="-flto=$j" -DCMAKE_SHARED_LINKER_FLAGS="-flto=$j" \ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e52590a..3c0b00a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -58,7 +58,7 @@ if(BUILD_PY_MOD) COMMAND ${CMAKE_INSTALL_FULL_BINDIR}/mod -f ${CMAKE_CURRENT_LIST_DIR}/${fileName}.py WORKING_DIRECTORY ${workDir}) set_tests_properties(${testName} PROPERTIES - ENVIRONMENT "MOD_NUM_POST_THREADS=1;MOD_NO_DEPRECATED=1") + ENVIRONMENT "MOD_NUM_POST_THREADS=1;PYTHONWARNINGS=error;MOD_NO_DEPRECATED=1") add_coverage_case(${testName}) endforeach() endif() \ No newline at end of file diff --git a/examples/py/0000_hello.py b/examples/py/000_basics/000_hello.py similarity index 88% rename from examples/py/0000_hello.py rename to examples/py/000_basics/000_hello.py index 06b6986..d3a2cf6 100644 --- a/examples/py/0000_hello.py +++ b/examples/py/000_basics/000_hello.py @@ -1,8 +1,8 @@ # Normal printing to the terminal: print("Hello world") # Make some headers in the summary: -postChapter("Hello") -postSection("World") +post.summaryChapter("Hello") +post.summarySection("World") # Load a moleucle from a SMILES string: mol = smiles("Cn1cnc2c1c(=O)n(c(=O)n2C)C", name="Caffeine") # Put a visualisation of the molecule in the summary: diff --git a/examples/py/0010_graphLoading.py b/examples/py/000_basics/010_graphLoading.py similarity index 100% rename from examples/py/0010_graphLoading.py rename to examples/py/000_basics/010_graphLoading.py diff --git a/examples/py/0011_graphPrinting.py b/examples/py/000_basics/011_graphPrinting.py similarity index 100% rename from examples/py/0011_graphPrinting.py rename to examples/py/000_basics/011_graphPrinting.py diff --git a/examples/py/0012_graphInterface.py b/examples/py/000_basics/012_graphInterface.py similarity index 100% rename from examples/py/0012_graphInterface.py rename to examples/py/000_basics/012_graphInterface.py diff --git a/examples/py/0013_graphMorphisms.py b/examples/py/000_basics/013_graphMorphisms.py similarity index 100% rename from examples/py/0013_graphMorphisms.py rename to examples/py/000_basics/013_graphMorphisms.py diff --git a/examples/py/0030_ruleLoading.py b/examples/py/000_basics/030_ruleLoading.py similarity index 100% rename from examples/py/0030_ruleLoading.py rename to examples/py/000_basics/030_ruleLoading.py diff --git a/examples/py/0031_ruleMorphisms.py b/examples/py/000_basics/031_ruleMorphisms.py similarity index 100% rename from examples/py/0031_ruleMorphisms.py rename to examples/py/000_basics/031_ruleMorphisms.py diff --git a/examples/py/0050_formoseGrammar.py b/examples/py/000_basics/050_formoseGrammar.py similarity index 100% rename from examples/py/0050_formoseGrammar.py rename to examples/py/000_basics/050_formoseGrammar.py diff --git a/examples/py/000_basics/051_fileInclusion.py b/examples/py/000_basics/051_fileInclusion.py new file mode 100644 index 0000000..77243bd --- /dev/null +++ b/examples/py/000_basics/051_fileInclusion.py @@ -0,0 +1,9 @@ +include("050_formoseGrammar.py") +post.summarySection("Input Graphs") +for a in inputGraphs: + a.print() +post.summarySection("Input Rules") +for a in inputRules: + a.print() +# rst-name: Including Files +# rst: We can include other files (a la C/C++) to separate functionality. diff --git a/examples/py/000_basics/meta.json b/examples/py/000_basics/meta.json new file mode 100644 index 0000000..5c20fe7 --- /dev/null +++ b/examples/py/000_basics/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Basics, Graphs, and Rules", + "oldIds": ["basics"] +} diff --git a/examples/py/0051_fileInclusion.py b/examples/py/0051_fileInclusion.py deleted file mode 100644 index 93d57ac..0000000 --- a/examples/py/0051_fileInclusion.py +++ /dev/null @@ -1,9 +0,0 @@ -include("0050_formoseGrammar.py") -postSection("Input Graphs") -for a in inputGraphs: - a.print() -postSection("Input Rules") -for a in inputRules: - a.print() -# rst-name: Including Files -# rst: We can include other files (a la C/C++) to seperate functionality. diff --git a/examples/py/0100_rcGraphOps.py b/examples/py/010_rc/100_rcGraphOps.py similarity index 81% rename from examples/py/0100_rcGraphOps.py rename to examples/py/010_rc/100_rcGraphOps.py index 0802dcd..63dc6f9 100644 --- a/examples/py/0100_rcGraphOps.py +++ b/examples/py/010_rc/100_rcGraphOps.py @@ -1,4 +1,4 @@ -include("0050_formoseGrammar.py") +include("../000_basics/050_formoseGrammar.py") glycolaldehyde.print() # A graph G can be used to construct special rules: # (\emptyset <- \emptyset -> G) @@ -13,13 +13,13 @@ bindRules = rc.eval(bindExp) unbindRules = rc.eval(unbindExp) idRules = rc.eval(idExp) -postSection("Bind Rules") +post.summarySection("Bind Rules") for p in bindRules: p.print() -postSection("Unbind Rules") +post.summarySection("Unbind Rules") for p in unbindRules: p.print() -postSection("Id Rules") +post.summarySection("Id Rules") for p in idRules: p.print() # rst-name: Unary Operators diff --git a/examples/py/0101_rcParallel.py b/examples/py/010_rc/101_rcParallel.py similarity index 88% rename from examples/py/0101_rcParallel.py rename to examples/py/010_rc/101_rcParallel.py index 6385363..8282b05 100644 --- a/examples/py/0101_rcParallel.py +++ b/examples/py/010_rc/101_rcParallel.py @@ -1,4 +1,4 @@ -include("0050_formoseGrammar.py") +include("../000_basics/050_formoseGrammar.py") rc = rcEvaluator(inputRules) # The special global object 'rcParallel' is used to make a pseudo-operator: exp = rcId(formaldehyde) *rcParallel* rcUnbind(glycolaldehyde) diff --git a/examples/py/0102_rcSuper.py b/examples/py/010_rc/102_rcSuper.py similarity index 85% rename from examples/py/0102_rcSuper.py rename to examples/py/010_rc/102_rcSuper.py index b059aa2..f51172a 100644 --- a/examples/py/0102_rcSuper.py +++ b/examples/py/010_rc/102_rcSuper.py @@ -1,4 +1,4 @@ -include("0050_formoseGrammar.py") +include("../000_basics/050_formoseGrammar.py") rc = rcEvaluator(inputRules) exp = rcId(formaldehyde) *rcParallel* rcId(glycolaldehyde) exp = exp *rcSuper* ketoEnol_F diff --git a/examples/py/0120_rcFormose.py b/examples/py/010_rc/120_rcFormose.py similarity index 92% rename from examples/py/0120_rcFormose.py rename to examples/py/010_rc/120_rcFormose.py index ee5ee0f..9a6c959 100644 --- a/examples/py/0120_rcFormose.py +++ b/examples/py/010_rc/120_rcFormose.py @@ -1,4 +1,4 @@ -include("0050_formoseGrammar.py") +include("../000_basics/050_formoseGrammar.py") rc = rcEvaluator(inputRules) exp = ( rcId(glycolaldehyde) diff --git a/examples/py/010_rc/meta.json b/examples/py/010_rc/meta.json new file mode 100644 index 0000000..334fb06 --- /dev/null +++ b/examples/py/010_rc/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Rule Composition", + "oldIds": ["rc"] +} diff --git a/examples/py/0210_dgFixed.py b/examples/py/020_dg/210_dgFixed.py similarity index 93% rename from examples/py/0210_dgFixed.py rename to examples/py/020_dg/210_dgFixed.py index 09ae59e..40529f6 100644 --- a/examples/py/0210_dgFixed.py +++ b/examples/py/020_dg/210_dgFixed.py @@ -1,4 +1,4 @@ -include("0050_formoseGrammar.py") +include("../000_basics/050_formoseGrammar.py") # Reaction networks are expaned using a strategy: strat = ( # A molecule can be active or passive during evaluation. diff --git a/examples/py/0211_dgRepeat.py b/examples/py/020_dg/211_dgRepeat.py similarity index 85% rename from examples/py/0211_dgRepeat.py rename to examples/py/020_dg/211_dgRepeat.py index f69007b..2e71375 100644 --- a/examples/py/0211_dgRepeat.py +++ b/examples/py/020_dg/211_dgRepeat.py @@ -1,4 +1,4 @@ -include("0050_formoseGrammar.py") +include("../000_basics/050_formoseGrammar.py") strat = ( addUniverse(formaldehyde) >> addSubset(glycolaldehyde) diff --git a/examples/py/0212_dgPredicate.py b/examples/py/020_dg/212_dgPredicate.py similarity index 83% rename from examples/py/0212_dgPredicate.py rename to examples/py/020_dg/212_dgPredicate.py index 01fcd41..5a526ac 100644 --- a/examples/py/0212_dgPredicate.py +++ b/examples/py/020_dg/212_dgPredicate.py @@ -1,9 +1,9 @@ -include("0050_formoseGrammar.py") +include("../000_basics/050_formoseGrammar.py") strat = ( addUniverse(formaldehyde) >> addSubset(glycolaldehyde) # Constrain the reactions: - # No molecules with more than 20 atom can be created. + # No molecules with more than 20 atoms can be created. >> rightPredicate[ lambda derivation: all(g.numVertices <= 20 for g in derivation.right) ]( diff --git a/examples/py/0250_dgPrinting.py b/examples/py/020_dg/250_dgPrinting.py similarity index 89% rename from examples/py/0250_dgPrinting.py rename to examples/py/020_dg/250_dgPrinting.py index 8bac1fd..577abb4 100644 --- a/examples/py/0250_dgPrinting.py +++ b/examples/py/020_dg/250_dgPrinting.py @@ -1,4 +1,4 @@ -include("0212_dgPredicate.py") +include("212_dgPredicate.py") # Create a printer with default options: p = DGPrinter() # Hide "large" molecules: those with > 4 Cs: @@ -10,7 +10,7 @@ def edgePred(e): return True p.pushEdgeVisible(edgePred) # Add the number of Cs to the molecule labels: -p.pushVertexLabel(lambda v: "\\#C=" + str(v.graph.vLabelCount("C"))) +p.pushVertexLabel(lambda v: "#C=" + str(v.graph.vLabelCount("C"))) # Highlight the molecules with 4 Cs: p.pushVertexColour(lambda v: "blue" if v.graph.vLabelCount("C") == 4 else "") # Print the network with the customised printer. diff --git a/examples/py/0260_dpoPrinting.py b/examples/py/020_dg/260_dpoPrinting.py similarity index 81% rename from examples/py/0260_dpoPrinting.py rename to examples/py/020_dg/260_dpoPrinting.py index bdb63a9..f61d6d4 100644 --- a/examples/py/0260_dpoPrinting.py +++ b/examples/py/020_dg/260_dpoPrinting.py @@ -1,4 +1,4 @@ -include("0212_dgPredicate.py") +include("212_dgPredicate.py") for e in dg.edges: e.print() # rst-name: Double Pushout Printing diff --git a/examples/py/020_dg/meta.json b/examples/py/020_dg/meta.json new file mode 100644 index 0000000..8d9aa21 --- /dev/null +++ b/examples/py/020_dg/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Derivations and Reaction Networks", + "oldIds": ["dg"] +} diff --git a/examples/py/0320_aconitase.py b/examples/py/030_stereo/320_aconitase.py similarity index 100% rename from examples/py/0320_aconitase.py rename to examples/py/030_stereo/320_aconitase.py diff --git a/examples/py/0330_tartaric.py b/examples/py/030_stereo/330_tartaric.py similarity index 100% rename from examples/py/0330_tartaric.py rename to examples/py/030_stereo/330_tartaric.py diff --git a/examples/py/0340_tree.py b/examples/py/030_stereo/340_tree.py similarity index 100% rename from examples/py/0340_tree.py rename to examples/py/030_stereo/340_tree.py diff --git a/examples/py/030_stereo/meta.json b/examples/py/030_stereo/meta.json new file mode 100644 index 0000000..d32cc32 --- /dev/null +++ b/examples/py/030_stereo/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Local Geometry and Stereochemistry", + "oldIds": ["stereo"] +} diff --git a/examples/py/5000_encoding_simple.py b/examples/py/900_epim/0_encoding_simple.py similarity index 100% rename from examples/py/5000_encoding_simple.py rename to examples/py/900_epim/0_encoding_simple.py diff --git a/examples/py/5001_encoding_restriction.py b/examples/py/900_epim/1_encoding_restriction.py similarity index 100% rename from examples/py/5001_encoding_restriction.py rename to examples/py/900_epim/1_encoding_restriction.py diff --git a/examples/py/5002_encoding_recursive.py b/examples/py/900_epim/2_encoding_recursive.py similarity index 100% rename from examples/py/5002_encoding_recursive.py rename to examples/py/900_epim/2_encoding_recursive.py diff --git a/examples/py/5003_encoding_config.py b/examples/py/900_epim/3_encoding_config.py similarity index 100% rename from examples/py/5003_encoding_config.py rename to examples/py/900_epim/3_encoding_config.py diff --git a/examples/py/5004_exec_space_simple.py b/examples/py/900_epim/4_exec_space_simple.py similarity index 100% rename from examples/py/5004_exec_space_simple.py rename to examples/py/900_epim/4_exec_space_simple.py diff --git a/examples/py/5005_exec_space_recursive.py b/examples/py/900_epim/5_exec_space_recursive.py similarity index 100% rename from examples/py/5005_exec_space_recursive.py rename to examples/py/900_epim/5_exec_space_recursive.py diff --git a/examples/py/5006_hospital.py b/examples/py/900_epim/6_hospital.py similarity index 100% rename from examples/py/5006_hospital.py rename to examples/py/900_epim/6_hospital.py diff --git a/examples/py/900_epim/meta.json b/examples/py/900_epim/meta.json new file mode 100644 index 0000000..43e35b9 --- /dev/null +++ b/examples/py/900_epim/meta.json @@ -0,0 +1,4 @@ +{ + "title": "EpiM", + "oldIds": ["epim"] +} diff --git a/examples/py/fileRenames.json b/examples/py/fileRenames.json new file mode 100644 index 0000000..90abd90 --- /dev/null +++ b/examples/py/fileRenames.json @@ -0,0 +1,63 @@ +[ + { + "000_hello.py": "0000_hello.py", + "010_graphLoading.py": "0010_graphLoading.py", + "011_graphPrinting.py": "0011_graphPrinting.py", + "012_graphInterface.py": "0012_graphInterface.py", + "013_graphMorphisms.py": "0013_graphMorphisms.py", + "030_ruleLoading.py": "0030_ruleLoading.py", + "031_ruleMorphisms.py": "0031_ruleMorphisms.py", + "050_formoseGrammar.py": "0050_formoseGrammar.py", + "051_fileInclusion.py": "0051_fileInclusion.py", + "100_rcGraphOps.py": "0100_rcGraphOps.py", + "101_rcParallel.py": "0101_rcParallel.py", + "102_rcSuper.py": "0102_rcSuper.py", + "120_rcFormose.py": "0120_rcFormose.py", + "210_dgFixed.py": "0210_dgFixed.py", + "211_dgRepeat.py": "0211_dgRepeat.py", + "212_dgPredicate.py": "0212_dgPredicate.py", + "250_dgPrinting.py": "0250_dgPrinting.py", + "260_dpoPrinting.py": "0260_dpoPrinting.py", + "320_aconitase.py": "0320_aconitase.py", + "330_tartaric.py": "0330_tartaric.py", + "340_tree.py": "0340_tree.py", + "500_basicFlow.py": "0500_basicFlow.py", + "501_extraConstraints.py": "0501_extraConstraints.py", + "502_multipleSolutions.py": "0502_multipleSolutions.py", + "510_autocatalysis.py": "0510_autocatalysis.py" + }, + { + "0000_hello.py": "000_basics/000_hello.py", + "0010_graphLoading.py": "000_basics/010_graphLoading.py", + "0011_graphPrinting.py": "000_basics/011_graphPrinting.py", + "0012_graphInterface.py": "000_basics/012_graphInterface.py", + "0013_graphMorphisms.py": "000_basics/013_graphMorphisms.py", + "0030_ruleLoading.py": "000_basics/030_ruleLoading.py", + "0031_ruleMorphisms.py": "000_basics/031_ruleMorphisms.py", + "0050_formoseGrammar.py": "000_basics/050_formoseGrammar.py", + "0051_fileInclusion.py": "000_basics/051_fileInclusion.py", + "0100_rcGraphOps.py": "010_rc/100_rcGraphOps.py", + "0101_rcParallel.py": "010_rc/101_rcParallel.py", + "0102_rcSuper.py": "010_rc/102_rcSuper.py", + "0120_rcFormose.py": "010_rc/120_rcFormose.py", + "0210_dgFixed.py": "020_dg/210_dgFixed.py", + "0211_dgRepeat.py": "020_dg/211_dgRepeat.py", + "0212_dgPredicate.py": "020_dg/212_dgPredicate.py", + "0250_dgPrinting.py": "020_dg/250_dgPrinting.py", + "0260_dpoPrinting.py": "020_dg/260_dpoPrinting.py", + "0320_aconitase.py": "030_stereo/320_aconitase.py", + "0330_tartaric.py": "030_stereo/330_tartaric.py", + "0340_tree.py": "030_stereo/340_tree.py", + "0500_basicFlow.py": "050_flow/500_basicFlow.py", + "0501_extraConstraints.py": "050_flow/501_extraConstraints.py", + "0502_multipleSolutions.py": "050_flow/502_multipleSolutions.py", + "0510_autocatalysis.py": "050_flow/510_autocatalysis.py", + "5000_encoding_simple.py": "900_epim/0_encoding_simple.py", + "5001_encoding_restriction.py": "900_epim/1_encoding_restriction.py", + "5002_encoding_recursive.py": "900_epim/2_encoding_recursive.py", + "5003_encoding_config.py": "900_epim/3_encoding_config.py", + "5004_exec_space_simple.py": "900_epim/4_exec_space_simple.py", + "5005_exec_space_recursive.py": "900_epim/5_exec_space_recursive.py", + "5006_hospital.py": "900_epim/6_hospital.py" + } +] diff --git a/examples/py/sections.json b/examples/py/sections.json deleted file mode 100644 index b80e074..0000000 --- a/examples/py/sections.json +++ /dev/null @@ -1,7 +0,0 @@ -[ -{"first":0, "last":100, "id":"basics","title":"Basics, Graphs, and Rules"}, -{"first":100, "last":200, "id":"rc", "title":"Rule Composition"}, -{"first":200, "last":300, "id":"dg", "title":"Derivations and Reaction Networks"}, -{"first":300, "last":350, "id":"stereo","title":"Local Geometry and Stereochemistry"}, -{"first":5000,"last":5010,"id":"epim", "title":"EpiM"} -] diff --git a/examples/pymod_extension/CMakeLists.txt b/examples/pymod_extension/CMakeLists.txt index 805616c..5f207bd 100644 --- a/examples/pymod_extension/CMakeLists.txt +++ b/examples/pymod_extension/CMakeLists.txt @@ -14,7 +14,7 @@ find_package(mod REQUIRED) # Boost.Python # ------------------------------------------------------------------------- set(v 1.64.0) -foreach(PY 3 34 35 36 37 38 39) +foreach(PY 3 34 35 36 37 38 39 310 311 312 313 314 315 316 317 318 319) set(lib "python${PY}") find_package(Boost ${v} QUIET COMPONENTS ${lib}) if(Boost_FOUND) diff --git a/libs/jla_boost/CMakeLists.txt b/libs/jla_boost/CMakeLists.txt index c56dbfc..eaf8127 100644 --- a/libs/jla_boost/CMakeLists.txt +++ b/libs/jla_boost/CMakeLists.txt @@ -2,22 +2,20 @@ # Targets and Artefacts ########################################################################### -add_library(jla_boost STATIC - ${mod_jla_boost_INCLUDE_FILES} - ${mod_jla_boost_SRC_FILES}) +add_library(jla_boost INTERFACE) +# TODO: when CMake 3.19 can be assumed the source files can be added to an INTERFACE target +# ${mod_jla_boost_INCLUDE_FILES} +# ${mod_jla_boost_SRC_FILES}) add_library(JLA::boost ALIAS jla_boost) target_include_directories(jla_boost - PUBLIC + INTERFACE $ $) -target_link_libraries(jla_boost PUBLIC GraphCanon::graph_canon Boost::boost) -target_compile_options(jla_boost PRIVATE -Wall -Wextra -pedantic - -Wno-unused-parameter) -set_target_properties(jla_boost PROPERTIES - POSITION_INDEPENDENT_CODE ON - CXX_VISIBILITY_PRESET hidden - VISIBILITY_INLINES_HIDDEN ON) -target_compile_definitions(jla_boost PRIVATE JLA_BOOST_SOURCE) +target_link_libraries(jla_boost INTERFACE GraphCanon::graph_canon Boost::boost) +#set_target_properties(jla_boost PROPERTIES +# POSITION_INDEPENDENT_CODE ON +# CXX_VISIBILITY_PRESET hidden +# VISIBILITY_INLINES_HIDDEN ON) target_add_coverage(jla_boost) diff --git a/libs/jla_boost/include/jla_boost/graph/EdgeIndexedAdjacencyList.hpp b/libs/jla_boost/include/jla_boost/graph/EdgeIndexedAdjacencyList.hpp index d5fe62d..de34639 100644 --- a/libs/jla_boost/include/jla_boost/graph/EdgeIndexedAdjacencyList.hpp +++ b/libs/jla_boost/include/jla_boost/graph/EdgeIndexedAdjacencyList.hpp @@ -6,17 +6,18 @@ namespace jla_boost { template + typename VertexProperty = boost::no_property, + typename EdgeProperty = boost::no_property, + typename GraphProperty = boost::no_property> struct EdgeIndexedAdjacencyList { using Self = EdgeIndexedAdjacencyList; - using GraphType = boost::adjacency_list< boost::vecS, boost::vecS, DirectedS, + using GraphType = boost::adjacency_list, GraphProperty, - boost::vecS>; + boost::listS>; + // Do not use vecS for the edges, as it seems that the edge properties are stored by value there + // and the edge descriptors contain a pointer to the property. public: - - EdgeIndexedAdjacencyList() { } + EdgeIndexedAdjacencyList() = default; public: // Graph using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; using edge_descriptor = typename boost::graph_traits::edge_descriptor; @@ -132,9 +133,9 @@ struct EdgeIndexedAdjacencyList { } template - friend void put(const boost::edge_index_t&, Self &g, VertexOrEdge ve, Value &&v) { + friend void put(const boost::edge_index_t &, Self &g, VertexOrEdge ve, Value &&v) { // TODO: change to use "= delete" when GCC 5 is required (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62101)) - static_assert(sizeof (ve) == 0, "You should not mess with this."); + static_assert(sizeof(ve) == 0, "You should not mess with this."); } template @@ -163,14 +164,14 @@ namespace boost { template struct property_map, Property> -: property_map::GraphType, Property> { + : property_map::GraphType, Property> { }; template struct property_map, Property> -: property_map::GraphType, Property> { + : property_map::GraphType, Property> { }; } // namespace boost -#endif /* JLA_BOOST_GRAPH_EDGEINDEXEDADJACENCYLIST_HPP */ \ No newline at end of file +#endif // JLA_BOOST_GRAPH_EDGEINDEXEDADJACENCYLIST_HPP \ No newline at end of file diff --git a/libs/jla_boost/include/jla_boost/graph/dpo/IO.hpp b/libs/jla_boost/include/jla_boost/graph/dpo/IO.hpp deleted file mode 100644 index 00504b0..0000000 --- a/libs/jla_boost/include/jla_boost/graph/dpo/IO.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef JLA_BOOST_GRAPH_DPO_IO_HPP -#define JLA_BOOST_GRAPH_DPO_IO_HPP - -#include - -#include - -namespace jla_boost { -namespace GraphDPO { - -std::ostream &operator<<(std::ostream &s, Membership m); - -} // namespace GraphDPO -} // namespace jla_boost - -#endif /* JLA_BOOST_GRAPH_DPO_IO_HPP */ - diff --git a/libs/jla_boost/include/jla_boost/graph/dpo/Rule.hpp b/libs/jla_boost/include/jla_boost/graph/dpo/Rule.hpp deleted file mode 100644 index c55e772..0000000 --- a/libs/jla_boost/include/jla_boost/graph/dpo/Rule.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef JLA_BOOST_GRAPH_DPO_RULE_HPP -#define JLA_BOOST_GRAPH_DPO_RULE_HPP - -#include - -namespace jla_boost { -namespace GraphDPO { - -enum class Membership { - Left, Right, Context -}; - -inline Membership invert(Membership m) { - switch(m) { - case Membership::Left: return Membership::Right; - case Membership::Right: return Membership::Left; - default: return Membership::Context; - } -} - -template -struct PushoutRuleTraits; - -template -struct PushoutRuleConcept { - using Traits = PushoutRuleTraits; - using GraphType = typename Traits::GraphType; - using LeftGraphType = typename Traits::LeftGraphType; - using ContextGraphType = typename Traits::ContextGraphType; - using RightGraphType = typename Traits::RightGraphType; - - using Vertex = typename boost::graph_traits::vertex_descriptor; - using Edge = typename boost::graph_traits::edge_descriptor; - - BOOST_CONCEPT_ASSERT((boost::GraphConcept)); - - BOOST_CONCEPT_USAGE(PushoutRuleConcept) { - const R &rConst = r; - const GraphType &gConst = get_graph(rConst); - (void) gConst; - const LeftGraphType &gLeft = get_left(rConst); - const ContextGraphType &gContext = get_context(rConst); - const RightGraphType &gRight = get_right(rConst); - (void) gLeft; - (void) gContext; - (void) gRight; - m = membership(r, v); - m = membership(r, e); - } -private: - R r; - Vertex v; - Edge e; -private: - Membership m; -}; - -template -struct WritablePushoutRuleConcept : PushoutRuleConcept { - using Traits = PushoutRuleTraits; - using GraphType = typename Traits::GraphType; - using LeftGraphType = typename Traits::LeftGraphType; - using ContextGraphType = typename Traits::ContextGraphType; - using RightGraphType = typename Traits::RightGraphType; - - using Vertex = typename boost::graph_traits::vertex_descriptor; - using Edge = typename boost::graph_traits::edge_descriptor; - - BOOST_CONCEPT_USAGE(WritablePushoutRuleConcept) { - GraphType &g = get_graph(r); - (void) g; - put_membership(r, v, m); - put_membership(r, e, m); - } -private: - R r; - Vertex v; - Edge e; -private: - Membership m; -}; - -template -struct PushoutRuleTraits { - using GraphType = typename R::GraphType; - using LeftGraphType = typename R::LeftGraphType; - using ContextGraphType = typename R::ContextGraphType; - using RightGraphType = typename R::RightGraphType; -}; - -} // namespace GraphDPO -} // namespace jla_boost - -#endif /* JLA_BOOST_GRAPH_DPO_RULE_HPP */ \ No newline at end of file diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/AsPropertyMap.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/AsPropertyMap.hpp index 9c75943..9761dd2 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/AsPropertyMap.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/AsPropertyMap.hpp @@ -1,7 +1,7 @@ #ifndef JLA_BOOST_GRAPHMORPHISM_AS_PROPERTY_MAP_HPP #define JLA_BOOST_GRAPHMORPHISM_AS_PROPERTY_MAP_HPP -#include +#include #include diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/Concepts.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/Concepts.hpp new file mode 100644 index 0000000..8e47593 --- /dev/null +++ b/libs/jla_boost/include/jla_boost/graph/morphism/Concepts.hpp @@ -0,0 +1,160 @@ +#ifndef JLA_BOOST_GRAPH_MORPHISM_VERTEX_MAP_H +#define JLA_BOOST_GRAPH_MORPHISM_VERTEX_MAP_H + +#include +#include +#include + +#include +#include +#include + +// - VertexMapConcept +// - InvertibleVertexMapConcept +// - WritableVertexMapConcept + +namespace jla_boost { +namespace GraphMorphism { + +template +struct VertexMapTraits; + +// Uni-directional +// ============================================================================= + +template +struct VertexMapConcept { + using Traits = VertexMapTraits; + using GraphDom = typename Traits::GraphDom; + using GraphCodom = typename Traits::GraphCodom; + using Storable = typename Traits::Storable; + + BOOST_CONCEPT_ASSERT((boost::GraphConcept)); + BOOST_CONCEPT_ASSERT((boost::GraphConcept)); + + using VertexDom = typename boost::graph_traits::vertex_descriptor; + using VertexCodom = typename boost::graph_traits::vertex_descriptor; + + BOOST_CONCEPT_USAGE(VertexMapConcept) { + [[maybe_unused]] constexpr bool storable = Storable::value; + const M &cVertexMap = vertexMap; + [[maybe_unused]] VertexCodom vCodom = get(cVertexMap, gDom, gCodom, vDom); + [[maybe_unused]] const auto mReinterpret = Traits::template reinterpret( + std::move(vertexMap), gDom, gCodom, gDom, gCodom); + const auto trans = [](auto &&m, auto &gDom, auto &gCodom) { return std::move(m); }; + [[maybe_unused]] const auto mTransform = Traits::transform(trans, std::move(vertexMap), gDom, gCodom); + } +private: + M vertexMap; + GraphDom gDom; + GraphCodom gCodom; + VertexDom vDom; +}; + +template +struct GraphMapConcept : VertexMapConcept { + using Traits = VertexMapTraits; + using GraphDom = typename Traits::GraphDom; + using GraphCodom = typename Traits::GraphCodom; + + using EdgeDom = typename boost::graph_traits::edge_descriptor; + using EdgeCodom = typename boost::graph_traits::edge_descriptor; + + BOOST_CONCEPT_USAGE(GraphMapConcept) { + const M &cGraphMap = graphMap; + [[maybe_unused]] const EdgeCodom eCodom = get(cGraphMap, gDom, gCodom, eDom); + } +private: + M graphMap; + GraphDom gDom; + GraphCodom gCodom; + EdgeDom eDom; +}; + +// Bi-directional +// ============================================================================= + +template +struct InvertibleVertexMapConcept : VertexMapConcept { + using Traits = VertexMapTraits; + using GraphDom = typename Traits::GraphDom; + using GraphCodom = typename Traits::GraphCodom; + using VertexDom = typename boost::graph_traits::vertex_descriptor; + using VertexCodom = typename boost::graph_traits::vertex_descriptor; + + BOOST_CONCEPT_USAGE(InvertibleVertexMapConcept) { + const M &cVertexMap = vertexMap; + [[maybe_unused]] const VertexDom vDom = get_inverse(cVertexMap, gDom, gCodom, vCodom); + } +private: + M vertexMap; + GraphDom gDom; + GraphCodom gCodom; + VertexCodom vCodom; +}; + +template +struct InvertibleGraphMapConcept : InvertibleVertexMapConcept { + using Traits = VertexMapTraits; + using GraphDom = typename Traits::GraphDom; + using GraphCodom = typename Traits::GraphCodom; + using EdgeDom = typename boost::graph_traits::edge_descriptor; + using EdgeCodom = typename boost::graph_traits::edge_descriptor; + + BOOST_CONCEPT_USAGE(InvertibleGraphMapConcept) { + const M &cGraphMap = graphMap; + [[maybe_unused]] const EdgeDom eDom = get_inverse(cGraphMap, gDom, gCodom, eCodom); + } +private: + M graphMap; + GraphDom gDom; + GraphCodom gCodom; + EdgeCodom eCodom; +}; + +// Writeable +// ============================================================================= + +template +struct WritableVertexMapConcept : VertexMapConcept { + using Traits = VertexMapTraits; + using GraphDom = typename Traits::GraphDom; + using GraphCodom = typename Traits::GraphCodom; + using VertexDom = typename boost::graph_traits::vertex_descriptor; + using VertexCodom = typename boost::graph_traits::vertex_descriptor; + + BOOST_CONCEPT_USAGE(WritableVertexMapConcept) { + const VertexDom vDom; + const VertexCodom vCodom; + put(vertexMap, gDom, gCodom, vDom, vCodom); + syncSize(gDom, gCodom); + } +private: + M vertexMap; + GraphDom gDom; + GraphCodom gCodom; +}; + +template +struct WritableGraphMapConcept : WritableVertexMapConcept { + using Traits = VertexMapTraits; + using GraphDom = typename Traits::GraphDom; + using GraphCodom = typename Traits::GraphCodom; + using EdgeDom = typename boost::graph_traits::edge_descriptor; + using EdgeCodom = typename boost::graph_traits::edge_descriptor; + + BOOST_CONCEPT_USAGE(WritableGraphMapConcept) { + const EdgeDom eDom; + const EdgeCodom eCodom; + put(graphMap, gDom, gCodom, eDom, eCodom); + } +private: + M graphMap; + GraphDom gDom; + GraphCodom gCodom; +}; + +} // namespace GraphMorphism +} // namespace jla_boost + +#endif /* JLA_BOOST_GRAPH_MORPHISM_VERTEX_MAP_H */ \ No newline at end of file diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/VertexMap.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/VertexMap.hpp deleted file mode 100644 index f61991a..0000000 --- a/libs/jla_boost/include/jla_boost/graph/morphism/VertexMap.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef JLA_BOOST_GRAPH_MORPHISM_VERTEX_MAP_H -#define JLA_BOOST_GRAPH_MORPHISM_VERTEX_MAP_H - -#include -#include -#include - -#include -#include -#include - -// - VertexMapConcept -// - InvertibleVertexMapConcept -// - WritableVertexMapConcept - -namespace jla_boost { -namespace GraphMorphism { - -template -struct VertexMapTraits; - -// VertexMapConcept -// ----------------------------------------------------------------------------- - -template -struct VertexMapConcept { - using Traits = VertexMapTraits; - using GraphDom = typename Traits::GraphDom; - using GraphCodom = typename Traits::GraphCodom; - using Storable = typename Traits::Storable; - - BOOST_CONCEPT_ASSERT((boost::GraphConcept)); - BOOST_CONCEPT_ASSERT((boost::GraphConcept)); - - using VertexDom = typename boost::graph_traits::vertex_descriptor; - using VertexCodom = typename boost::graph_traits::vertex_descriptor; - - BOOST_CONCEPT_USAGE(VertexMapConcept) { - bool storable = Storable::value; - (void) storable; - const M &cVertexMap = vertexMap; - VertexCodom vCodom = get(cVertexMap, gDom, gCodom, vDom); - (void) vCodom; - auto mReinterpret = Traits::template reinterpret(std::move(vertexMap), gDom, gCodom, gDom, gCodom); - (void) mReinterpret; - auto trans = [](auto &&m, auto &gDom, auto &gCodom) { - return std::move(m); - }; - auto mTransform = Traits::transform(trans, std::move(vertexMap), gDom, gCodom); - (void) mTransform; - } -private: - M vertexMap; - GraphDom gDom; - GraphCodom gCodom; - VertexDom vDom; -}; - -// InvertibleVertexMapConcept -// ----------------------------------------------------------------------------- - -template -struct InvertibleVertexMapConcept : VertexMapConcept { - using Traits = VertexMapTraits; - using GraphDom = typename Traits::GraphDom; - using GraphCodom = typename Traits::GraphCodom; - using VertexDom = typename boost::graph_traits::vertex_descriptor; - using VertexCodom = typename boost::graph_traits::vertex_descriptor; - - BOOST_CONCEPT_USAGE(InvertibleVertexMapConcept) { - const M &cVertexMap = vertexMap; - VertexDom vDom = get_inverse(cVertexMap, gDom, gCodom, vCodom); - (void) vDom; - } -private: - M vertexMap; - GraphDom gDom; - GraphCodom gCodom; - VertexCodom vCodom; -}; - -// WritableVertexMapConcept -// ----------------------------------------------------------------------------- - -template -struct WritableVertexMapConcept : VertexMapConcept { - using Traits = VertexMapTraits; - using GraphDom = typename Traits::GraphDom; - using GraphCodom = typename Traits::GraphCodom; - using VertexDom = typename boost::graph_traits::vertex_descriptor; - using VertexCodom = typename boost::graph_traits::vertex_descriptor; - - BOOST_CONCEPT_USAGE(WritableVertexMapConcept) { - VertexDom vDom; - VertexCodom vCodom; - put(vertexMap, gDom, gCodom, vDom, vCodom); - } -private: - M vertexMap; - GraphDom gDom; - GraphCodom gCodom; -}; - -} // namespace GraphMorphism -} // namespace jla_boost - -#endif /* JLA_BOOST_GRAPH_MORPHISM_VERTEX_MAP_H */ \ No newline at end of file diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Filter.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Filter.hpp index 6bb6c5e..61f2651 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Filter.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Filter.hpp @@ -1,7 +1,7 @@ #ifndef JLA_BOOST_GRAPH_MORPHISM_CALLBACKS_FILTER_HPP #define JLA_BOOST_GRAPH_MORPHISM_CALLBACKS_FILTER_HPP -#include +#include namespace jla_boost { namespace GraphMorphism { diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Limit.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Limit.hpp index 9a0d355..f285fb8 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Limit.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Limit.hpp @@ -2,7 +2,7 @@ #define JLA_BOOST_GRAPH_MORPHISM_CALLBACKS_LIMIT_HPP #include -#include +#include namespace jla_boost { namespace GraphMorphism { diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Store.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Store.hpp index 84f73b9..41197c7 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Store.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Store.hpp @@ -1,7 +1,7 @@ #ifndef JLA_BOOST_GRAPH_MORPHISM_CALLBACKS_STORE_HPP #define JLA_BOOST_GRAPH_MORPHISM_CALLBACKS_STORE_HPP -#include +#include namespace jla_boost { namespace GraphMorphism { diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Transform.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Transform.hpp index d05c3d9..19a3514 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Transform.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Transform.hpp @@ -1,7 +1,7 @@ #ifndef JLA_BOOST_GRAPH_MORPHISM_CALLBACKS_TRANSFORM_HPP #define JLA_BOOST_GRAPH_MORPHISM_CALLBACKS_TRANSFORM_HPP -#include +#include namespace jla_boost { namespace GraphMorphism { diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Unwrapper.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Unwrapper.hpp index a175f33..583d2bf 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Unwrapper.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/callbacks/Unwrapper.hpp @@ -5,7 +5,7 @@ // been unwrapped if they match the given graph. #include -#include +#include namespace jla_boost { namespace GraphMorphism { diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/finders/CommonSubgraph.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/finders/CommonSubgraph.hpp index 6c78e0a..6922b24 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/finders/CommonSubgraph.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/finders/CommonSubgraph.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -66,16 +67,16 @@ struct CommonSubgraphEnumerator : InjectiveEnumerationState< // NOTE: This will not work with parallel edges, since the // first matching edge is always chosen. - EdgeLeft edge_to_new1; - bool edge_to_new_exists1 = false; - EdgeRight edge_to_new2; - bool edge_to_new_exists2 = false; + EdgeLeft edgeToNewLeft; + bool edgeToNewExistsLeft = false; + EdgeRight edgeToNewRight; + bool edgeToNewExistsRight = false; // Search for edge from existing to new vertex (gLeft) for(auto eOutLeft : asRange(out_edges(vOtherLeft, this->gLeft))) { if(target(eOutLeft, this->gLeft) == vLeft) { - edge_to_new1 = eOutLeft; - edge_to_new_exists1 = true; + edgeToNewLeft = eOutLeft; + edgeToNewExistsLeft = true; break; } } @@ -83,8 +84,8 @@ struct CommonSubgraphEnumerator : InjectiveEnumerationState< // Search for edge from existing to new vertex (gRight) for(auto eOutRight : asRange(out_edges(vOtherRight, this->gRight))) { if(target(eOutRight, this->gRight) == vRight) { - edge_to_new2 = eOutRight; - edge_to_new_exists2 = true; + edgeToNewRight = eOutRight; + edgeToNewExistsRight = true; break; } } @@ -94,12 +95,16 @@ struct CommonSubgraphEnumerator : InjectiveEnumerationState< #ifdef MORPHISM_INJECTIVE_ENUMERATION_DEBUG std::cout << this->indent(3) << "undirected? " << std::boolalpha << is_undirected1 << ", " << is_undirected2 << std::endl; - std::cout << this->indent(3) << "edge_to_new_exists? " - << std::boolalpha << edge_to_new_exists1 << ", " << edge_to_new_exists1 << std::endl; + std::cout << this->indent(3) << "edgeToNew_exists? " + << std::boolalpha << edgeToNewExistsLeft << ", " << edgeToNewExistsLeft << std::endl; #endif - if(edge_to_new_exists1 && edge_to_new_exists2) { - if(this->edgePred(edge_to_new1, edge_to_new2)) { + if(edgeToNewExistsLeft && edgeToNewExistsRight) { +#ifdef MORPHISM_INJECTIVE_ENUMERATION_DEBUG + std::cout << this->indent(3) << "edgeToNewLeft: " << edgeToNewLeft << std::endl; + std::cout << this->indent(3) << "edgeToNewRight: " << edgeToNewRight << std::endl; +#endif + if(this->edgePred(edgeToNewLeft, edgeToNewRight)) { has_one_edge = true; } else { #ifdef MORPHISM_INJECTIVE_ENUMERATION_DEBUG @@ -317,7 +322,7 @@ struct CommonSubgraphCallbackHelper { } private: const GraphLeft &gLeft; - const GraphLeft &gRight; + const GraphRight &gRight; Next next; std::vector cache; }; diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/finders/mcgregor_common_subgraphs.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/finders/mcgregor_common_subgraphs.hpp new file mode 100644 index 0000000..af02576 --- /dev/null +++ b/libs/jla_boost/include/jla_boost/graph/morphism/finders/mcgregor_common_subgraphs.hpp @@ -0,0 +1,1126 @@ +//======================================================================= +// Copyright 2009 Trustees of Indiana University. +// Authors: Michael Hansen, Andrew Lumsdaine +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +//======================================================================= + +// Note: this is a copy from Boost 1.77 with the following modifications: +// - in maximum_subgraph_interceptor: +// - const GraphFirst& m_graph2; +// + const GraphSecond& m_graph2; +// - and in the same class +// - BGL_FORALL_VERTICES_T(vertex2, m_graph2, GraphFirst) +// + BGL_FORALL_VERTICES_T(vertex2, m_graph2, GraphSecond) + + +#ifndef BOOST_GRAPH_MCGREGOR_COMMON_SUBGRAPHS_HPP +#define BOOST_GRAPH_MCGREGOR_COMMON_SUBGRAPHS_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ + +namespace detail +{ + +// Traits associated with common subgraphs, used mainly to keep a +// consistent type for the correspondence maps. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond > +struct mcgregor_common_subgraph_traits +{ + typedef typename graph_traits< GraphFirst >::vertex_descriptor + vertex_first_type; + typedef typename graph_traits< GraphSecond >::vertex_descriptor + vertex_second_type; + + typedef shared_array_property_map< vertex_second_type, + VertexIndexMapFirst > + correspondence_map_first_to_second_type; + + typedef shared_array_property_map< vertex_first_type, + VertexIndexMapSecond > + correspondence_map_second_to_first_type; +}; + +} // namespace detail + +// ========================================================================== + +// Binary function object that returns true if the values for item1 +// in property_map1 and item2 in property_map2 are equivalent. +template < typename PropertyMapFirst, typename PropertyMapSecond > +struct property_map_equivalent +{ + + property_map_equivalent(const PropertyMapFirst property_map1, + const PropertyMapSecond property_map2) + : m_property_map1(property_map1), m_property_map2(property_map2) + { + } + + template < typename ItemFirst, typename ItemSecond > + bool operator()(const ItemFirst item1, const ItemSecond item2) + { + return (get(m_property_map1, item1) == get(m_property_map2, item2)); + } + +private: + const PropertyMapFirst m_property_map1; + const PropertyMapSecond m_property_map2; +}; + +// Returns a property_map_equivalent object that compares the values +// of property_map1 and property_map2. +template < typename PropertyMapFirst, typename PropertyMapSecond > +property_map_equivalent< PropertyMapFirst, PropertyMapSecond > +make_property_map_equivalent( + const PropertyMapFirst property_map1, const PropertyMapSecond property_map2) +{ + + return (property_map_equivalent< PropertyMapFirst, PropertyMapSecond >( + property_map1, property_map2)); +} + +// Binary function object that always returns true. Used when +// vertices or edges are always equivalent (i.e. have no labels). +struct always_equivalent +{ + + template < typename ItemFirst, typename ItemSecond > + bool operator()(const ItemFirst&, const ItemSecond&) + { + return (true); + } +}; + +// ========================================================================== + +namespace detail +{ + +// Return true if new_vertex1 and new_vertex2 can extend the +// subgraph represented by correspondence_map_1_to_2 and +// correspondence_map_2_to_1. The vertices_equivalent and +// edges_equivalent predicates are used to test vertex and edge +// equivalency between the two graphs. +template < typename GraphFirst, typename GraphSecond, + typename CorrespondenceMapFirstToSecond, + typename CorrespondenceMapSecondToFirst, + typename EdgeEquivalencePredicate, typename VertexEquivalencePredicate > +bool can_extend_graph(const GraphFirst& graph1, const GraphSecond& graph2, + CorrespondenceMapFirstToSecond correspondence_map_1_to_2, + CorrespondenceMapSecondToFirst /*correspondence_map_2_to_1*/, + typename graph_traits< GraphFirst >::vertices_size_type subgraph_size, + typename graph_traits< GraphFirst >::vertex_descriptor new_vertex1, + typename graph_traits< GraphSecond >::vertex_descriptor new_vertex2, + EdgeEquivalencePredicate edges_equivalent, + VertexEquivalencePredicate vertices_equivalent, + bool only_connected_subgraphs) +{ + typedef typename graph_traits< GraphSecond >::vertex_descriptor + VertexSecond; + + typedef typename graph_traits< GraphFirst >::edge_descriptor EdgeFirst; + typedef + typename graph_traits< GraphSecond >::edge_descriptor EdgeSecond; + + // Check vertex equality + if (!vertices_equivalent(new_vertex1, new_vertex2)) + { + return (false); + } + + // Vertices match and graph is empty, so we can extend the subgraph + if (subgraph_size == 0) + { + return (true); + } + + bool has_one_edge = false; + + // Verify edges with existing sub-graph + BGL_FORALL_VERTICES_T(existing_vertex1, graph1, GraphFirst) + { + + VertexSecond existing_vertex2 + = get(correspondence_map_1_to_2, existing_vertex1); + + // Skip unassociated vertices + if (existing_vertex2 == graph_traits< GraphSecond >::null_vertex()) + { + continue; + } + + // NOTE: This will not work with parallel edges, since the + // first matching edge is always chosen. + EdgeFirst edge_to_new1, edge_from_new1; + bool edge_to_new_exists1 = false, edge_from_new_exists1 = false; + + EdgeSecond edge_to_new2, edge_from_new2; + bool edge_to_new_exists2 = false, edge_from_new_exists2 = false; + + // Search for edge from existing to new vertex (graph1) + BGL_FORALL_OUTEDGES_T(existing_vertex1, edge1, graph1, GraphFirst) + { + if (target(edge1, graph1) == new_vertex1) + { + edge_to_new1 = edge1; + edge_to_new_exists1 = true; + break; + } + } + + // Search for edge from existing to new vertex (graph2) + BGL_FORALL_OUTEDGES_T(existing_vertex2, edge2, graph2, GraphSecond) + { + if (target(edge2, graph2) == new_vertex2) + { + edge_to_new2 = edge2; + edge_to_new_exists2 = true; + break; + } + } + + // Make sure edges from existing to new vertices are equivalent + if ((edge_to_new_exists1 != edge_to_new_exists2) + || ((edge_to_new_exists1 && edge_to_new_exists2) + && !edges_equivalent(edge_to_new1, edge_to_new2))) + { + + return (false); + } + + bool is_undirected1 = is_undirected(graph1), + is_undirected2 = is_undirected(graph2); + + if (is_undirected1 && is_undirected2) + { + + // Edge in both graphs exists and both graphs are undirected + if (edge_to_new_exists1 && edge_to_new_exists2) + { + has_one_edge = true; + } + + continue; + } + else + { + + if (!is_undirected1) + { + + // Search for edge from new to existing vertex (graph1) + BGL_FORALL_OUTEDGES_T( + new_vertex1, edge1, graph1, GraphFirst) + { + if (target(edge1, graph1) == existing_vertex1) + { + edge_from_new1 = edge1; + edge_from_new_exists1 = true; + break; + } + } + } + + if (!is_undirected2) + { + + // Search for edge from new to existing vertex (graph2) + BGL_FORALL_OUTEDGES_T( + new_vertex2, edge2, graph2, GraphSecond) + { + if (target(edge2, graph2) == existing_vertex2) + { + edge_from_new2 = edge2; + edge_from_new_exists2 = true; + break; + } + } + } + + // Make sure edges from new to existing vertices are equivalent + if ((edge_from_new_exists1 != edge_from_new_exists2) + || ((edge_from_new_exists1 && edge_from_new_exists2) + && !edges_equivalent(edge_from_new1, edge_from_new2))) + { + + return (false); + } + + if ((edge_from_new_exists1 && edge_from_new_exists2) + || (edge_to_new_exists1 && edge_to_new_exists2)) + { + has_one_edge = true; + } + + } // else + + } // BGL_FORALL_VERTICES_T + + // Make sure new vertices are connected to the existing subgraph + if (only_connected_subgraphs && !has_one_edge) + { + return (false); + } + + return (true); +} + +// Recursive method that does a depth-first search in the space of +// potential subgraphs. At each level, every new vertex pair from +// both graphs is tested to see if it can extend the current +// subgraph. If so, the subgraph is output to subgraph_callback +// in the form of two correspondence maps (one for each graph). +// Returning false from subgraph_callback will terminate the +// search. Function returns true if the entire search space was +// explored. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename CorrespondenceMapFirstToSecond, + typename CorrespondenceMapSecondToFirst, typename VertexStackFirst, + typename EdgeEquivalencePredicate, typename VertexEquivalencePredicate, + typename SubGraphInternalCallback > +bool mcgregor_common_subgraphs_internal(const GraphFirst& graph1, + const GraphSecond& graph2, const VertexIndexMapFirst& vindex_map1, + const VertexIndexMapSecond& vindex_map2, + CorrespondenceMapFirstToSecond correspondence_map_1_to_2, + CorrespondenceMapSecondToFirst correspondence_map_2_to_1, + VertexStackFirst& vertex_stack1, + EdgeEquivalencePredicate edges_equivalent, + VertexEquivalencePredicate vertices_equivalent, + bool only_connected_subgraphs, + SubGraphInternalCallback subgraph_callback) +{ + typedef + typename graph_traits< GraphFirst >::vertex_descriptor VertexFirst; + typedef typename graph_traits< GraphSecond >::vertex_descriptor + VertexSecond; + typedef typename graph_traits< GraphFirst >::vertices_size_type + VertexSizeFirst; + + // Get iterators for vertices from both graphs + typename graph_traits< GraphFirst >::vertex_iterator vertex1_iter, + vertex1_end; + + typename graph_traits< GraphSecond >::vertex_iterator vertex2_begin, + vertex2_end, vertex2_iter; + + boost::tie(vertex1_iter, vertex1_end) = vertices(graph1); + boost::tie(vertex2_begin, vertex2_end) = vertices(graph2); + vertex2_iter = vertex2_begin; + + // Iterate until all vertices have been visited + BGL_FORALL_VERTICES_T(new_vertex1, graph1, GraphFirst) + { + + VertexSecond existing_vertex2 + = get(correspondence_map_1_to_2, new_vertex1); + + // Skip already matched vertices in first graph + if (existing_vertex2 != graph_traits< GraphSecond >::null_vertex()) + { + continue; + } + + BGL_FORALL_VERTICES_T(new_vertex2, graph2, GraphSecond) + { + + VertexFirst existing_vertex1 + = get(correspondence_map_2_to_1, new_vertex2); + + // Skip already matched vertices in second graph + if (existing_vertex1 + != graph_traits< GraphFirst >::null_vertex()) + { + continue; + } + + // Check if current sub-graph can be extended with the matched + // vertex pair + if (can_extend_graph(graph1, graph2, correspondence_map_1_to_2, + correspondence_map_2_to_1, + (VertexSizeFirst)vertex_stack1.size(), new_vertex1, + new_vertex2, edges_equivalent, vertices_equivalent, + only_connected_subgraphs)) + { + + // Keep track of old graph size for restoring later + VertexSizeFirst old_graph_size + = (VertexSizeFirst)vertex_stack1.size(), + new_graph_size = old_graph_size + 1; + + // Extend subgraph + put(correspondence_map_1_to_2, new_vertex1, new_vertex2); + put(correspondence_map_2_to_1, new_vertex2, new_vertex1); + vertex_stack1.push(new_vertex1); + + // Returning false from the callback will cancel iteration + if (!subgraph_callback(correspondence_map_1_to_2, + correspondence_map_2_to_1, new_graph_size)) + { + return (false); + } + + // Depth-first search into the state space of possible + // sub-graphs + bool continue_iteration + = mcgregor_common_subgraphs_internal(graph1, graph2, + vindex_map1, vindex_map2, correspondence_map_1_to_2, + correspondence_map_2_to_1, vertex_stack1, + edges_equivalent, vertices_equivalent, + only_connected_subgraphs, subgraph_callback); + + if (!continue_iteration) + { + return (false); + } + + // Restore previous state + if (vertex_stack1.size() > old_graph_size) + { + + VertexFirst stack_vertex1 = vertex_stack1.top(); + VertexSecond stack_vertex2 + = get(correspondence_map_1_to_2, stack_vertex1); + + // Contract subgraph + put(correspondence_map_1_to_2, stack_vertex1, + graph_traits< GraphSecond >::null_vertex()); + + put(correspondence_map_2_to_1, stack_vertex2, + graph_traits< GraphFirst >::null_vertex()); + + vertex_stack1.pop(); + } + + } // if can_extend_graph + + } // BGL_FORALL_VERTICES_T (graph2) + + } // BGL_FORALL_VERTICES_T (graph1) + + return (true); +} + +// Internal method that initializes blank correspondence maps and +// a vertex stack for use in mcgregor_common_subgraphs_internal. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename EdgeEquivalencePredicate, typename VertexEquivalencePredicate, + typename SubGraphInternalCallback > +inline void mcgregor_common_subgraphs_internal_init( + const GraphFirst& graph1, const GraphSecond& graph2, + const VertexIndexMapFirst vindex_map1, + const VertexIndexMapSecond vindex_map2, + EdgeEquivalencePredicate edges_equivalent, + VertexEquivalencePredicate vertices_equivalent, + bool only_connected_subgraphs, + SubGraphInternalCallback subgraph_callback) +{ + typedef mcgregor_common_subgraph_traits< GraphFirst, GraphSecond, + VertexIndexMapFirst, VertexIndexMapSecond > + SubGraphTraits; + + typename SubGraphTraits::correspondence_map_first_to_second_type + correspondence_map_1_to_2(num_vertices(graph1), vindex_map1); + + BGL_FORALL_VERTICES_T(vertex1, graph1, GraphFirst) + { + put(correspondence_map_1_to_2, vertex1, + graph_traits< GraphSecond >::null_vertex()); + } + + typename SubGraphTraits::correspondence_map_second_to_first_type + correspondence_map_2_to_1(num_vertices(graph2), vindex_map2); + + BGL_FORALL_VERTICES_T(vertex2, graph2, GraphSecond) + { + put(correspondence_map_2_to_1, vertex2, + graph_traits< GraphFirst >::null_vertex()); + } + + typedef + typename graph_traits< GraphFirst >::vertex_descriptor VertexFirst; + + std::stack< VertexFirst > vertex_stack1; + + mcgregor_common_subgraphs_internal(graph1, graph2, vindex_map1, + vindex_map2, correspondence_map_1_to_2, correspondence_map_2_to_1, + vertex_stack1, edges_equivalent, vertices_equivalent, + only_connected_subgraphs, subgraph_callback); +} + +} // namespace detail + +// ========================================================================== + +// Enumerates all common subgraphs present in graph1 and graph2. +// Continues until the search space has been fully explored or false +// is returned from user_callback. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename EdgeEquivalencePredicate, typename VertexEquivalencePredicate, + typename SubGraphCallback > +void mcgregor_common_subgraphs(const GraphFirst& graph1, + const GraphSecond& graph2, const VertexIndexMapFirst vindex_map1, + const VertexIndexMapSecond vindex_map2, + EdgeEquivalencePredicate edges_equivalent, + VertexEquivalencePredicate vertices_equivalent, + bool only_connected_subgraphs, SubGraphCallback user_callback) +{ + + detail::mcgregor_common_subgraphs_internal_init(graph1, graph2, vindex_map1, + vindex_map2, edges_equivalent, vertices_equivalent, + only_connected_subgraphs, user_callback); +} + +// Variant of mcgregor_common_subgraphs with all default parameters +template < typename GraphFirst, typename GraphSecond, + typename SubGraphCallback > +void mcgregor_common_subgraphs(const GraphFirst& graph1, + const GraphSecond& graph2, bool only_connected_subgraphs, + SubGraphCallback user_callback) +{ + + detail::mcgregor_common_subgraphs_internal_init(graph1, graph2, + get(vertex_index, graph1), get(vertex_index, graph2), + always_equivalent(), always_equivalent(), only_connected_subgraphs, + user_callback); +} + +// Named parameter variant of mcgregor_common_subgraphs +template < typename GraphFirst, typename GraphSecond, typename SubGraphCallback, + typename Param, typename Tag, typename Rest > +void mcgregor_common_subgraphs(const GraphFirst& graph1, + const GraphSecond& graph2, bool only_connected_subgraphs, + SubGraphCallback user_callback, + const bgl_named_params< Param, Tag, Rest >& params) +{ + + detail::mcgregor_common_subgraphs_internal_init(graph1, graph2, + choose_const_pmap( + get_param(params, vertex_index1), graph1, vertex_index), + choose_const_pmap( + get_param(params, vertex_index2), graph2, vertex_index), + choose_param( + get_param(params, edges_equivalent_t()), always_equivalent()), + choose_param( + get_param(params, vertices_equivalent_t()), always_equivalent()), + only_connected_subgraphs, user_callback); +} + +// ========================================================================== + +namespace detail +{ + +// Binary function object that intercepts subgraphs from +// mcgregor_common_subgraphs_internal and maintains a cache of +// unique subgraphs. The user callback is invoked for each unique +// subgraph. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename SubGraphCallback > +struct unique_subgraph_interceptor +{ + + typedef typename graph_traits< GraphFirst >::vertices_size_type + VertexSizeFirst; + + typedef mcgregor_common_subgraph_traits< GraphFirst, GraphSecond, + VertexIndexMapFirst, VertexIndexMapSecond > + SubGraphTraits; + + typedef typename SubGraphTraits::correspondence_map_first_to_second_type + CachedCorrespondenceMapFirstToSecond; + + typedef typename SubGraphTraits::correspondence_map_second_to_first_type + CachedCorrespondenceMapSecondToFirst; + + typedef std::pair< VertexSizeFirst, + std::pair< CachedCorrespondenceMapFirstToSecond, + CachedCorrespondenceMapSecondToFirst > > + SubGraph; + + typedef std::vector< SubGraph > SubGraphList; + + unique_subgraph_interceptor(const GraphFirst& graph1, + const GraphSecond& graph2, const VertexIndexMapFirst vindex_map1, + const VertexIndexMapSecond vindex_map2, + SubGraphCallback user_callback) + : m_graph1(graph1) + , m_graph2(graph2) + , m_vindex_map1(vindex_map1) + , m_vindex_map2(vindex_map2) + , m_subgraphs(make_shared< SubGraphList >()) + , m_user_callback(user_callback) + { + } + + template < typename CorrespondenceMapFirstToSecond, + typename CorrespondenceMapSecondToFirst > + bool operator()( + CorrespondenceMapFirstToSecond correspondence_map_1_to_2, + CorrespondenceMapSecondToFirst correspondence_map_2_to_1, + VertexSizeFirst subgraph_size) + { + + for (typename SubGraphList::const_iterator subgraph_iter + = m_subgraphs->begin(); + subgraph_iter != m_subgraphs->end(); ++subgraph_iter) + { + + SubGraph subgraph_cached = *subgraph_iter; + + // Compare subgraph sizes + if (subgraph_size != subgraph_cached.first) + { + continue; + } + + if (!are_property_maps_different(correspondence_map_1_to_2, + subgraph_cached.second.first, m_graph1)) + { + + // New subgraph is a duplicate + return (true); + } + } + + // Subgraph is unique, so make a cached copy + CachedCorrespondenceMapFirstToSecond new_subgraph_1_to_2 + = CachedCorrespondenceMapFirstToSecond( + num_vertices(m_graph1), m_vindex_map1); + + CachedCorrespondenceMapSecondToFirst new_subgraph_2_to_1 + = CorrespondenceMapSecondToFirst( + num_vertices(m_graph2), m_vindex_map2); + + BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst) + { + put(new_subgraph_1_to_2, vertex1, + get(correspondence_map_1_to_2, vertex1)); + } + + BGL_FORALL_VERTICES_T(vertex2, m_graph2, GraphFirst) + { + put(new_subgraph_2_to_1, vertex2, + get(correspondence_map_2_to_1, vertex2)); + } + + m_subgraphs->push_back(std::make_pair(subgraph_size, + std::make_pair(new_subgraph_1_to_2, new_subgraph_2_to_1))); + + return (m_user_callback(correspondence_map_1_to_2, + correspondence_map_2_to_1, subgraph_size)); + } + +private: + const GraphFirst& m_graph1; + const GraphFirst& m_graph2; + const VertexIndexMapFirst m_vindex_map1; + const VertexIndexMapSecond m_vindex_map2; + shared_ptr< SubGraphList > m_subgraphs; + SubGraphCallback m_user_callback; +}; + +} // namespace detail + +// Enumerates all unique common subgraphs between graph1 and graph2. +// The user callback is invoked for each unique subgraph as they are +// discovered. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename EdgeEquivalencePredicate, typename VertexEquivalencePredicate, + typename SubGraphCallback > +void mcgregor_common_subgraphs_unique(const GraphFirst& graph1, + const GraphSecond& graph2, const VertexIndexMapFirst vindex_map1, + const VertexIndexMapSecond vindex_map2, + EdgeEquivalencePredicate edges_equivalent, + VertexEquivalencePredicate vertices_equivalent, + bool only_connected_subgraphs, SubGraphCallback user_callback) +{ + detail::unique_subgraph_interceptor< GraphFirst, GraphSecond, + VertexIndexMapFirst, VertexIndexMapSecond, SubGraphCallback > + unique_callback( + graph1, graph2, vindex_map1, vindex_map2, user_callback); + + detail::mcgregor_common_subgraphs_internal_init(graph1, graph2, vindex_map1, + vindex_map2, edges_equivalent, vertices_equivalent, + only_connected_subgraphs, unique_callback); +} + +// Variant of mcgregor_common_subgraphs_unique with all default +// parameters. +template < typename GraphFirst, typename GraphSecond, + typename SubGraphCallback > +void mcgregor_common_subgraphs_unique(const GraphFirst& graph1, + const GraphSecond& graph2, bool only_connected_subgraphs, + SubGraphCallback user_callback) +{ + mcgregor_common_subgraphs_unique(graph1, graph2, get(vertex_index, graph1), + get(vertex_index, graph2), always_equivalent(), always_equivalent(), + only_connected_subgraphs, user_callback); +} + +// Named parameter variant of mcgregor_common_subgraphs_unique +template < typename GraphFirst, typename GraphSecond, typename SubGraphCallback, + typename Param, typename Tag, typename Rest > +void mcgregor_common_subgraphs_unique(const GraphFirst& graph1, + const GraphSecond& graph2, bool only_connected_subgraphs, + SubGraphCallback user_callback, + const bgl_named_params< Param, Tag, Rest >& params) +{ + mcgregor_common_subgraphs_unique(graph1, graph2, + choose_const_pmap( + get_param(params, vertex_index1), graph1, vertex_index), + choose_const_pmap( + get_param(params, vertex_index2), graph2, vertex_index), + choose_param( + get_param(params, edges_equivalent_t()), always_equivalent()), + choose_param( + get_param(params, vertices_equivalent_t()), always_equivalent()), + only_connected_subgraphs, user_callback); +} + +// ========================================================================== + +namespace detail +{ + +// Binary function object that intercepts subgraphs from +// mcgregor_common_subgraphs_internal and maintains a cache of the +// largest subgraphs. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename SubGraphCallback > +struct maximum_subgraph_interceptor +{ + + typedef typename graph_traits< GraphFirst >::vertices_size_type + VertexSizeFirst; + + typedef mcgregor_common_subgraph_traits< GraphFirst, GraphSecond, + VertexIndexMapFirst, VertexIndexMapSecond > + SubGraphTraits; + + typedef typename SubGraphTraits::correspondence_map_first_to_second_type + CachedCorrespondenceMapFirstToSecond; + + typedef typename SubGraphTraits::correspondence_map_second_to_first_type + CachedCorrespondenceMapSecondToFirst; + + typedef std::pair< VertexSizeFirst, + std::pair< CachedCorrespondenceMapFirstToSecond, + CachedCorrespondenceMapSecondToFirst > > + SubGraph; + + typedef std::vector< SubGraph > SubGraphList; + + maximum_subgraph_interceptor(const GraphFirst& graph1, + const GraphSecond& graph2, const VertexIndexMapFirst vindex_map1, + const VertexIndexMapSecond vindex_map2, + SubGraphCallback user_callback) + : m_graph1(graph1) + , m_graph2(graph2) + , m_vindex_map1(vindex_map1) + , m_vindex_map2(vindex_map2) + , m_subgraphs(make_shared< SubGraphList >()) + , m_largest_size_so_far(make_shared< VertexSizeFirst >(0)) + , m_user_callback(user_callback) + { + } + + template < typename CorrespondenceMapFirstToSecond, + typename CorrespondenceMapSecondToFirst > + bool operator()( + CorrespondenceMapFirstToSecond correspondence_map_1_to_2, + CorrespondenceMapSecondToFirst correspondence_map_2_to_1, + VertexSizeFirst subgraph_size) + { + + if (subgraph_size > *m_largest_size_so_far) + { + m_subgraphs->clear(); + *m_largest_size_so_far = subgraph_size; + } + + if (subgraph_size == *m_largest_size_so_far) + { + + // Make a cached copy + CachedCorrespondenceMapFirstToSecond new_subgraph_1_to_2 + = CachedCorrespondenceMapFirstToSecond( + num_vertices(m_graph1), m_vindex_map1); + + CachedCorrespondenceMapSecondToFirst new_subgraph_2_to_1 + = CachedCorrespondenceMapSecondToFirst( + num_vertices(m_graph2), m_vindex_map2); + + BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst) + { + put(new_subgraph_1_to_2, vertex1, + get(correspondence_map_1_to_2, vertex1)); + } + + BGL_FORALL_VERTICES_T(vertex2, m_graph2, GraphSecond) + { + put(new_subgraph_2_to_1, vertex2, + get(correspondence_map_2_to_1, vertex2)); + } + + m_subgraphs->push_back(std::make_pair(subgraph_size, + std::make_pair(new_subgraph_1_to_2, new_subgraph_2_to_1))); + } + + return (true); + } + + void output_subgraphs() + { + for (typename SubGraphList::const_iterator subgraph_iter + = m_subgraphs->begin(); + subgraph_iter != m_subgraphs->end(); ++subgraph_iter) + { + + SubGraph subgraph_cached = *subgraph_iter; + m_user_callback(subgraph_cached.second.first, + subgraph_cached.second.second, subgraph_cached.first); + } + } + +private: + const GraphFirst& m_graph1; + const GraphSecond& m_graph2; + const VertexIndexMapFirst m_vindex_map1; + const VertexIndexMapSecond m_vindex_map2; + shared_ptr< SubGraphList > m_subgraphs; + shared_ptr< VertexSizeFirst > m_largest_size_so_far; + SubGraphCallback m_user_callback; +}; + +} // namespace detail + +// Enumerates the largest common subgraphs found between graph1 +// and graph2. Note that the ENTIRE search space is explored before +// user_callback is actually invoked. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename EdgeEquivalencePredicate, typename VertexEquivalencePredicate, + typename SubGraphCallback > +void mcgregor_common_subgraphs_maximum(const GraphFirst& graph1, + const GraphSecond& graph2, const VertexIndexMapFirst vindex_map1, + const VertexIndexMapSecond vindex_map2, + EdgeEquivalencePredicate edges_equivalent, + VertexEquivalencePredicate vertices_equivalent, + bool only_connected_subgraphs, SubGraphCallback user_callback) +{ + detail::maximum_subgraph_interceptor< GraphFirst, GraphSecond, + VertexIndexMapFirst, VertexIndexMapSecond, SubGraphCallback > + max_interceptor( + graph1, graph2, vindex_map1, vindex_map2, user_callback); + + detail::mcgregor_common_subgraphs_internal_init(graph1, graph2, vindex_map1, + vindex_map2, edges_equivalent, vertices_equivalent, + only_connected_subgraphs, max_interceptor); + + // Only output the largest subgraphs + max_interceptor.output_subgraphs(); +} + +// Variant of mcgregor_common_subgraphs_maximum with all default +// parameters. +template < typename GraphFirst, typename GraphSecond, + typename SubGraphCallback > +void mcgregor_common_subgraphs_maximum(const GraphFirst& graph1, + const GraphSecond& graph2, bool only_connected_subgraphs, + SubGraphCallback user_callback) +{ + mcgregor_common_subgraphs_maximum(graph1, graph2, get(vertex_index, graph1), + get(vertex_index, graph2), always_equivalent(), always_equivalent(), + only_connected_subgraphs, user_callback); +} + +// Named parameter variant of mcgregor_common_subgraphs_maximum +template < typename GraphFirst, typename GraphSecond, typename SubGraphCallback, + typename Param, typename Tag, typename Rest > +void mcgregor_common_subgraphs_maximum(const GraphFirst& graph1, + const GraphSecond& graph2, bool only_connected_subgraphs, + SubGraphCallback user_callback, + const bgl_named_params< Param, Tag, Rest >& params) +{ + mcgregor_common_subgraphs_maximum(graph1, graph2, + choose_const_pmap( + get_param(params, vertex_index1), graph1, vertex_index), + choose_const_pmap( + get_param(params, vertex_index2), graph2, vertex_index), + choose_param( + get_param(params, edges_equivalent_t()), always_equivalent()), + choose_param( + get_param(params, vertices_equivalent_t()), always_equivalent()), + only_connected_subgraphs, user_callback); +} + +// ========================================================================== + +namespace detail +{ + +// Binary function object that intercepts subgraphs from +// mcgregor_common_subgraphs_internal and maintains a cache of the +// largest, unique subgraphs. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename SubGraphCallback > +struct unique_maximum_subgraph_interceptor +{ + + typedef typename graph_traits< GraphFirst >::vertices_size_type + VertexSizeFirst; + + typedef mcgregor_common_subgraph_traits< GraphFirst, GraphSecond, + VertexIndexMapFirst, VertexIndexMapSecond > + SubGraphTraits; + + typedef typename SubGraphTraits::correspondence_map_first_to_second_type + CachedCorrespondenceMapFirstToSecond; + + typedef typename SubGraphTraits::correspondence_map_second_to_first_type + CachedCorrespondenceMapSecondToFirst; + + typedef std::pair< VertexSizeFirst, + std::pair< CachedCorrespondenceMapFirstToSecond, + CachedCorrespondenceMapSecondToFirst > > + SubGraph; + + typedef std::vector< SubGraph > SubGraphList; + + unique_maximum_subgraph_interceptor(const GraphFirst& graph1, + const GraphSecond& graph2, const VertexIndexMapFirst vindex_map1, + const VertexIndexMapSecond vindex_map2, + SubGraphCallback user_callback) + : m_graph1(graph1) + , m_graph2(graph2) + , m_vindex_map1(vindex_map1) + , m_vindex_map2(vindex_map2) + , m_subgraphs(make_shared< SubGraphList >()) + , m_largest_size_so_far(make_shared< VertexSizeFirst >(0)) + , m_user_callback(user_callback) + { + } + + template < typename CorrespondenceMapFirstToSecond, + typename CorrespondenceMapSecondToFirst > + bool operator()( + CorrespondenceMapFirstToSecond correspondence_map_1_to_2, + CorrespondenceMapSecondToFirst correspondence_map_2_to_1, + VertexSizeFirst subgraph_size) + { + + if (subgraph_size > *m_largest_size_so_far) + { + m_subgraphs->clear(); + *m_largest_size_so_far = subgraph_size; + } + + if (subgraph_size == *m_largest_size_so_far) + { + + // Check if subgraph is unique + for (typename SubGraphList::const_iterator subgraph_iter + = m_subgraphs->begin(); + subgraph_iter != m_subgraphs->end(); ++subgraph_iter) + { + + SubGraph subgraph_cached = *subgraph_iter; + + if (!are_property_maps_different(correspondence_map_1_to_2, + subgraph_cached.second.first, m_graph1)) + { + + // New subgraph is a duplicate + return (true); + } + } + + // Subgraph is unique, so make a cached copy + CachedCorrespondenceMapFirstToSecond new_subgraph_1_to_2 + = CachedCorrespondenceMapFirstToSecond( + num_vertices(m_graph1), m_vindex_map1); + + CachedCorrespondenceMapSecondToFirst new_subgraph_2_to_1 + = CachedCorrespondenceMapSecondToFirst( + num_vertices(m_graph2), m_vindex_map2); + + BGL_FORALL_VERTICES_T(vertex1, m_graph1, GraphFirst) + { + put(new_subgraph_1_to_2, vertex1, + get(correspondence_map_1_to_2, vertex1)); + } + + BGL_FORALL_VERTICES_T(vertex2, m_graph2, GraphFirst) + { + put(new_subgraph_2_to_1, vertex2, + get(correspondence_map_2_to_1, vertex2)); + } + + m_subgraphs->push_back(std::make_pair(subgraph_size, + std::make_pair(new_subgraph_1_to_2, new_subgraph_2_to_1))); + } + + return (true); + } + + void output_subgraphs() + { + for (typename SubGraphList::const_iterator subgraph_iter + = m_subgraphs->begin(); + subgraph_iter != m_subgraphs->end(); ++subgraph_iter) + { + + SubGraph subgraph_cached = *subgraph_iter; + m_user_callback(subgraph_cached.second.first, + subgraph_cached.second.second, subgraph_cached.first); + } + } + +private: + const GraphFirst& m_graph1; + const GraphFirst& m_graph2; + const VertexIndexMapFirst m_vindex_map1; + const VertexIndexMapSecond m_vindex_map2; + shared_ptr< SubGraphList > m_subgraphs; + shared_ptr< VertexSizeFirst > m_largest_size_so_far; + SubGraphCallback m_user_callback; +}; + +} // namespace detail + +// Enumerates the largest, unique common subgraphs found between +// graph1 and graph2. Note that the ENTIRE search space is explored +// before user_callback is actually invoked. +template < typename GraphFirst, typename GraphSecond, + typename VertexIndexMapFirst, typename VertexIndexMapSecond, + typename EdgeEquivalencePredicate, typename VertexEquivalencePredicate, + typename SubGraphCallback > +void mcgregor_common_subgraphs_maximum_unique(const GraphFirst& graph1, + const GraphSecond& graph2, const VertexIndexMapFirst vindex_map1, + const VertexIndexMapSecond vindex_map2, + EdgeEquivalencePredicate edges_equivalent, + VertexEquivalencePredicate vertices_equivalent, + bool only_connected_subgraphs, SubGraphCallback user_callback) +{ + detail::unique_maximum_subgraph_interceptor< GraphFirst, GraphSecond, + VertexIndexMapFirst, VertexIndexMapSecond, SubGraphCallback > + unique_max_interceptor( + graph1, graph2, vindex_map1, vindex_map2, user_callback); + + detail::mcgregor_common_subgraphs_internal_init(graph1, graph2, vindex_map1, + vindex_map2, edges_equivalent, vertices_equivalent, + only_connected_subgraphs, unique_max_interceptor); + + // Only output the largest, unique subgraphs + unique_max_interceptor.output_subgraphs(); +} + +// Variant of mcgregor_common_subgraphs_maximum_unique with all default +// parameters +template < typename GraphFirst, typename GraphSecond, + typename SubGraphCallback > +void mcgregor_common_subgraphs_maximum_unique(const GraphFirst& graph1, + const GraphSecond& graph2, bool only_connected_subgraphs, + SubGraphCallback user_callback) +{ + + mcgregor_common_subgraphs_maximum_unique(graph1, graph2, + get(vertex_index, graph1), get(vertex_index, graph2), + always_equivalent(), always_equivalent(), only_connected_subgraphs, + user_callback); +} + +// Named parameter variant of +// mcgregor_common_subgraphs_maximum_unique +template < typename GraphFirst, typename GraphSecond, typename SubGraphCallback, + typename Param, typename Tag, typename Rest > +void mcgregor_common_subgraphs_maximum_unique(const GraphFirst& graph1, + const GraphSecond& graph2, bool only_connected_subgraphs, + SubGraphCallback user_callback, + const bgl_named_params< Param, Tag, Rest >& params) +{ + mcgregor_common_subgraphs_maximum_unique(graph1, graph2, + choose_const_pmap( + get_param(params, vertex_index1), graph1, vertex_index), + choose_const_pmap( + get_param(params, vertex_index2), graph2, vertex_index), + choose_param( + get_param(params, edges_equivalent_t()), always_equivalent()), + choose_param( + get_param(params, vertices_equivalent_t()), always_equivalent()), + only_connected_subgraphs, user_callback); +} + +// ========================================================================== + +// Fills a membership map (vertex -> bool) using the information +// present in correspondence_map_1_to_2. Every vertex in a +// membership map will have a true value only if it is not +// associated with a null vertex in the correspondence map. +template < typename GraphSecond, typename GraphFirst, + typename CorrespondenceMapFirstToSecond, typename MembershipMapFirst > +void fill_membership_map(const GraphFirst& graph1, + const CorrespondenceMapFirstToSecond correspondence_map_1_to_2, + MembershipMapFirst membership_map1) +{ + + BGL_FORALL_VERTICES_T(vertex1, graph1, GraphFirst) + { + put(membership_map1, vertex1, + get(correspondence_map_1_to_2, vertex1) + != graph_traits< GraphSecond >::null_vertex()); + } +} + +// Traits associated with a membership map filtered graph. Provided +// for convenience to access graph and vertex filter types. +template < typename Graph, typename MembershipMap > +struct membership_filtered_graph_traits +{ + typedef property_map_filter< MembershipMap > vertex_filter_type; + typedef filtered_graph< Graph, keep_all, vertex_filter_type > graph_type; +}; + +// Returns a filtered sub-graph of graph whose edge and vertex +// inclusion is dictated by membership_map. +template < typename Graph, typename MembershipMap > +typename membership_filtered_graph_traits< Graph, MembershipMap >::graph_type +make_membership_filtered_graph( + const Graph& graph, MembershipMap& membership_map) +{ + + typedef membership_filtered_graph_traits< Graph, MembershipMap > MFGTraits; + typedef typename MFGTraits::graph_type MembershipFilteredGraph; + + typename MFGTraits::vertex_filter_type v_filter(membership_map); + + return (MembershipFilteredGraph(graph, keep_all(), v_filter)); +} + +} // namespace boost + +#endif // BOOST_GRAPH_MCGREGOR_COMMON_SUBGRAPHS_HPP \ No newline at end of file diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/models/Inverted.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/models/Inverted.hpp index 8896105..d070b8e 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/models/Inverted.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/models/Inverted.hpp @@ -1,7 +1,7 @@ #ifndef JLA_BOOST_GRAPHMORPHISM_AS_MODELS_INVERTED_HPP #define JLA_BOOST_GRAPHMORPHISM_AS_MODELS_INVERTED_HPP -#include +#include #include diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/models/InvertibleAdaptor.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/models/InvertibleAdaptor.hpp index 295d8c5..891b775 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/models/InvertibleAdaptor.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/models/InvertibleAdaptor.hpp @@ -4,7 +4,7 @@ // This is just some convenience that wraps a map and its inverse, // and exposes them as an invertible vertex map. -#include +#include namespace jla_boost { namespace GraphMorphism { diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/models/InvertibleVector.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/models/InvertibleVector.hpp new file mode 100644 index 0000000..19be0a7 --- /dev/null +++ b/libs/jla_boost/include/jla_boost/graph/morphism/models/InvertibleVector.hpp @@ -0,0 +1,256 @@ +#ifndef JLA_BOOST_GRAPH_MORPHISM_MODELS_INVERTIBLE_VECTOR_HPP +#define JLA_BOOST_GRAPH_MORPHISM_MODELS_INVERTIBLE_VECTOR_HPP + +#include + +#include + +namespace jla_boost::GraphMorphism { + +// InvertibleVertexMap +// ============================================================================= + +template +struct InvertibleVectorVertexMap { + using GraphDom = GraphDomT; + using GraphCodom = GraphCodomT; + using Storable = std::true_type; + + template + static auto + reinterpret(InvertibleVectorVertexMap &&m, const GraphDom &gDom, const GraphCodom &gCodom, + const GraphDomU &gDomReinterpreted, const GraphCodomU &gCodomReinterpreted) { + // this of course assumes that the vertex_descriptors represent exactly the same on the reinterpreted graphs + return InvertibleVectorVertexMap(std::move(m.forward), std::move(m.backward), + gDomReinterpreted, gCodomReinterpreted); + } + + InvertibleVectorVertexMap(const GraphDom &gDom, const GraphCodom &gCodom) + : forward(num_vertices(gDom), boost::graph_traits::null_vertex()), + backward(num_vertices(gCodom), boost::graph_traits::null_vertex()) {} + + template + InvertibleVectorVertexMap(const VertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom) + : InvertibleVectorVertexMap(gDom, gCodom) { + BOOST_CONCEPT_ASSERT((InvertibleVertexMapConcept)); + for(auto vDom: asRange(vertices(gDom))) { + auto vId = get(boost::vertex_index_t(), gDom, vDom); + forward[vId] = get(m, gDom, gCodom, vDom); + } + for(auto vCodom: asRange(vertices(gCodom))) { + auto vId = get(boost::vertex_index_t(), gCodom, vCodom); + backward[vId] = get_inverse(m, gDom, gCodom, vCodom); + } + { // just asserts + for(auto vDom: asRange(vertices(gDom))) { + auto vCodom = get(*this, gDom, gCodom, vDom); + if(vCodom != boost::graph_traits::null_vertex()) + assert(get_inverse(*this, gDom, gCodom, vCodom) == vDom); + } + for(auto vCodom: asRange(vertices(gCodom))) { + auto vDom = get_inverse(*this, gDom, gCodom, vCodom); + if(vDom != boost::graph_traits::null_vertex()) + assert(get(*this, gDom, gCodom, vDom) == vCodom); + } + } + } +private: + template + friend + class InvertibleVectorVertexMap; + + InvertibleVectorVertexMap( + std::vector::vertex_descriptor> &&forward, + std::vector::vertex_descriptor> &&backward, + const GraphDom &gDom, const GraphCodom &gCodom) + : forward(std::move(forward)), backward(std::move(backward)) { + assert(this->forward.size() == num_vertices(gDom)); + assert(this->backward.size() == num_vertices(gCodom)); + } +private: + std::vector::vertex_descriptor> forward; + std::vector::vertex_descriptor> backward; +public: + friend typename boost::graph_traits::vertex_descriptor + get(const InvertibleVectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, + typename boost::graph_traits::vertex_descriptor v) { + assert(v != boost::graph_traits::null_vertex()); + auto vId = get(boost::vertex_index_t(), gDom, v); + assert(vId < m.forward.size()); + return m.forward[vId]; + } + + friend typename boost::graph_traits::vertex_descriptor + get_inverse(const InvertibleVectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, + typename boost::graph_traits::vertex_descriptor v) { + assert(v != boost::graph_traits::null_vertex()); + auto vId = get(boost::vertex_index_t(), gCodom, v); + assert(vId < m.backward.size()); + return m.backward[vId]; + } + + friend void put(InvertibleVectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, + typename boost::graph_traits::vertex_descriptor vDom, + typename boost::graph_traits::vertex_descriptor vCodom) { + assert(vDom != boost::graph_traits::null_vertex()); + auto vIdDom = get(boost::vertex_index_t(), gDom, vDom); + if(vCodom == boost::graph_traits::null_vertex()) { + // we need to reset the inverse before we forget it, + auto vCodomOld = m.forward[vIdDom]; + if(vCodomOld != boost::graph_traits::null_vertex()) { + m.forward[vIdDom] = boost::graph_traits::null_vertex(); + auto vIdCodomOld = get(boost::vertex_index_t(), gCodom, vCodomOld); + m.backward[vIdCodomOld] = boost::graph_traits::null_vertex(); + } + } else { + auto vIdCodom = get(boost::vertex_index_t(), gCodom, vCodom); + assert(vIdDom < m.forward.size()); + assert(vIdCodom < m.backward.size()); + m.forward[vIdDom] = vCodom; + m.backward[vIdCodom] = vDom; + } + } +public: + friend void syncSize(InvertibleVectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom) { + m.forward.resize(num_vertices(gDom), boost::graph_traits::null_vertex()); + m.backward.resize(num_vertices(gCodom), boost::graph_traits::null_vertex()); + } +}; + +struct ToInvertibleVectorVertexMap { + template + auto operator()(VertexMap &&m, + const typename VertexMapTraits::GraphDom &gDom, + const typename VertexMapTraits::GraphCodom &gCodom) const { + using Map = InvertibleVectorVertexMap::GraphDom, typename VertexMapTraits::GraphCodom>; + return Map(std::forward(m), gDom, gCodom); + } +}; + + +// InvertibleGraphMap +// ============================================================================= + +template +struct InvertibleVectorGraphMap : InvertibleVectorVertexMap { + using Base = InvertibleVectorVertexMap; + using GraphDom = GraphDomT; + using GraphCodom = GraphCodomT; + using Storable = std::true_type; + +// template +// static auto +// reinterpret(InvertibleVectorVertexMap &&m, const GraphDom &gDom, const GraphCodom &gCodom, +// const GraphDomU &gDomReinterpreted, const GraphCodomU &gCodomReinterpreted) { +// // this of course assumes that the vertex_descriptors represent exactly the same on the reinterpreted graphs +// return InvertibleVectorVertexMap(std::move(m.forward), std::move(m.backward), +// gDomReinterpreted, gCodomReinterpreted); +// } + + InvertibleVectorGraphMap(const GraphDom &gDom, const GraphCodom &gCodom) + : Base(gDom, gCodom), forward(num_edges(gDom)), backward(num_edges(gCodom)) {} + +// template +// InvertibleVectorVertexMap(const VertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom) +// : InvertibleVectorVertexMap(gDom, gCodom) { +// BOOST_CONCEPT_ASSERT((InvertibleVertexMapConcept)); +// for(auto vDom: asRange(vertices(gDom))) { +// auto vId = get(boost::vertex_index_t(), gDom, vDom); +// forward[vId] = get(m, gDom, gCodom, vDom); +// } +// for(auto vCodom: asRange(vertices(gCodom))) { +// auto vId = get(boost::vertex_index_t(), gCodom, vCodom); +// backward[vId] = get_inverse(m, gDom, gCodom, vCodom); +// } +// { // just asserts +// for(auto vDom: asRange(vertices(gDom))) { +// auto vCodom = get(*this, gDom, gCodom, vDom); +// if(vCodom != boost::graph_traits::null_vertex()) +// assert(get_inverse(*this, gDom, gCodom, vCodom) == vDom); +// } +// for(auto vCodom: asRange(vertices(gCodom))) { +// auto vDom = get_inverse(*this, gDom, gCodom, vCodom); +// if(vDom != boost::graph_traits::null_vertex()) +// assert(get(*this, gDom, gCodom, vDom) == vCodom); +// } +// } +// } +private: + template + friend + class InvertibleVectorGraphMap; + +// InvertibleVectorVertexMap( +// std::vector::vertex_descriptor> &&forward, +// std::vector::vertex_descriptor> &&backward, +// const GraphDom &gDom, const GraphCodom &gCodom) +// : forward(std::move(forward)), backward(std::move(backward)) { +// assert(this->forward.size() == num_vertices(gDom)); +// assert(this->backward.size() == num_vertices(gCodom)); +// } +private: + std::vector::edge_descriptor> forward; + std::vector::edge_descriptor> backward; +public: + friend typename boost::graph_traits::edge_descriptor + get(const InvertibleVectorGraphMap &m, const GraphDom &gDom, const GraphCodom &gCodom, + typename boost::graph_traits::edge_descriptor e) { + assert(e != typename boost::graph_traits::edge_descriptor()); + const auto eId = get(boost::edge_index_t(), gDom, e); + assert(eId < m.forward.size()); + return m.forward[eId]; + } + + friend typename boost::graph_traits::edge_descriptor + get_inverse(const InvertibleVectorGraphMap &m, const GraphDom &gDom, const GraphCodom &gCodom, + typename boost::graph_traits::edge_descriptor e) { + assert(e != typename boost::graph_traits::edge_descriptor()); + const auto eId = get(boost::edge_index_t(), gCodom, e); + assert(eId < m.backward.size()); + return m.backward[eId]; + } + + friend void put(InvertibleVectorGraphMap &m, const GraphDom &gDom, const GraphCodom &gCodom, + typename boost::graph_traits::edge_descriptor eDom, + typename boost::graph_traits::edge_descriptor eCodom) { + using DomEdge = typename boost::graph_traits::edge_descriptor; + using CodomEdge = typename boost::graph_traits::edge_descriptor; + assert(eDom != DomEdge()); + const auto eIdDom = get(boost::edge_index_t(), gDom, eDom); + if(eCodom == CodomEdge()) { + // we need to reset the inverse before we forget it, + const auto eCodomOld = m.forward[eIdDom]; + if(eCodomOld != CodomEdge()) { + m.forward[eIdDom] = CodomEdge(); + const auto eIdCodomOld = get(boost::edge_index_t(), gCodom, eCodomOld); + m.backward[eIdCodomOld] = DomEdge(); + } + } else { + const auto eIdCodom = get(boost::edge_index_t(), gCodom, eCodom); + assert(eIdDom < m.forward.size()); + assert(eIdCodom < m.backward.size()); + m.forward[eIdDom] = eCodom; + m.backward[eIdCodom] = eDom; + } + } +public: + friend void syncSize(InvertibleVectorGraphMap &m, const GraphDom &gDom, const GraphCodom &gCodom) { + syncSize(static_cast(m), gDom, gCodom); + m.forward.resize(num_edges(gDom)); + m.backward.resize(num_edges(gCodom)); + } +}; + +struct ToInvertibleVectorGraphMap { + template + auto operator()(VertexMap &&m, + const typename VertexMapTraits::GraphDom &gDom, + const typename VertexMapTraits::GraphCodom &gCodom) const { + using Map = InvertibleVectorGraphMap::GraphDom, typename VertexMapTraits::GraphCodom>; + return Map(std::forward(m), gDom, gCodom); + } +}; + +} // namespace jla_boost::GraphMorphism + +#endif // JLA_BOOST_GRAPH_MORPHISM_MODELS_INVERTIBLE_VECTOR_HPP \ No newline at end of file diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/models/PropertyMap.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/models/PropertyMap.hpp index 8352963..bcb9219 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/models/PropertyMap.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/models/PropertyMap.hpp @@ -1,10 +1,10 @@ #ifndef JLA_BOOST_GRAPH_MORPHISM_MODELS_PROPERTY_MAP_H -#define JLA_BOOST_GRAPH_MORPHISM_MODELS_PROPERTY_MAP_H +#define JLA_BOOST_GRAPH_MORPHISM_MODELS_PROPERTY_MAP_H // This is just some convenience that wraps a map and it's inverse, // and exposes them as an invertible vertex map. -#include +#include namespace jla_boost { namespace GraphMorphism { @@ -17,33 +17,34 @@ struct MatchAsVertexMap { template static MatchAsVertexMap - reinterpret(MatchAsVertexMap &&m, const GraphDom&, const GraphCodom&, const GraphDomU&, const GraphCodomU&) { + reinterpret(MatchAsVertexMap &&m, const GraphDom &, const GraphCodom &, + const GraphDomU &, const GraphCodomU &) { return MatchAsVertexMap(m.map, m.mapInv); } - MatchAsVertexMap(Map map, MapInv mapInv) : map(std::move(map)), mapInv(std::move(mapInv)) { } + MatchAsVertexMap(Map map, MapInv mapInv) : map(std::move(map)), mapInv(std::move(mapInv)) {} private: Map map; MapInv mapInv; public: - friend typename boost::graph_traits::vertex_descriptor get(const MatchAsVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, - typename boost::graph_traits::vertex_descriptor v) { + typename boost::graph_traits::vertex_descriptor v) { return get(m.map, v); } friend typename boost::graph_traits::vertex_descriptor - get_inverse(const MatchAsVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, - typename boost::graph_traits::vertex_descriptor v) { + get_inverse(const MatchAsVertexMap &m, const GraphDom &gDom, + const GraphCodom &gCodom, + typename boost::graph_traits::vertex_descriptor v) { return get(m.mapInv, v); } }; template struct MatchAsVertexMapWrapper { - - MatchAsVertexMapWrapper(const GraphDom &gDom, const GraphCodom &gCodom, Next next) : gDom(gDom), gCodom(gCodom), next(next) { } + MatchAsVertexMapWrapper(const GraphDom &gDom, const GraphCodom &gCodom, Next next) + : gDom(gDom), gCodom(gCodom), next(next) {} template bool operator()(MapDC mDomCodom, MapCD mCodomDom) const { @@ -56,11 +57,12 @@ struct MatchAsVertexMapWrapper { }; template -MatchAsVertexMapWrapper makeMatchAsVertexMapWrapper(const GraphDom &gDom, const GraphCodom &gCodom, Next next) { +MatchAsVertexMapWrapper +makeMatchAsVertexMapWrapper(const GraphDom &gDom, const GraphCodom &gCodom, Next next) { return MatchAsVertexMapWrapper(gDom, gCodom, next); } } // namespace GraphMorphism } // namespace jla_boost -#endif /* JLA_BOOST_GRAPH_MORPHISM_MODELS_PROPERTY_MAP_H */ \ No newline at end of file +#endif /* JLA_BOOST_GRAPH_MORPHISM_MODELS_PROPERTY_MAP_H */ \ No newline at end of file diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/models/PropertyVertexMap.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/models/PropertyVertexMap.hpp index babd605..8c5b436 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/models/PropertyVertexMap.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/models/PropertyVertexMap.hpp @@ -3,7 +3,7 @@ // A wrapper that attaches arbitrary tagged data to a vertex map implementation. -#include +#include #include diff --git a/libs/jla_boost/include/jla_boost/graph/morphism/models/Vector.hpp b/libs/jla_boost/include/jla_boost/graph/morphism/models/Vector.hpp index 66b5ef4..719756f 100644 --- a/libs/jla_boost/include/jla_boost/graph/morphism/models/Vector.hpp +++ b/libs/jla_boost/include/jla_boost/graph/morphism/models/Vector.hpp @@ -1,19 +1,14 @@ #ifndef JLA_BOOST_GRAPH_MORPHISM_MODELS_VECTOR_HPP #define JLA_BOOST_GRAPH_MORPHISM_MODELS_VECTOR_HPP -#include +#include #include -// - VectorVertexMap -// - ToVectorVertexMap -// - InvertibleVectorVertexMap -// - ToInvertibleVectorVertexMap - namespace jla_boost::GraphMorphism { -// VectorVertexMap -//------------------------------------------------------------------------------ +// VertexMap +// ============================================================================= template struct VectorVertexMap { @@ -23,19 +18,19 @@ struct VectorVertexMap { template static auto reinterpret(VectorVertexMap &&m, const GraphDom &gDom, const GraphCodom &gCodom, - const GraphDomU &gDomReinterpreted, const GraphCodomU &gCodomReinterpreted) { + const GraphDomU &gDomReinterpreted, const GraphCodomU &gCodomReinterpreted) { // this of course assumes that the vertex_descriptors represent exactly the same on the reinterpreted graphs return VectorVertexMap(std::move(m.data), gDomReinterpreted, gCodomReinterpreted); } public: VectorVertexMap(const GraphDom &gDom, const GraphCodom &gCodom) - : data(num_vertices(gDom), boost::graph_traits::null_vertex()) { } + : data(num_vertices(gDom), boost::graph_traits::null_vertex()) {} template VectorVertexMap(VertexMap &&m, const GraphDom &gDom, const GraphCodom &gCodom) - : VectorVertexMap(gDom, gCodom) { + : VectorVertexMap(gDom, gCodom) { BOOST_CONCEPT_ASSERT((VertexMapConcept)); - for(auto v : asRange(vertices(gDom))) { + for(auto v: asRange(vertices(gDom))) { auto vId = get(boost::vertex_index_t(), gDom, v); data[vId] = get(m, gDom, gCodom, v); } @@ -46,12 +41,15 @@ struct VectorVertexMap { } private: template - friend class VectorVertexMap; + friend + class VectorVertexMap; template - friend class VertexMapTraits; + friend + class VertexMapTraits; - VectorVertexMap(std::vector::vertex_descriptor> &&data, const GraphDom &gDom, const GraphCodom &gCodom) - : data(std::move(data)) { + VectorVertexMap(std::vector::vertex_descriptor> &&data, + const GraphDom &gDom, const GraphCodom &gCodom) + : data(std::move(data)) { assert(this->data.size() == num_vertices(gDom)); } private: @@ -59,150 +57,113 @@ struct VectorVertexMap { public: friend typename boost::graph_traits::vertex_descriptor get(const VectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, - typename boost::graph_traits::vertex_descriptor v) { + typename boost::graph_traits::vertex_descriptor v) { auto vId = get(boost::vertex_index_t(), gDom, v); assert(vId < m.data.size()); return m.data[vId]; } friend void put(VectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, - typename boost::graph_traits::vertex_descriptor vDom, - typename boost::graph_traits::vertex_descriptor vCodom) { + typename boost::graph_traits::vertex_descriptor vDom, + typename boost::graph_traits::vertex_descriptor vCodom) { auto vId = get(boost::vertex_index_t(), gDom, vDom); assert(vId < m.data.size()); m.data[vId] = vCodom; } -}; -// ToVectorVertexMap -//------------------------------------------------------------------------------ + friend void syncSize(VectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom) { + m.data.resize(num_vertices(gDom), boost::graph_traits::null_vertex()); + } +}; struct ToVectorVertexMap { template auto operator()(VertexMap &&m, - const typename VertexMapTraits::GraphDom &gDom, - const typename VertexMapTraits::GraphCodom &gCodom) const { - using Map = VectorVertexMap::GraphDom, typename VertexMapTraits::GraphCodom >; + const typename VertexMapTraits::GraphDom &gDom, + const typename VertexMapTraits::GraphCodom &gCodom) const { + using Map = VectorVertexMap::GraphDom, typename VertexMapTraits::GraphCodom>; return Map(std::forward(m), gDom, gCodom); } }; -// InvertibleVectorVertexMap -//------------------------------------------------------------------------------ +// GraphMap +// ============================================================================= template -struct InvertibleVectorVertexMap { +struct VectorGraphMap : VectorVertexMap { + using Base = VectorVertexMap; using GraphDom = GraphDomT; using GraphCodom = GraphCodomT; using Storable = std::true_type; - template - static auto reinterpret(InvertibleVectorVertexMap &&m, const GraphDom &gDom, const GraphCodom &gCodom, - const GraphDomU &gDomReinterpreted, const GraphCodomU &gCodomReinterpreted) { - // this of course assumes that the vertex_descriptors represent exactly the same on the reinterpreted graphs - return InvertibleVectorVertexMap(std::move(m.forward), std::move(m.backward), gDomReinterpreted, gCodomReinterpreted); - } - - InvertibleVectorVertexMap(const GraphDom &gDom, const GraphCodom &gCodom) - : forward(num_vertices(gDom), boost::graph_traits::null_vertex()), - backward(num_vertices(gCodom), boost::graph_traits::null_vertex()) { } +// template +// static auto reinterpret(VectorGraphMap &&m, const GraphDom &gDom, const GraphCodom &gCodom, +// const GraphDomU &gDomReinterpreted, const GraphCodomU &gCodomReinterpreted) { +// // this of course assumes that the descriptors represent exactly the same on the reinterpreted graphs +// return VectorGraphMap(std::move(m.data), gDomReinterpreted, gCodomReinterpreted); +// } +public: + VectorGraphMap(const GraphDom &gDom, const GraphCodom &gCodom) + : Base(gDom, gCodom), edgeData(num_edges(gDom)) {} + +// template +// VectorGraphMap(VertexMap &&m, const GraphDom &gDom, const GraphCodom &gCodom) +// : VectorGraphMap(gDom, gCodom) { +// BOOST_CONCEPT_ASSERT((VertexMapConcept)); +// for(const auto e: asRange(edges(gDom))) { +// const auto eId = get(boost::edge_index_t(), gDom, e); +// edgeData[eId] = get(m, gDom, gCodom, e); +// } +// } - template - InvertibleVectorVertexMap(const VertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom) - : InvertibleVectorVertexMap(gDom, gCodom) { - BOOST_CONCEPT_ASSERT((InvertibleVertexMapConcept)); - for(auto vDom : asRange(vertices(gDom))) { - auto vId = get(boost::vertex_index_t(), gDom, vDom); - forward[vId] = get(m, gDom, gCodom, vDom); - } - for(auto vCodom : asRange(vertices(gCodom))) { - auto vId = get(boost::vertex_index_t(), gCodom, vCodom); - backward[vId] = get_inverse(m, gDom, gCodom, vCodom); - } - { // just asserts - for(auto vDom : asRange(vertices(gDom))) { - auto vCodom = get(*this, gDom, gCodom, vDom); - if(vCodom != boost::graph_traits::null_vertex()) - assert(get_inverse(*this, gDom, gCodom, vCodom) == vDom); - } - for(auto vCodom : asRange(vertices(gCodom))) { - auto vDom = get_inverse(*this, gDom, gCodom, vCodom); - if(vDom != boost::graph_traits::null_vertex()) - assert(get(*this, gDom, gCodom, vDom) == vCodom); - } - } + std::size_t size() const { + return edgeData.size(); } private: template - friend class InvertibleVectorVertexMap; - - InvertibleVectorVertexMap( - std::vector::vertex_descriptor> &&forward, - std::vector::vertex_descriptor> &&backward, - const GraphDom &gDom, const GraphCodom &gCodom) - : forward(std::move(forward)), backward(std::move(backward)) { - assert(this->forward.size() == num_vertices(gDom)); - assert(this->backward.size() == num_vertices(gCodom)); - } + friend + class VectorGraphMap; + template + friend + class VertexMapTraits; + +// VectorGraphMap(std::vector::edge_descriptor> &&edgeData, +// const GraphDom &gDom, const GraphCodom &gCodom) +// : edgeData(std::move(edgeData)) { +// assert(this->data.size() == num_vertices(gDom)); +// } private: - std::vector::vertex_descriptor> forward; - std::vector::vertex_descriptor> backward; + std::vector::edge_descriptor> edgeData; public: - friend typename boost::graph_traits::vertex_descriptor - get(const InvertibleVectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, - typename boost::graph_traits::vertex_descriptor v) { - assert(v != boost::graph_traits::null_vertex()); - auto vId = get(boost::vertex_index_t(), gDom, v); - assert(vId < m.forward.size()); - return m.forward[vId]; + friend typename boost::graph_traits::edge_descriptor + get(const VectorGraphMap &m, const GraphDom &gDom, const GraphCodom &gCodom, + typename boost::graph_traits::edge_descriptor e) { + const auto eId = get(boost::edge_index_t(), gDom, e); + assert(eId < m.edgeData.size()); + return m.edgeData[eId]; } - friend typename boost::graph_traits::vertex_descriptor - get_inverse(const InvertibleVectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, - typename boost::graph_traits::vertex_descriptor v) { - assert(v != boost::graph_traits::null_vertex()); - auto vId = get(boost::vertex_index_t(), gCodom, v); - assert(vId < m.backward.size()); - return m.backward[vId]; + friend void put(VectorGraphMap &m, const GraphDom &gDom, const GraphCodom &gCodom, + typename boost::graph_traits::edge_descriptor eDom, + typename boost::graph_traits::edge_descriptor eCodom) { + const auto eId = get(boost::edge_index_t(), gDom, eDom); + assert(eId < m.edgeData.size()); + m.edgeData[eId] = eCodom; } - friend void put(InvertibleVectorVertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom, - typename boost::graph_traits::vertex_descriptor vDom, - typename boost::graph_traits::vertex_descriptor vCodom) { - assert(vDom != boost::graph_traits::null_vertex()); - auto vIdDom = get(boost::vertex_index_t(), gDom, vDom); - if(vCodom == boost::graph_traits::null_vertex()) { - // we need to reset the inverse before we forget it, - auto vCodomOld = m.forward[vIdDom]; - if(vCodomOld != boost::graph_traits::null_vertex()) { - m.forward[vIdDom] = boost::graph_traits::null_vertex(); - auto vIdCodomOld = get(boost::vertex_index_t(), gCodom, vCodomOld); - m.backward[vIdCodomOld] = boost::graph_traits::null_vertex(); - } - } else { - auto vIdCodom = get(boost::vertex_index_t(), gCodom, vCodom); - assert(vIdDom < m.forward.size()); - assert(vIdCodom < m.backward.size()); - m.forward[vIdDom] = vCodom; - m.backward[vIdCodom] = vDom; - } - } -public: - void resizeRight(const GraphDom &gDom, const GraphCodom &gCodom) { - backward.resize(num_vertices(gCodom), boost::graph_traits::null_vertex()); + friend void syncSize(VectorGraphMap &m, const GraphDom &gDom, const GraphCodom &gCodom) { + syncSize(static_cast(m), gDom, gCodom); + m.edgeData.resize(num_edges(gDom)); } }; -// ToInvertibleVectorVertexMap -//------------------------------------------------------------------------------ - -struct ToInvertibleVectorVertexMap { - template - auto operator()(VertexMap &&m, - const typename VertexMapTraits::GraphDom &gDom, - const typename VertexMapTraits::GraphCodom &gCodom) const { - using Map = InvertibleVectorVertexMap::GraphDom, typename VertexMapTraits::GraphCodom >; - return Map(std::forward(m), gDom, gCodom); +struct ToVectorGraphMap { + template + auto operator()(GraphMap &&m, + const typename VertexMapTraits::GraphDom &gDom, + const typename VertexMapTraits::GraphCodom &gCodom) const { + using Map = VectorGraphMap::GraphDom, typename VertexMapTraits::GraphCodom>; + return Map(std::forward(m), gDom, gCodom); } }; diff --git a/libs/jla_boost/src/graph/dpo/IO.cpp b/libs/jla_boost/src/graph/dpo/IO.cpp deleted file mode 100644 index 4490b22..0000000 --- a/libs/jla_boost/src/graph/dpo/IO.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -namespace jla_boost { -namespace GraphDPO { - -std::ostream &operator<<(std::ostream &s, Membership m) { - switch(m) { - case Membership::Left: return s << "Left"; - case Membership::Right: return s << "Right"; - case Membership::Context: return s << "Context"; - } - assert(false); - return s; -} - -} // namespace GraphDPO -} // namespace jla_boost \ No newline at end of file diff --git a/libs/libmod/CMakeLists.txt b/libs/libmod/CMakeLists.txt index 28743e7..29024cf 100644 --- a/libs/libmod/CMakeLists.txt +++ b/libs/libmod/CMakeLists.txt @@ -39,7 +39,7 @@ target_link_libraries(libmod # Boost::graph needs Boost::regex which needs the RPATH of libmod to have Boost in it. # But because all Boost libs are linked privately the path is not exposed. # So if someone links against libmod, but no Boost lib, then the linking will fail. -# Perhapse fixed in https://gitlab.kitware.com/cmake/cmake/-/merge_requests/3642 +# Perhaps fixed in https://gitlab.kitware.com/cmake/cmake/-/merge_requests/3642 # TODO: remove the next line with some higher CMAKE version is required? target_link_libraries(libmod PUBLIC Boost::iostreams) target_link_libraries(libmod PRIVATE diff --git a/libs/libmod/src/mod/Chem.cpp b/libs/libmod/src/mod/Chem.cpp index df6eaea..3356def 100644 --- a/libs/libmod/src/mod/Chem.cpp +++ b/libs/libmod/src/mod/Chem.cpp @@ -32,11 +32,7 @@ std::ostream &operator<<(std::ostream &s, const AtomData &data) { if(data.atomId == AtomIds::Invalid) throw LogicError("Can not print atom data with atom id AtomIds::Invalid."); if(data.isotope != Isotope()) s << data.isotope; s << lib::Chem::symbolFromAtomId(data.atomId); - if(data.charge != 0) { - if(data.charge > 1 || data.charge < -1) s << std::abs(data.charge); - if(data.charge < 0) s << '-'; - else s << '+'; - } + if(data.charge != 0) s << lib::Chem::chargeSuffix(data.charge); if(data.radical) s << '.'; return s; } @@ -46,7 +42,7 @@ std::ostream &operator<<(std::ostream &s, const AtomData &data) { //------------------------------------------------------------------------------ std::ostream &operator<<(std::ostream &s, BondType bt) { - if(bt == BondType::Invalid) throw LogicError("Can not print bond type BondType::Invalid."); + if(bt == BondType::Invalid) throw LogicError("Can not print BondType::Invalid."); return s << lib::Chem::bondToChar(bt); } diff --git a/libs/libmod/src/mod/Config.cpp b/libs/libmod/src/mod/Config.cpp index 8c937c2..b6ac02d 100644 --- a/libs/libmod/src/mod/Config.cpp +++ b/libs/libmod/src/mod/Config.cpp @@ -8,13 +8,11 @@ namespace mod { std::ostream &operator<<(std::ostream &s, const LabelType lt) { switch(lt) { case LabelType::String: - s << "string"; - break; + return s << "string"; case LabelType::Term: - s << "term"; - break; + return s << "term"; } - return s; + __builtin_unreachable(); } std::ostream &operator<<(std::ostream &s, const LabelRelation lt) { @@ -26,7 +24,7 @@ std::ostream &operator<<(std::ostream &s, const LabelRelation lt) { case LabelRelation::Unification: return s << "unification"; } - return s; + __builtin_unreachable(); } bool operator==(LabelSettings a, LabelSettings b) { @@ -59,7 +57,7 @@ std::ostream &operator<<(std::ostream &s, IsomorphismPolicy p) { case IsomorphismPolicy::TrustMe: return s << "trustMe"; } - return s; + __builtin_unreachable(); } std::ostream &operator<<(std::ostream &s, SmilesClassPolicy p) { @@ -71,7 +69,19 @@ std::ostream &operator<<(std::ostream &s, SmilesClassPolicy p) { case SmilesClassPolicy::MapUnique: return s << "mapUnique"; } - return s; + __builtin_unreachable(); +} + +std::ostream &operator<<(std::ostream &s, Action a) { + switch(a) { + case Action::Error: + return s << "error"; + case Action::Warn: + return s << "warn"; + case Action::Ignore: + return s << "ignore"; + } + __builtin_unreachable(); } Config &getConfig() { diff --git a/libs/libmod/src/mod/Config.hpp b/libs/libmod/src/mod/Config.hpp index 3b820c4..5a1a18a 100644 --- a/libs/libmod/src/mod/Config.hpp +++ b/libs/libmod/src/mod/Config.hpp @@ -75,7 +75,6 @@ MOD_DECL std::ostream &operator<<(std::ostream &s, LabelRelation lt); // rst: // rst: A class simply for grouping label settings. // rst: - struct LabelSettings { // rst: .. function:: LabelSettings(LabelType type, LabelRelation relation) // rst: @@ -154,6 +153,105 @@ enum class SmilesClassPolicy { }; MOD_DECL std::ostream &operator<<(std::ostream &s, SmilesClassPolicy p); +// rst: .. enum-struct:: Action +// rst: +// rst: Utility enum for deciding what to do in certain cases. +// rst: +enum class Action { + // rst: .. enumerator:: Error + // rst: + // rst: Abort the function and produce an error message, e.g., through and exception. + Error, + // rst: .. enumerator:: Warn + // rst: + // rst: Write a warning, but otherwise do as if it was `Ignore`. + Warn, + // rst: .. enumerator:: Ignore + // rst: + // rst: Ignore the case. The function taking the action as argument should describe what this means. + Ignore +}; +MOD_DECL std::ostream &operator<<(std::ostream &s, Action a); + +// rst: .. class:: MDLOptions +// rst: +// rst: An aggregation of options for the various loading functions for MDL formats. +// rst: Generally each option is defaulted to follow the specification of the formats, +// rst: unless it is harmless to deviate (e.g., relaxed white-space parsing). +// rst: +struct MOD_DECL MDLOptions { + // rst: .. member:: bool addHydrogens = true + // rst: + // rst: Use the MDL valence model to add hydrogens to atoms with default valence, or disable all hydrogen addition. + bool addHydrogens = true; + // rst: .. member:: bool allowAbstract = false + // rst: + // rst: Allow non-standard atom symbols. The standard symbols are the element symbols and those specifying wildcard atoms. + bool allowAbstract = false; + // rst: .. member:: bool applyV2000AtomAliases = true + // rst: + // rst: In MOL V2000 CTAB blocks, replace atom labels by their aliases. + // rst: After application, the atom is considered abstract without errors, and hydrogen addition is suppressed. + bool applyV2000AtomAliases = true; + // rst: .. member:: Action onPatternIsotope = Action::Error + // rst: Action onPatternCharge = Action::Error; + // rst: Action onPatternRadical = Action::Error; + // rst: + // rst: What to do when an atom with symbol ``*`` has an isotope, charge, or radical. + // rst: `Action::Ignore` means assuming the isotope, charge, or radical was not there. + Action onPatternIsotope = Action::Error; + Action onPatternCharge = Action::Error; + Action onPatternRadical = Action::Error; + // rst: .. member:: Action onImplicitValenceOnAbstract = Action::Error + // rst: + // rst: What to do when `addHydrogens && allowAbstract` and an abstract atom is encountered with implicit valence. + // rst: `Action::Ignore` means adding no hydrogens. + Action onImplicitValenceOnAbstract = Action::Error; + // rst: .. member:: Action onV2000UnhandledProperty = Action::Warn + // rst: + // rst: What to do when a property line in a V2000 MOL file is not recognized. + // rst: `Action::Ignore` means simply ignoring that particular line. + Action onV2000UnhandledProperty = Action::Warn; + // rst: .. member:: bool fullyIgnoreV2000UnhandledKnownProperty = false + // rst: + // rst: Warnings are usually stored as "loading warnings", even when they are ignored during parsing. + // rst: Setting this to `true` will act as if `onV2000UnhandledProperty = Action::Ignore` and + // rst: skip the storage, but only for a pre-defined known subset of properties. + bool fullyIgnoreV2000UnhandledKnownProperty = false; + // rst: .. member:: Action onV3000UnhandledAtomProperty = Action::Warn + // rst: + // rst: What to do when a property in atom line in a V3000 MOL file is not recognized. + // rst: `Action::Ignore` means simply ignoring that particular property. + Action onV3000UnhandledAtomProperty = Action::Warn; + // rst: .. member:: Action onV2000Charge4 = Action::Error + // rst: + // rst: What to do when an atom in a V2000 MOL file has the charge 4 (doublet radical). + // rst: `Action::Ignore` means assuming it was charge 0. + Action onV2000Charge4 = Action::Error; + // rst: .. member:: Action onV2000AbstractISO = Action::Error + // rst: + // rst: What to do when an abstract atom in a V2000 MOL file has a non-default ISO or mass difference value. + // rst: `Action::Ignore` means assuming it had no ISO or mass difference value. + Action onV2000AbstractISO = Action::Error; + // rst: .. member:: Action onRAD1 = Action::Error + // rst: Action onRAD3 = Action::Error + // rst: Action onRAD4 = Action::Error + // rst: Action onRAD5 = Action::Error + // rst: Action onRAD6 = Action::Error + // rst: + // rst: What to do when an atom has assigned the indicated radical state. + // rst: `Action::Ignore` means pretending the atom has no radical state assigned. + Action onRAD1 = Action::Error; + Action onRAD3 = Action::Error; + Action onRAD4 = Action::Error; + Action onRAD5 = Action::Error; + Action onRAD6 = Action::Error; + // rst: .. member:: Action onUnsupportedQueryBondType = Action::Error + // rst: + // rst: What to do when a bond type 5, 6, or 7 are encountered (constrained query bond types). + // rst: `Action::Ignore` means assigning a term variable, as if the type was 8. + Action onUnsupportedQueryBondType = Action::Error; +}; // rst: .. function:: Config &getConfig() // rst: @@ -258,16 +356,8 @@ struct Config { ((bool, checkIsoInPermutation, false)) \ ((unsigned long, numIsomorphismCalls, 0)) \ )) \ - ((IO, io, \ - ((std::string, dotCoordOptions, "")) \ - ((bool, useOpenBabelCoords, true)) \ - )) \ - ((OBabel, obabel, \ - ((bool, verbose, false)) \ - )) \ ((Rule, rule, \ ((bool, ignoreConstraintsDuringInversion, false)) \ - ((std::string, changeColour, "")) \ ((std::string, changeColourL, "NavyBlue")) \ ((std::string, changeColourK, "Purple")) \ ((std::string, changeColourR, "Green")) \ @@ -284,9 +374,6 @@ struct Config { )) \ ((Stereo, stereo, \ ((bool, silenceDeductionWarnings, false)) \ - )) \ - ((Term, unification, \ - ((bool, verboseMGU, false)) \ )) #define MOD_CONFIG_nsIter(rNS, dataNS, tNS) \ diff --git a/libs/libmod/src/mod/Derivation.hpp b/libs/libmod/src/mod/Derivation.hpp index a784baa..a0db081 100644 --- a/libs/libmod/src/mod/Derivation.hpp +++ b/libs/libmod/src/mod/Derivation.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_DERIVATION_H -#define MOD_DERIVATION_H +#ifndef MOD_DERIVATION_HPP +#define MOD_DERIVATION_HPP #include #include @@ -20,7 +20,6 @@ struct Derivations; // rst: :math:`G\Rightarrow^p H`, though the validity of the data is not checked. // rst: // rst-class-start: - struct MOD_DECL Derivation { // rst: .. type:: GraphList = std::vector> // rst: @@ -61,7 +60,6 @@ struct MOD_DECL Derivation { // rst: though the validity of the data is not checked. // rst: // rst-class-start: - struct MOD_DECL Derivations { // rst: .. type:: GraphList = std::vector> // rst: @@ -88,4 +86,4 @@ struct MOD_DECL Derivations { } // namespace mod -#endif /* MOD_DERIVATION_H */ +#endif // MOD_DERIVATION_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/Error.cpp b/libs/libmod/src/mod/Error.cpp index e48abfc..1b47c4b 100644 --- a/libs/libmod/src/mod/Error.cpp +++ b/libs/libmod/src/mod/Error.cpp @@ -83,7 +83,7 @@ const char *FatalError::what() const noexcept { std::stringstream ss; Exception::printStacktrace(0, ss); ss << getName() << ": " << text; - ss << "(Do _not_ try to recover from this exception!)\n"; + ss << "\n(Do _not_ try to recover from this exception!)\n"; whatString = ss.str(); return whatString.c_str(); } diff --git a/libs/libmod/src/mod/Function.cpp b/libs/libmod/src/mod/Function.cpp deleted file mode 100644 index 0beb668..0000000 --- a/libs/libmod/src/mod/Function.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "Function.hpp" - -namespace mod::detail_function { - -void dont_call_only_for_test() { - std::function f = []() { - }; - auto a = fromStdFunction(f); - auto b = fromStdFunction("test", f); -} - -} // namespace mod::detail_function \ No newline at end of file diff --git a/libs/libmod/src/mod/GraphConcepts.hpp b/libs/libmod/src/mod/GraphConcepts.hpp index e87ac74..aa195d2 100644 --- a/libs/libmod/src/mod/GraphConcepts.hpp +++ b/libs/libmod/src/mod/GraphConcepts.hpp @@ -37,7 +37,7 @@ auto getGraphFromHandle(GraphHandle g) { } template -auto &getGraphFromHandle(std::shared_ptr &g) { +auto &getGraphFromHandle(const std::shared_ptr &g) { return *g; } diff --git a/libs/libmod/src/mod/Misc.cpp b/libs/libmod/src/mod/Misc.cpp index 968fe6c..f3506cf 100644 --- a/libs/libmod/src/mod/Misc.cpp +++ b/libs/libmod/src/mod/Misc.cpp @@ -6,10 +6,10 @@ #include #include #include -#include -#include +#include #include #include +#include #include #include @@ -44,7 +44,7 @@ void showDump(const std::string &file) { } void printGeometryGraph() { - lib::IO::Stereo::Write::summary(lib::Stereo::getGeometryGraph()); + lib::Stereo::Write::summary(lib::Stereo::getGeometryGraph()); } } // namespace mod diff --git a/libs/libmod/src/mod/Post.cpp b/libs/libmod/src/mod/Post.cpp index 0f40563..fed456e 100644 --- a/libs/libmod/src/mod/Post.cpp +++ b/libs/libmod/src/mod/Post.cpp @@ -7,37 +7,71 @@ namespace mod::post { -FileHandle::FileHandle(std::string name) : name(name) { - stream.open(name.c_str()); - if(!stream) throw LogicError("Can not open file '" + name + "'."); +FileHandle::FileHandle(const std::string &name) : name(name) { + stream.open(name.data()); + if(!stream) throw LogicError("Can not open file '" + this->name + "'."); } -void command(const std::string &text) { - lib::IO::post() << text << std::endl; +std::string makeUniqueFilePrefix() { + return lib::IO::makeUniqueFilePrefix(); } -void reset() { - lib::IO::postReset(); +void command(const std::string &line) { + lib::IO::post() << line << '\n'; } -void flush() { +void flushCommands() { lib::IO::post() << std::flush; } -void disable() { +void disableCommands() { lib::IO::postDisable(); } -void summaryChapter(const std::string &chapterTitle) { - lib::IO::post() << "summaryChapter \"" << chapterTitle << "\"" << std::endl; +void enableCommands() { + lib::IO::postEnable(); } -void summarySection(const std::string §ionTitle) { - lib::IO::post() << "summarySection \"" << sectionTitle << "\"" << std::endl; +void reopenCommandFile() { + lib::IO::postReopenCommandFile(); } -std::string makeUniqueFilePrefix() { - return lib::IO::getUniqueFilePrefix(); +void summaryChapter(const std::string &heading) { + lib::IO::post() << "summaryChapter \"" << heading << "\"\n"; +} + +void summarySection(const std::string &heading) { + lib::IO::post() << "summarySection \"" << heading << "\"\n"; +} + +void summaryRaw(const std::string &latexCode) { + summaryRaw(latexCode, "raw.tex"); +} + +void summaryRaw(const std::string &latexCode, const std::string &file) { + FileHandle s(lib::IO::makeUniqueFilePrefix() + file); + s << latexCode; + lib::IO::post() << "summaryInput '" << std::string(s) << "'\n"; +} + +void summaryInput(const std::string &filename) { + lib::IO::post() << "summaryInput '" << filename << "'\n"; +} + +void disableInvokeMake() { + lib::IO::post() << "disableInvokeMake\n"; +} + +void enableInvokeMake() { + lib::IO::post() << "enableInvokeMake\n"; +} + +void disableCompileSummary() { + lib::IO::post() << "disableCompileSummary\n"; +} + +void enableCompileSummary() { + lib::IO::post() << "enableInvokeMake\n"; } } // namespace mod::post \ No newline at end of file diff --git a/libs/libmod/src/mod/Post.hpp b/libs/libmod/src/mod/Post.hpp index 59e6efe..612ba77 100644 --- a/libs/libmod/src/mod/Post.hpp +++ b/libs/libmod/src/mod/Post.hpp @@ -6,11 +6,16 @@ #include #include +// rst: This header contains various functions to manipulate post-processing (:ref:`mod_post`). +// rst: Commands for the post-processor are written to the command file ``out/post.sh`` +// rst: which the post-processor executes internally as a Bash script. +// rst: + namespace mod::post { struct MOD_DECL FileHandle { // throws LogicError if the file can not be opened - explicit FileHandle(std::string name); + explicit FileHandle(const std::string &name); operator std::ostream &() { return stream; @@ -29,19 +34,71 @@ struct MOD_DECL FileHandle { std::string name; }; -MOD_DECL void command(const std::string &text); -MOD_DECL void reset(); -MOD_DECL void flush(); -MOD_DECL void disable(); - -MOD_DECL void summaryChapter(const std::string &chapterTitle); -MOD_DECL void summarySection(const std::string §ionTitle); // rst: .. function:: std::string post::makeUniqueFilePrefix() // rst: -// rst: :returns: a unique file prefix from the ``out/`` folder. +// rst: :returns: a string on the form ``out/iii_`` where ```iii``` is the next zero-padded integer +// rst: from an internal counter. MOD_DECL std::string makeUniqueFilePrefix(); +// rst: .. function:: void post::command(const std::string &line) +// rst: +// rst: Write the given text to the command file and write a newline character. +// rst: +// rst: .. warning:: The contents of the command file is executed without any security checks. +MOD_DECL void command(const std::string &line); +// rst: .. function:: void post::flushCommands() +// rst: +// rst: Flush the command file buffer. +MOD_DECL void flushCommands(); +// rst: .. function:: void post::disableCommands() +// rst: void post::enableCommands() +// rst: +// rst: Disable/enable command writing and flushing, also for commands emitted internally in the library. +MOD_DECL void disableCommands(); +MOD_DECL void enableCommands(); + +// rst: .. function:: void post::reopenCommandFile() +// rst: +// rst: Reopen the command file, which may be useful if it was modified externally while open by the library. +MOD_DECL void reopenCommandFile(); + +// rst: .. function:: void post::summaryChapter(const std::string &heading) +// rst: +// rst: Command the post-processor to insert a ``\chapter`` macro in the summary. +MOD_DECL void summaryChapter(const std::string &heading); +// rst: .. function:: void post::summarySection(const std::string &heading) +// rst: +// rst: Command the post-processor to insert a ``\section`` macro in the summary. +MOD_DECL void summarySection(const std::string &heading); +// rst: .. function:: void post::summaryRaw(const std::string &latexCode) +// rst: void post::summaryRaw(const std::string &latexCode, const std::string &file) +// rst: +// rst: Command the post-processor to insert the given code verbatim in the summary. +// rst: If `file` is given then that will be appended to a unique prefix for the final filename the code is stored in. +MOD_DECL void summaryRaw(const std::string &latexCode); +MOD_DECL void summaryRaw(const std::string &latexCode, const std::string &file); +// rst: .. function:: void post::summaryInput(const std::string &filename) +// rst: +// rst: Command the post-processor to insert a ``\input`` macro in the summary. +MOD_DECL void summaryInput(const std::string &filename); + +// rst: .. function:: void post::disableInvokeMake() +// rst: void post::enableInvokeMake() +// rst: +// rst: Disable/enable the invocation of Make in the post-processor. +// rst: The processing of commands and generation of Makefiles will still be carried out, +// rst: and Make invocation can be done manually afterwards through the post-processor +MOD_DECL void disableInvokeMake(); +MOD_DECL void enableInvokeMake(); +// rst: .. function:: void post::disableCompileSummary() +// rst: void post::enableCompileSummary() +// rst: +// rst: Disable/enable the compilation of the final summary during post-processing. +// rst: The compilation can be invoked manually afterwards through the post-processor. +MOD_DECL void disableCompileSummary(); +MOD_DECL void enableCompileSummary(); + } // namespace mod::post #endif // MOD_POST_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/Term.cpp b/libs/libmod/src/mod/Term.cpp index 0e872c0..bc4d3e7 100644 --- a/libs/libmod/src/mod/Term.cpp +++ b/libs/libmod/src/mod/Term.cpp @@ -2,9 +2,10 @@ #include #include -#include #include #include +#include +#include #include @@ -13,8 +14,8 @@ namespace mod::Term { void mgu(const std::string &left, const std::string &right) { lib::Term::RawTerm tRawLeft, tRawRight; try { - tRawLeft = lib::IO::Term::Read::rawTerm(left, lib::Term::getStrings()); - tRawRight = lib::IO::Term::Read::rawTerm(right, lib::Term::getStrings()); + tRawLeft = lib::Term::Read::rawTerm(left, lib::Term::getStrings()); + tRawRight = lib::Term::Read::rawTerm(right, lib::Term::getStrings()); } catch(const lib::IO::ParsingError &e) { std::cout << e.msg << '\n'; return; @@ -27,25 +28,25 @@ void mgu(const std::string &left, const std::string &right) { machine.setTemp(machineTemp); addrRight.type = lib::Term::AddressType::Temp; std::cout << "=================================================" << std::endl; - lib::IO::Term::Write::wam(machine, lib::Term::getStrings(), std::cout); + lib::Term::Write::wam(machine, lib::Term::getStrings(), std::cout); std::cout << "Left = " << addrLeft << ", Right = " << addrRight << std::endl; std::cout << "Most general unifier of" << std::endl << "\t"; - lib::IO::Term::Write::term(machine, addrLeft, lib::Term::getStrings(), std::cout); + lib::Term::Write::term(machine, addrLeft, lib::Term::getStrings(), std::cout); std::cout << " =? "; - lib::IO::Term::Write::term(machine, addrRight, lib::Term::getStrings(), std::cout); + lib::Term::Write::term(machine, addrRight, lib::Term::getStrings(), std::cout); std::cout << std::endl; std::cout << "\t"; - lib::IO::Term::Write::rawTerm(tRawLeft, lib::Term::getStrings(), std::cout); + lib::Term::Write::rawTerm(tRawLeft, lib::Term::getStrings(), std::cout); std::cout << " =? "; - lib::IO::Term::Write::rawTerm(tRawRight, lib::Term::getStrings(), std::cout); + lib::Term::Write::rawTerm(tRawRight, lib::Term::getStrings(), std::cout); std::cout << std::endl; auto mgu = machine.unifyHeapTemp(addrLeft.addr, addrRight.addr); - lib::IO::Term::Write::wam(machine, lib::Term::getStrings(), std::cout << "is "); + lib::Term::Write::wam(machine, lib::Term::getStrings(), std::cout << "is "); std::cout << "Left = " << machine.deref(addrLeft) << ", Right = " << machine.deref(addrRight) << std::endl; - lib::IO::Term::Write::mgu(machine, mgu, lib::Term::getStrings(), std::cout); + lib::Term::Write::mgu(machine, mgu, lib::Term::getStrings(), std::cout); std::cout << std::endl; } diff --git a/libs/libmod/src/mod/VertexMap.hpp b/libs/libmod/src/mod/VertexMap.hpp index 0e4f2c8..38f1dc7 100644 --- a/libs/libmod/src/mod/VertexMap.hpp +++ b/libs/libmod/src/mod/VertexMap.hpp @@ -31,12 +31,14 @@ struct VertexMap { std::function forward, std::function backward) : dom(dom), codom(codom), forward(forward), backward(backward) {} + // rst: .. function:: friend std::ostream &operator<<(std::ostream &s, const VertexMap &m) friend std::ostream &operator<<(std::ostream &s, const VertexMap &m) { - return s << "VertexMap{" - << getGraphFromHandle(m.getDomain()) << ", " - << getGraphFromHandle(m.getCodomain()) << "}"; + const Domain &dom = getGraphFromHandle(m.getDomain()); + const Codomain &codom = getGraphFromHandle(m.getCodomain()); + return s << "VertexMap{" << dom << ", " << codom << "}"; } + // rst: .. function:: DomainHandle getDomain() const // rst: CodomainHandle getCodomain() const // rst: @@ -54,6 +56,7 @@ struct VertexMap { if(v.getGraph() != getDomain()) throw LogicError("Vertex does not belong to the domain graph."); return forward(v); } + // rst: .. function:: DomVertex getInverse(CodomVertex v) const // rst: // rst: :returns: the domain vertex that maps to the given codomain vertex. diff --git a/libs/libmod/src/mod/dg/Builder.cpp b/libs/libmod/src/mod/dg/Builder.cpp index cdc0e42..a68dcb5 100644 --- a/libs/libmod/src/mod/dg/Builder.cpp +++ b/libs/libmod/src/mod/dg/Builder.cpp @@ -6,8 +6,8 @@ #include #include #include +#include #include -#include #include @@ -63,6 +63,26 @@ DG::HyperEdge Builder::addDerivation(const Derivations &d, IsomorphismPolicy gra return p->dg_->getHyper().getInterfaceEdge(p->dg_->getNonHyper().getHyperEdge(innerRes.first)); } +DG::HyperEdge Builder::addHyperEdge(const DG::HyperEdge &e) { + return addHyperEdge(e, IsomorphismPolicy::Check); +} + +DG::HyperEdge Builder::addHyperEdge(const DG::HyperEdge &e, IsomorphismPolicy graphPolicy) { + check(p); + if(!e) throw LogicError("The hyperedge is null."); + Derivations d; + d.left.reserve(e.numSources()); + d.right.reserve(e.numTargets()); + d.rules.reserve(e.rules().size()); + for(const auto &v : e.sources()) + d.left.push_back(v.getGraph()); + for(const auto &v : e.targets()) + d.right.push_back(v.getGraph()); + for(const auto &r: e.rules()) + d.rules.push_back(r); + return addDerivation(d, graphPolicy); +} + ExecuteResult Builder::execute(std::shared_ptr strategy) { return execute(strategy, 1); } diff --git a/libs/libmod/src/mod/dg/Builder.hpp b/libs/libmod/src/mod/dg/Builder.hpp index 0edff0d..3c75048 100644 --- a/libs/libmod/src/mod/dg/Builder.hpp +++ b/libs/libmod/src/mod/dg/Builder.hpp @@ -54,6 +54,23 @@ class MOD_DECL Builder { // rst: in the internal graph database in the associated derivation graph. DG::HyperEdge addDerivation(const Derivations &d); DG::HyperEdge addDerivation(const Derivations &d, IsomorphismPolicy graphPolicy); + // rst: .. function:: DG::HyperEdge addHyperEdge(const DG::HyperEdge &e) + // rst: DG::HyperEdge addHyperEdge(const DG::HyperEdge &e, IsomorphismPolicy graphPolicy) + // rst: + // rst: Adds a hyperedge to the associated :class:`DG` from a copy of the given hyperedge + // rst: (from a different :class:`DG`). + // rst: If it already exists, only add the rules to the edge. + // rst: The given :var:`graphPolicy` refers to adding the graphs associated with :var:`e`, + // rst: and it defaults to :enumerator:`IsomorphismPolicy::Check`. + // rst: + // rst: :returns: the hyperedge corresponding to the given derivation. + // rst: :throws: :class:`LogicError` if `!isActive()`. + // rst: :throws: :class:`LogicError` if `!e`. + // rst: :throws: :class:`LogicError` if `graphPolicy == IsomorphismPolicy::Check` and a given graph object + // rst: is different but isomorphic to another given graph object or to a graph object already + // rst: in the internal graph database in the associated derivation graph. + DG::HyperEdge addHyperEdge(const DG::HyperEdge &e); + DG::HyperEdge addHyperEdge(const DG::HyperEdge &e, IsomorphismPolicy graphPolicy); // rst: .. function:: ExecuteResult execute(std::shared_ptr strategy) // rst: ExecuteResult execute(std::shared_ptr strategy, int verbosity) // rst: ExecuteResult execute(std::shared_ptr strategy, int verbosity, bool ignoreRuleLabelTypes) diff --git a/libs/libmod/src/mod/dg/DG.cpp b/libs/libmod/src/mod/dg/DG.cpp index d9b59d8..5c323d9 100644 --- a/libs/libmod/src/mod/dg/DG.cpp +++ b/libs/libmod/src/mod/dg/DG.cpp @@ -10,9 +10,9 @@ #include #include #include +#include +#include #include -#include -#include #include #include @@ -172,11 +172,11 @@ std::pair DG::print(const Printer &printer, const Prin << ") than this (id=" << getId() << ")" << std::endl; throw LogicError(err.str()); } - return lib::IO::DG::Write::summary(data.getData(), printer.getPrinter(), printer.getGraphPrinter().getOptions()); + return lib::DG::Write::summary(data.getData(), printer.getPrinter(), printer.getGraphPrinter().getOptions()); } std::string DG::printNonHyper() const { - return lib::IO::DG::Write::summaryNonHyper(getNonHyper()); + return lib::DG::Write::summaryNonHyper(getNonHyper()); } std::string DG::dump() const { @@ -186,11 +186,11 @@ std::string DG::dump() const { std::string DG::dump(const std::string &filename) const { if(!isLocked()) throw LogicError("Can not dump DG before it is locked."); if(filename.empty()) { - std::string name = lib::IO::getUniqueFilePrefix() + "DG.dg"; - lib::IO::writeJsonFile(name, lib::IO::DG::Write::dumpToJson(getNonHyper())); + std::string name = lib::IO::makeUniqueFilePrefix() + "DG.dg"; + lib::IO::writeJsonFile(name, lib::DG::Write::dumpToJson(getNonHyper())); return name; } else { - lib::IO::writeJsonFile(filename, lib::IO::DG::Write::dumpToJson(getNonHyper())); + lib::IO::writeJsonFile(filename, lib::DG::Write::dumpToJson(getNonHyper())); return filename; } } @@ -250,7 +250,7 @@ std::shared_ptr DG::load(const std::vector> &g std::ostringstream err; std::unique_ptr dgInternal( - lib::IO::DG::Read::dump(graphDatabase, ruleDatabase, file, graphPolicy, err, verbosity)); + lib::DG::Read::dump(graphDatabase, ruleDatabase, file, graphPolicy, err, verbosity)); if(!dgInternal) throw InputError("DG load error: " + err.str()); return wrapIt(new DG(std::move(dgInternal))); } diff --git a/libs/libmod/src/mod/dg/ForwardDecl.hpp b/libs/libmod/src/mod/dg/ForwardDecl.hpp index b55dc40..7e760a7 100644 --- a/libs/libmod/src/mod/dg/ForwardDecl.hpp +++ b/libs/libmod/src/mod/dg/ForwardDecl.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_DG_FORWARDDECL_H -#define MOD_DG_FORWARDDECL_H +#ifndef MOD_DG_FORWARDDECL_HPP +#define MOD_DG_FORWARDDECL_HPP namespace mod::dg { struct Builder; @@ -9,19 +9,17 @@ struct PrintData; struct Printer; struct Strategy; } // namespace mod::dg -namespace mod::lib { -namespace DG { +namespace mod::lib::DG { struct Hyper; struct NonHyper; struct NonHyperBuilder; -namespace Strategies { +} // namespace mod::lib::DG +namespace mod::lib::DG::Strategies { struct Strategy; -} // namespace Strategies -} // namespace DG -namespace IO::DG::Write { +} // namespace mod::lib::DG::Strategies +namespace mod::lib::DG::Write { struct Data; struct Printer; -} // namespace IO::DG::Write -} // namespace mod::lib +} // namespace mod::lib::DG::Write -#endif /* MOD_DG_FORWARDDECL_H */ \ No newline at end of file +#endif // MOD_DG_FORWARDDECL_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/dg/GraphInterface.cpp b/libs/libmod/src/mod/dg/GraphInterface.cpp index 008c85f..112fe89 100644 --- a/libs/libmod/src/mod/dg/GraphInterface.cpp +++ b/libs/libmod/src/mod/dg/GraphInterface.cpp @@ -2,9 +2,9 @@ #include #include +#include #include #include -#include #include namespace mod::dg { @@ -13,7 +13,7 @@ namespace mod::dg { // Vertex //------------------------------------------------------------------------------ -MOD_GRAPHPIMPL_Define_Vertex_noGraph(DG, DG,g->getHyper().getGraph(), g, /* VertexPrint */) +MOD_GRAPHPIMPL_Define_Vertex_noGraph(DG, DG, g->getHyper().getGraph(), g, /* VertexPrint */) std::shared_ptr DG::Vertex::getDG() const { if(!g) throw LogicError("Can not get DG on a null vertex."); @@ -156,7 +156,7 @@ DG::HyperEdge::print(const graph::Printer &printer, const std::string &nomatchCo if(rules().size() == 0) throw LogicError("The edge has no rules."); const auto &dg = g->getHyper(); const auto v = dg.getInternalVertex(*this); - return lib::IO::Derivation::Write::summary(g->getNonHyper(), v, printer.getOptions(), nomatchColour, matchColour); + return lib::DG::Write::summaryDerivation(g->getNonHyper(), v, printer.getOptions(), nomatchColour, matchColour); } //------------------------------------------------------------------------------ diff --git a/libs/libmod/src/mod/dg/GraphInterface.hpp b/libs/libmod/src/mod/dg/GraphInterface.hpp index d220669..4a0b7a8 100644 --- a/libs/libmod/src/mod/dg/GraphInterface.hpp +++ b/libs/libmod/src/mod/dg/GraphInterface.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_DG_GRAPHINTERFACE_H -#define MOD_DG_GRAPHINTERFACE_H +#ifndef MOD_DG_GRAPHINTERFACE_HPP +#define MOD_DG_GRAPHINTERFACE_HPP // rst: This header contains the definitions for the hypergraph interface for :class:`dg::DG`. // rst: @@ -626,4 +626,4 @@ struct std::hash { } }; -#endif /* MOD_DG_GRAPHINTERFACE_H */ \ No newline at end of file +#endif // MOD_DG_GRAPHINTERFACE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/dg/Printer.cpp b/libs/libmod/src/mod/dg/Printer.cpp index 6915c7f..c2b6ca0 100644 --- a/libs/libmod/src/mod/dg/Printer.cpp +++ b/libs/libmod/src/mod/dg/Printer.cpp @@ -5,8 +5,8 @@ #include #include #include +#include #include -#include #include @@ -50,24 +50,24 @@ void reconnectCommon(std::shared_ptr dg, DG::HyperEdge e, unsigned int eDup, PrintData::PrintData(std::shared_ptr dg) : dg(dg), data(nullptr) { if(!dg->isLocked()) throw LogicError("Can not create print data. The DG is not locked yet."); - data.reset(new lib::IO::DG::Write::Data(dg->getHyper())); + data.reset(new lib::DG::Write::Data(dg->getHyper())); } -PrintData::PrintData(const PrintData &other) : dg(other.dg), data(new lib::IO::DG::Write::Data(*other.data)) {} +PrintData::PrintData(const PrintData &other) : dg(other.dg), data(new lib::DG::Write::Data(*other.data)) {} PrintData &PrintData::operator=(const PrintData &other) { dg = other.dg; - data = std::make_unique(*other.data); + data = std::make_unique(*other.data); return *this; } PrintData::~PrintData() = default; -lib::IO::DG::Write::Data &PrintData::getData() { +lib::DG::Write::Data &PrintData::getData() { return *data; } -lib::IO::DG::Write::Data &PrintData::getData() const { +const lib::DG::Write::Data &PrintData::getData() const { return *data; } @@ -112,14 +112,14 @@ void PrintData::reconnectTarget(DG::HyperEdge e, int eDup, DG::Vertex v, int vDu //------------------------------------------------------------------------------ Printer::Printer() : graphPrinter(std::make_unique()), - printer(std::make_unique()) { + printer(std::make_unique()) { graphPrinter->enableAll(); graphPrinter->setWithIndex(false); } -Printer::~Printer() {} +Printer::~Printer() = default; -lib::IO::DG::Write::Printer &Printer::getPrinter() const { +lib::DG::Write::Printer &Printer::getPrinter() const { return *printer; } @@ -293,15 +293,26 @@ void Printer::popEdgeColour() { } void Printer::setRotationOverwrite(std::function)> f) { - if(!f) throw LogicError("Can not push empty callback."); + if(!f) throw LogicError("Can not set empty callback."); printer->setRotationOverwrite(f); } void Printer::setMirrorOverwrite(std::function)> f) { - if(!f) throw LogicError("Can not push empty callback."); + if(!f) throw LogicError("Can not set empty callback."); printer->setMirrorOverwrite(f); } +void Printer::setImageOverwrite(std::function(DG::Vertex v, int dupNum)> f) { + if(!f) { + printer->setImageOverwrite(nullptr); + } else { + printer->setImageOverwrite([f](lib::DG::HyperVertex v, int dupNum, const lib::DG::Hyper &dg) { + assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Vertex); + return f(dg.getInterfaceVertex(v), dupNum); + }); + } +} + void Printer::setGraphvizPrefix(const std::string &prefix) { printer->setGraphvizPrefix(prefix); } diff --git a/libs/libmod/src/mod/dg/Printer.hpp b/libs/libmod/src/mod/dg/Printer.hpp index 09cc0e3..dcf8375 100644 --- a/libs/libmod/src/mod/dg/Printer.hpp +++ b/libs/libmod/src/mod/dg/Printer.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_DG_PRINTER_H -#define MOD_DG_PRINTER_H +#ifndef MOD_DG_PRINTER_HPP +#define MOD_DG_PRINTER_HPP #include #include @@ -37,8 +37,8 @@ struct MOD_DECL PrintData { PrintData(const PrintData &other); PrintData &operator=(const PrintData &other); ~PrintData(); - lib::IO::DG::Write::Data &getData(); - lib::IO::DG::Write::Data &getData() const; + lib::DG::Write::Data &getData(); + const lib::DG::Write::Data &getData() const; // rst: .. function:: std::shared_ptr getDG() const // rst: // rst: :returns: the derivation graph the object holds data for. @@ -86,7 +86,7 @@ struct MOD_DECL PrintData { void reconnectTarget(DG::HyperEdge e, int eDup, DG::Vertex v, int vDupTar); private: std::shared_ptr dg; - std::unique_ptr data; + std::unique_ptr data; }; // rst-class-end: @@ -101,7 +101,7 @@ struct MOD_DECL Printer { Printer(const Printer &) = delete; Printer &operator=(const Printer &) = delete; ~Printer(); - lib::IO::DG::Write::Printer &getPrinter() const; + lib::DG::Write::Printer &getPrinter() const; // rst: .. function:: graph::Printer &getGraphPrinter() // rst: const graph::Printer &getGraphPrinter() const // rst: @@ -118,7 +118,7 @@ struct MOD_DECL Printer { // rst: .. function:: void setWithGraphImages(bool value) // rst: bool getWithGraphImages() const // rst: - // rst: Control whether or not each vertex is printed with a image of its graph in it. + // rst: Control whether or not each vertex is printed with an image of its graph in it. void setWithGraphImages(bool value); bool getWithGraphImages() const; // rst: .. function:: void setLabelsAsLatexMath(bool value) @@ -265,6 +265,27 @@ struct MOD_DECL Printer { // rst: // rst: :throws: :class:`LogicError` if `!f`. void setMirrorOverwrite(std::function)> f); +public: + // rst: .. function:: void setImageOverwrite(std::function(DG::Vertex v, int dupNum)> f) + // rst: + // rst: Overwrite the image generation for graphs depicted in the vertices of the printed derivation graph. + // rst: For each duplicate of each vertex to be depicted, the given callback is called. + // rst: It must then return two strings: + // rst: + // rst: 1. Either + // rst: + // rst: - an empty string if the standard depiction should be used, in which case the second string is ignored, or + // rst: - the filename of the PDF that should be included in the final compiled figure. + // rst: + // rst: 2. Either + // rst: + // rst: - an empty string if no additional post-processing command is needed, or + // rst: - a string of Bash code which will be inserted in the post-processing instructions. + // rst: For example, one can write out source code in the callback, and then return a command that compiles + // rst: that code into the PDF needed by inclusion. + // rst: + // rst: The image overwrite can be removed by calling with `nullptr`. + void setImageOverwrite(std::function(DG::Vertex v, int dupNum)> f); public: // rst: .. function:: void setGraphvizPrefix(const std::string &prefix) // rst: const std::string &getGraphvizPrefix() const @@ -282,10 +303,10 @@ struct MOD_DECL Printer { const std::string &getTikzpictureOption() const; private: std::unique_ptr graphPrinter; - std::unique_ptr printer; + std::unique_ptr printer; }; // rst-class-end: } // namespace mod::dg -#endif /* MOD_DG_PRINTER_H */ \ No newline at end of file +#endif // MOD_DG_PRINTER_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/graph/Graph.cpp b/libs/libmod/src/mod/graph/Graph.cpp index 1661a47..833b053 100644 --- a/libs/libmod/src/mod/graph/Graph.cpp +++ b/libs/libmod/src/mod/graph/Graph.cpp @@ -1,22 +1,23 @@ #include "Graph.hpp" #include +#include +#include #include #include #include #include -#include +#include +#include +#include #include #include #include #include -#include -#include #include #include -#include #include namespace mod::graph { @@ -79,25 +80,25 @@ std::pair Graph::print() const { } std::pair Graph::print(const Printer &first, const Printer &second) const { - return lib::IO::Graph::Write::summary(*g, first.getOptions(), second.getOptions()); + return lib::Graph::Write::summary(*g, first.getOptions(), second.getOptions()); } void Graph::printTermState() const { - lib::IO::Graph::Write::termState(*g); + lib::Graph::Write::termState(*g); } std::string Graph::getGMLString(bool withCoords) const { if(withCoords && !g->getDepictionData().getHasCoordinates()) throw LogicError("Coordinates are not available for this graph (" + getName() + ")."); std::stringstream ss; - lib::IO::Graph::Write::gml(*g, withCoords, ss); + lib::Graph::Write::gml(*g, withCoords, ss); return ss.str(); } std::string Graph::printGML(bool withCoords) const { if(withCoords && !g->getDepictionData().getHasCoordinates()) throw LogicError("Coordinates are not available for this graph (" + getName() + ")."); - return lib::IO::Graph::Write::gml(*g, withCoords); + return lib::Graph::Write::gml(*g, withCoords); } const std::string &Graph::getName() const { @@ -171,17 +172,29 @@ void checkTermParsing(const lib::Graph::Single &g, LabelSettings ls) { } // namespace std::size_t -Graph::isomorphism(std::shared_ptr host, std::size_t maxNumMatches, LabelSettings labelSettings) const { +Graph::isomorphism(std::shared_ptr codomain, std::size_t maxNumMatches, LabelSettings labelSettings) const { + if(!codomain) throw LogicError("codomain is null."); checkTermParsing(*g, labelSettings); - checkTermParsing(*host->g, labelSettings); - return lib::Graph::Single::isomorphism(*g, *host->g, maxNumMatches, labelSettings); + checkTermParsing(*codomain->g, labelSettings); + return lib::Graph::Single::isomorphism(*g, *codomain->g, maxNumMatches, labelSettings); } std::size_t -Graph::monomorphism(std::shared_ptr host, std::size_t maxNumMatches, LabelSettings labelSettings) const { +Graph::monomorphism(std::shared_ptr codomain, std::size_t maxNumMatches, LabelSettings labelSettings) const { + if(!codomain) throw LogicError("codomain is null."); checkTermParsing(*g, labelSettings); - checkTermParsing(*host->g, labelSettings); - return lib::Graph::Single::monomorphism(*g, *host->g, maxNumMatches, labelSettings); + checkTermParsing(*codomain->g, labelSettings); + return lib::Graph::Single::monomorphism(*g, *codomain->g, maxNumMatches, labelSettings); +} + +void Graph::enumerateMonomorphisms(std::shared_ptr codomain, + std::shared_ptr)>> callback, + LabelSettings labelSettings) const { + if(!codomain) throw LogicError("codomain is null."); + if(!callback) throw LogicError("callback is null."); + checkTermParsing(*g, labelSettings); + checkTermParsing(*codomain->g, labelSettings); + return lib::Graph::Single::enumerateMonomorphisms(*g, *codomain->g, toStdFunction(callback), labelSettings); } std::shared_ptr Graph::makePermutation() const { @@ -239,7 +252,7 @@ std::vector> Graph::getLoadingWarnings() const { namespace { std::shared_ptr -makeGraphFromData(lib::IO::Graph::Read::Data data, std::vector> warnings) { +makeGraphFromData(lib::Graph::Read::Data data, std::vector> warnings) { auto gInternal = std::make_unique( std::move(data.g), std::move(data.pString), std::move(data.pStereo)); std::shared_ptr g = Graph::create(std::move(gInternal), @@ -249,87 +262,118 @@ makeGraphFromData(lib::IO::Graph::Read::Data data, std::vector -handleLoadedGraph(lib::IO::Result> dataRes, lib::IO::Warnings warnings, - const std::string &source) { - std::cout << warnings << std::flush; - if(!dataRes) throw InputError("Error in graph loading from " + source + ".\n" + dataRes.extractError()); - auto data = std::move(*dataRes); +handleLoadedGraph(std::vector data, lib::IO::Warnings warnings, + const std::string &type, const std::string &source) { if(data.size() != 1) - throw InputError("Error in graph loading from " + source - + ".\nThe graph is not connected (" + std::to_string(data.size()) + " components)."); + throw InputError("Error in loading " + type + " from " + source + + ".\nA/the graph is not connected (" + std::to_string(data.size()) + " components)."); return makeGraphFromData(std::move(data.front()), warnings.extractWarnings()); } std::vector> -handleLoadedGraphs(lib::IO::Result> dataRes, lib::IO::Warnings warnings, - const std::string &source) { - std::cout << warnings << std::flush; - if(!dataRes) throw InputError("Error in graph loading from " + source + ".\n" + dataRes.extractError()); - auto data = std::move(*dataRes); +handleLoadedGraphs(std::vector data, lib::IO::Warnings warnings, + const std::string &type, const std::string &source) { // the warnings are copied into each graph const auto warningList = warnings.extractWarnings(); std::vector> res; res.reserve(data.size()); - for(auto &d : data) + for(auto &d: data) res.push_back(makeGraphFromData(std::move(d), warningList)); return res; } -} // namespace +std::vector> +handleLoadedGraphVector(std::vector> data, lib::IO::Warnings warnings, + const std::string &type, const std::string &source) { + std::vector> res; + res.reserve(data.size()); + for(auto &ds: data) + res.push_back(handleLoadedGraph(std::move(ds), lib::IO::Warnings(warnings), type, source)); + return res; +} -std::shared_ptr Graph::fromGMLString(const std::string &data) { - lib::IO::Warnings warnings; - auto res = lib::IO::Graph::Read::gml(warnings, data); - return handleLoadedGraph(std::move(res), std::move(warnings), "inline GML string"); +std::vector>> +handleLoadedGraphsVector(std::vector> data, lib::IO::Warnings warnings, + const std::string &type, const std::string &source) { + std::vector>> res; + res.reserve(data.size()); + for(auto &ds: data) + res.push_back(handleLoadedGraphs(std::move(ds), lib::IO::Warnings(warnings), type, source)); + return res; } -std::shared_ptr Graph::fromGMLFile(const std::string &file) { +boost::iostreams::mapped_file_source openFile(const std::string &file, const std::string &type) { boost::iostreams::mapped_file_source ifs; try { ifs.open(file); } catch(const BOOST_IOSTREAMS_FAILURE &e) { - throw InputError("Could not open graph GML file '" + file + "':\n" + e.what()); + throw InputError("Could not open " + type + " '" + file + "':\n" + e.what()); } - if(!ifs) throw InputError("Could not open graph GML file '" + file + "'.\n"); + if(!ifs) throw InputError("Could not open " + type + " '" + file + "'.\n"); + return ifs; +} + + +template +auto load(const std::string &data, const std::string &type, FParse parse, FProcess process, ParseArgs &&... parseArgs) { lib::IO::Warnings warnings; - auto res = lib::IO::Graph::Read::gml(warnings, {ifs.begin(), ifs.size()}); - return handleLoadedGraph(std::move(res), std::move(warnings), "file, '" + file + "'"); + auto parsedData = [&]() { + if constexpr(IsFile) { + auto ifs = openFile(data, type + " file"); + return parse(warnings, {ifs.begin(), ifs.size()}, std::forward(parseArgs)...); + } else { + return parse(warnings, data, std::forward(parseArgs)...); + } + }(); + std::cout << warnings << std::flush; + std::string source = IsFile ? type + " file '" + data + "'" : "inline " + type + " string"; + if(!parsedData) throw InputError("Error in loading from " + source + ".\n" + parsedData.extractError()); + return process(std::move(*parsedData), std::move(warnings), type, source); +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +std::shared_ptr Graph::fromGMLString(const std::string &data) { + return load(data, "GML", &lib::Graph::Read::gml, &handleLoadedGraph); +} + +std::shared_ptr Graph::fromGMLFile(const std::string &file) { + return load(file, "GML", &lib::Graph::Read::gml, &handleLoadedGraph); } std::vector> Graph::fromGMLStringMulti(const std::string &data) { - lib::IO::Warnings warnings; - auto res = lib::IO::Graph::Read::gml(warnings, data); - return handleLoadedGraphs(std::move(res), std::move(warnings), "inline GML string"); + return load(data, "GML", &lib::Graph::Read::gml, &handleLoadedGraphs); } std::vector> Graph::fromGMLFileMulti(const std::string &file) { - boost::iostreams::mapped_file_source ifs; - try { - ifs.open(file); - } catch(const BOOST_IOSTREAMS_FAILURE &e) { - throw InputError("Could not open graph GML file '" + file + "':\n" + e.what()); - } - if(!ifs) throw InputError("Could not open graph GML file '" + file + "'.\n"); - lib::IO::Warnings warnings; - auto res = lib::IO::Graph::Read::gml(warnings, {ifs.begin(), ifs.size()}); - return handleLoadedGraphs(std::move(res), std::move(warnings), "file, '" + file + "'"); + return load(file, "GML", &lib::Graph::Read::gml, &handleLoadedGraphs); } -std::shared_ptr Graph::fromDFS(const std::string &graphDFS) { - auto data = lib::IO::Graph::Read::dfs(graphDFS); - if(!data) throw InputError("Error in graph loading from graphDFS, '" + graphDFS + "'.\n" + data.extractError()); - return makeGraphFromData(std::move(*data), {}); +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +std::shared_ptr Graph::fromDFS(const std::string &data) { + return load(data, "GraphDFS", &lib::Graph::Read::dfs, &handleLoadedGraph); +} + +std::vector> Graph::fromDFSMulti(const std::string &data) { + return load(data, "GraphDFS", &lib::Graph::Read::dfs, &handleLoadedGraphs); } + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + std::shared_ptr Graph::fromSMILES(const std::string &smiles) { return Graph::fromSMILES(smiles, false, SmilesClassPolicy::NoneOnDuplicate); } std::shared_ptr Graph::fromSMILES(const std::string &smiles, const bool allowAbstract, SmilesClassPolicy classPolicy) { - lib::IO::Warnings warnings; - auto res = lib::IO::Graph::Read::smiles(warnings, smiles, allowAbstract, classPolicy); - return handleLoadedGraph(std::move(res), std::move(warnings), "smiles string, '" + smiles + "'"); + return load(smiles, "SMILES", &lib::Graph::Read::smiles, &handleLoadedGraph, allowAbstract, classPolicy); } std::vector> Graph::fromSMILESMulti(const std::string &smiles) { @@ -338,11 +382,54 @@ std::vector> Graph::fromSMILESMulti(const std::string &sm std::vector> Graph::fromSMILESMulti(const std::string &smiles, bool allowAbstract, SmilesClassPolicy classPolicy) { - lib::IO::Warnings warnings; - auto res = lib::IO::Graph::Read::smiles(warnings, smiles, allowAbstract, classPolicy); - return handleLoadedGraphs(std::move(res), std::move(warnings), "smiles string, '" + smiles + "'"); + return load(smiles, "SMILES", &lib::Graph::Read::smiles, &handleLoadedGraphs, allowAbstract, classPolicy); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +std::shared_ptr Graph::fromMOLString(const std::string &data, const MDLOptions &options) { + return load(data, "MOL", &lib::Graph::Read::MDLMOL, &handleLoadedGraph, options); +} + +std::shared_ptr Graph::fromMOLFile(const std::string &file, const MDLOptions &options) { + return load(file, "MOL", &lib::Graph::Read::MDLMOL, &handleLoadedGraph, options); } +std::vector> Graph::fromMOLStringMulti(const std::string &data, const MDLOptions &options) { + return load(data, "MOL", &lib::Graph::Read::MDLMOL, &handleLoadedGraphs, options); +} + +std::vector> Graph::fromMOLFileMulti(const std::string &file, const MDLOptions &options) { + return load(file, "MOL", &lib::Graph::Read::MDLMOL, &handleLoadedGraphs, options); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +std::vector> Graph::fromSDString(const std::string &data, const MDLOptions &options) { + + return load(data, "SD", &lib::Graph::Read::MDLSD, &handleLoadedGraphVector, options); +} + +std::vector> Graph::fromSDFile(const std::string &file, const MDLOptions &options) { + return load(file, "SD", &lib::Graph::Read::MDLSD, &handleLoadedGraphVector, options); +} + + +std::vector>> +Graph::fromSDStringMulti(const std::string &data, const MDLOptions &options) { + return load(data, "SD", &lib::Graph::Read::MDLSD, &handleLoadedGraphsVector, options); +} + +std::vector>> +Graph::fromSDFileMulti(const std::string &file, const MDLOptions &options) { + return load(file, "SD", &lib::Graph::Read::MDLSD, &handleLoadedGraphsVector, options); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + std::shared_ptr Graph::create(std::unique_ptr g) { if(!g) MOD_ABORT; std::shared_ptr wrapped = std::shared_ptr(new Graph(std::move(g))); diff --git a/libs/libmod/src/mod/graph/Graph.hpp b/libs/libmod/src/mod/graph/Graph.hpp index 554d264..27b69fb 100644 --- a/libs/libmod/src/mod/graph/Graph.hpp +++ b/libs/libmod/src/mod/graph/Graph.hpp @@ -12,8 +12,13 @@ #include namespace mod { + template struct Function; + +template +struct VertexMap; + } // namespace mod namespace mod::graph { @@ -21,8 +26,7 @@ namespace mod::graph { // rst: // rst: This class models an undirected graph with labels on vertices and edges, // rst: without loops and without parallel edges. -// rst: Certain labels are regarded as models of chemical atoms and bonds. -// rst: See :ref:`mol-enc` for more information on this. +// rst: See :ref:`graph-model` for more details. // rst: See also :ref:`cpp-graph/GraphInterface` for the documentation for the // rst: graph interface for this class. // rst: @@ -124,16 +128,16 @@ struct MOD_DECL Graph { const std::string &getSmilesWithIds() const; // rst: .. function:: const std::string &getGraphDFS() const // rst: - // rst: :returns: a :ref:`GraphDFS ` string of the graph. + // rst: :returns: a :ref:`GraphDFS ` string of the graph. const std::string &getGraphDFS() const; // rst: .. function:: const std::string &getGraphDFSWithIds() const // rst: - // rst: :returns: a :ref:`GraphDFS ` string of the graph, where each vertices have an explicit id, + // rst: :returns: a :ref:`GraphDFS ` string of the graph, where each vertices have an explicit id, // rst: corresponding to its internal vertex id. const std::string &getGraphDFSWithIds() const; // rst: .. function:: const std::string &getLinearEncoding() const // rst: - // rst: :returns: the :ref:`SMILES ` string if the graph is a molecule, otherwise the :ref:`GraphDFS ` string. + // rst: :returns: the :ref:`SMILES ` string if the graph is a molecule, otherwise the :ref:`GraphDFS ` string. const std::string &getLinearEncoding() const; // rst: .. function:: bool getIsMolecule() const // rst: @@ -166,14 +170,32 @@ struct MOD_DECL Graph { // rst: // rst: :returns: the number of edges in the graph with the given label. unsigned int eLabelCount(const std::string &label) const; - // rst: .. function:: std::size_t isomorphism(std::shared_ptr host, std::size_t maxNumMatches, LabelSettings labelSettings) const - // rst: - // rst: :returns: the number of isomorphisms found from this graph to `host`, but at most `maxNumMatches`. - std::size_t isomorphism(std::shared_ptr host, std::size_t maxNumMatches, LabelSettings labelSettings) const; - // rst: .. function:: std::size_t monomorphism(std::shared_ptr host, std::size_t maxNumMatches, LabelSettings labelSettings) const - // rst: - // rst: :returns: the number of monomorphisms from this graph to `host`, though at most `maxNumMatches`. - std::size_t monomorphism(std::shared_ptr host, std::size_t maxNumMatches, LabelSettings labelSettings) const; +public: // Morphisms + // rst: .. function:: std::size_t isomorphism(std::shared_ptr codomain, std::size_t maxNumMatches, LabelSettings labelSettings) const + // rst: + // rst: :returns: the number of isomorphisms found from this graph to `codomain`, but at most `maxNumMatches`. + // rst: :throws LogicError: if `codomain` is null. + std::size_t isomorphism(std::shared_ptr codomain, std::size_t maxNumMatches, LabelSettings labelSettings) const; + // rst: .. function:: std::size_t monomorphism(std::shared_ptr codomain, std::size_t maxNumMatches, LabelSettings labelSettings) const + // rst: + // rst: :returns: the number of monomorphisms from this graph to `codomain`, though at most `maxNumMatches`. + // rst: :throws LogicError: if `codomain` is null. + std::size_t monomorphism(std::shared_ptr codomain, std::size_t maxNumMatches, LabelSettings labelSettings) const; + // rst: .. function:: void enumerateMonomorphisms(std::shared_ptr codomain, \ + // rst: std::shared_ptr)>> callback, \ + // rst: LabelSettings labelSettings) const + // rst: + // rst: Perform substructure search of this graph into the given codomain graph. + // rst: Whenever a match is found, the corresponding monomorphism is copied into a vertex map + // rst: and the given callback is invoked with it. + // rst: The return value from the callback determines whether to continue the search or not. + // rst: + // rst: :throws LogicError: if `codomain` is null. + // rst: :throws LogicError: if `callback` is null. + void enumerateMonomorphisms(std::shared_ptr codomain, + std::shared_ptr)>> callback, + LabelSettings labelSettings) const; +public: // rst: .. function:: std::shared_ptr makePermutation() const // rst: // rst: :returns: a graph isomorphic to this, but with the vertex indices randomly permuted. @@ -255,13 +277,23 @@ struct MOD_DECL Graph { // rst: :throws: :class:`InputError` on bad input. static std::vector> fromGMLStringMulti(const std::string &data); static std::vector> fromGMLFileMulti(const std::string &file); + // =========================================================================== // rst: .. function:: static std::shared_ptr fromDFS(const std::string &graphDFS) // rst: - // rst: :returns: a graph loaded from the given :ref:`GraphDFS ` string. + // rst: :returns: a graph loaded from the given :ref:`GraphDFS ` string. + // rst: The graph must be connected. Use :func:`fromDFSMulti` if it is not. // rst: :throws: :class:`InputError` on bad input. static std::shared_ptr fromDFS(const std::string &graphDFS); + // rst: .. function:: static std::vector> fromDFSMulti(const std::string &graphDFS) + // rst: + // rst: :returns: a list of graphs loaded from the given :ref:`GraphDFS ` string. + // rst: The graphs are the connected components of the graph specified in the data. + // rst: :throws: :class:`InputError` on bad input. + static std::vector> fromDFSMulti(const std::string &graphDFS); +// =========================================================================== // rst: .. function:: static std::shared_ptr fromSMILES(const std::string &smiles) - // rst: static std::shared_ptr fromSMILES(const std::string &smiles, bool allowAbstract, SmilesClassPolicy classPolicy) + // rst: static std::shared_ptr fromSMILES(const std::string &smiles, bool allowAbstract, \ + // rst: SmilesClassPolicy classPolicy) // rst: // rst: :param allowAbstract: whether abstract atoms, e.g., ``*``, are allowed. Defaults to `false`. // rst: :param classPolicy: which policy to use for class labels. Defaults to `SmilesClassPolicy::NoneOnDuplicate`. @@ -270,18 +302,54 @@ struct MOD_DECL Graph { // rst: :throws: :class:`InputError` on bad input. // rst: :throws: :class:`InputError` if `classPolicy == SmilesClassPolicy::NoneOnDuplicate` and a class label is duplicated. static std::shared_ptr fromSMILES(const std::string &smiles); - static std::shared_ptr - fromSMILES(const std::string &smiles, bool allowAbstract, SmilesClassPolicy classPolicy); + static std::shared_ptr fromSMILES(const std::string &smiles, bool allowAbstract, + SmilesClassPolicy classPolicy); // rst: .. function:: static std::vector> fromSMILESMulti(const std::string &smiles) - // rst: static std::vector> fromSMILESMulti(const std::string &smiles, bool allowAbstract, SmilesClassPolicy classPolicy) + // rst: static std::vector> fromSMILESMulti(const std::string &smiles, bool allowAbstract, \ + // rst: SmilesClassPolicy classPolicy) // rst: // rst: See :func:`fromSMILES` for parameter and exception descriptions. // rst: // rst: :returns: a list of graphs representing molecules, loaded from the given :ref:`SMILES ` string. // rst: The graphs are the connected components of the graph specified in the SMILES string. static std::vector> fromSMILESMulti(const std::string &smiles); - static std::vector> - fromSMILESMulti(const std::string &smiles, bool allowAbstract, SmilesClassPolicy classPolicy); + static std::vector> fromSMILESMulti(const std::string &smiles, bool allowAbstract, + SmilesClassPolicy classPolicy); + // =========================================================================== + // rst: .. function:: static std::shared_ptr fromMOLString(const std::string &data, const MDLOptions &options) + // rst: static std::shared_ptr fromMOLFile(const std::string &file, const MDLOptions &options) + // rst: + // rst: :returns: a graph created from the given :ref:`MOL ` data. + // rst: :throws: :class:`InputError` on bad input. + static std::shared_ptr fromMOLString(const std::string &data, const MDLOptions &options); + static std::shared_ptr fromMOLFile(const std::string &file, const MDLOptions &options); + // rst: .. function:: static std::vector> fromMOLStringMulti(const std::string &data, const MDLOptions &options) + // rst: static std::vector> fromMOLFileMulti(const std::string &file, const MDLOptions &options) + // rst: + // rst: See :func:`fromMOLString` and :func:`fromMOLFile` for parameter and exception descriptions. + // rst: + // rst: :returns: a list of graphs representing molecules, loaded from the given string or file with :ref:`MOL ` data. + // rst: The graphs are the connected components of the graph specified in the data. + static std::vector> fromMOLStringMulti(const std::string &data, const MDLOptions &options); + static std::vector> fromMOLFileMulti(const std::string &file, const MDLOptions &options); + // =========================================================================== + // rst: .. function:: static std::vector> fromSDString(const std::string &data, const MDLOptions &options) + // rst: static std::vector> fromSDFile(const std::string &file, const MDLOptions &options) + // rst: + // rst: :returns: a list of graphs graph created from the given :ref:`SD ` data. + // rst: :throws: :class:`InputError` on bad input. + static std::vector> fromSDString(const std::string &data, const MDLOptions &options); + static std::vector> fromSDFile(const std::string &file, const MDLOptions &options); + // rst: .. function:: static std::vector>> fromSDStringMulti(const std::string &data, const MDLOptions &options) + // rst: static std::vector>> fromSDFileMulti(const std::string &file, const MDLOptions &options) + // rst: + // rst: :returns: a list of lists of graphs graph created from the given :ref:`SD ` data. + // rst: :throws: :class:`InputError` on bad input. + static std::vector>> + fromSDStringMulti(const std::string &data, const MDLOptions &options); + static std::vector>> + fromSDFileMulti(const std::string &file, const MDLOptions &options); + // =========================================================================== // rst: .. function:: static std::shared_ptr create(std::unique_ptr g) // rst: static std::shared_ptr create(std::unique_ptr g, \ // rst: std::map externalToInternalIds, \ diff --git a/libs/libmod/src/mod/graph/GraphInterface.cpp b/libs/libmod/src/mod/graph/GraphInterface.cpp index b8478e4..13d3952 100644 --- a/libs/libmod/src/mod/graph/GraphInterface.cpp +++ b/libs/libmod/src/mod/graph/GraphInterface.cpp @@ -3,11 +3,11 @@ #include #include #include +#include #include #include #include #include -#include namespace mod::graph { @@ -76,7 +76,7 @@ std::string Graph::Vertex::printStereo(const Printer &p) const { using boost::vertices; auto v = *(vertices(graph).first + vId); const auto &conf = get_stereo(gLabelled)[v]; - return lib::IO::Stereo::Write::summary(g->getGraph(), v, *conf, p.getOptions(), 0, ""); + return lib::Graph::Write::stereoSummary(g->getGraph(), v, *conf, p.getOptions(), 0, ""); } //------------------------------------------------------------------------------ diff --git a/libs/libmod/src/mod/graph/Printer.cpp b/libs/libmod/src/mod/graph/Printer.cpp index 2583124..f9abf3d 100644 --- a/libs/libmod/src/mod/graph/Printer.cpp +++ b/libs/libmod/src/mod/graph/Printer.cpp @@ -1,6 +1,6 @@ #include "Printer.hpp" -#include +#include namespace mod::graph { @@ -39,7 +39,8 @@ void Printer::setMolDefault() { } void Printer::setReactionDefault() { - options->All().Thick(false).WithIndex(false).SimpleCarbons(false); + setMolDefault(); + options->SimpleCarbons(false); } void Printer::disableAll() { @@ -66,6 +67,14 @@ bool Printer::getCollapseHydrogens() const { return options->collapseHydrogens; } +void Printer::setRaiseIsotopes(bool value) { + options->RaiseIsotopes(value); +} + +bool Printer::getRaiseIsotopes() const { + return options->raiseIsotopes; +} + void Printer::setRaiseCharges(bool value) { options->RaiseCharges(value); } @@ -146,4 +155,22 @@ bool Printer::getMirror() const { return options->mirror; } +void Printer::setWithGraphvizCoords(bool value) { + options->WithGraphvizCoords(value); +} + +bool Printer::getWithGraphvizCoords() const { + return options->withGraphvizCoords; +} + +// =================================================================== + +void Printer::setGraphvizPrefix(const std::string &prefix) { + options->graphvizPrefix = prefix; +} + +const std::string &Printer::getGraphvizPrefix() const { + return options->graphvizPrefix; +} + } // namespace mod::graph \ No newline at end of file diff --git a/libs/libmod/src/mod/graph/Printer.hpp b/libs/libmod/src/mod/graph/Printer.hpp index faf305f..eb4abd4 100644 --- a/libs/libmod/src/mod/graph/Printer.hpp +++ b/libs/libmod/src/mod/graph/Printer.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_GRAPH_PRINTER_H -#define MOD_GRAPH_PRINTER_H +#ifndef MOD_GRAPH_PRINTER_HPP +#define MOD_GRAPH_PRINTER_HPP #include #include @@ -40,11 +40,11 @@ struct MOD_DECL Printer { void setReactionDefault(); // rst: .. function:: void disableAll() // rst: - // rst: Disable all special printing features. + // rst: Disable all special printing features. void disableAll(); // rst: .. function:: void enableAll() // rst: - // rst: Enable all special printing features, except typewriter font. + // rst: Enable all special printing features, except typewriter font. void enableAll(); // rst: .. function:: void setEdgesAsBonds(bool value) // rst: bool getEdgesAsBonds() const @@ -58,6 +58,12 @@ struct MOD_DECL Printer { // rst: Control whether vertices representing hydrogen atoms are collapsed into their neighbours labels. void setCollapseHydrogens(bool value); bool getCollapseHydrogens() const; + // rst: .. function:: void setRaiseIsotopes(bool value) + // rst: bool getRaiseIsotopes() const + // rst: + // rst: Control whether a vertex label prefix encoding an isotope is written as a superscript to the rest of the label. + void setRaiseIsotopes(bool value); + bool getRaiseIsotopes() const; // rst: .. function:: void setRaiseCharges(bool value) // rst: bool getRaiseCharges() const // rst: @@ -118,6 +124,24 @@ struct MOD_DECL Printer { // rst: Mirror internally computed coordinates in the y-axis. void setMirror(bool value); bool getMirror() const; + // rst: .. function:: void setWithGraphvizCoords(bool value) + // rst: bool getWithGraphvizCoords() const + // rst: + // rst: Do not use Open Babel for coordinate generation, but only the Graphviz fallback + // rst: during post-processing. + // rst: When setting this to `true` consider setting `setSimpleCarbons(false)` to avoid + // rst: misleading depictions due to colinear carbon chains. + void setWithGraphvizCoords(bool value); + bool getWithGraphvizCoords() const; +public: + // rst: .. function:: void setGraphvizPrefix(const std::string &prefix) + // rst: const std::string &getGraphvizPrefix() const + // rst: + // rst: Access the string that will be inserted into generated DOT files, + // rst: just after the graph declaration. + // rst: DOT files are only generated when `getWithGraphvizCoords()`. + void setGraphvizPrefix(const std::string &prefix); + const std::string &getGraphvizPrefix() const; private: std::unique_ptr options; }; @@ -125,4 +149,4 @@ struct MOD_DECL Printer { } // namespace mod::graph -#endif /* MOD_GRAPH_PRINTER_H */ \ No newline at end of file +#endif // MOD_GRAPH_PRINTER_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/graph/Union.cpp b/libs/libmod/src/mod/graph/Union.cpp index 94e5a98..1fa5490 100644 --- a/libs/libmod/src/mod/graph/Union.cpp +++ b/libs/libmod/src/mod/graph/Union.cpp @@ -9,10 +9,10 @@ #include #include #include +#include #include #include #include -#include #include #include @@ -23,13 +23,14 @@ struct Union::Pimpl { friend std::ostream &operator<<(std::ostream &s, const Pimpl &p) { s << "UnionGraph{"; bool first = true; - for(const auto &g : p.graphs) { + for(const auto &g: p.graphs) { if(first) first = false; else s << ", "; s << g->getName(); } return s << "}"; } + public: std::vector> graphs; lib::LabelledUnionGraph g; @@ -38,8 +39,11 @@ struct Union::Pimpl { Union::Union() : p(std::make_unique()) {} Union::Union(std::vector> graphs) { + for(const auto &g: graphs) + if(!g) throw LogicError("A graph is null."); + auto p = std::unique_ptr(new Pimpl{std::move(graphs), {}}); - for(const auto &gOther : p->graphs) + for(const auto &gOther: p->graphs) p->g.push_back(&gOther->getGraph().getLabelledGraph()); this->p = std::move(p); } @@ -109,6 +113,7 @@ Union::EdgeRange Union::edges() const { //------------------------------------------------------------------------------ MOD_GRAPHPIMPL_Define_Vertex(Union, UnionGraph, get_graph(this->g->p->g), g, /* VertexPrint */) + MOD_GRAPHPIMPL_Define_Vertex_Undirected(Union, get_graph(this->g->p->g), g) const std::string &Union::Vertex::getStringLabel() const { @@ -173,7 +178,7 @@ std::string Union::Vertex::printStereo(const Printer &p) const { const auto vInner = v.v; const auto gIdx = v.gIdx; const auto &gInner = g->p->graphs[gIdx]->getGraph(); - return lib::IO::Stereo::Write::summary( + return lib::Graph::Write::stereoSummary( gInner, vInner, *conf, p.getOptions(), graph.get_vertex_idx_offset(gIdx), " (" + boost::lexical_cast(*this)) + ")"; } diff --git a/libs/libmod/src/mod/graph/Union.hpp b/libs/libmod/src/mod/graph/Union.hpp index 0637503..8fe088f 100644 --- a/libs/libmod/src/mod/graph/Union.hpp +++ b/libs/libmod/src/mod/graph/Union.hpp @@ -51,6 +51,8 @@ struct MOD_DECL Union { // rst: .. function:: explicit Union(std::vector> graphs) // rst: // rst: Construct a graph representing the disjoint union of `graphs`. + // rst: + // rst: :throws LogicError: if a given graph is null. explicit Union(std::vector> graphs); Union(const Union &other); Union &operator=(const Union &other); diff --git a/libs/libmod/src/mod/graph/internal/Graph.cpp b/libs/libmod/src/mod/graph/internal/Graph.cpp index 3876177..c0006d6 100644 --- a/libs/libmod/src/mod/graph/internal/Graph.cpp +++ b/libs/libmod/src/mod/graph/internal/Graph.cpp @@ -2,10 +2,10 @@ #include #include +#include #include #include #include -#include namespace mod::graph::internal { @@ -43,7 +43,7 @@ std::shared_ptr makeGraph( } std::string writePDF(const lib::Graph::Single &g, const mod::lib::IO::Graph::Write::Options &options) { - return lib::IO::Graph::Write::pdf(g, options); + return lib::Graph::Write::pdf(g, options); } // LabelledGraph diff --git a/libs/libmod/src/mod/lib/Algorithm/ConnectedComponents.hpp b/libs/libmod/src/mod/lib/Algorithm/ConnectedComponents.hpp new file mode 100644 index 0000000..9d08c72 --- /dev/null +++ b/libs/libmod/src/mod/lib/Algorithm/ConnectedComponents.hpp @@ -0,0 +1,55 @@ +#ifndef MOD_LIB_ALGORITHM_CONNECTEDCOMPONENTS_HPP +#define MOD_LIB_ALGORITHM_CONNECTEDCOMPONENTS_HPP + +#include +#include + +namespace mod::lib { + +struct ConnectedComponents { + ConnectedComponents(int n) : components(n) { + for(int i = 0; i != n; ++i) + components[i] = i; + } + + int findRoot(int i) { + while(components[i] != i) + i = components[i]; + return i; + } + + void join(int a, int b) { + a = findRoot(a); + b = findRoot(b); + // always make the smallest the root + if(a < b) components[b] = a; + else if(b < a) components[a] = b; + } +public: + int finalize() { + int next = 0; + for(int i = 0; i != components.size(); ++i) { + // everything below i has been changed to component numbers + // and joining always goes downward, so just do a single root-finding step + const int root = components[i]; + if(root == i) { + components[i] = next; + ++next; + } else { + assert(root < i); + components[i] = components[root]; + } + } + return next; + } +public: + int operator[](int i) const { + return components[i]; + } +private: + std::vector components; +}; + +} // namespace mod::lib + +#endif // MOD_LIB_ALGORITHM_CONNECTEDCOMPONENTS_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Algorithm/Container.hpp b/libs/libmod/src/mod/lib/Algorithm/Container.hpp new file mode 100644 index 0000000..5cfba0d --- /dev/null +++ b/libs/libmod/src/mod/lib/Algorithm/Container.hpp @@ -0,0 +1,23 @@ +#ifndef MOD_LIB_ALGORITHM_CONTAINER_HPP +#define MOD_LIB_ALGORITHM_CONTAINER_HPP + +#include +#include + +namespace mod::lib { + +template +std::pair findAndInsert(Container &c, T &&t, Pred pred) { + auto iter = std::find_if(begin(c), end(c), [&](const T &tCand) { + return pred(tCand, t); + }); + if(iter != end(c)) return {*iter, false}; + else { + iter = c.insert(end(c), std::move(t)); + return {*iter, true}; + } +} + +} // namesapce mod::lib + +#endif // MOD_LIB_ALGORITHM_CONTAINER_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Algorithm.hpp b/libs/libmod/src/mod/lib/Algorithm/MultiDimSelector.hpp similarity index 86% rename from libs/libmod/src/mod/lib/Algorithm.hpp rename to libs/libmod/src/mod/lib/Algorithm/MultiDimSelector.hpp index af5c2f8..99ff9a6 100644 --- a/libs/libmod/src/mod/lib/Algorithm.hpp +++ b/libs/libmod/src/mod/lib/Algorithm/MultiDimSelector.hpp @@ -1,33 +1,8 @@ -#ifndef MOD_LIB_ALGORITHM_HPP -#define MOD_LIB_ALGORITHM_HPP - -#include - -#include -#include -#include - -// - findAndInsert -// - MultiDimSelector +#ifndef MOD_LIB_ALGORITHM_MULTIDIMSELECTOR_HPP +#define MOD_LIB_ALGORITHM_MULTIDIMSELECTOR_HPP namespace mod::lib { -template -std::pair findAndInsert(Container &c, T t, Pred pred) { - auto iter = std::find_if(begin(c), end(c), [&](const T & tCand) { - return pred(tCand, t); - }); - if(iter != end(c)) return std::make_pair(*iter, false); - else { - iter = c.insert(end(c), t); - return std::make_pair(*iter, true); - } -} - -//------------------------------------------------------------------------------ -// MultiDimSelector -//------------------------------------------------------------------------------ - template struct MultiDimSelector { using Self = MultiDimSelector; @@ -35,11 +10,10 @@ struct MultiDimSelector { using InnerIterator = decltype(std::declval().begin()); struct const_iterator; - friend class const_iterator; + friend struct const_iterator; public: - MultiDimSelector(std::size_t numPatterns, std::size_t numHosts, InnerRangeProvider morphismProvider) - : preDisabled(numPatterns, false) { + : preDisabled(numPatterns, false) { assert(numPatterns > 0); assert(numHosts > 0); morphisms.reserve(numPatterns); @@ -218,4 +192,4 @@ struct MultiDimSelector::const_iterator { } // namespace mod::lib -#endif // MOD_LIB_ALGORITHM_HPP \ No newline at end of file +#endif // MOD_LIB_ALGORITHM_MULTIDIMSELECTOR_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Algorithm/Point.hpp b/libs/libmod/src/mod/lib/Algorithm/Point.hpp new file mode 100644 index 0000000..9eacc85 --- /dev/null +++ b/libs/libmod/src/mod/lib/Algorithm/Point.hpp @@ -0,0 +1,27 @@ +#ifndef MOD_LIB_ALGORITHM_POINT_HPP +#define MOD_LIB_ALGORITHM_POINT_HPP + +#include +#include + +namespace mod::lib { + +constexpr double pi = 3.14159265358979323846; + +inline std::pair pointRotation(double xRaw, double yRaw, int rotation) { + double angle = rotation * pi / 180; + auto s = std::sin(angle); + auto c = std::cos(angle); + double x = xRaw * c - yRaw * s; + double y = xRaw * s + yRaw * c; + return std::make_pair(x, y); +} + +inline std::pair pointTransform(double xRaw, double yRaw, int rotation, bool mirror) { + if(mirror) xRaw *= -1; + return pointRotation(xRaw, yRaw, rotation); +} + +} // namespace mod::lib + +#endif // MOD_LIB_ALGORITHM_POINT_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Chem/MDL.cpp b/libs/libmod/src/mod/lib/Chem/MDL.cpp new file mode 100644 index 0000000..3387e4e --- /dev/null +++ b/libs/libmod/src/mod/lib/Chem/MDL.cpp @@ -0,0 +1,1881 @@ +#include "MDL.hpp" + +//#define BOOST_SPIRIT_X3_DEBUG + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +template +using Result = mod::lib::IO::Result; + +#ifdef BOOST_SPIRIT_X3_DEBUG + +namespace boost::spirit::x3::traits { + +template +struct print_attribute_debug, void> { + static void call(Out &out, const iterator_range &val) { + out << "boost::iterator_range<>"; + } +}; + +} // namespace boost::spirit::x3::traits + +#endif + +namespace mod::lib::Chem { +namespace { +// defined in the bottom +// Returns: line, hasLine +std::pair getLine(std::string_view &src); +unsigned int MDLValence(unsigned int elem, int q, unsigned int val); + +Result<> handleAction(lib::IO::Warnings &warnings, const Action action, std::string msg) { + switch(action) { + case Action::Ignore: + warnings.add(std::move(msg), false); + return {}; + case Action::Warn: + warnings.add(std::move(msg), true); + return {}; + case Action::Error: + return Result<>::Error(std::move(msg)); + } + __builtin_unreachable(); +} + + +struct Atom { + std::string symbol; + Isotope iso; + Charge chg; + bool radical = false; + int valence = -1; // optional in V3000 +public: // V3000 data + int id; + int aamap; +public: // derived data + AtomId aId; +}; + +struct Bond { + int src, tar; + std::string type; +public: // V3000 data + int id; +public: // derived data + BondType bondType; +}; + +struct MOL { + std::vector atoms; + std::vector bonds; + int lineFirst, lineLast; +public: + Result> convert(lib::IO::Warnings &warnings, const MDLOptions &options) &&{ + auto res = convertImpl(warnings, options); + if(!res) + return Result<>::Error(res.extractError() + "\nCould not convert MOL at line " + + std::to_string(lineFirst) + " to " + std::to_string(lineLast) + "."); + return res; + } +private: + Result> convertImpl(lib::IO::Warnings &warnings, const MDLOptions &options) { + lib::ConnectedComponents components(atoms.size()); + for(const Bond &b : bonds) + components.join(b.src - 1, b.tar - 1); + const auto numComponents = components.finalize(); + if(numComponents == 0) return Result<>::Error("Molecule has no atoms."); + + std::vector datas; + datas.resize(numComponents); + const auto onError = [&datas](std::string msg) -> Result<> { + for(auto &d : datas) d.reset(); + return Result<>::Error(std::move(msg)); + }; + for(int i = 0; i != numComponents; ++i) { + datas[i].g = std::make_unique(); + datas[i].pString = std::make_unique(*datas[i].g); + } + + for(int i = 0; i != atoms.size(); ++i) { + const Atom &a = atoms[i]; + const auto comp = components[i]; + const auto v = add_vertex(*datas[comp].g); + datas[comp].externalToInternalIds[i + 1] = get(boost::vertex_index_t(), *datas[comp].g, v); + if(a.symbol != "*") { + std::string s; + if(a.iso != Isotope()) + s += std::to_string(a.iso); + s += a.symbol; + s += chargeSuffix(a.chg); + if(a.radical) s += '.'; + datas[comp].pString->addVertex(v, std::move(s)); + } else { + if(a.iso != Isotope()) { + auto res = handleAction(warnings, options.onPatternIsotope, + "Pattern atom with ID " + std::to_string(i + 1) + + " has isotope " + boost::lexical_cast(a.iso) + "."); + if(!res) return onError(res.extractError()); + } + if(a.chg != Charge()) { + auto res = handleAction(warnings, options.onPatternCharge, + "Pattern atom with ID " + std::to_string(i + 1) + + " has charge " + boost::lexical_cast(a.chg) + "."); + if(!res) return onError(res.extractError()); + } + if(a.radical) { + auto res = handleAction(warnings, options.onPatternRadical, + "Pattern atom with ID " + std::to_string(i + 1) + " has radical."); + if(!res) return onError(res.extractError()); + } + datas[comp].pString->addVertex(v, "*"); + } + } + + std::vector valences(atoms.size(), 0); + std::vector numAromatic(atoms.size(), 0); + std::vector hasAnyIncident(atoms.size(), false); + for(const Bond &b : bonds) { + assert(components[b.src - 1] == components[b.tar - 1]); + const auto comp = components[b.src - 1]; + auto &g = *datas[comp].g; + const auto vSrc = vertices(g).first[datas[comp].externalToInternalIds.find(b.src)->second]; + const auto vTar = vertices(g).first[datas[comp].externalToInternalIds.find(b.tar)->second]; + if(const auto eQuery = edge(vSrc, vTar, g); eQuery.second) + return onError("Parallel edges in MOL file between atom " + std::to_string(b.src) + + " and " + std::to_string(b.tar) + "."); + const auto e = add_edge(vSrc, vTar, g).first; + datas[comp].pString->addEdge(e, b.type); + if(b.bondType == BondType::Invalid) { + hasAnyIncident[b.src - 1] = true; + hasAnyIncident[b.tar - 1] = true; + } else { + const int order = [&]() { + switch(b.bondType) { + case BondType::Single: + return 1; + case BondType::Double: + return 2; + case BondType::Triple: + return 3; + case BondType::Aromatic: + return 1; + case BondType::Invalid: + __builtin_unreachable(); + } + __builtin_unreachable(); + }(); + valences[b.src - 1] += order; + valences[b.tar - 1] += order; + if(b.bondType == BondType::Aromatic) { + ++numAromatic[b.src - 1]; + ++numAromatic[b.tar - 1]; + } + } + } + // Handle valence + for(int i = 0; i != atoms.size(); ++i) { + const Atom &a = atoms[i]; + int valence; + if(a.valence == -1) { + if(!options.addHydrogens) continue; + if(a.aId == AtomIds::Invalid) { + auto res = handleAction(warnings, options.onImplicitValenceOnAbstract, + "Implicit valence for atom " + std::to_string(i + 1) + + " can not be handled. Atom has no concrete atom symbol." + + + "\nSet onImplicitValenceOnAbstract=Ignore or Warn to not try to add hydrogens to such abstract atoms." + + " Or set addHydrogens=False to disable addition of hydrogens on all atoms."); + if(!res) return onError(res.extractError()); + } + valence = MDLValence(a.aId, a.chg, valences[i]); + if(a.radical) --valence; + } else { + valence = a.valence; + } + if(valence > valences[i]) { + const auto comp = components[i]; + auto &g = *datas[comp].g; + const auto v = vertices(g).first[datas[comp].externalToInternalIds.find(i + 1)->second]; + for(int j = valences[i]; j != valence; ++j) { + const auto vH = add_vertex(g); + datas[comp].pString->addVertex(vH, "H"); + const auto eH = add_edge(v, vH, g).first; + datas[comp].pString->addEdge(eH, "-"); + } + } + } + return std::move(datas); // TODO: remove std::move when C++20/P1825R0 is available + } +}; + +template +Result getUInt(Iter first, const Iter last, Desc &&desc) { + const auto origFirst = first; + for(; first != last && *first == ' '; ++first); + if(first == last) + return Result<>::Error(std::string("Expected ") + desc + ". Got '" + std::string(origFirst, last) + "'."); + int res = 0; + for(; first != last; ++first) { + const char c = *first; + if(c < '0' || c > '9') + return Result<>::Error(std::string("Expected ") + desc + ". Got '" + std::string(origFirst, last) + "'."); + res *= 10; + res += c - '0'; + } + return res; +} + +template +Result getInt(Iter first, const Iter last, Desc &&desc) { + const auto origFirst = first; + for(; first != last && *first == ' '; ++first); + if(first == last) + return Result<>::Error(std::string("Expected ") + desc + ". Got '" + std::string(origFirst, last) + "'."); + bool neg = false; + int res = 0; + if(*first == '-') { + neg = true; + ++first; + if(first == last) + return Result<>::Error(std::string("Expected ") + desc + ". Got '" + std::string(origFirst, last) + "'."); + } + for(; first != last; ++first) { + const char c = *first; + if(c < '0' || c > '9') + return Result<>::Error(std::string("Expected ") + desc + ". Got '" + std::string(origFirst, last) + "'."); + res *= 10; + res += c - '0'; + } + if(neg) return -res; + else return res; +} + +Result<> handleRAD(lib::IO::Warnings &warnings, Atom &a, const int radical, const MDLOptions &options) { + switch(radical) { + case 0: + a.radical = false; + return {}; + case 1: + break; + case 2: + a.radical = true; + return {}; + case 3: + case 4: + case 5: + case 6: + break; + default: + return Result<>::Error("Radical out of range. Got " + std::to_string(radical) + ", must be in 0 to 6."); + } + const Action act = [radical, &options]() { + switch(radical) { + case 1: + return options.onRAD1; + case 3: + return options.onRAD3; + case 4: + return options.onRAD4; + case 5: + return options.onRAD5; + case 6: + return options.onRAD6; + } + __builtin_unreachable(); + }(); + return handleAction(warnings, act, + "Radical value " + std::to_string(radical) + " is currently not supported in atom."); +} + +#define FETCH_LINE(var, desc) \ + std::string_view var; \ + { \ + bool hasLine; \ + if(std::tie(var, hasLine) = getLine(src); !hasLine) \ + return Result<>::Error("Expected " + std::string(desc) + ". Got nothing."); \ + } + +Result +parseMOLV2000(lib::IO::Warnings &warnings, std::string_view &src, const MDLOptions &options, + const std::string_view counts, int &lineCount) { + // Counts line: + // aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv + // - a: #atoms + // - b: #bonds + // - l: #atom lists [query] + // - f: obsolete + // - c: chiral flag + // - s: #stext entries + // - x, r, p, i: obsolete + // - m: #properties lines, but is now just always 999 + + auto numAtoms = getUInt(counts.begin(), counts.begin() + 3, "V2000 atom count"); + if(!numAtoms) return std::move(numAtoms); + auto numBonds = getUInt(counts.begin() + 3, counts.begin() + 6, "V2000 bond count"); + if(!numBonds) return std::move(numBonds); + ++lineCount; + + MOL mol; + mol.atoms.reserve(*numAtoms); + mol.bonds.reserve(*numBonds); + + // Atom block: + for(int i = 0; i != *numAtoms; ++i) { + // xxxxx.xxxxyyyyy.yyyyzzzzz.zzzz aaaddcccssshhhbbbvvvHHHrrriiimmmnnneee + // x, y, z: coordinates + // a 31:34 - entry in periodic table + // - L for atom list + // - A, Q, * for unspecified atom + // - LP for lone pair + // d 34:36 mass difference, i.e., isotope stuff. Can be overwritten by an ISO line. + // c 36:39 charge. Can be overwritten by CHG and RAD lines. + // - 0: neutral + // - 1,2,3: +3,+2,+1 + // - 4: "doublet radical" + // - 5,6,7: -1,-2,-3 + // s 39:42 atom stereo parity. Ignored. + // h 42:45 hydrogen count. Only query. + // b 45:48 stereo care box. Only query. + // v 48:51 valence, the total valence. Missing bonds implies implicit hydrogens. + // - 0: nothing special + // - 1-14: that valence + // - 15: 0 + // H 51:54 H0 designator + // r 54:57 not used + // i 57:60 not used + // m 60:63 atom-atom map: 1 to numAtoms + // n 63:66 inversion/retention flag + // e 66:69 exact change flag + FETCH_LINE(line, "V2000 atom block line") + constexpr int Len = 69; + if(line.size() != Len) + return Result<>::Error("Wrong length of V2000 atom block line. Expected length " + + std::to_string(Len) + ", got " + std::to_string(line.size()) + + ".\nLine: >>>" + std::string(line) + "<<<"); + Atom atom; + // ========================================================================= + auto fSymbol = line.begin() + 31; + auto lSymbol = line.begin() + 34; + for(; fSymbol != lSymbol && *fSymbol == ' '; ++fSymbol); + for(; fSymbol != lSymbol && *(lSymbol - 1) == ' '; --lSymbol); + atom.symbol = std::string(fSymbol, lSymbol); + if(atom.symbol == "LP") { + // consider it as abstract + } else if(atom.symbol == "A" || atom.symbol == "Q" || atom.symbol == "*") { + atom.symbol = "*"; + } else if(atom.symbol == "L") { + // consider it as abstract + } else { + const auto aId = atomIdFromSymbol(atom.symbol); + if(aId == AtomIds::Invalid && !options.allowAbstract) + return Result<>::Error("Unknown atom symbol. Got '" + atom.symbol + "'." + + " Set allowAbstract=True to allow it."); + atom.aId = aId; + } + // ========================================================================= + auto massDiff = getInt(line.begin() + 34, line.begin() + 36, "V2000 mass difference in atom block line"); + if(!massDiff) return std::move(massDiff); + if(*massDiff != 0) { + if(atom.aId != AtomIds::Invalid) { + atom.iso = Isotope(atom.aId + *massDiff); + } else { + auto res = handleAction(warnings, options.onV2000AbstractISO, + "Can not compute isotope from mass difference and non-concrete atom in V2000 atom block."); + if(!res) return res; + } + } + // ========================================================================= + auto charge = getUInt(line.begin() + 36, line.begin() + 39, "V2000 charge in atom block line"); + if(!charge) return std::move(charge); + switch(*charge) { + case 0: + break; + case 1: + case 2: + case 3: + case 5: + case 6: + case 7: + atom.chg = Charge(4 - *charge); + break; + case 4: { + auto res = handleAction(warnings, options.onV2000Charge4, + "Charge '4' (doublet radical) not yet supported in V2000 atom block line."); + if(!res) return res; + break; + } + default: + return Result<>::Error("Unknown charge in V2000 atom block line. Got " + std::to_string(*charge) + "."); + } + // ========================================================================= + auto valence = getUInt(line.begin() + 48, line.begin() + 51, "V2000 valence in atom block line"); + if(!valence) return std::move(valence); + if(*valence == 0) atom.valence = -1; + else if(*valence >= 1 && *valence <= 14) atom.valence = *valence; + else if(*valence == 15) atom.valence = 0; + else return Result<>::Error("Unknown valence in V2000 atom block line. Got " + std::to_string(*valence) + "."); + // ========================================================================= + auto aamapRes = getUInt(line.begin() + 60, line.begin() + 63, "V2000 atom-atom map in atom block line"); + if(!aamapRes) return std::move(aamapRes); + atom.aamap = *aamapRes; + // ========================================================================= + mol.atoms.push_back(std::move(atom)); + ++lineCount; + } // for each atom line + // Bond block: + for(int i = 0; i != *numBonds; ++i) { + // 111222tttsssxxxrrrccc + FETCH_LINE(line, "V2000 bond block line") + constexpr int Len = 7 * 3; + if(line.size() != Len) + return Result<>::Error("Wrong length of V2000 atom block line. Expected length " + + std::to_string(Len) + ", got " + std::to_string(line.size()) + + ".\nLine: >>>" + std::string(line) + "<<<"); + Bond bond; + // ========================================================================= + { // src, tar + auto src = getUInt(line.begin(), line.begin() + 3, "atom id in V2000 bond block line"); + if(!src) return std::move(src); + if(*src < 1 || *src > mol.atoms.size()) + return Result<>::Error("Atom id in V2000 bond block line is out of range. Got " + + std::to_string(*src) + ", but should be in 1 to " + + std::to_string(mol.atoms.size()) + "."); + auto tar = getUInt(line.begin() + 3, line.begin() + 6, "atom id in V2000 bond block line"); + if(!tar) return std::move(tar); + if(*tar < 1 || *tar > mol.atoms.size()) + return Result<>::Error("Atom id in V2000 bond block line is out of range. Got " + + std::to_string(*tar) + ", but should be in 1 to " + + std::to_string(mol.atoms.size()) + "."); + if(*src == *tar) + return Result<>::Error("Atom IDs for bond endpoints in V2000 bond line are the same. Got " + + std::to_string(*src) + " and " + std::to_string(*tar) + "."); + bond.src = *src; + bond.tar = *tar; + } // src, tar + if(auto order = getUInt(line.begin() + 6, line.begin() + 9, "bond order in V2000 bond block line")) { + switch(*order) { + case 1: + bond.type = "-"; + bond.bondType = BondType::Single; + break; + case 2: + bond.type = "="; + bond.bondType = BondType::Double; + break; + case 3: + bond.type = "#"; + bond.bondType = BondType::Triple; + break; + case 4: + bond.type = ":"; + bond.bondType = BondType::Aromatic; + break; + case 5: + case 6: + case 7: { + auto res = handleAction(warnings, options.onUnsupportedQueryBondType, + "Unsupported bond type in V2000 bond block line. Got " + + std::to_string(*order) + "."); + switch(options.onUnsupportedQueryBondType) { + case Action::Ignore: + case Action::Warn: + bond.type = "_Q" + std::to_string(bond.src) + "_" + std::to_string(bond.tar) + + "_" + std::to_string(*order); + bond.bondType = BondType::Invalid; + assert(res); + break; + case Action::Error: + assert(!res); + return res; + } + break; + } + case 8: + bond.type = "*"; + bond.bondType = BondType::Invalid; + break; + default: + return Result<>::Error("Bond type in V2000 bond block line is out of range. Got " + + std::to_string(*order) + ", but should be in 1 to 8."); + } + } else return std::move(order); + mol.bonds.push_back(std::move(bond)); + ++lineCount; + } + // =========================================================================== + // Properties + auto handleChgRad = [&mol, chargeRadicalCleared = false]( + std::string_view line, const std::string type, const std::string desc, auto output) mutable -> Result<> { + if(!chargeRadicalCleared) { + for(Atom &a : mol.atoms) { + a.chg = Charge(); + a.radical = false; + } + chargeRadicalCleared = true; + } + if(line.size() < 9) + return Result<>::Error("V2000 " + type + " line too short. Line of length " + + std::to_string(line.size()) + " was >>>" + std::string(line) + "<<<"); + auto numEntries = getUInt(line.begin() + 6, line.begin() + 9, "number of " + desc + "s in V2000 CHG line"); + if(!numEntries) return std::move(numEntries); + const auto len = 9 + *numEntries * 4 * 2; + if(line.size() != len) + return Result<>::Error("V2000 " + type + " line has wrong size. Expected " + std::to_string(len) + + ", got line of length " + std::to_string(line.size()) + + " >>>" + std::string(line) + "<<<"); + for(int i = 0; i != *numEntries; ++i) { + const auto f = line.begin() + 9 + i * 8; + const auto l = f + 4; + auto aId = getUInt(f + 1, l, "atom id in V2000 " + type + " line"); + if(!aId) return std::move(aId); + if(*aId < 1 || *aId > mol.atoms.size()) + return Result<>::Error("Atom id in V2000 " + type + " line out of range. Got " + + std::to_string(*aId) + ", must be in 1 to " + + std::to_string(mol.atoms.size()) + "."); + auto val = getInt(f + 4 + 1, l + 4, desc + " in V2000 " + type + " line"); + if(!val) return std::move(val); + auto res = output(mol.atoms[*aId - 1], *val); + if(!res) return res; + } + return {}; + }; + while(true) { + // hax to support certain bad writers that don't use 'M END' + if(!src.empty() && src[0] == '$') + break; // emulate that 'M END' was read + + // and now the normal parsing + FETCH_LINE(line, "V2000 property line or 'M END'") + const auto cmd = line.substr(0, 6); + if(cmd == "M END") break; + if(cmd == "M CHG") { + auto res = handleChgRad(line, "CHG", "charge", [&](Atom &a, int charge) { + a.chg = Charge(charge); + return Result<>(); + }); + if(!res) return res; + } else if(cmd == "M RAD") { + auto res = handleChgRad(line, "RAD", "radical", [&](Atom &a, int rad) { + return handleRAD(warnings, a, rad, options); + }); + if(!res) return res; + } else if(cmd == "M ISO") { + auto numEntries = getUInt(line.begin() + 6, line.begin() + 9, "number of entries in V2000 ISO line"); + if(!numEntries) return std::move(numEntries); + for(int i = 0; i != *numEntries; ++i) { + const auto f = line.begin() + 9 + i * 8; + const auto l = f + 4; + auto aId = getUInt(f + 1, l, "atom id in V2000 ISO line"); + if(!aId) return std::move(aId); + if(*aId < 1 || *aId > mol.atoms.size()) + return Result<>::Error( + "Atom id in V2000 ISO line out of range. Got " + std::to_string(*aId) + ", must be in 1 to " + + std::to_string(mol.atoms.size()) + "."); + auto val = getInt(f + 4 + 1, l + 4, "ISO value in V2000 ISO line"); + if(!val) return std::move(val); + mol.atoms[*aId - 1].iso = Isotope(*val); + } + } else if(line.substr(0, 3) == "A ") { // Atom Alias + if(line.size() != 6) + return Result<>::Error("V2000 alias line has wrong size. Expected 6, got line of length " + + std::to_string(line.size()) + " >>>" + std::string(line) + "<<<"); + auto aId = getUInt(line.begin() + 3, line.begin() + 6, "atom ID in V2000 atom alias line"); + if(!aId) return std::move(aId); + if(*aId < 1 || *aId > mol.atoms.size()) + return Result<>::Error("Atom id in V2000 alias line out of range. Got " + + std::to_string(*aId) + ", must be in 1 to " + + std::to_string(mol.atoms.size()) + "."); + FETCH_LINE(labelLine, "alias text line in V2000 atom alias") + if(options.applyV2000AtomAliases) { + mol.atoms[*aId - 1].symbol = labelLine; + mol.atoms[*aId - 1].aId = AtomIds::Invalid; + mol.atoms[*aId - 1].valence = 0; + } + } else { + if(options.fullyIgnoreV2000UnhandledKnownProperty) { + if(line.substr(0, 3) == "V ") { // Atom Value + } else if(line.substr(0, 3) == "G ") { // Group Abbreviation + } else if(cmd == "M RBC") { // Ring Bond Count [Query] + } else if(cmd == "M SUB") { // Substitution Count [Query] + } else if(cmd == "M UNS") { // Unsaturated Atom [Query] + } else if(cmd == "M LIN") { // Link Atom [Query] + } else if(cmd == "M ALS") { // Atom List [Query] + } else if(cmd == "M APO") { // Attachment Point [Rgroup] + } else if(cmd == "M AAL") { // Atom Attachment Order [Rgroup] + } else if(cmd == "M RGP") { // Rgroup Label Location [Rgroup] + } else if(cmd == "M LOG") { // Rgroup Logic, Unsatisfied Sites, Range of Occurrence [Rgroup] + } else if(cmd == "M STY") { // Sgroup Type [Sgroup] + } else if(cmd == "M SST") { // Sgroup Subtype [Sgroup] + } else if(cmd == "M SLB") { // Sgroup Labels [Sgroup] + } else if(cmd == "M SCN") { // Sgroup Connectivity [Sgroup] + } else if(cmd == "M SDS") { // Sgroup Expansion [Sgroup] + } else if(cmd == "M SAL") { // Sgroup Atom List [Sgroup] + } else if(cmd == "M SBL") { // Sgroup Bond List [Sgroup] + } else if(cmd == "M SPA") { // Multiple Group Parent Atom List [Sgroup] + } else if(cmd == "M SMT") { // Sgroup Subscript [Sgroup] + } else if(cmd == "M CRS") { // Sgroup Correspondence [Sgroup] + } else if(cmd == "M SDI") { // Sgroup Display Information [Sgroup] + } else if(cmd == "M SBV") { // Abbreviation Sgroup Bond and Vector Information [Sgroup] + } else if(cmd == "M SDT") { // Data Sgroup Field Description [Sgroup] + } else if(cmd == "M SDD") { // Data Sgroup Display Information [Sgroup] + } else if(cmd == "M SCD") { // Data Sgroup Data [Sgroup] (initial lines) + } else if(cmd == "M SED") { // Data Sgroup Data [Sgroup] (final line) + } else if(cmd == "M SPL") { // Sgroup Hierarchy Information [Sgroup] + } else if(cmd == "M SCN") { // Sgroup Component Numbers [Sgroup] + } else if(cmd == "M $3D") { // 3D Feature Properties [3D] + } else if(cmd == "M PXA") { // Phantom Extra Atom + } else if(cmd == "M SAP") { // Abbreviation Sgroup Attachment Point + } else if(cmd == "M SCL") { // Abbreviation Sgroup Class + } else if(cmd == "M SCL") { // Abbreviation Sgroup Class + } else if(cmd == "M REG") { // Large REGNO + // the small REGNO is in line 2 + } else if(cmd == "M SBT") { // Sgroup Bracket Style + } else if(cmd == "M SBT") { // Sgroup Bracket Style + } else { + auto res = handleAction(warnings, options.onV2000UnhandledProperty, + "Property line in MOL V2000 not supported. Line was >>>" + std::string(line) + + "<<<"); + if(!res) return res; + } + } else { + auto res = handleAction(warnings, options.onV2000UnhandledProperty, + "Property line in MOL V2000 not supported. Line was >>>" + std::string(line) + + "<<<"); + if(!res) return res; + } + } + ++lineCount; + } + ++lineCount; + return mol; +} + +struct V3000LineResult { + std::vector lines; + std::vector args; + std::unique_ptr fullLine; // a pointer so it doesn't move if small +public: + friend std::ostream &operator<<(std::ostream &s, const V3000LineResult &res) { + if(res.lines.size() == 1) { + s << "Line >>>" << res.lines.front() << "<<<"; + } else { + s << "Lines >>>\n"; + for(const auto line : res.lines) + s << line << '\n'; + s << "<<<"; + } + return s; + } +}; + +namespace argparser { + +using Iter = const char *; +using PosIter = IO::PositionIter; +using IterRange = boost::iterator_range; + +const auto basicValue = x3::rule{"non-string value"} + = x3::lexeme[+(x3::char_ - x3::char_("\" =()"))]; +const auto string = x3::rule{"string"} + = x3::lexeme[x3::char_('"') + > +((x3::char_ - x3::char_('"')) | x3::lit("\"\"")) + > x3::lit('"')]; +const auto val = x3::rule{"value"} + = string | basicValue; +const auto listvalInner = x3::rule{"list values"} + = +val; +const auto listval = x3::rule{"list"} + = x3::lit('(') > listvalInner > ')'; +const auto optval = x3::rule{"optional value"} + = listval | val; +const auto posarg = x3::rule{"positional argument"} + = x3::raw[val >> !x3::char_('=')]; +const auto optarg = x3::rule{"optional argument"} + = x3::raw[basicValue > '=' > optval]; +const auto args = x3::rule>{"arguments"} + = *posarg > *optarg; + +} // namespace argparser + +template +Result<> parseV3000Args(V3000LineResult &res, const std::string_view line, Desc &&desc) { + std::vector args; + try { + IO::parse(line.begin(), line.end(), argparser::args, args, false, x3::ascii::space); + } catch(const lib::IO::ParsingError &e) { + return Result<>::Error("Parsing error in " + std::string(desc) + ".\n" + e.msg); + } + res.args.reserve(args.size()); + for(const auto &arg : args) + res.args.emplace_back(arg.begin().base(), arg.end().base() - arg.begin().base()); + if(res.args.empty()) return Result<>::Error("Unexpected empty " + std::string(desc) + "."); + return {}; +} + +template +Result parseV3000Line(std::string_view &src, Desc &&desc) { + V3000LineResult res; + FETCH_LINE(line, desc) + if(line.find("M V30 ") != 0) + return Result<>::Error("Expected " + std::string(desc) + ", staring with 'M V30 '." + + "\nLine: >>>" + std::string(line) + "<<<"); + res.lines.push_back(line); + if(line.back() == '-') { + res.fullLine.reset(new std::string(line.substr(7, line.size() - 8))); + while(true) { + FETCH_LINE(line, desc) + if(line.find("M V30 ") != 0) + return Result<>::Error("Expected " + std::string(desc) + ", staring with 'M V30 '." + + "\nLine: >>>" + std::string(line) + "<<<"); + res.lines.push_back(line); + if(line.back() == '-') { + *res.fullLine += line.substr(7, line.size() - 8); + } else { + *res.fullLine += line.substr(7, line.size() - 7); + break; + } + } + if(auto subres = parseV3000Args(res, *res.fullLine, desc); !subres) return subres; + } else { + if(auto subres = parseV3000Args(res, line.substr(7), desc); !subres) return subres; + } + return std::move(res); // TODO: remove std::move when C++20/P1825R0 is available +} + +Result +parseMOLV3000(lib::IO::Warnings &warnings, std::string_view &src, const MDLOptions &options, int &lineCount) { + if(auto line = parseV3000Line(src, "V3000 BEGIN CTAB line")) { + if(line->args.size() < 2 || line->args[0] != "BEGIN" || line->args[1] != "CTAB") + return Result<>::Error("Expected beginning of V3000 CTAB block ('M V30 BEGIN CTAB').\n" + + boost::lexical_cast(*line)); + lineCount += line->lines.size(); + } else return std::move(line); + auto counts = parseV3000Line(src, "V3000 counts line"); + if(!counts) return std::move(counts); + if(counts->args.front() != "COUNTS" || counts->args.size() < 6) + return Result<>::Error("Expected V3000 counts line.\n" + boost::lexical_cast(*counts)); + auto numAtoms = getUInt(counts->args[1].begin(), counts->args[1].end(), "atom count"); + if(!numAtoms) return std::move(numAtoms); + + auto numBonds = getUInt(counts->args[2].begin(), counts->args[2].end(), "bond count"); + if(!numBonds) return std::move(numBonds); + lineCount += counts->lines.size(); + + MOL mol; + mol.atoms.reserve(*numAtoms); + mol.bonds.reserve(*numBonds); + + // Atom block: + if(auto line = parseV3000Line(src, "V3000 BEGIN ATOM line")) { + if(line->args.size() < 2 || line->args[0] != "BEGIN" || line->args[1] != "ATOM") + return Result<>::Error("Expected V3000 BEGIN ATOM line.\n" + boost::lexical_cast(*line)); + lineCount += line->lines.size(); + } else return std::move(line); + + std::map lineFromAtomId; + for(int i = 0; i != *numAtoms; ++i) { + auto line = parseV3000Line(src, "V3000 atom line"); + if(!line) return std::move(line); + if(line->args.size() < 6) + return Result<>::Error("Too few arguments in V3000 atom line.\n" + boost::lexical_cast(*line)); + Atom atom; + + auto id = getUInt(line->args[0].begin(), line->args[0].end(), "atom ID"); + if(!id) return std::move(id); + if(const auto iter = lineFromAtomId.find(*id); iter != end(lineFromAtomId)) + return Result<>::Error("Duplicate atom ID " + std::to_string(*id) + ". Also defined in line " + + std::to_string(iter->second) + ".\n" + boost::lexical_cast(*line)); + lineFromAtomId.emplace(*id, lineCount); + atom.id = *id; + if(atom.id == 0) return Result<>::Error("Atom ID must be non-zero.\n" + boost::lexical_cast(*line)); + + atom.symbol = line->args[1]; + if(atom.symbol[0] == '"') + return Result<>::Error("V3000 quoted atom types not supported.\n" + boost::lexical_cast(*line)); + if(atom.symbol == "A" || atom.symbol == "Q" || atom.symbol == "*") { + // TODO: Q is really "not C or H" + atom.symbol = "*"; + } else { + const auto aId = atomIdFromSymbol(atom.symbol); + if(aId == AtomIds::Invalid && !options.allowAbstract) + return Result<>::Error("Unknown atom symbol. Got '" + atom.symbol + "'." + + " Set allowAbstract=True to allow it."); + atom.aId = aId; + } + + auto aamap = getUInt(line->args[5].begin(), line->args[5].end(), "atom aamap ID"); + if(!aamap) return std::move(aamap); + atom.aamap = *aamap; + + for(int iArg = 6; iArg != line->args.size(); ++iArg) { + const auto arg = line->args[iArg]; + const auto eqPos = arg.find('='); + if(eqPos == std::string_view::npos) + return Result<>::Error("Optional argument for V3000 atom does not contain '='. Got '" + std::string(arg) + + "'.\n" + boost::lexical_cast(*line)); + const auto argName = arg.substr(0, eqPos); + const auto argVal = arg.substr(eqPos + 1); + if(argName == "CHG") { + auto charge = getInt(argVal.begin(), argVal.end(), "V3000 atom CHG"); + if(!charge) return std::move(charge); + atom.chg = Charge(*charge); + } else if(argName == "RAD") { + auto radical = getInt(argVal.begin(), argVal.end(), "V3000 atom RAD"); + if(!radical) return std::move(radical); + if(auto res = handleRAD(warnings, atom, *radical, options); !res) return res; + } else if(argName == "VAL") { + auto val = getInt(argVal.begin(), argVal.end(), "V3000 atom VAL"); + if(!val) return std::move(val); + if(*val == 0) atom.valence = -1; + else if(*val == -1) atom.valence = 0; + else atom.valence = 0; + } else if(argName == "MASS") { + auto iso = getUInt(argVal.begin(), argVal.end(), "V3000 atom MASS"); + if(!iso) return std::move(iso); + atom.iso = Isotope(*iso); + } else { + auto res = handleAction(warnings, options.onV3000UnhandledAtomProperty, + "Unknown optional argument to V3000 atom. Got '" + std::string(arg) + "'.\n"); + if(!res) return res; + } + } + mol.atoms.push_back(std::move(atom)); + lineCount += line->lines.size(); + } // for each atom + if(auto line = parseV3000Line(src, "V3000 END ATOM line")) { + if(line->args.size() < 2 || line->args[0] != "END" || line->args[1] != "ATOM") + return Result<>::Error("Expected V3000 END ATOM line.\n" + boost::lexical_cast(*line)); + lineCount += line->lines.size(); + } else return std::move(line); + + // Bond block: + // it's mandatory, but some leave it out if empty + const auto srcBeforeBondBlock = src; + auto bondBlockBegin = parseV3000Line(src, "V3000 BEGIN BOND line"); + if(!bondBlockBegin) return std::move(bondBlockBegin); + if(bondBlockBegin->args.size() < 2 || bondBlockBegin->args[0] != "BEGIN" || + bondBlockBegin->args[1] != "BOND") { + if(*numBonds > 0) { + return Result<>::Error( + "Expected V3000 BEGIN BOND line.\n" + boost::lexical_cast(*bondBlockBegin)); + } else { + src = srcBeforeBondBlock; + } + } else { // actual bond block + lineCount += bondBlockBegin->lines.size(); + + std::map lineFromBondId; + for(int i = 0; i != *numBonds; ++i) { + auto line = parseV3000Line(src, "V3000 bond line"); + if(!line) return std::move(line); + if(line->args.size() < 4) + return Result<>::Error("Too few arguments in V3000 bond line.\n" + boost::lexical_cast(*line)); + + Bond bond; + + auto id = getUInt(line->args[0].begin(), line->args[0].end(), "bond ID"); + if(!id) return std::move(id); + if(const auto iter = lineFromBondId.find(*id); iter != end(lineFromBondId)) + return Result<>::Error("Duplicate bond ID " + std::to_string(*id) + + ". Also defined in line " + std::to_string(iter->second) + ".\n" + + boost::lexical_cast(*line)); + lineFromBondId.emplace(*id, lineCount); + bond.id = *id; + if(bond.id == 0) + return Result<>::Error("Bond ID must be non-zero.\n" + boost::lexical_cast(*line)); + + // do the src and tar before the type to have them available for the type handling + auto src = getUInt(line->args[2].begin(), line->args[2].end(), "first atom ID for bond"); + if(!src) return std::move(src); + if(*src < 1 || *src > mol.atoms.size()) + return Result<>::Error("First atom ID in V3000 bond line is out of range. Got " + + std::to_string(*src) + ", but should be in 1 to " + + std::to_string(mol.atoms.size()) + "."); + auto tar = getUInt(line->args[3].begin(), line->args[3].end(), "second atom ID for bond"); + if(!tar) return std::move(tar); + if(*tar < 1 || *tar > mol.atoms.size()) + return Result<>::Error("Second atom ID in V3000 bond line is out of range. Got " + + std::to_string(*src) + ", but should be in 1 to " + + std::to_string(mol.atoms.size()) + "."); + if(*src == *tar) + return Result<>::Error("Atom IDs for bond endpoints in V3000 bond line are the same. Got " + + std::to_string(*src) + " and " + std::to_string(*tar) + "."); + bond.src = *src; + bond.tar = *tar; + + auto type = getUInt(line->args[1].begin(), line->args[1].end(), "bond type"); + if(!type) return std::move(type); + switch(*type) { + case 1: + bond.type = "-"; + bond.bondType = BondType::Single; + break; + case 2: + bond.type = "="; + bond.bondType = BondType::Double; + break; + case 3: + bond.type = "#"; + bond.bondType = BondType::Triple; + break; + case 4: + bond.type = ":"; + bond.bondType = BondType::Aromatic; + break; + case 5: + case 6: + case 7: { + auto res = handleAction(warnings, options.onUnsupportedQueryBondType, + "Unsupported bond type in V3000 bond block line. Got " + + std::to_string(*type) + "."); + switch(options.onUnsupportedQueryBondType) { + case Action::Ignore: + case Action::Warn: + bond.type = "_Q" + std::to_string(*src) + "_" + std::to_string(*tar) + "_" + std::to_string(*type); + bond.bondType = BondType::Invalid; + assert(res); + break; + case Action::Error: + assert(!res); + return res; + } + break; + } + case 8: + bond.type = "*"; + bond.bondType = BondType::Invalid; + break; + case 9: + case 10: + return Result<>::Error("Unsupported bond type in V3000 bond type. Got " + std::to_string(*type) + "."); + default: + return Result<>::Error("Bond type in V3000 bond line is out of range. Got " + std::to_string(*type) + + ", but should be in 1 to 10."); + } + + mol.bonds.push_back(std::move(bond)); + lineCount += line->lines.size(); + } // for each bond + if(auto line = parseV3000Line(src, "V3000 END BOND line")) { + if(line->args.size() < 2 || line->args[0] != "END" || line->args[1] != "BOND") + return Result<>::Error("Expected V3000 BOND END line.\n" + boost::lexical_cast(*line)); + lineCount += line->lines.size(); + } else return std::move(line); + } // end of bond block + + // Optional blocks: skip + while(true) { + auto line = parseV3000Line(src, "V3000 END CTAB line or BEGIN line"); + if(!line) return std::move(line); + if(line->args.size() >= 2 && line->args[0] == "END" && line->args[1] == "CTAB") { + lineCount += line->lines.size(); + break; + } + // should be an optional block + if(line->args.size() < 2 || line->args[0] != "BEGIN") + return Result<>::Error( + "Expected V3000 END CTAB line or beginning of block.\n" + boost::lexical_cast(*line)); + const auto block = line->args[1]; + lineCount += line->lines.size(); + while(true) { + auto line = parseV3000Line(src, "V3000 END line or content line"); + if(!line) return std::move(line); + if(line->args[0] == "BEGIN") + return Result<>::Error( + "Nested blocks not supported in optional V3000 blocks.\n" + boost::lexical_cast(*line)); + lineCount += line->lines.size(); + if(line->args.size() >= 2 && line->args[0] == "END" && line->args[1] == block) + break; + } + } + return mol; +} + +lib::IO::Result +parseMOL(lib::IO::Warnings &warnings, std::string_view &src, const MDLOptions &options, int &lineCount) { + const int lineFirst = lineCount; + // Name + FETCH_LINE(name, "name line in MOL"); + // May not contain: + // - $MDL (RGfile) + // - $$$$ (SDfile record separator) + // - $RXN (rxnfile) + // - $RDFILE (RDfile headers). + for(auto &&tag :{"$MDL", "$$$$", "$RXN", "$RDFILE"}) + if(name.find(tag) != name.npos) + return Result<>::Error(std::string("Expected name line in MOL. Found '") + tag + "'."); + ++lineCount; + + // Various stuff: + // IIPPPPPPPPMMDDYYHHmmddSSssssssssssEEEEEEEEEEEERRRRRR + // or blank + // - I 0: 2 user's first and last initials + // - P 2:10 program name + // - M,D,Y,H,m 10:20 date/time + // - d 20:22 dimensional codes + // - S 22:24 scaling factor + // - s 24:34 scaling factor, float + // - E 34:46 energy + // - R 46:52 internal registry number + FETCH_LINE(line2, "'line 2 in MOL'"); + // Note: we do not parse that line. + ++lineCount; + + // Comment + FETCH_LINE(comment, "comment line in MOL"); + ++lineCount; + + // Counts (with version) + FETCH_LINE(counts, "counts line in MOL"); + constexpr int CountsLen = 11 * 3 + 6; + if(counts.size() > CountsLen || counts.size() < 6) + return Result<>::Error("Wrong length of counts line in MOL. Expected length " + std::to_string(CountsLen) + + ", got " + std::to_string(counts.size()) + ".\nLine: >>>" + std::string(counts) + "<<<"); + std::string version; + if(counts.size() < CountsLen) { + warnings.add( + "Counts line in MOL too short. Assuming version=' V2000'. Expected length " + std::to_string(CountsLen) + + ", got " + std::to_string(counts.size()) + ".\nLine: >>>" + std::string(counts) + "<<<\n"); + version = " V2000"; + } else { + version = std::string(counts.end() - 6, counts.end()); + } + if(version == " V2000") { + auto res = parseMOLV2000(warnings, src, options, counts, lineCount); + if(res) { + res->lineFirst = lineFirst; + res->lineLast = lineCount; + return res; + } else { + return Result<>::Error(res.extractError() + "\nMOL started at line " + std::to_string(lineFirst) + "."); + } + } else if(version == " V3000") { + ++lineCount; + auto res = parseMOLV3000(warnings, src, options, lineCount); + if(res) { + res->lineFirst = lineFirst; + res->lineLast = lineCount; + } else { + return Result<>::Error(res.extractError() + "\nMOL started at line " + std::to_string(lineFirst) + "."); + } + FETCH_LINE(endLine, "V3000 end line ('M END')") + if(endLine.substr(0, 6) != "M END") + return Result<>::Error("Expected V3000 end line ('M END').\nLine: >>>" + std::string(endLine) + "<<<"); + ++lineCount; + return res; + } else { + return Result<>::Error("Expected version ' V2000' or ' V3000'. Got '" + version + "'."); + } +} + +Result> +parseSD(lib::IO::Warnings &warnings, std::string_view &src, const MDLOptions &options, int &lineCount) { + std::vector res; + while(true) { + auto mol = parseMOL(warnings, src, options, lineCount); + if(!mol) return std::move(mol); + res.push_back(std::move(*mol)); + { + std::string_view line; + bool hasLine; + if(std::tie(line, hasLine) = getLine(src); !hasLine) + return Result<>::Error("Expected SD property line or '$$$$'. Got nothing."); + if(line == "$$$$") { + ++lineCount; + if(src.empty()) break; + continue; + } + if(line.empty()) return Result<>::Error("Expected SD property line or '$$$$'. Got empty line."); + // skip properties + if(line.front() != '>') + return Result<>::Error("Expected SD property line or '$$$$'. Got >>>" + std::string(line) + "<<<"); + } + while(true) { + ++lineCount; + std::string_view line; + bool hasLine; + if(std::tie(line, hasLine) = getLine(src); !hasLine) + return Result<>::Error("Expected SD property line or blank line. Got nothing."); + if(line.empty()) break; + if(line.front() != '>') + return Result<>::Error("Expected SD property line or blank line. Got >>>" + std::string(line) + "<<<"); + } + ++lineCount; + { + std::string_view line; + bool hasLine; + if(std::tie(line, hasLine) = getLine(src); !hasLine) + return Result<>::Error("Expected '$$$$' to end MOL and properties in SD. Got nothing."); + if(line != "$$$$") + return Result<>::Error( + "Expected '$$$$' to end MOL and properties in SD. Got >>>" + std::string(line) + "<<<"); + } + if(src.empty()) break; + } + return res; +} + +} // namespace + +lib::IO::Result> +readMDLMOL(lib::IO::Warnings &warnings, std::string_view src, const MDLOptions &options) { + int lineCount = 1; + auto res = parseMOL(warnings, src, options, lineCount); + if(!res) return Result<>::Error(res.extractError() + "\nError at line " + std::to_string(lineCount) + "."); + std::string line; + while(!src.empty()) { + const auto[line, hasLine] = getLine(src); + if(line.empty()) continue; + return Result<>::Error("Expected end of input. Got line >>>" + std::string(line) + + "<<<\nError at line " + std::to_string(lineCount) + "."); + } + return std::move(*res).convert(warnings, options); +} + +Result>> +readMDLSD(lib::IO::Warnings &warnings, std::string_view src, const MDLOptions &options) { + std::vector> data; + int lineCount = 1; + auto res = parseSD(warnings, src, options, lineCount); + if(!res) return Result<>::Error(res.extractError() + "\nError at line " + std::to_string(lineCount) + "."); + std::string_view line; + bool hasLine; + if(std::tie(line, hasLine) = getLine(src); hasLine) + return Result<>::Error("Expected end of input. Got line >>>" + std::string(line) + + "<<<\nError at line " + std::to_string(lineCount) + "."); + data.reserve(res->size()); + for(auto &mol : *res) { + auto d = std::move(mol).convert(warnings, options); + if(!d) return std::move(d); + data.push_back(std::move(*d)); + } + return std::move(data); // TODO: remove std::move when C++20/P1825R0 is available +} + +namespace { + +std::pair getLine(std::string_view &src) { + if(src.empty()) return {{}, false}; + + const char *first = src.begin(); + int count = 0; + for(const char *next = first; next != src.end(); ++next) { + switch(*next) { + case '\n': + src.remove_prefix(count + 1); + return {std::string_view(first, count), true}; + case '\r': + ++next; + if(next != src.end() && *next == '\n') { + src.remove_prefix(count + 2); + } else { + src.remove_prefix(count + 1); + } + return {std::string_view(first, count), true}; + default: + ++count; + } + } + // no trailing newline + src.remove_prefix(count); + return {std::string_view(first, count), true}; +} + +// The following is a copy from Open Babel, of most of src/formats/mdlvalence.h +// Only formatting and a few syntactic things have been changed. + +/********************************************************************** +mdlvalence.h - Implement MDL valence model. + +Copyright (C) 2012 by NextMove Software + +This file is part of the Open Babel project. +For more information, see + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation version 2 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + ***********************************************************************/ + +/* Return the implicit MDL valence for element "elem" with charge "q". */ +unsigned int MDLValence(unsigned int elem, int q, unsigned int val) { + switch(elem) { + case 1: // H + case 3: // Li + case 11: // Na + case 19: // K + case 37: // Rb + case 55: // Cs + case 87: // Fr + if(q == 0 && val <= 1) + return 1; + break; + + case 4: // Be + case 12: // Mg + case 20: // Ca + case 38: // Sr + case 56: // Ba + case 88: // Ra + switch(q) { + case 0: + if(val <= 2) return 2; + break; + case 1: + if(val <= 1) return 1; + break; + } + break; + + case 5: // B + switch(q) { + case -4: + if(val <= 1) return 1; + break; + case -3: + if(val <= 2) return 2; + break; + case -2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case -1: + if(val <= 4) return 4; + break; + case 0: + if(val <= 3) return 3; + break; + case 1: + if(val <= 2) return 2; + break; + case 2: + if(val <= 1) return 1; + break; + } + break; + + case 6: // C + switch(q) { + case -3: + if(val <= 1) return 1; + break; + case -2: + if(val <= 2) return 2; + break; + case -1: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 0: + if(val <= 4) return 4; + break; + case 1: + if(val <= 3) return 3; + break; + case 2: + if(val <= 2) return 2; + break; + case 3: + if(val <= 1) return 1; + break; + } + break; + + case 7: // N + switch(q) { + case -2: + if(val <= 1) return 1; + break; + case -1: + if(val <= 2) return 2; + break; + case 0: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 1: + if(val <= 4) return 4; + break; + case 2: + if(val <= 3) return 3; + break; + case 3: + if(val <= 2) return 2; + break; + case 4: + if(val <= 1) return 1; + break; + } + break; + + case 8: // O + switch(q) { + case -1: + if(val <= 1) return 1; + break; + case 0: + if(val <= 2) return 2; + break; + case 1: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 2: + if(val <= 4) return 4; + break; + case 3: + if(val <= 3) return 3; + break; + case 4: + if(val <= 2) return 2; + break; + case 5: + if(val <= 1) return 1; + break; + } + break; + + case 9: // F + switch(q) { + case 0: + if(val <= 1) return 1; + break; + case 1: + if(val <= 2) return 2; + break; + case 2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 3: + if(val <= 4) return 4; + break; + case 4: + if(val <= 3) return 3; + break; + case 5: + if(val <= 2) return 2; + break; + case 6: + if(val <= 1) return 1; + break; + } + break; + + case 13: // Al + switch(q) { + case -4: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -3: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case -2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case -1: + if(val <= 4) return 4; + break; + case 0: + if(val <= 3) return 3; + break; + case 1: + if(val <= 2) return 2; + break; + case 2: + if(val <= 1) return 1; + break; + } + break; + + case 14: // Si + switch(q) { + case -3: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -2: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case -1: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 0: + if(val <= 4) return 4; + break; + case 1: + if(val <= 3) return 3; + break; + case 2: + if(val <= 2) return 2; + break; + case 3: + if(val <= 1) return 1; + break; + } + break; + + case 15: // P + switch(q) { + case -2: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -1: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 0: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 1: + if(val <= 4) return 4; + break; + case 2: + if(val <= 3) return 3; + break; + case 3: + if(val <= 2) return 2; + break; + case 4: + if(val <= 1) return 1; + break; + } + break; + + case 16: // S + switch(q) { + case -1: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case 0: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 1: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 2: + if(val <= 4) return 4; + break; + case 3: + if(val <= 3) return 3; + break; + case 4: + if(val <= 2) return 2; + break; + case 5: + if(val <= 1) return 1; + break; + } + break; + + case 17: // Cl + switch(q) { + case 0: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case 1: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 3: + if(val <= 4) return 4; + break; + case 4: + if(val <= 3) return 3; + break; + case 5: + if(val <= 2) return 2; + break; + case 6: + if(val <= 1) return 1; + break; + } + break; + + case 31: // Ga + switch(q) { + case -4: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -3: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case -2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case -1: + if(val <= 4) return 4; + break; + case 0: + if(val <= 3) return 3; + break; + case 2: + if(val <= 1) return 1; + break; + } + break; + + case 32: // Ge + switch(q) { + case -3: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -2: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case -1: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 0: + if(val <= 4) return 4; + break; + case 1: + if(val <= 3) return 3; + break; + case 3: + if(val <= 1) return 1; + break; + } + break; + + case 33: // As + switch(q) { + case -2: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -1: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 0: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 1: + if(val <= 4) return 4; + break; + case 2: + if(val <= 3) return 3; + break; + case 4: + if(val <= 1) return 1; + break; + } + break; + + case 34: // Se + switch(q) { + case -1: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case 0: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 1: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 2: + if(val <= 4) return 4; + break; + case 3: + if(val <= 3) return 3; + break; + case 5: + if(val <= 1) return 1; + break; + } + break; + + case 35: // Br + switch(q) { + case 0: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case 1: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 3: + if(val <= 4) return 4; + break; + case 4: + if(val <= 3) return 3; + break; + case 6: + if(val <= 1) return 1; + break; + } + break; + + case 49: // In + switch(q) { + case -4: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -3: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case -2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case -1: + if(val <= 2) return 2; + if(val <= 4) return 4; + break; + case 0: + if(val <= 3) return 3; + break; + case 2: + if(val <= 1) return 1; + break; + } + break; + + case 50: // Sn + case 82: // Pb + switch(q) { + case -3: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -2: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case -1: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 0: + if(val <= 2) return 2; + if(val <= 4) return 4; + break; + case 1: + if(val <= 3) return 3; + break; + case 3: + if(val <= 1) return 1; + break; + } + break; + + case 51: // Sb + case 83: // Bi + switch(q) { + case -2: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -1: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 0: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 1: + if(val <= 2) return 2; + if(val <= 4) return 4; + break; + case 2: + if(val <= 3) return 3; + break; + case 4: + if(val <= 1) return 1; + break; + } + break; + + case 52: // Te + case 84: // Po + switch(q) { + case -1: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case 0: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 1: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 2: + if(val <= 2) return 2; + if(val <= 4) return 4; + break; + case 3: + if(val <= 3) return 3; + break; + case 5: + if(val <= 1) return 1; + break; + } + break; + + case 53: // I + case 85: // At + switch(q) { + case 0: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case 1: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case 2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case 3: + if(val <= 2) return 2; + if(val <= 4) return 4; + break; + case 4: + if(val <= 3) return 3; + break; + case 6: + if(val <= 1) return 1; + break; + } + break; + + case 81: // Tl + switch(q) { + case -4: + if(val <= 1) return 1; + if(val <= 3) return 3; + if(val <= 5) return 5; + if(val <= 7) return 7; + break; + case -3: + if(val <= 2) return 2; + if(val <= 4) return 4; + if(val <= 6) return 6; + break; + case -2: + if(val <= 3) return 3; + if(val <= 5) return 5; + break; + case -1: + if(val <= 2) return 2; + if(val <= 4) return 4; + break; + case 0: + if(val <= 1) return 1; + if(val <= 3) return 3; + break; + } + break; + + } + return val; +} + +} // namespace +} // namespace mod::lib::Chem \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Chem/MDL.hpp b/libs/libmod/src/mod/lib/Chem/MDL.hpp new file mode 100644 index 0000000..8099710 --- /dev/null +++ b/libs/libmod/src/mod/lib/Chem/MDL.hpp @@ -0,0 +1,19 @@ +#ifndef MOD_LIB_CHEM_MDL_HPP +#define MOD_LIB_CHEM_MDL_HPP + +#include +#include + +#include +#include + +namespace mod::lib::Chem { + +auto readMDLMOL(lib::IO::Warnings &warnings, std::string_view src, + const MDLOptions &options) -> lib::IO::Result>; +auto readMDLSD(lib::IO::Warnings &warnings, std::string_view src, + const MDLOptions &options) -> lib::IO::Result>>; + +} // namespace mod::lib::Chem + +#endif // MOD_LIB_CHEM_MDL_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Chem/MoleculeUtil.cpp b/libs/libmod/src/mod/lib/Chem/MoleculeUtil.cpp index fe16721..8cb7df1 100644 --- a/libs/libmod/src/mod/lib/Chem/MoleculeUtil.cpp +++ b/libs/libmod/src/mod/lib/Chem/MoleculeUtil.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -375,4 +376,13 @@ char bondToChar(BondType bt) { } } +std::string chargeSuffix(Charge c) { + std::string s; + if(c == 0) return s; + if(c > 1 || c < -1) s += boost::lexical_cast(std::abs(c)); + if(c < 0) s += '-'; + else s += '+'; + return s; +} + } // namespace mod::lib::Chem \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Chem/MoleculeUtil.hpp b/libs/libmod/src/mod/lib/Chem/MoleculeUtil.hpp index d79df81..dbc8075 100644 --- a/libs/libmod/src/mod/lib/Chem/MoleculeUtil.hpp +++ b/libs/libmod/src/mod/lib/Chem/MoleculeUtil.hpp @@ -29,16 +29,18 @@ void markSpecialAtomsUsed(std::vector &used); std::string symbolFromAtomId(AtomId atomId); void appendSymbolFromAtomId(std::string &s, AtomId atomId); char bondToChar(BondType bt); +std::string chargeSuffix(Charge c); + double exactMass(AtomId a, Isotope i); // implemented in Mass.cpp constexpr double electronMass = 0.000548580; constexpr double GasConstant = 1.98720425864083e-3; // kcal/(K * mol) template -bool isCollapsible(const typename boost::graph_traits::vertex_descriptor v, - const Graph &g, - const AtomDataT &atomData, - const EdgeDataT &edgeData, - const HasImportantStereo &hasImportantStereo) { +bool isCollapsibleHydrogen(const typename boost::graph_traits::vertex_descriptor v, + const Graph &g, + const AtomDataT &atomData, + const EdgeDataT &edgeData, + const HasImportantStereo &hasImportantStereo) { if(!isCleanHydrogen(atomData(v))) return false; if(out_degree(v, g) != 1) return false; const auto e = *out_edges(v, g).first; diff --git a/libs/libmod/src/mod/lib/Chem/OBabel.cpp b/libs/libmod/src/mod/lib/Chem/OBabel.cpp index 7b8f5a3..7243802 100644 --- a/libs/libmod/src/mod/lib/Chem/OBabel.cpp +++ b/libs/libmod/src/mod/lib/Chem/OBabel.cpp @@ -2,12 +2,11 @@ #ifdef MOD_HAVE_OPENBABEL -#include #include #include #include -#include +#include #include #include @@ -38,14 +37,9 @@ struct OBMolHandle::Pimpl { std::map from; }; -OBMolHandle::OBMolHandle() {} - -OBMolHandle::OBMolHandle(OBMolHandle &&o) : p(std::move(o.p)) {} - -OBMolHandle &OBMolHandle::operator=(OBMolHandle &&o) { - p = std::move(o.p); - return *this; -} +OBMolHandle::OBMolHandle() = default; +OBMolHandle::OBMolHandle(OBMolHandle &&o) = default; +OBMolHandle &OBMolHandle::operator=(OBMolHandle &&o) = default; OBMolHandle::OBMolHandle(std::unique_ptr p) : p(std::move(p)) {} @@ -59,7 +53,7 @@ void OBMolHandle::setCoordinates(const std::vector &x, const std::vector assert(x.size() == y.size()); auto &mol = const_cast (*p->m); // becuase bah mol.BeginModify(); - for(std::size_t i = 0; i < x.size(); ++i) { + for(std::size_t i = 0; i != x.size(); ++i) { auto *aPtr = mol.GetAtomById(i); if(!aPtr) continue; aPtr->SetVector(x[i], y[i], 0); @@ -70,27 +64,21 @@ void OBMolHandle::setCoordinates(const std::vector &x, const std::vector mol.EndModify(); } -double OBMolHandle::getMolarMass() const { - double weight = p->m->GetMolWt(false); - unsigned int intWeight = weight * 1024; - return static_cast (intWeight) / 1024; -} - -double OBMolHandle::getEnergy() const { +double OBMolHandle::getEnergy(bool verbose) const { auto &mol = const_cast (*p->m); // becuase bah - if(getConfig().obabel.verbose.get()) - std::cout << "OBabel energy: '" << mol.GetTitle() << "'" + if(verbose) + std::cout << "OBMolHandle::getEnergy: '" << mol.GetTitle() << "'" << "\t" << mol.NumAtoms() << " atoms\t" << mol.NumBonds() << " bonds" << std::endl; // code originally from GGL OpenBabel::OBForceField *pFF = nullptr; - unsigned int conformers = 25; - unsigned int geomSteps = 100; + constexpr int conformers = 25; + constexpr int geomSteps = 100; { // gen3D stuff - if(getConfig().obabel.verbose.get()) std::cout << "\tgen 3D" << std::endl; + if(verbose) std::cout << "OBMolHandle::getEnergy: gen 3D" << std::endl; pFF = OpenBabel::OBForceField::FindForceField("MMFF94"); assert(pFF); OpenBabel::OBBuilder builder; @@ -98,10 +86,7 @@ double OBMolHandle::getEnergy() const { mol.SetDimension(3); const bool setupOK = pFF->Setup(mol); - if(!setupOK) { - std::cout << "ERROR: Setup of Open Babel forcefield failed, using 0 as energy." << std::endl; - return 0; - } + if(!setupOK) throw FatalError("OBMolHandle::getEnergy: ERROR: Setup of Open Babel forcefield failed."); // Since we only want a rough geometry, use distance cutoffs for VDW, Electrostatics pFF->EnableCutOff(true); @@ -116,7 +101,7 @@ double OBMolHandle::getEnergy() const { } { // conformer stuff - if(getConfig().obabel.verbose.get()) std::cout << "\tconformer" << std::endl; + if(verbose) std::cout << "OBMolHandle::getEnergy: conformer" << std::endl; pFF = OpenBabel::OBForceField::FindForceField("MMFF94"); assert(pFF); const bool setupOK = pFF->Setup(mol); @@ -129,8 +114,8 @@ double OBMolHandle::getEnergy() const { } // calculate energy - if(getConfig().obabel.verbose.get()) std::cout << "\tenergy" << std::endl; - double energy = pFF->Energy(false); + if(verbose) std::cout << "OBMolHandle::getEnergy: energy" << std::endl; + const double energy = pFF->Energy(false); // calculate appropriate unit scaling std::string unit = pFF->GetUnit(); @@ -162,23 +147,23 @@ double OBMolHandle::getCoordScaling() const { return p->m->NumBonds() / sumLengths; // == 1 / (length / count); } -bool OBMolHandle::hasAtom(unsigned int id) const { +bool OBMolHandle::hasAtom(int id) const { return p->m->GetAtomById(id) != nullptr; } -double OBMolHandle::getAtomX(unsigned int id) const { - auto *a = p->m->GetAtomById(id); +double OBMolHandle::getAtomX(int id) const { + const auto *a = p->m->GetAtomById(id); assert(a); return a->GetX(); } -double OBMolHandle::getAtomY(unsigned int id) const { - auto *a = p->m->GetAtomById(id); +double OBMolHandle::getAtomY(int id) const { + const auto *a = p->m->GetAtomById(id); assert(a); return a->GetY(); } -lib::IO::Graph::Write::EdgeFake3DType OBMolHandle::getBondFake3D(unsigned int idSrc, unsigned int idTar) const { +lib::IO::Graph::Write::EdgeFake3DType OBMolHandle::getBondFake3D(int idSrc, int idTar) const { // mimic what OpenBabel is doing in openbabel/depict/depict.cpp using Type = lib::IO::Graph::Write::EdgeFake3DType; auto *aSrc = p->m->GetAtomById(idSrc); @@ -204,33 +189,33 @@ void generateCoordinates(OpenBabel::OBMol &mol) { OpenBabel::OBConversion conv; // because the constructor apparently takes care of lib loading OpenBabel::OBOp *op = OpenBabel::OBOp::FindType("gen2D"); if(!op) { - std::cerr << "MØD Error: Could not load Open Babel gen2D operation." - " This can happen if the MØD library or an extension library was loaded with dlopen," - " but without the RTLD_GLOBAL flag. If you are executing from Python then you can insert\n" - "\timport ctypes\n" - "\timport sys\n" - "\tsys.setdlopenflags(sys.getdlopenflags() | ctypes.RTLD_GLOBAL)\n" - "before importing any modules. If this does not work, please open an issue.\n"; - std::exit(1); + std::string msg = "MØD Error: Could not load Open Babel gen2D operation." + " This can happen if the MØD library or an extension library was loaded with dlopen," + " but without the RTLD_GLOBAL flag. If you are executing from Python then you can insert\n" + "\timport ctypes\n" + "\timport sys\n" + "\tsys.setdlopenflags(sys.getdlopenflags() | ctypes.RTLD_GLOBAL)\n" + "before importing any modules. If this does not work, please open an issue."; + throw FatalError(std::move(msg)); } bool res = op->Do(&mol); if(!res) MOD_ABORT; } template -OBMolHandle makeOBMolImpl(const Graph &g, - std::function::vertex_descriptor)> atomData, - std::function::edge_descriptor)> bondData, - MayCollapse mayCollapse, - const bool ignoreDuplicateBonds, const bool withCoordinates, Callback callback) { - std::unique_ptr mol(new OpenBabel::OBMol()); +OBMolHandle makeOBMolImpl( + const Graph &g, + std::function::vertex_descriptor)> atomData, + std::function::edge_descriptor)> bondData, + MayCollapse mayCollapse, + const bool ignoreDuplicateBonds, const bool withCoordinates, Callback callback) { + auto mol = std::make_unique(); mol->BeginModify(); // add all visible vertices - for(const auto v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { const auto vId = get(boost::vertex_index_t(), g, v); - if(mayCollapse(v, g, atomData, bondData)) continue; + if(mayCollapse(v)) continue; OpenBabel::OBAtom *atom = mol->NewAtom(vId); assert(atomData(v).getAtomId() != AtomIds::Invalid); atom->SetAtomicNum(atomData(v).getAtomId()); @@ -239,13 +224,13 @@ OBMolHandle makeOBMolImpl(const Graph &g, } // add all edges - for(const auto e : asRange(edges(g))) { + for(const auto e: asRange(edges(g))) { const auto vSrc = source(e, g); const auto vTar = target(e, g); const auto srcId = get(boost::vertex_index_t(), g, vSrc); const auto tarId = get(boost::vertex_index_t(), g, vTar); - if(mayCollapse(vSrc, g, atomData, bondData)) continue; - if(mayCollapse(vTar, g, atomData, bondData)) continue; + if(mayCollapse(vSrc)) continue; + if(mayCollapse(vTar)) continue; OpenBabel::OBAtom *src = mol->GetAtomById(srcId), *tar = mol->GetAtomById(tarId); assert(src); assert(tar); @@ -268,7 +253,7 @@ OBMolHandle makeOBMolImpl(const Graph &g, std::abort(); } OpenBabel::OBBond *bond = mol->GetBond(src, tar); - if(!ignoreDuplicateBonds && bond) MOD_ABORT; + if(!ignoreDuplicateBonds && bond) std::abort(); if(!bond) { bond = mol->NewBond(); bond->SetBegin(src); @@ -297,7 +282,7 @@ void convertStereo(const Graph &g, std::function::vertex_descriptor)> hasImportantStereo, const bool withHydrogen, const Stereo &pStereo, OpenBabel::OBMol &mol) { using Emb = lib::Stereo::EmbeddingEdge; - for(const auto v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { const auto vId = get(boost::vertex_index_t(), g, v); auto *atom = mol.GetAtomById(vId); if(!atom) continue; @@ -318,7 +303,7 @@ void convertStereo(const Graph &g, const auto e = emb.getEdge(v, g); const auto vAdj = target(e, g); const auto vIdAdj = get(boost::vertex_index_t(), g, vAdj); - if(!withHydrogen && isCollapsible(vAdj, g, atomData, bondData, hasImportantStereo)) { + if(!withHydrogen && isCollapsibleHydrogen(vAdj, g, atomData, bondData, hasImportantStereo)) { return OpenBabel::OBStereo::ImplicitRef; } else { return vIdAdj; @@ -338,9 +323,9 @@ void convertStereo(const Graph &g, case 1: break; // hmm, does OpenBabel like this? case 2: - continue; // otherwise OpenBabel seg. faults (GetVector on a nullptr Atom) + continue; // otherwise, OpenBabel seg. faults (GetVector on a nullptr Atom) case 3: - continue; // otherwise OpenBabel loops infinitely + continue; // otherwise, OpenBabel loops infinitely } // { // debug // std::cout << "OBabelTetra(" << config.center << "): " << config.from; @@ -367,9 +352,9 @@ OBMolHandle makeOBMol(const lib::Graph::GraphType &g, std::function bondData, std::function hasImportantStereo, const bool withHydrogen, const lib::Graph::PropStereo *pStereo) { - const auto mayCollapse = [&](const auto v, const auto &g, const auto &atomData, const auto &bondData) { + const auto mayCollapse = [&](const auto v) { if(withHydrogen) return false; - return isCollapsible(v, g, atomData, bondData, hasImportantStereo); + return isCollapsibleHydrogen(v, g, atomData, bondData, hasImportantStereo); }; auto res = makeOBMolImpl(g, atomData, bondData, mayCollapse, false, true, [&](OpenBabel::OBMol &mol) { if(pStereo) convertStereo(g, atomData, bondData, hasImportantStereo, withHydrogen, *pStereo, mol); @@ -379,54 +364,42 @@ OBMolHandle makeOBMol(const lib::Graph::GraphType &g, std::tuple makeOBMol( const lib::Rules::LabelledRule &lr, - std::function atomData, - std::function bondData, - std::function atomDataLeft, - std::function bondDataLeft, - std::function atomDataRight, - std::function bondDataRight, + std::function cgAtom, + std::function cgBond, + std::function leftAtom, + std::function leftBond, + std::function rightAtom, + std::function rightBond, + std::function mayCollapse, const bool withHydrogen) { - OBMolHandle obMol, obMolLeft, obMolRight; - const auto hasImportantStereo = [&](const auto v) { - if(!has_stereo(lr)) return false; - const auto &g = get_graph(lr); - const auto m = g[v].membership; - if(m != lib::Rules::Membership::Right && !get_stereo(get_labelled_left(lr))[v]->morphismDynamicOk()) return true; - if(m != lib::Rules::Membership::Left && !get_stereo(get_labelled_right(lr))[v]->morphismDynamicOk()) return true; - return false; - }; - const auto mayCollapse = [&](const auto v, const auto &g, const auto &atomData, const auto &bondData) { + const auto mayCollapseCG = [mayCollapse, withHydrogen](const auto v) { if(withHydrogen) return false; - if(membership(lr, v) != lib::Rules::Membership::Context) - return false; - for(const auto e : asRange(out_edges(v, get_graph(lr)))) { - if(membership(lr, e) != lib::Rules::Membership::Context) - return false; - } - return isCollapsible(v, g, atomData, bondData, hasImportantStereo); + return mayCollapse(v); }; - obMol = makeOBMolImpl(get_graph(lr), atomData, bondData, mayCollapse, true, true, - [](OpenBabel::OBMol &mol) {}); - const auto &lgLeft = get_labelled_left(lr); - const auto &lgRight = get_labelled_right(lr); - const auto hasImportantStereoLeft = [&](const auto v) { - const auto &lg = get_labelled_left(lr); - if(!has_stereo(lg)) return false; - return !get_stereo(lg)[v]->morphismDynamicOk(); + const auto mayCollapseL = [&mayCollapseCG](const auto vL) { + // TODO: map to CG + return mayCollapseCG(vL); }; - const auto hasImportantStereoRight = [&](const auto v) { - const auto &lg = get_labelled_right(lr); - if(!has_stereo(lg)) return false; - return !get_stereo(lg)[v]->morphismDynamicOk(); + const auto mayCollapseR = [&mayCollapseCG](const auto vR) { + // TODO: map to CG + return mayCollapseCG(vR); }; - obMolLeft = makeOBMolImpl( - get_graph(lgLeft), atomDataLeft, bondDataLeft, mayCollapse, false, false, - [&](OpenBabel::OBMol &mol) { + OBMolHandle obMol = makeOBMolImpl(get_graph(lr), cgAtom, cgBond, mayCollapseCG, true, true, + [](OpenBabel::OBMol &mol) {}); + const auto &lgLeft = get_labelled_left(lr); + OBMolHandle obMolLeft = makeOBMolImpl( + get_graph(lgLeft), leftAtom, leftBond, mayCollapseL, false, false, + [&lgLeft, &lr, leftAtom, leftBond, &obMol](OpenBabel::OBMol &mol) { + const auto hasImportantStereoLeft = [&lr](const auto v) { + const auto &lg = get_labelled_left(lr); + if(!has_stereo(lg)) return false; + return !get_stereo(lg)[v]->morphismDynamicOk(); + }; const auto &g = get_graph(lgLeft); if(has_stereo(lr)) - convertStereo(g, atomDataLeft, bondDataLeft, hasImportantStereoLeft, true, + convertStereo(g, leftAtom, leftBond, hasImportantStereoLeft, true, get_stereo(lgLeft), mol); - for(const auto v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { const auto vId = get(boost::vertex_index_t(), g, v); const auto *aBase = obMol.p->m->GetAtomById(vId); if(!aBase) continue; @@ -436,14 +409,20 @@ std::tuple makeOBMol( } mol.SetDimension(2); }); - obMolRight = makeOBMolImpl( - get_graph(lgRight), atomDataRight, bondDataRight, mayCollapse, false, false, - [&](OpenBabel::OBMol &mol) { + const auto &lgRight = get_labelled_right(lr); + OBMolHandle obMolRight = makeOBMolImpl( + get_graph(lgRight), rightAtom, rightBond, mayCollapseR, false, false, + [&lr, &lgRight, rightAtom, rightBond, &obMol](OpenBabel::OBMol &mol) { + const auto hasImportantStereoRight = [&lr](const auto v) { + const auto &lg = get_labelled_right(lr); + if(!has_stereo(lg)) return false; + return !get_stereo(lg)[v]->morphismDynamicOk(); + }; const auto &g = get_graph(lgRight); if(has_stereo(lr)) - convertStereo(g, atomDataRight, bondDataRight, hasImportantStereoRight, true, + convertStereo(g, rightAtom, rightBond, hasImportantStereoRight, true, get_stereo(lgRight), mol); - for(const auto v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { const auto vId = get(boost::vertex_index_t(), g, v); const auto *aBase = obMol.p->m->GetAtomById(vId); if(!aBase) continue; diff --git a/libs/libmod/src/mod/lib/Chem/OBabel.hpp b/libs/libmod/src/mod/lib/Chem/OBabel.hpp index 80a0a61..79456c1 100644 --- a/libs/libmod/src/mod/lib/Chem/OBabel.hpp +++ b/libs/libmod/src/mod/lib/Chem/OBabel.hpp @@ -3,12 +3,15 @@ #include #include + #ifndef MOD_HAVE_OPENBABEL #include #else + #include #include + #endif namespace mod { @@ -16,60 +19,61 @@ struct AtomData; enum class BondType; } // namespace mod namespace mod::lib::IO::Graph::Write { -enum class EdgeFake3DType; +enum struct EdgeFake3DType; } // namespace mod::lib::IO::Graph::Write namespace mod::lib::Graph { struct PropStereo; struct Single; } // namespace mod::lib::Graph namespace mod::lib::Rules { -struct PropStereoCore; +struct PropStereo; } // namespace mod::lib::Rules namespace mod::lib::Chem { #ifndef MOD_HAVE_OPENBABEL -#define MOD_NO_OPENBABEL_ERROR \ - std::cout << "Call to '" << __FUNCTION__ << "' failed." << std::endl; \ - std::cout << "Open Babel features are not available. Rebuild with Open Babel enabled." << std::endl; +#define MOD_NO_OPENBABEL_ERROR_STR \ + std::string("Call to '") + __func__ + "' failed.\n" \ + + "Open Babel features are not available. Rebuild with Open Babel enabled." #else struct OBMolHandle { struct Pimpl; public: OBMolHandle(); - OBMolHandle(OBMolHandle&&); - OBMolHandle &operator=(OBMolHandle&&); + OBMolHandle(OBMolHandle &&); + OBMolHandle &operator=(OBMolHandle &&); OBMolHandle(std::unique_ptr p); ~OBMolHandle(); explicit operator bool() const; void setCoordinates(const std::vector &x, const std::vector &y); public: - double getMolarMass() const; // will truncate by *1024, cast to unsigned int, /1024 - double getEnergy() const; + double getEnergy(bool verbose) const; void print2Dsvg(std::ostream &s) const; public: double getCoordScaling() const; - bool hasAtom(unsigned int id) const; - double getAtomX(unsigned int id) const; - double getAtomY(unsigned int id) const; - lib::IO::Graph::Write::EdgeFake3DType getBondFake3D(unsigned int idSrc, unsigned int idTar) const; + bool hasAtom(int id) const; + double getAtomX(int id) const; + double getAtomY(int id) const; + lib::IO::Graph::Write::EdgeFake3DType getBondFake3D(int idSrc, int idTar) const; public: std::unique_ptr p; }; OBMolHandle copyOBMol(const OBMolHandle &mol); OBMolHandle makeOBMol(const lib::Graph::GraphType &g, - std::function atomData, - std::function bondData, - std::function hasImportantStereo, - bool withHydrogen, const lib::Graph::PropStereo *pStereo); -std::tuple makeOBMol(const lib::Rules::LabelledRule &lr, - std::function atomData, - std::function bondData, - std::function atomDataLeft, - std::function bondDataLeft, - std::function atomDataRight, - std::function bondDataRight, + std::function atomData, + std::function bondData, + std::function hasImportantStereo, + bool withHydrogen, const lib::Graph::PropStereo *pStereo); +std::tuple makeOBMol( + const lib::Rules::LabelledRule &lr, + std::function cgAtom, + std::function cgBond, + std::function leftAtom, + std::function leftBond, + std::function rightAtom, + std::function rightBond, + std::function mayCollapse, const bool withHydrogen); #endif diff --git a/libs/libmod/src/mod/lib/Chem/Smiles.hpp b/libs/libmod/src/mod/lib/Chem/Smiles.hpp index 446230e..a314fa2 100644 --- a/libs/libmod/src/mod/lib/Chem/Smiles.hpp +++ b/libs/libmod/src/mod/lib/Chem/Smiles.hpp @@ -1,12 +1,15 @@ #ifndef MOD_LIB_CHEM_SMILES_HPP #define MOD_LIB_CHEM_SMILES_HPP -#include +#include +#include #include +#include namespace mod { struct AtomId; +enum class SmilesClassPolicy; } // namespace mod namespace mod::lib::Graph { struct PropMolecule; @@ -16,8 +19,8 @@ namespace mod::lib::Chem { std::string getSmiles(const lib::Graph::GraphType &g, const lib::Graph::PropMolecule &molState, const std::vector *ranks, bool withIds); -lib::IO::Result> -readSmiles(lib::IO::Warnings &warnings, const std::string &smiles, bool allowAbstract, SmilesClassPolicy classPolicy); +lib::IO::Result> +readSmiles(lib::IO::Warnings &warnings, std::string_view smiles, bool allowAbstract, SmilesClassPolicy classPolicy); const std::vector &getSmilesOrganicSubset(); bool isInSmilesOrganicSubset(AtomId atomId); void addImplicitHydrogens(lib::Graph::GraphType &g, lib::Graph::PropString &pString, lib::Graph::Vertex v, diff --git a/libs/libmod/src/mod/lib/Chem/SmilesRead.cpp b/libs/libmod/src/mod/lib/Chem/SmilesRead.cpp index 8fc1a75..08f8486 100644 --- a/libs/libmod/src/mod/lib/Chem/SmilesRead.cpp +++ b/libs/libmod/src/mod/lib/Chem/SmilesRead.cpp @@ -6,12 +6,13 @@ #include #include +#include #include #include #include #include #include -#include +#include #include #include @@ -264,7 +265,7 @@ struct ChiralSymbl : x3::symbols { const auto abstractSymbolChar = x3::char_ - x3::char_("[]:"); const auto nestedAbstractSymbol = x3::rule("nestedAbstractSymbol"); const auto nestedAbstractSymbol_def = - *abstractSymbolChar > -(x3::char_('[') > nestedAbstractSymbol > x3::char_(']')) > *abstractSymbolChar; + *abstractSymbolChar > *(x3::char_('[') > nestedAbstractSymbol > x3::char_(']') > *abstractSymbolChar); // no recursion const auto singleDigit = x3::uint_parser(); const auto singleDoubleDigit = x3::rule{"singleDoubleDigit"} @@ -474,9 +475,8 @@ struct AssignConnectedComponentID { int nextID = 0; }; - struct JoinConnected { - JoinConnected(lib::IO::Graph::Read::ConnectedComponents &components) : components(components) {} + JoinConnected(ConnectedComponents &components) : components(components) {} void operator()(const SmilesChain &c) { (*this)(c.branchedAtom); @@ -512,13 +512,13 @@ struct JoinConnected { components.join(a.connectedComponentID, b.connectedComponentID); } public: - lib::IO::Graph::Read::ConnectedComponents &components; + ConnectedComponents &components; }; struct Converter { Converter(std::vector> &gPtrs, std::vector> &pStringPtrs, - const lib::IO::Graph::Read::ConnectedComponents &components, + const ConnectedComponents &components, lib::IO::Warnings &warnings, bool allowAbstract) : gPtrs(gPtrs), pStringPtrs(pStringPtrs), components(components), warnings(warnings), allowAbstract(allowAbstract) {} @@ -659,7 +659,7 @@ struct Converter { private: std::vector> &gPtrs; std::vector> &pStringPtrs; - const lib::IO::Graph::Read::ConnectedComponents &components; + const ConnectedComponents &components; lib::IO::Warnings &warnings; public: std::multimap> classToVertexId; @@ -671,7 +671,7 @@ struct Converter { struct ExplicitHydrogenAdder { ExplicitHydrogenAdder(std::vector> &gPtrs, std::vector> &pStringPtrs, - const lib::IO::Graph::Read::ConnectedComponents &components) + const ConnectedComponents &components) : gPtrs(gPtrs), pStringPtrs(pStringPtrs), components(components) {} void operator()(SmilesChain &c) { @@ -701,13 +701,13 @@ struct ExplicitHydrogenAdder { private: std::vector> &gPtrs; std::vector> &pStringPtrs; - const lib::IO::Graph::Read::ConnectedComponents &components; + const ConnectedComponents &components; }; struct ImplicitHydrogenAdder { ImplicitHydrogenAdder(std::vector> &gPtrs, std::vector> &pStringPtrs, - const lib::IO::Graph::Read::ConnectedComponents &components) + const ConnectedComponents &components) : gPtrs(gPtrs), pStringPtrs(pStringPtrs), components(components) {} void operator()(SmilesChain &c) { @@ -735,13 +735,13 @@ struct ImplicitHydrogenAdder { private: std::vector> &gPtrs; std::vector> &pStringPtrs; - const lib::IO::Graph::Read::ConnectedComponents &components; + const ConnectedComponents &components; }; struct StereoConverter { StereoConverter(std::vector> &gPtrs, const std::vector &pMols, - const lib::IO::Graph::Read::ConnectedComponents &components, + const ConnectedComponents &components, lib::IO::Warnings &warnings) : gPtrs(gPtrs), pMols(pMols), components(components), warnings(warnings), hasAssigned(gPtrs.size(), false) { @@ -856,21 +856,19 @@ struct StereoConverter { public: std::vector> &gPtrs; const std::vector &pMols; - const lib::IO::Graph::Read::ConnectedComponents &components; + const ConnectedComponents &components; lib::IO::Warnings &warnings; public: std::vector> infs; std::vector hasAssigned; }; -lib::IO::Result> -parseSmiles(lib::IO::Warnings &warnings, const std::string &smiles, const bool allowAbstract, +lib::IO::Result> +parseSmiles(lib::IO::Warnings &warnings, std::string_view smiles, const bool allowAbstract, SmilesClassPolicy classPolicy) { - using IteratorType = std::string::const_iterator; - IteratorType iterStart = begin(smiles), iterEnd = end(smiles); SmilesChain ast; try { - lib::IO::parse(iterStart, iterEnd, parser::smiles, ast); + lib::IO::parse(smiles.begin(), smiles.end(), parser::smiles, ast, true); } catch(const lib::IO::ParsingError &e) { return lib::IO::Result<>::Error(e.msg); } @@ -911,7 +909,7 @@ parseSmiles(lib::IO::Warnings &warnings, const std::string &smiles, const bool a } const int numAtoms = AssignConnectedComponentID()(ast); - lib::IO::Graph::Read::ConnectedComponents components(numAtoms); + ConnectedComponents components(numAtoms); (JoinConnected(components)(ast)); const int numComponents = components.finalize(); for(int i = 0; i != numAtoms; ++i) { @@ -930,7 +928,7 @@ parseSmiles(lib::IO::Warnings &warnings, const std::string &smiles, const bool a (ExplicitHydrogenAdder(gPtrs, pStringPtrs, components))(ast); (ImplicitHydrogenAdder(gPtrs, pStringPtrs, components)(ast)); - std::vector datas(numComponents); + std::vector datas(numComponents); const auto iter = std::find_if(conv.classToVertexId.begin(), conv.classToVertexId.end(), [&conv](auto &vp) { return conv.classToVertexId.count(vp.first) > 1; @@ -991,17 +989,17 @@ parseSmiles(lib::IO::Warnings &warnings, const std::string &smiles, const bool a datas[i].g = std::move(gPtrs[i]); datas[i].pString = std::move(pStringPtrs[i]); } - return lib::IO::Result>(std::move(datas)); + return lib::IO::Result>(std::move(datas)); } } // namespace } // namespace mod::lib::Chem::Smiles namespace mod::lib::Chem { -lib::IO::Result> -readSmiles(lib::IO::Warnings &warnings, const std::string &smiles, const bool allowAbstract, +lib::IO::Result> +readSmiles(lib::IO::Warnings &warnings, std::string_view src, const bool allowAbstract, SmilesClassPolicy classPolicy) { - return Smiles::parseSmiles(warnings, smiles, allowAbstract, classPolicy); + return Smiles::parseSmiles(warnings, src, allowAbstract, classPolicy); } } // namespace mod::lib::Chem diff --git a/libs/libmod/src/mod/lib/DG/Dump.cpp b/libs/libmod/src/mod/lib/DG/Dump.cpp index 3e3b447..52145b7 100644 --- a/libs/libmod/src/mod/lib/DG/Dump.cpp +++ b/libs/libmod/src/mod/lib/DG/Dump.cpp @@ -7,9 +7,9 @@ #include #include #include -#include +#include #include -#include +#include #include #include @@ -148,14 +148,14 @@ bool parse(Iter &textFirst, const bool res = IO::detail::ParseDispatch::parse(first, last, p, attr, x3::ascii::space); if(!res) { err << "Error while parsing DG dump.\n" - << IO::detail::makeParserError(textFirst, first, last) + << IO::detail::makeParserError(textFirst, first, last, true) << '\n'; return false; } return true; } catch(const x3::expectation_failure> &e) { err << "Error while parsing DG dump.\n" - << IO::detail::makeParserExpectationError(e, textFirst, last) + << IO::detail::makeParserExpectationError(e, textFirst, last, true) << '\n'; return false; } @@ -206,13 +206,17 @@ std::unique_ptr load(const std::vector > PARSE("vertex:" >> x3::uint_, id); PARSE('"' >> x3::lexeme[*(x3::char_ - '"') >> '"'], name); PARSE('"' >> x3::lexeme[*(x3::char_ - '"') >> '"'], dfs); - auto gDataRes = IO::Graph::Read::dfs(dfs); + IO::Warnings warnings; + auto gDataRes = Graph::Read::dfs(warnings, dfs); + assert(warnings.empty()); if(!gDataRes) { err << gDataRes.extractError() << '\n'; err << "GraphDFS \"" << dfs << "\" could not be parsed for vertex " << id << '\n'; return nullptr; } - auto gData = std::move(*gDataRes); + auto gDatas = std::move(*gDataRes); + if(gDatas.size() != 1) MOD_ABORT; + auto gData = std::move(gDatas[0]); if(gData.pStereo) MOD_ABORT; vertices.emplace_back(id, std::move(name), std::move(gData.g), std::move(gData.pString)); validVertices.insert(id); diff --git a/libs/libmod/src/mod/lib/DG/Hyper.cpp b/libs/libmod/src/mod/lib/DG/Hyper.cpp index 6e2f251..0300827 100644 --- a/libs/libmod/src/mod/lib/DG/Hyper.cpp +++ b/libs/libmod/src/mod/lib/DG/Hyper.cpp @@ -6,8 +6,6 @@ #include #include #include -#include -#include #include #include @@ -15,9 +13,7 @@ #include -namespace mod { -namespace lib { -namespace DG { +namespace mod::lib::DG { //------------------------------------------------------------------------------ // HyperCreator @@ -366,6 +362,4 @@ mod::Derivation Hyper::getDerivation(Vertex v) const { return d; } -} // namespace DG -} // namespace lib -} // namespace mod +} // namespace mod::lib::DG \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/DG/Hyper.hpp b/libs/libmod/src/mod/lib/DG/Hyper.hpp index 29e1df6..919a6e4 100644 --- a/libs/libmod/src/mod/lib/DG/Hyper.hpp +++ b/libs/libmod/src/mod/lib/DG/Hyper.hpp @@ -1,26 +1,25 @@ -#ifndef MOD_LIB_DG_HYPER_H -#define MOD_LIB_DG_HYPER_H +#ifndef MOD_LIB_DG_HYPER_HPP +#define MOD_LIB_DG_HYPER_HPP -#include #include +#include #include namespace mod { template -class Function; -namespace lib { -namespace Graph { -class Single; -} // namespace Graph -namespace DG { +struct Function; +} // namespace mod +namespace mod::lib::Graph { +struct Single; +} // namespace mod::lib::Graph +namespace mod::lib::DG { struct Expanded; -class HyperCreator { +struct HyperCreator { HyperCreator(const HyperCreator &) = delete; HyperCreator &operator=(const HyperCreator &) = delete; public: - friend class Hyper; - + friend struct Hyper; private: HyperCreator() = default; explicit HyperCreator(Hyper &hyper); @@ -34,18 +33,16 @@ class HyperCreator { Hyper *owner = nullptr; }; -class Hyper { +struct Hyper { Hyper(const Hyper &) = delete; - Hyper(Hyper &&) = delete; Hyper &operator=(const Hyper &) = delete; - Hyper &operator=(Hyper &&) = delete; public: // bipartite representation using GraphType = HyperGraphType; using Vertex = HyperVertex; using Edge = HyperEdge; private: - friend class HyperCreator; + friend struct HyperCreator; Hyper(const NonHyper &dg); public: static std::pair, HyperCreator> makeHyper(const NonHyper &dg); @@ -81,8 +78,6 @@ class Hyper { std::map graphToHyperVertex; }; -} // namespace DG -} // namespace lib -} // namespace mod +} // namespace mod::lib::DG -#endif /* MOD_LIB_DG_HYPER_H */ +#endif // MOD_LIB_DG_HYPER_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/DGRead.cpp b/libs/libmod/src/mod/lib/DG/IO/Read.cpp similarity index 52% rename from libs/libmod/src/mod/lib/IO/DGRead.cpp rename to libs/libmod/src/mod/lib/DG/IO/Read.cpp index 5600709..b6534e5 100644 --- a/libs/libmod/src/mod/lib/IO/DGRead.cpp +++ b/libs/libmod/src/mod/lib/DG/IO/Read.cpp @@ -1,4 +1,4 @@ -#include "DG.hpp" +#include "Read.hpp" #include //#define BOOST_SPIRIT_X3_DEBUG @@ -11,9 +11,10 @@ #include #include #include -#include -#include +#include +#include +#include #include #include #include @@ -31,13 +32,13 @@ #include -BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::DG::Read::AbstractDerivation, - (mod::lib::IO::DG::Read::AbstractDerivation::List, left) +BOOST_FUSION_ADAPT_STRUCT(mod::lib::DG::Read::AbstractDerivation, + (mod::lib::DG::Read::AbstractDerivation::List, left) (bool, reversible) - (mod::lib::IO::DG::Read::AbstractDerivation::List, right) + (mod::lib::DG::Read::AbstractDerivation::List, right) ) -namespace mod::lib::IO::DG::Read { +namespace mod::lib::DG::Read { namespace { namespace parser { @@ -72,7 +73,7 @@ std::optional loadDump(const std::string &file, std::ostream &er "vertices": {"type": "array", "items": { "type": "array", "items": [ - {"type": "integer", "descirption": "id"}, + {"type": "integer", "description": "id"}, {"type": "string", "description": "graphName"}, {"type": "string", "description": "graphGML"} ] @@ -103,11 +104,11 @@ std::optional loadDump(const std::string &file, std::ostream &er return jOpt; } -std::unique_ptr dump(const std::vector> &graphDatabase, - const std::vector> &ruleDatabase, - const std::string &file, - IsomorphismPolicy graphPolicy, - std::ostream &err, int verbosity) { +std::unique_ptr dump(const std::vector> &graphDatabase, + const std::vector> &ruleDatabase, + const std::string &file, + IsomorphismPolicy graphPolicy, + std::ostream &err, int verbosity) { boost::iostreams::mapped_file_source ifs; try { ifs.open(file); @@ -115,9 +116,15 @@ std::unique_ptr dump(const std::vector 8 && std::string(ifs.begin(), ifs.begin() + 8) == "version:") { ifs.close(); - return lib::DG::Dump::load(graphDatabase, ruleDatabase, file, err); + return Dump::load(graphDatabase, ruleDatabase, file, err); + } + // but the older old versions do not + if(ifs.size() > 12 && std::string(ifs.begin(), ifs.begin() + 12) == "numVertices:") { + ifs.close(); + return Dump::load(graphDatabase, ruleDatabase, file, err); } ifs.close(); auto jOpt = loadDump(file, err); @@ -125,20 +132,20 @@ std::unique_ptr dump(const std::vector(labelSettings, graphDatabase, graphPolicy); + auto dgInternal = std::make_unique(labelSettings, graphDatabase, graphPolicy); { // construction auto b = dgInternal->build(); auto res = b.trustLoadDump(std::move(j), ruleDatabase, err, verbosity); if(!res) return {}; } - return std::unique_ptr(dgInternal.release()); + return std::unique_ptr(dgInternal.release()); } std::optional> abstract(const std::string &s, std::ostream &err) { auto iterStart = s.begin(), iterEnd = s.end(); std::vector derivations; try { - lib::IO::parse(iterStart, iterEnd, parser::derivations, derivations, x3::ascii::space); + lib::IO::parse(iterStart, iterEnd, parser::derivations, derivations, true, x3::ascii::space); } catch(const lib::IO::ParsingError &e) { err << e.msg; return {}; @@ -146,4 +153,81 @@ std::optional> abstract(const std::string &s, st return derivations; } -} // namespace mod::lib::IO::DG::Read \ No newline at end of file +// ----------------------------------------------------------------------------------- + +bool dumpDigest(const HyperGraphType &dg, const nlohmann::json &j, std::ostream &err, const std::string &errType) { + static const nlohmann::json schema = R"({ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "numUDGVertices": {"type": "integer"}, + "isVertex": {"type": "array", "items": {"type": "integer"}} + }, + "required": ["numUDGVertices", "isVertex"] + })"_json; + static const nlohmann::json_schema::json_validator validator(schema); + if(!IO::validateJson(j, validator, err, "Data does not conform to schema:")) + return false; + const int numUDGVertices = j["numUDGVertices"]; + if(num_vertices(dg) != numUDGVertices) { + err << "DG size mismatch. Given DG has " << num_vertices(dg) + << " vertices and edges, but the " << errType << " is based on one with " + << numUDGVertices << "."; + return false; + } + const auto &bitmap = j["isVertex"]; + std::vector blocks; + blocks.reserve(bitmap.size()); + for(const auto &b: bitmap) + blocks.push_back(b.get()); + boost::dynamic_bitset isVertex(num_vertices(dg)); + boost::from_block_range(blocks.begin(), blocks.end(), isVertex); + assert(isVertex.size() >= num_vertices(dg)); + isVertex.resize(num_vertices(dg)); + for(const auto v: asRange(vertices(dg))) { + const auto isVertexActual = dg[v].kind == DG::HyperVertexKind::Vertex; + const auto vId = get(boost::vertex_index_t(), dg, v); + if(isVertex[vId] != isVertexActual) { + err << "DG mismatch at "; + if(isVertexActual) err << "vertex"; + else err << "edge"; + err << " " << vId << "."; + return false; + } + } + return true; +} + +std::optional +vertexOrEdge(const HyperGraphType &dg, std::size_t id, std::ostream &err, const std::string &errPrefix) { + if(id >= num_vertices(dg)) { + err << errPrefix << " ID for vertex or edge is out of range (id=" << id << ", last=" << num_vertices(dg) << ")."; + return {}; + } + const auto vs = vertices(dg); + const auto v = *std::next(vs.first, id); + assert(get(boost::vertex_index_t(), dg, v) == id); + return v; +} + +std::optional +vertex(const HyperGraphType &dg, std::size_t id, std::ostream &err, const std::string &errPrefix) { + auto vOpt = vertexOrEdge(dg, id, err, errPrefix); + if(vOpt && dg[*vOpt].kind != HyperVertexKind::Vertex) { + err << errPrefix << " ID does not represent a vertex (id=" << id << ")."; + return {}; + } + return vOpt; +} + +std::optional +edge(const HyperGraphType &dg, std::size_t id, std::ostream &err, const std::string &errPrefix) { + auto vOpt = vertexOrEdge(dg, id, err, errPrefix); + if(vOpt && dg[*vOpt].kind != HyperVertexKind::Edge) { + err << errPrefix << " ID does not represent a edge (id=" << id << ")."; + return {}; + } + return vOpt; +} + +} // namespace mod::lib::DG::Read \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/DG/IO/Read.hpp b/libs/libmod/src/mod/lib/DG/IO/Read.hpp new file mode 100644 index 0000000..200ebc7 --- /dev/null +++ b/libs/libmod/src/mod/lib/DG/IO/Read.hpp @@ -0,0 +1,52 @@ +#ifndef MOD_LIB_DG_IO_READ_HPP +#define MOD_LIB_DG_IO_READ_HPP + +#include +#include + +#include +#include + +#include +#include +#include + +namespace mod::lib::DG { +struct Hyper; +struct NonHyper; +} // namespace mod::lib::DG +namespace mod::lib::DG::Read { + +struct AbstractDerivation { + using List = std::vector>; +public: + List left; + bool reversible; + List right; +public: + friend bool operator==(const AbstractDerivation &l, const AbstractDerivation &r) { + return std::tie(l.left, l.reversible, l.right) == std::tie(r.left, r.reversible, r.right); + } +}; + +std::optional loadDump(const std::string &file, std::ostream &err); + +std::unique_ptr dump(const std::vector > &graphDatabase, + const std::vector > &ruleDatabase, + const std::string &file, + IsomorphismPolicy graphPolicy, + std::ostream &err, int verbosity); +std::optional> abstract(const std::string &s, std::ostream &err); + +// utilities for those referring to DG elements in their dumps +bool dumpDigest(const HyperGraphType &dg, const nlohmann::json &j, std::ostream &err, const std::string &errType); +std::optional +vertexOrEdge(const HyperGraphType &dg, std::size_t id, std::ostream &err, const std::string &errPrefix); +std::optional +vertex(const HyperGraphType &dg, std::size_t id, std::ostream &err, const std::string &errPrefix); +std::optional +edge(const HyperGraphType &dg, std::size_t id, std::ostream &err, const std::string &errPrefix); + +} // namespace mod::lib::DG::Read + +#endif // MOD_LIB_DG_IO_READ_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/DGWrite.cpp b/libs/libmod/src/mod/lib/DG/IO/Write.cpp similarity index 78% rename from libs/libmod/src/mod/lib/IO/DGWrite.cpp rename to libs/libmod/src/mod/lib/DG/IO/Write.cpp index 9ec4d1e..bc23486 100644 --- a/libs/libmod/src/mod/lib/IO/DGWrite.cpp +++ b/libs/libmod/src/mod/lib/DG/IO/Write.cpp @@ -1,33 +1,33 @@ -#include "DG.hpp" +#include "Write.hpp" #include #include #include -#include #include #include +#include #include #include +#include #include #include -#include -#include #include -#include -#include +#include #include +#include +#include #include #include -namespace mod::lib::IO::DG::Write { +namespace mod::lib::DG::Write { -nlohmann::json dumpToJson(const lib::DG::NonHyper &dgNonHyper) { +nlohmann::json dumpToJson(const NonHyper &dgNonHyper) { if(dgNonHyper.getLabelSettings().withStereo) throw mod::LogicError("Can not yet dump DGs with stereo data."); - using VertexKind = lib::DG::HyperVertexKind; + using VertexKind = HyperVertexKind; const auto &dgHyper = dgNonHyper.getHyper(); const auto &dg = dgHyper.getGraph(); @@ -36,7 +36,7 @@ nlohmann::json dumpToJson(const lib::DG::NonHyper &dgNonHyper) { j["labelSettings"] = dgNonHyper.getLabelSettings(); auto jVertices = nlohmann::json::array(); - for(const auto v : asRange(vertices(dg))) { + for(const auto v: asRange(vertices(dg))) { if(dg[v].kind != VertexKind::Vertex) continue; const auto id = get(boost::vertex_index_t(), dg, v); const lib::Graph::Single *g = dg[v].graph; @@ -45,7 +45,7 @@ nlohmann::json dumpToJson(const lib::DG::NonHyper &dgNonHyper) { vertex.push_back(id); vertex.push_back(g->getName()); std::ostringstream ss; - lib::IO::Graph::Write::gml(*g, false, ss); + lib::Graph::Write::gml(*g, false, ss); vertex.push_back(ss.str()); jVertices.push_back(std::move(vertex)); } @@ -53,17 +53,17 @@ nlohmann::json dumpToJson(const lib::DG::NonHyper &dgNonHyper) { std::set rules; - for(const auto v : asRange(vertices(dg))) { + for(const auto v: asRange(vertices(dg))) { if(dg[v].kind != VertexKind::Edge) continue; - for(const auto *r : dgHyper.getRulesFromEdge(v)) + for(const auto *r: dgHyper.getRulesFromEdge(v)) rules.insert(r); } auto jRules = nlohmann::json::array(); std::unordered_map idFromRule; int rId = 0; - for(const auto *r : rules) { + for(const auto *r: rules) { std::ostringstream ss; - lib::IO::Rules::Write::gml(*r, false, ss); + Rules::Write::gml(*r, false, ss); jRules.push_back(ss.str()); idFromRule.emplace(r, rId); ++rId; @@ -71,27 +71,27 @@ nlohmann::json dumpToJson(const lib::DG::NonHyper &dgNonHyper) { j["rules"] = std::move(jRules); auto jEdges = nlohmann::json::array(); - for(const auto v : asRange(vertices(dg))) { + for(const auto v: asRange(vertices(dg))) { if(dg[v].kind != VertexKind::Edge) continue; const auto id = get(boost::vertex_index_t(), dg, v); auto jEdge = nlohmann::json::array(); jEdge.push_back(id); auto jSources = nlohmann::json::array(); - for(const auto e : asRange(in_edges(v, dg))) { + for(const auto e: asRange(in_edges(v, dg))) { const auto v = source(e, dg); const auto id = get(boost::vertex_index_t(), dg, v); jSources.push_back(id); } jEdge.push_back(std::move(jSources)); auto jTargets = nlohmann::json::array(); - for(const auto e : asRange(out_edges(v, dg))) { + for(const auto e: asRange(out_edges(v, dg))) { const auto v = target(e, dg); const auto id = get(boost::vertex_index_t(), dg, v); jTargets.push_back(id); } jEdge.push_back(std::move(jTargets)); auto jRules = nlohmann::json::array(); - for(const auto *r : dgHyper.getRulesFromEdge(v)) { + for(const auto *r: dgHyper.getRulesFromEdge(v)) { const auto iter = idFromRule.find(r); assert(iter != end(idFromRule)); jRules.push_back(iter->second); @@ -104,26 +104,26 @@ nlohmann::json dumpToJson(const lib::DG::NonHyper &dgNonHyper) { return j; } -std::string dotNonHyper(const lib::DG::NonHyper &nonHyper) { +std::string dotNonHyper(const NonHyper &nonHyper) { post::FileHandle s( - getUniqueFilePrefix() + "dgNonHyper_" + boost::lexical_cast(nonHyper.getId()) + ".dot"); + IO::makeUniqueFilePrefix() + "dgNonHyper_" + boost::lexical_cast(nonHyper.getId()) + ".dot"); { // printing - using Vertex = lib::DG::NonHyperVertex; - using Edge = lib::DG::NonHyperEdge; - const lib::DG::NonHyperGraphType &g = nonHyper.getGraph(); + using Vertex = NonHyperVertex; + using Edge = NonHyperEdge; + const NonHyperGraphType &g = nonHyper.getGraph(); s << "digraph g {" << std::endl; - for(const auto v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { const auto id = get(boost::vertex_index_t(), g, v); s << id << " [ label=\"" << g[v].graphs << "\" ];\n"; } - for(const auto e : asRange(edges(g))) { + for(const auto e: asRange(edges(g))) { const auto srcId = get(boost::vertex_index_t(), g, source(e, g)); const auto tarId = get(boost::vertex_index_t(), g, target(e, g)); s << srcId << " -> " << tarId << " [ label=\""; bool first = true; - for(auto *r : g[e].rules) { + for(auto *r: g[e].rules) { if(!first) s << ", "; first = false; s << "r_" << r->getId(); @@ -137,7 +137,7 @@ std::string dotNonHyper(const lib::DG::NonHyper &nonHyper) { return fileNoExt; } -std::string pdfNonHyper(const lib::DG::NonHyper &nonHyper) { +std::string pdfNonHyper(const NonHyper &nonHyper) { std::string fileNoExt = dotNonHyper(nonHyper); IO::post() << "gv dgNonHyper \"" << fileNoExt << "\" pdf" << std::endl; return fileNoExt; @@ -183,8 +183,8 @@ void TikzPrinter::vertex(const std::string &id, if(haveImage && haveLabel) s << "\\\\"; if(haveLabel) { s << "{"; - if(options.labelsAsLatexMath) s << asLatexMath(label); - else s << escapeForLatex(label); + if(options.labelsAsLatexMath) s << IO::asLatexMath(label); + else s << IO::escapeForLatex(label); s << "}"; } s << "};" << std::endl; @@ -210,8 +210,8 @@ void TikzPrinter::hyperEdge(const std::string &id, const std::string &label, con if(!colour.empty()) s << ", draw=" << colour; s << "] (v-" << id << ") {"; if(label != "") { - if(options.labelsAsLatexMath) s << asLatexMath(label); - else s << escapeForLatex(label); + if(options.labelsAsLatexMath) s << IO::asLatexMath(label); + else s << IO::escapeForLatex(label); } s << "};" << std::endl; } @@ -259,8 +259,8 @@ void TikzPrinter::shortcutEdge(const std::string &idTail, if(hasReverse) s << "[modStyleDGHyperHasReverseShortcut]"; s << " node[auto, swap] {"; if(label != "") { - if(options.labelsAsLatexMath) s << asLatexMath(label); - else s << escapeForLatex(label); + if(options.labelsAsLatexMath) s << IO::asLatexMath(label); + else s << IO::escapeForLatex(label); } s << "} (v-" << idHead << ");" << std::endl; } @@ -274,21 +274,28 @@ void TikzPrinter::transitEdge(const std::string &idTail, s << "] (v-" << idTail << ") to"; s << " node[auto, swap] {"; if(label != "") { - if(options.labelsAsLatexMath) s << asLatexMath(label); - else s << escapeForLatex(label); + if(options.labelsAsLatexMath) s << IO::asLatexMath(label); + else s << IO::escapeForLatex(label); } s << "} (v-" << idHead << ");" << std::endl; } -std::function +std::function TikzPrinter::getImageCreator() { - return [this](const lib::DG::Hyper &dg, lib::DG::HyperVertex v, const std::string &id) -> std::string { + return [this](const Hyper &dg, HyperVertex v, Options::DupVertex vDup, const std::string &id) -> std::string { + if(this->options.imageOverwrite) { + auto[file, cmd] = this->options.imageOverwrite(v, options.dupGraph[vDup].dupNum, dg); + if(!file.empty()) { + if(!cmd.empty()) lib::IO::post() << cmd << '\n'; + return file; + } + } const auto &g = *dg.getGraph()[v].graph; const auto doIt = [&](const auto &gOpts) { if(this->options.withInlineGraphs) { - return IO::Graph::Write::tikz(g, gOpts, true, "v-" + id + "-"); + return lib::Graph::Write::tikz(g, gOpts, true, "v-" + id + "-"); } else { - return IO::Graph::Write::pdf(g, gOpts); + return lib::Graph::Write::pdf(g, gOpts); } }; if(!options.rotationOverwrite && !options.mirrorOverwrite) { @@ -309,11 +316,11 @@ TikzPrinter::getImageCreator() { // Options //------------------------------------------------------------------------------ -std::pair Options::inDegreeVisible(DupVertex e, const lib::DG::Hyper &dg) const { +std::pair Options::inDegreeVisible(DupVertex e, const Hyper &dg) const { const auto &g = dg.getGraph(); DupVertex vDupFirst = g.null_vertex(); int numVisible = 0; - for(DupVertex vDupIn : asRange(inv_adjacent_vertices(e, dupGraph))) { + for(DupVertex vDupIn: asRange(inv_adjacent_vertices(e, dupGraph))) { Vertex vIn = dupGraph[vDupIn].v; bool isVisible = isVertexVisible(vIn, dg); if(!isVisible) continue; @@ -323,11 +330,11 @@ std::pair Options::inDegreeVisible(DupVertex e, const l return std::make_pair(numVisible, vDupFirst); } -std::pair Options::outDegreeVisible(DupVertex e, const lib::DG::Hyper &dg) const { +std::pair Options::outDegreeVisible(DupVertex e, const Hyper &dg) const { const auto &g = dg.getGraph(); DupVertex vDupFirst = g.null_vertex(); int numVisible = 0; - for(DupVertex vDupOut : asRange(adjacent_vertices(e, dupGraph))) { + for(DupVertex vDupOut: asRange(adjacent_vertices(e, dupGraph))) { Vertex vOut = dupGraph[vDupOut].v; bool isVisible = isVertexVisible(vOut, dg); if(!isVisible) continue; @@ -338,7 +345,7 @@ std::pair Options::outDegreeVisible(DupVertex e, const } bool Options::isShortcutEdge(DupVertex e, - const lib::DG::Hyper &dg, + const Hyper &dg, int inDegreeVisible, int outDegreeVisible) const { const auto &g = dg.getGraph(); @@ -348,7 +355,7 @@ bool Options::isShortcutEdge(DupVertex e, || (withShortcutEdgesAfterVisibility && inDegreeVisible == 1 && outDegreeVisible == 1); } -std::string Options::vDupToId(DupVertex vDup, const lib::DG::Hyper &dg) const { +std::string Options::vDupToId(DupVertex vDup, const Hyper &dg) const { const auto dupNum = dupGraph[vDup].dupNum; const auto v = dupGraph[vDup].v; const auto vId = get(boost::vertex_index_t(), dg.getGraph(), v); @@ -359,9 +366,9 @@ std::string Options::vDupToId(DupVertex vDup, const lib::DG::Hyper &dg) const { // Data //------------------------------------------------------------------------------ -Data::Data(const lib::DG::Hyper &dg) : dg(dg) { - for(Vertex v : asRange(vertices(dg.getGraph()))) { - if(dg.getGraph()[v].kind != lib::DG::HyperVertexKind::Edge) continue; +Data::Data(const Hyper &dg) : dg(dg) { + for(Vertex v: asRange(vertices(dg.getGraph()))) { + if(dg.getGraph()[v].kind != HyperVertexKind::Edge) continue; std::unordered_map val; Connections c(in_degree(v, dg.getGraph()), out_degree(v, dg.getGraph())); val.emplace(0, std::move(c)); @@ -370,7 +377,7 @@ Data::Data(const lib::DG::Hyper &dg) : dg(dg) { } bool Data::makeDuplicate(Vertex e, int eDup) { - assert(dg.getGraph()[e].kind == lib::DG::HyperVertexKind::Edge); + assert(dg.getGraph()[e].kind == HyperVertexKind::Edge); const auto iterEdgeDups = connections.find(e); assert(iterEdgeDups != end(connections)); const auto iterEdgeCons = iterEdgeDups->second.find(eDup); @@ -382,7 +389,7 @@ bool Data::makeDuplicate(Vertex e, int eDup) { } bool Data::removeDuplicate(Vertex e, int eDup) { - assert(dg.getGraph()[e].kind == lib::DG::HyperVertexKind::Edge); + assert(dg.getGraph()[e].kind == HyperVertexKind::Edge); auto iterEdgeDups = connections.find(e); assert(iterEdgeDups != end(connections)); const auto count = iterEdgeDups->second.erase(eDup); @@ -391,15 +398,15 @@ bool Data::removeDuplicate(Vertex e, int eDup) { namespace { -void reconnectCommon(const lib::DG::Hyper &dgHyper, +void reconnectCommon(const Hyper &dgHyper, Data::ConnectionsStore &connections, Vertex v, int eDup, Vertex headOrTail, int vDupTar, int vDupSrc, bool isTail) { // std::cout << "reconnect(" << std::boolalpha << isTail << ", " << v << ", " << eDup << ", " << headOrTail << ", " << vDupTar << ", " << vDupSrc << ")" << std::endl; const auto &dg = dgHyper.getGraph(); - assert(dg[v].kind == lib::DG::HyperVertexKind::Edge); - assert(dg[headOrTail].kind == lib::DG::HyperVertexKind::Vertex); + assert(dg[v].kind == HyperVertexKind::Edge); + assert(dg[headOrTail].kind == HyperVertexKind::Vertex); const auto iterEdgeDups = connections.find(v); assert(iterEdgeDups != end(connections)); const auto iterEdgeCons = iterEdgeDups->second.find(eDup); @@ -423,7 +430,6 @@ void reconnectCommon(const lib::DG::Hyper &dgHyper, if(isTail) ss << "Tail"; else ss << "Head"; ss << " duplicate " << vDupSrc << " does not exist. Duplicates are:"; - ss << std::endl; throw FatalError(ss.str()); } dupNums[offset] = vDupTar; @@ -440,7 +446,7 @@ void Data::reconnectTarget(Vertex v, int eDup, Vertex head, int vDupTar, int vDu } void Data::removeVertexIfDegreeZero(Vertex v) { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Vertex); + assert(dg.getGraph()[v].kind == HyperVertexKind::Vertex); removedIfDegreeZero.insert(v); } @@ -452,9 +458,9 @@ void Data::compile(Options &options) const { std::vector> duplicates(num_vertices(dg)); auto idx = get(boost::vertex_index_t(), dg); // collect duplicates, set all to null_vertex - for(const auto &eDupsCons : connections) { + for(const auto &eDupsCons: connections) { Vertex e = eDupsCons.first; - for(const auto &eDupCons : eDupsCons.second) { + for(const auto &eDupCons: eDupsCons.second) { duplicates[idx[e]][eDupCons.first] = dupGraph.null_vertex(); for(int i = 0; i < in_degree(e, dg); i++) { Vertex vIn = *(inv_adjacent_vertices(e, dg).first + i); @@ -469,8 +475,8 @@ void Data::compile(Options &options) const { } } // create vertices - for(Vertex v : asRange(vertices(dg))) { - if(dg[v].kind != lib::DG::HyperVertexKind::Vertex) continue; + for(Vertex v: asRange(vertices(dg))) { + if(dg[v].kind != HyperVertexKind::Vertex) continue; int vId = idx[v]; const auto &dups = duplicates[vId]; if(dups.empty() && @@ -479,7 +485,7 @@ void Data::compile(Options &options) const { dupGraph[vDup].v = v; dupGraph[vDup].dupNum = 0; } else { - for(auto &pDup : duplicates[vId]) { + for(auto &pDup: duplicates[vId]) { DupVertex vDup = add_vertex(dupGraph); pDup.second = vDup; dupGraph[vDup].v = v; @@ -488,10 +494,10 @@ void Data::compile(Options &options) const { } } // create edges - for(Vertex v : asRange(vertices(dg))) { - if(dg[v].kind != lib::DG::HyperVertexKind::Edge) continue; + for(Vertex v: asRange(vertices(dg))) { + if(dg[v].kind != HyperVertexKind::Edge) continue; int vId = idx[v]; - for(auto &pDup : duplicates[vId]) { + for(auto &pDup: duplicates[vId]) { DupVertex vDup = add_vertex(dupGraph); pDup.second = vDup; dupGraph[vDup].v = v; @@ -499,9 +505,9 @@ void Data::compile(Options &options) const { } } // create connectors - for(const auto &eDupsCons : connections) { + for(const auto &eDupsCons: connections) { Vertex e = eDupsCons.first; - for(const auto &eDupCons : eDupsCons.second) { + for(const auto &eDupCons: eDupsCons.second) { DupVertex eDup = duplicates[idx[e]][eDupCons.first]; for(int i = 0; i < in_degree(e, dg); i++) { Vertex vIn = *(inv_adjacent_vertices(e, dg).first + i); @@ -531,7 +537,7 @@ void Data::compile(Options &options) const { std::pair Printer::printHyper( const Data &data, const IO::Graph::Write::Options &graphOptions) { Options options = prePrint(data); - const auto files = IO::DG::Write::pdf(data.dg, options, graphOptions); + const auto files = pdf(data.dg, options, graphOptions); postPrint(); return files; } @@ -545,10 +551,8 @@ void Printer::popSuffix() { suffixes.pop_back(); } -void Printer::pushVertexVisible(std::function f) { - vertexVisibles. - push_back(f); +void Printer::pushVertexVisible(std::function f) { + vertexVisibles.push_back(f); } void Printer::popVertexVisible() { @@ -560,10 +564,8 @@ bool Printer::hasVertexVisible() const { return !vertexVisibles.empty(); } -void Printer::pushEdgeVisible(std::function f) { - edgeVisibles. - push_back(f); +void Printer::pushEdgeVisible(std::function f) { + edgeVisibles.push_back(f); } void Printer::popEdgeVisible() { @@ -575,10 +577,8 @@ bool Printer::hasEdgeVisible() const { return !edgeVisibles.empty(); } -void Printer::pushVertexLabel(std::function f) { - vertexLabels. - push_back(f); +void Printer::pushVertexLabel(std::function f) { + vertexLabels.push_back(f); } void Printer::popVertexLabel() { @@ -590,10 +590,8 @@ bool Printer::hasVertexLabel() const { return !vertexLabels.empty(); } -void Printer::pushEdgeLabel(std::function f) { - edgeLabels. - push_back(f); +void Printer::pushEdgeLabel(std::function f) { + edgeLabels.push_back(f); } void Printer::popEdgeLabel() { @@ -605,13 +603,8 @@ bool Printer::hasEdgeLabel() const { return !edgeLabels.empty(); } -void Printer::pushVertexColour(std::function f, - bool extendToEdges -) { - vertexColour. - emplace_back(f, extendToEdges - ); +void Printer::pushVertexColour(std::function f, bool extendToEdges) { + vertexColour.emplace_back(f, extendToEdges); } void Printer::popVertexColour() { @@ -623,10 +616,8 @@ bool Printer::hasVertexColour() const { return !vertexColour.empty(); } -void Printer::pushEdgeColour(std::function f) { - edgeColour. - push_back(f); +void Printer::pushEdgeColour(std::function f) { + edgeColour.push_back(f); } void Printer::popEdgeColour() { @@ -646,6 +637,11 @@ void Printer::setMirrorOverwrite(std::function( + Vertex v, int dupNum, const Hyper &)> f) { + baseOptions.imageOverwrite = f; +} + void Printer::setGraphvizPrefix(const std::string &prefix) { baseOptions.graphvizPrefix = prefix; } @@ -666,18 +662,18 @@ Options Printer::prePrint(const Data &data) { Options options = baseOptions; data.compile(options); if(withGraphName) { - pushVertexLabel([](Vertex v, const lib::DG::Hyper &dg) -> std::string { + pushVertexLabel([](Vertex v, const Hyper &dg) -> std::string { const auto &g = dg.getGraph(); - assert(g[v].kind == lib::DG::HyperVertexKind::Vertex); + assert(g[v].kind == HyperVertexKind::Vertex); return g[v].graph->getName(); }); } if(withRuleId) { // we want the rule id before its name - pushEdgeLabel([this](Vertex v, const lib::DG::Hyper &dg) -> std::string { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Edge); + pushEdgeLabel([this](Vertex v, const Hyper &dg) -> std::string { + assert(dg.getGraph()[v].kind == HyperVertexKind::Edge); std::string res; bool first = true; - for(auto *r : dg.getRulesFromEdge(v)) { + for(auto *r: dg.getRulesFromEdge(v)) { if(!first) res += this->edgeLabelSep; first = false; res += "r_{"; @@ -688,11 +684,11 @@ Options Printer::prePrint(const Data &data) { }); } if(withRuleName) { - pushEdgeLabel([this](Vertex v, const lib::DG::Hyper &dg) -> std::string { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Edge); + pushEdgeLabel([this](Vertex v, const Hyper &dg) -> std::string { + assert(dg.getGraph()[v].kind == HyperVertexKind::Edge); std::string res; bool first = true; - for(auto *r : dg.getRulesFromEdge(v)) { + for(auto *r: dg.getRulesFromEdge(v)) { if(!first) res += this->edgeLabelSep; first = false; res += r->getName(); @@ -712,23 +708,23 @@ void Printer::postPrint() { } void Printer::setup(Options &options) { - for(const auto &s : suffixes) options.suffix += s; + for(const auto &s: suffixes) options.suffix += s; - options.vertexVisible = [this](Vertex v, const lib::DG::Hyper &dg) { - for(const auto &f : vertexVisibles) { + options.vertexVisible = [this](Vertex v, const Hyper &dg) { + for(const auto &f: vertexVisibles) { if(!f(v, dg)) return false; } return true; }; - options.hyperedgeVisible = [this](Vertex v, const lib::DG::Hyper &dg) { - for(const auto &f : edgeVisibles) { + options.hyperedgeVisible = [this](Vertex v, const Hyper &dg) { + for(const auto &f: edgeVisibles) { if(!f(v, dg)) return false; } return true; }; - options.vertexLabel = [this](Vertex v, const lib::DG::Hyper &dg) { + options.vertexLabel = [this](Vertex v, const Hyper &dg) { std::string label; if(!vertexLabels.empty()) label += vertexLabels.front()(v, dg); for(int i = 1; i < vertexLabels.size(); i++) { @@ -737,7 +733,7 @@ void Printer::setup(Options &options) { } return label; }; - options.hyperedgeLabel = [this](Vertex v, const lib::DG::Hyper &dg) { + options.hyperedgeLabel = [this](Vertex v, const Hyper &dg) { std::string label; if(!edgeLabels.empty()) label += edgeLabels.front()(v, dg); for(int i = 1; i < edgeLabels.size(); i++) { @@ -747,26 +743,26 @@ void Printer::setup(Options &options) { return label; }; - options.vertexColour = [this](Vertex v, const lib::DG::Hyper &dg) { - for(const auto &fp : vertexColour) { + options.vertexColour = [this](Vertex v, const Hyper &dg) { + for(const auto &fp: vertexColour) { std::string colour = fp.first(v, dg); if(!colour.empty()) return colour; } return std::string(); }; - auto getRawHyperEdgeColour = [this](Vertex v, const lib::DG::Hyper &dg) { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Edge); - for(const auto &f : edgeColour) { + auto getRawHyperEdgeColour = [this](Vertex v, const Hyper &dg) { + assert(dg.getGraph()[v].kind == HyperVertexKind::Edge); + for(const auto &f: edgeColour) { std::string colour = f(v, dg); if(!colour.empty()) return colour; } return std::string(); }; - auto getExtendColour = [this](Vertex v, const lib::DG::Hyper &dg) { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Vertex); - for(const auto &fp : vertexColour) { + auto getExtendColour = [this](Vertex v, const Hyper &dg) { + assert(dg.getGraph()[v].kind == HyperVertexKind::Vertex); + for(const auto &fp: vertexColour) { if(!fp.second) continue; std::string colour = fp.first(v, dg); if(!colour.empty()) return colour; @@ -774,14 +770,14 @@ void Printer::setup(Options &options) { return std::string(); }; - auto getHyperedgeExtendColour = [getExtendColour](Vertex v, const lib::DG::Hyper &dg) { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Edge); + auto getHyperedgeExtendColour = [getExtendColour](Vertex v, const Hyper &dg) { + assert(dg.getGraph()[v].kind == HyperVertexKind::Edge); std::string tailColour, headColour; - for(Vertex vAdj : asRange(inv_adjacent_vertices(v, dg.getGraph()))) { + for(Vertex vAdj: asRange(inv_adjacent_vertices(v, dg.getGraph()))) { tailColour = getExtendColour(vAdj, dg); if(!tailColour.empty()) break; } - for(Vertex vAdj : asRange(adjacent_vertices(v, dg.getGraph()))) { + for(Vertex vAdj: asRange(adjacent_vertices(v, dg.getGraph()))) { headColour = getExtendColour(vAdj, dg); if(!headColour.empty()) break; } @@ -789,16 +785,16 @@ void Printer::setup(Options &options) { else return std::string(); }; - options.hyperedgeColour = [getRawHyperEdgeColour, getHyperedgeExtendColour](Vertex v, const lib::DG::Hyper &dg) { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Edge); + options.hyperedgeColour = [getRawHyperEdgeColour, getHyperedgeExtendColour](Vertex v, const Hyper &dg) { + assert(dg.getGraph()[v].kind == HyperVertexKind::Edge); std::string colour = getRawHyperEdgeColour(v, dg); if(!colour.empty()) return colour; return getHyperedgeExtendColour(v, dg); }; options.tailColour = [getRawHyperEdgeColour, getExtendColour, - getHyperedgeExtendColour](Vertex v, Vertex e, const lib::DG::Hyper &dg) { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Vertex); - assert(dg.getGraph()[e].kind == lib::DG::HyperVertexKind::Edge); + getHyperedgeExtendColour](Vertex v, Vertex e, const Hyper &dg) { + assert(dg.getGraph()[v].kind == HyperVertexKind::Vertex); + assert(dg.getGraph()[e].kind == HyperVertexKind::Edge); std::string colour = getRawHyperEdgeColour(e, dg); if(!colour.empty()) return colour; colour = getHyperedgeExtendColour(e, dg); @@ -806,9 +802,9 @@ void Printer::setup(Options &options) { else return getExtendColour(v, dg); }; options.headColour = [getRawHyperEdgeColour, getExtendColour, - getHyperedgeExtendColour](Vertex e, Vertex v, const lib::DG::Hyper &dg) { - assert(dg.getGraph()[v].kind == lib::DG::HyperVertexKind::Vertex); - assert(dg.getGraph()[e].kind == lib::DG::HyperVertexKind::Edge); + getHyperedgeExtendColour](Vertex e, Vertex v, const Hyper &dg) { + assert(dg.getGraph()[v].kind == HyperVertexKind::Vertex); + assert(dg.getGraph()[e].kind == HyperVertexKind::Edge); std::string colour = getRawHyperEdgeColour(e, dg); if(!colour.empty()) return colour; colour = getHyperedgeExtendColour(e, dg); @@ -821,7 +817,7 @@ void Printer::setup(Options &options) { // Algorithms //------------------------------------------------------------------------------ -void generic(const lib::DG::Hyper &dg, const Options &options, SyntaxPrinter &print) { +void generic(const Hyper &dg, const Options &options, SyntaxPrinter &print) { using DupVertex = Options::DupVertex; using DupEdge = Options::DupEdge; const auto imageCreator = print.getImageCreator(); @@ -833,7 +829,7 @@ void generic(const lib::DG::Hyper &dg, const Options &options, SyntaxPrinter &pr std::string id = options.vDupToId(vDup, dg); std::string label = options.getVertexLabel(dg, vDup); std::string image; - if(options.withGraphImages) image = imageCreator(dg, v, id); + if(options.withGraphImages) image = imageCreator(dg, v, vDup, id); std::string colour = options.getVertexColour(vDup, dg); print.vertex(id, label, image, colour); }); @@ -881,7 +877,7 @@ void generic(const lib::DG::Hyper &dg, const Options &options, SyntaxPrinter &pr print.end(); } -std::string dot(const lib::DG::Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions) { +std::string dot(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions) { struct DotPrinter : SyntaxPrinter { DotPrinter(std::string file, std::string prefix, const IO::Graph::Write::Options &graphOptions) : SyntaxPrinter(file), prefix(prefix), graphOptions(graphOptions) {} @@ -957,12 +953,12 @@ std::string dot(const lib::DG::Hyper &dg, const Options &options, const IO::Grap s << " ];\n"; } - virtual std::function getImageCreator() override { - return [this](const lib::DG::Hyper &dg, lib::DG::HyperVertex v, const std::string &id) -> std::string { + return [this](const Hyper &dg, HyperVertex v, Options::DupVertex vDup, const std::string &id) -> std::string { const auto &g = *dg.getGraph()[v].graph; - return IO::Graph::Write::svg(g, graphOptions); + return lib::Graph::Write::svg(g, graphOptions); }; } @@ -970,7 +966,7 @@ std::string dot(const lib::DG::Hyper &dg, const Options &options, const IO::Grap std::string prefix; const IO::Graph::Write::Options &graphOptions; }; - std::string file = getUniqueFilePrefix(); + std::string file = IO::makeUniqueFilePrefix(); file += "dg_" + boost::lexical_cast(dg.getNonHyper().getId()) + "_"; file += options; file += ".dot"; @@ -979,17 +975,17 @@ std::string dot(const lib::DG::Hyper &dg, const Options &options, const IO::Grap return file; } -std::string coords(const lib::DG::Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions) { +std::string coords(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions) { std::string fileNoExt = dot(dg, options, graphOptions); fileNoExt.erase(end(fileNoExt) - 4, end(fileNoExt)); IO::post() << "coordsFromGV dgHyper \"" << fileNoExt << "\"" << std::endl; return fileNoExt + "_coord.tex"; } -std::pair tikz(const lib::DG::Hyper &dg, const Options &options, +std::pair tikz(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions) { std::string fileCoordsExt = coords(dg, options, graphOptions); - std::string file = getUniqueFilePrefix(); + std::string file = IO::makeUniqueFilePrefix(); file += "dg_" + boost::lexical_cast(dg.getNonHyper().getId()) + "_"; file += options; file += ".tex"; @@ -998,7 +994,7 @@ std::pair tikz(const lib::DG::Hyper &dg, const Options return std::make_pair(file, fileCoordsExt); } -std::string pdfFromDot(const lib::DG::Hyper &dg, const Options &options, +std::string pdfFromDot(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions) { std::string fileNoExt = dot(dg, options, graphOptions); fileNoExt.erase(end(fileNoExt) - 4, end(fileNoExt)); @@ -1006,7 +1002,7 @@ std::string pdfFromDot(const lib::DG::Hyper &dg, const Options &options, return fileNoExt + ".pdf"; } -std::pair pdf(const lib::DG::Hyper &dg, const Options &options, +std::pair pdf(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions) { auto tikzFiles = tikz(dg, options, graphOptions); std::string fileNoExt = tikzFiles.first.substr(0, tikzFiles.first.length() - 4); @@ -1027,10 +1023,26 @@ std::pair summary(const Data &data, Printer &printer, return files; } -std::string summaryNonHyper(const lib::DG::NonHyper &dg) { +std::string summaryNonHyper(const NonHyper &dg) { std::string file = pdfNonHyper(dg); IO::post() << "summaryDGNonHyper \"dg_" << dg.getId() << "\" \"" << file << "\"\n"; return file; } -} // namespace mod::lib::IO::DG::Write \ No newline at end of file +// ----------------------------------------------------------------------------------- + +nlohmann::json dumpDigest(const HyperGraphType &dg) { + nlohmann::json res; + // apparently it 53 bits is the maximum that is generally supported in some JSON implementations, + // so 32 is the maximum block size we can use + boost::dynamic_bitset isVertex(num_vertices(dg)); + for(const auto v: asRange(vertices(dg))) + isVertex[get(boost::vertex_index_t(), dg, v)] = dg[v].kind == HyperVertexKind::Vertex; + auto bitmap = nlohmann::json::array(); + boost::to_block_range(isVertex, std::back_inserter(bitmap)); + res["numUDGVertices"] = num_vertices(dg); + res["isVertex"] = std::move(bitmap); + return res; +} + +} // namespace mod::lib::DG::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/DG.hpp b/libs/libmod/src/mod/lib/DG/IO/Write.hpp similarity index 64% rename from libs/libmod/src/mod/lib/IO/DG.hpp rename to libs/libmod/src/mod/lib/DG/IO/Write.hpp index 1622548..fa65348 100644 --- a/libs/libmod/src/mod/lib/IO/DG.hpp +++ b/libs/libmod/src/mod/lib/DG/IO/Write.hpp @@ -1,144 +1,34 @@ -#ifndef MOD_LIB_IO_DG_HPP -#define MOD_LIB_IO_DG_HPP +#ifndef MOD_LIB_DG_WRITE_HPP +#define MOD_LIB_DG_WRITE_HPP #include #include -#include + #include -#include +#include #include #include #include namespace mod::lib::DG { -class Hyper; -class NonHyper; +struct Hyper; +struct NonHyper; } // namespace mod::lib::DG -namespace mod::lib::IO::DG::Read { - -struct AbstractDerivation { - using List = std::vector>; -public: - List left; - bool reversible; - List right; -public: - friend bool operator==(const AbstractDerivation &l, const AbstractDerivation &r) { - return std::tie(l.left, l.reversible, l.right) == std::tie(r.left, r.reversible, r.right); - } -}; +namespace mod::lib::DG::Write { +using Vertex = HyperVertex; +using Edge = HyperEdge; -std::optional loadDump(const std::string &file, std::ostream &err); +nlohmann::json dumpToJson(const NonHyper &dg); -std::unique_ptr dump(const std::vector > &graphDatabase, - const std::vector > &ruleDatabase, - const std::string &file, - IsomorphismPolicy graphPolicy, - std::ostream &err, int verbosity); -std::optional> abstract(const std::string &s, std::ostream &err); -} // namespace mod::lib::IO::DG::Read -namespace mod::lib::IO::DG::Write { -using Vertex = lib::DG::HyperVertex; -using Edge = lib::DG::HyperEdge; - -nlohmann::json dumpToJson(const lib::DG::NonHyper &dg); - -std::string dotNonHyper(const lib::DG::NonHyper &nonHyper); -std::string pdfNonHyper(const lib::DG::NonHyper &nonHyper); +std::string dotNonHyper(const NonHyper &nonHyper); +std::string pdfNonHyper(const NonHyper &nonHyper); //------------------------------------------------------------------------------ // Old/New delimiter //------------------------------------------------------------------------------ -struct Options; - -struct SyntaxPrinter { - SyntaxPrinter(std::string file) : s(file) {} - - virtual std::string getName() const = 0; - virtual void begin() = 0; - virtual void end() = 0; - virtual void comment(const std::string &str) = 0; - virtual void vertex(const std::string &id, - const std::string &label, - const std::string &image, - const std::string &colour) = 0; - virtual void vertexHidden(const std::string &id, bool large) = 0; - virtual void hyperEdge(const std::string &id, const std::string &label, const std::string &colour) = 0; - virtual void tailConnector(const std::string &idVertex, - const std::string &idHyperEdge, - const std::string &colour, - int num, int maxNum) = 0; - virtual void headConnector(const std::string &idHyperEdge, - const std::string &idVertex, - const std::string &colour, - int num, int maxNum) = 0; - virtual void shortcutEdge(const std::string &idTail, - const std::string &idHead, - const std::string &label, - const std::string &colour, - bool hasReverse) = 0; - virtual std::function getImageCreator() = 0; -public: - post::FileHandle s; -}; - -struct TikzPrinter : SyntaxPrinter { - TikzPrinter(std::string file, - std::string coords, - const Options &options, - const IO::Graph::Write::Options &graphOptions) - : SyntaxPrinter(file), coords(coords), options(options), graphOptions(graphOptions) {} - - std::string getName() const override { - return "tikz"; - } - - void begin() override; - void end() override; - void comment(const std::string &str) override; - void vertex(const std::string &id, - const std::string &label, - const std::string &image, - const std::string &colour) override; - void vertexHidden(const std::string &id, bool large) override; - void transitVertex(const std::string &idHost, - const std::string &idTransit, - const std::string &angle, - const std::string &label); - void hyperEdge(const std::string &id, const std::string &label, const std::string &colour) override; - void connector(const std::string &idTail, - const std::string &idHead, - const std::string &colour, - int num, int maxNum); - void tailConnector(const std::string &idVertex, - const std::string &idHyperEdge, - const std::string &colour, - int num, int maxNum) override; - void headConnector(const std::string &idHyperEdge, - const std::string &idVertex, - const std::string &colour, - int num, int maxNum) override; - void shortcutEdge(const std::string &idTail, - const std::string &idHead, - const std::string &label, - const std::string &colour, - bool hasReverse) override; - void transitEdge(const std::string &idTail, - const std::string &idHead, - const std::string &label, - const std::string &colour); - std::function getImageCreator() override; -public: - std::string coords; - const Options &options; - const IO::Graph::Write::Options &graphOptions; -}; +struct SyntaxPrinter; struct Options { struct DupVProp { @@ -203,11 +93,11 @@ struct Options { // stuff not giving state - bool isVertexVisible(Vertex v, const lib::DG::Hyper &dg) const { + bool isVertexVisible(Vertex v, const Hyper &dg) const { return vertexVisible ? vertexVisible(v, dg) : true; } - std::string getVertexLabel(const lib::DG::Hyper &dg, DupVertex vDup) const { + std::string getVertexLabel(const Hyper &dg, DupVertex vDup) const { std::string label; const auto v = dupGraph[vDup].v; const auto dupNum = dupGraph[vDup].dupNum; @@ -217,15 +107,15 @@ struct Options { return label; } - bool isHyperedgeVisible(Vertex v, const lib::DG::Hyper &dg) const { + bool isHyperedgeVisible(Vertex v, const Hyper &dg) const { return hyperedgeVisible ? hyperedgeVisible(v, dg) : true; } - std::string getHyperedgeLabel(Vertex v, const lib::DG::Hyper &dg) const { + std::string getHyperedgeLabel(Vertex v, const Hyper &dg) const { return hyperedgeLabel ? hyperedgeLabel(v, dg) : ""; } - std::string getVertexColour(DupVertex vDup, const lib::DG::Hyper &dg) const { + std::string getVertexColour(DupVertex vDup, const Hyper &dg) const { const auto v = dupGraph[vDup].v; const auto dupNum = dupGraph[vDup].dupNum; if(dupVertexColour) { @@ -237,24 +127,24 @@ struct Options { } } - std::string getHyperedgeColour(Vertex v, const lib::DG::Hyper &dg) const { + std::string getHyperedgeColour(Vertex v, const Hyper &dg) const { return hyperedgeColour ? hyperedgeColour(v, dg) : ""; } - std::string getTailColour(Vertex v, Vertex e, const lib::DG::Hyper &dg) const { + std::string getTailColour(Vertex v, Vertex e, const Hyper &dg) const { return tailColour ? tailColour(v, e, dg) : ""; } - std::string getHeadColour(Vertex e, Vertex v, const lib::DG::Hyper &dg) const { + std::string getHeadColour(Vertex e, Vertex v, const Hyper &dg) const { return headColour ? headColour(e, v, dg) : ""; } - std::pair inDegreeVisible(DupVertex e, const lib::DG::Hyper &dg) const; - std::pair outDegreeVisible(DupVertex e, const lib::DG::Hyper &dg) const; + std::pair inDegreeVisible(DupVertex e, const Hyper &dg) const; + std::pair outDegreeVisible(DupVertex e, const Hyper &dg) const; bool isShortcutEdge(DupVertex e, - const lib::DG::Hyper &dg, + const Hyper &dg, int inDegreeVisible, int outDegreeVisible) const; - std::string vDupToId(DupVertex vDup, const lib::DG::Hyper &dg) const; + std::string vDupToId(DupVertex vDup, const Hyper &dg) const; public: bool withShortcutEdges = true; bool withGraphImages = true; @@ -263,19 +153,21 @@ struct Options { bool withInlineGraphs = false; std::string suffix; // appearance (not giving state) - std::function vertexVisible; - std::function vertexLabel; - std::function dupVertexLabel; - std::function hyperedgeVisible; - std::function hyperedgeLabel; - std::function vertexColour; - std::function dupVertexColour; - std::function hyperedgeColour; - std::function tailColour, headColour; - std::function auxPrinter; - // + std::function vertexVisible; + std::function vertexLabel; + std::function dupVertexLabel; + std::function hyperedgeVisible; + std::function hyperedgeLabel; + std::function vertexColour; + std::function dupVertexColour; + std::function hyperedgeColour; + std::function tailColour, headColour; + std::function auxPrinter; + // GraphPrinter overrides std::function)> rotationOverwrite; std::function)> mirrorOverwrite; + // Graph overrides + std::function(Vertex v, int dupNum, const Hyper &)> imageOverwrite; // rendering engine things std::string graphvizPrefix; std::string tikzpictureOption = "scale=\\modDGHyperScale"; @@ -287,9 +179,99 @@ struct Options { DupGraphType dupGraph; }; +struct SyntaxPrinter { + SyntaxPrinter(std::string file) : s(file) {} + + virtual std::string getName() const = 0; + virtual void begin() = 0; + virtual void end() = 0; + virtual void comment(const std::string &str) = 0; + virtual void vertex(const std::string &id, + const std::string &label, + const std::string &image, + const std::string &colour) = 0; + virtual void vertexHidden(const std::string &id, bool large) = 0; + virtual void hyperEdge(const std::string &id, const std::string &label, const std::string &colour) = 0; + virtual void tailConnector(const std::string &idVertex, + const std::string &idHyperEdge, + const std::string &colour, + int num, int maxNum) = 0; + virtual void headConnector(const std::string &idHyperEdge, + const std::string &idVertex, + const std::string &colour, + int num, int maxNum) = 0; + virtual void shortcutEdge(const std::string &idTail, + const std::string &idHead, + const std::string &label, + const std::string &colour, + bool hasReverse) = 0; + virtual std::function getImageCreator() = 0; +public: + post::FileHandle s; +}; + +struct TikzPrinter : SyntaxPrinter { + TikzPrinter(std::string file, + std::string coords, + const Options &options, + const IO::Graph::Write::Options &graphOptions) + : SyntaxPrinter(file), coords(coords), options(options), graphOptions(graphOptions) {} + + std::string getName() const override { + return "tikz"; + } + + void begin() override; + void end() override; + void comment(const std::string &str) override; + void vertex(const std::string &id, + const std::string &label, + const std::string &image, + const std::string &colour) override; + void vertexHidden(const std::string &id, bool large) override; + void transitVertex(const std::string &idHost, + const std::string &idTransit, + const std::string &angle, + const std::string &label); + void hyperEdge(const std::string &id, const std::string &label, const std::string &colour) override; + void connector(const std::string &idTail, + const std::string &idHead, + const std::string &colour, + int num, int maxNum); + void tailConnector(const std::string &idVertex, + const std::string &idHyperEdge, + const std::string &colour, + int num, int maxNum) override; + void headConnector(const std::string &idHyperEdge, + const std::string &idVertex, + const std::string &colour, + int num, int maxNum) override; + void shortcutEdge(const std::string &idTail, + const std::string &idHead, + const std::string &label, + const std::string &colour, + bool hasReverse) override; + void transitEdge(const std::string &idTail, + const std::string &idHead, + const std::string &label, + const std::string &colour); + std::function getImageCreator() override; +public: + std::string coords; + const Options &options; + const IO::Graph::Write::Options &graphOptions; +}; + struct Data { struct Connections { Connections(int numTails, int numHeads) : tail(numTails, 0), head(numHeads, 0) {} + public: // indices are offsets on the in-/out-edge iterators // values are the duplicate numbers @@ -297,7 +279,7 @@ struct Data { }; using ConnectionsStore = std::unordered_map>; public: - explicit Data(const lib::DG::Hyper &dg); + explicit Data(const Hyper &dg); void compile(Options &options) const; // returns: was newly created bool makeDuplicate(Vertex e, int eDup); @@ -307,41 +289,44 @@ struct Data { void reconnectTarget(Vertex e, int eDup, Vertex head, int vDupTar, int vDupSrc); void removeVertexIfDegreeZero(Vertex v); public: - const lib::DG::Hyper &dg; + const Hyper &dg; private: ConnectionsStore connections; // hyper-edge -> dupNum -> Connections std::set removedIfDegreeZero; public: - std::function dupVertexLabel; - std::function dupVertexColour; + std::function dupVertexLabel; + std::function dupVertexColour; }; struct Printer { std::pair printHyper(const Data &data, const IO::Graph::Write::Options &graphOptions); void pushSuffix(const std::string suffix); void popSuffix(); - void pushVertexVisible(std::function f); // visible(v) <=> all of pushed f(v)) + void pushVertexVisible(std::function f); // visible(v) <=> all of pushed f(v)) void popVertexVisible(); bool hasVertexVisible() const; - void pushEdgeVisible(std::function f); // visible(v) <=> all of pushed f(v)) + void pushEdgeVisible(std::function f); // visible(v) <=> all of pushed f(v)) void popEdgeVisible(); bool hasEdgeVisible() const; - void pushVertexLabel(std::function f); + void pushVertexLabel(std::function f); void popVertexLabel(); bool hasVertexLabel() const; - void pushEdgeLabel(std::function f); + void pushEdgeLabel(std::function f); void popEdgeLabel(); bool hasEdgeLabel() const; - void pushVertexColour(std::function f, + void pushVertexColour(std::function f, bool extendToEdges); // colour(v) == first f(v) != "" void popVertexColour(); bool hasVertexColour() const; - void pushEdgeColour(std::function f); // colour(v) == first f(v) != "" + void pushEdgeColour(std::function f); // colour(v) == first f(v) != "" void popEdgeColour(); bool hasEdgeColour() const; -public: +public: // GraphPrinter overrides void setRotationOverwrite(std::function)> f); void setMirrorOverwrite(std::function)> f); +public: // Graph overrides + void setImageOverwrite(std::function( + Vertex v, int dupNum, const Hyper &)> f); public: // rendering engine things void setGraphvizPrefix(const std::string &prefix); const std::string &getGraphvizPrefix() const; @@ -357,27 +342,34 @@ struct Printer { private: std::vector suffixes; // not giving state - std::vector > vertexVisibles, edgeVisibles; - std::vector > vertexLabels, edgeLabels; - std::vector, bool> > vertexColour; - std::vector > edgeColour; + std::vector > vertexVisibles, edgeVisibles; + std::vector > vertexLabels, edgeLabels; + std::vector, bool> > vertexColour; + std::vector > edgeColour; public: std::string vertexLabelSep = ", ", edgeLabelSep = ", "; bool withGraphName = true, withRuleName = false, withRuleId = true; }; -void generic(const lib::DG::Hyper &dg, const Options &options, SyntaxPrinter &print); -std::string dot(const lib::DG::Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions); -std::string coords(const lib::DG::Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions); -std::pair tikz(const lib::DG::Hyper &dg, const Options &options, +void generic(const Hyper &dg, const Options &options, SyntaxPrinter &print); +std::string dot(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions); +std::string coords(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions); +std::pair tikz(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions); -std::string pdfFromDot(const lib::DG::Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions); -std::pair pdf(const lib::DG::Hyper &dg, const Options &options, +std::string pdfFromDot(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions); +std::pair pdf(const Hyper &dg, const Options &options, const IO::Graph::Write::Options &graphOptions); std::pair summary(const Data &data, Printer &printer, const IO::Graph::Write::Options &graphOptions); -std::string summaryNonHyper(const lib::DG::NonHyper &dg); +std::string summaryNonHyper(const NonHyper &dg); + +std::vector> +summaryDerivation(const NonHyper &dg, HyperVertex v, const IO::Graph::Write::Options &options, + const std::string &nomatchColour, const std::string &matchColour); + +// utilities for those referring to DG elements in their dumps +nlohmann::json dumpDigest(const HyperGraphType &dg); -} // namespace mod::lib::IO::DG::Write +} // namespace mod::lib::DG::Write -#endif // MOD_LIB_IO_DG_HPP \ No newline at end of file +#endif // MOD_LIB_DG_WRITE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/Derivation.cpp b/libs/libmod/src/mod/lib/DG/IO/WriteDerivation.cpp similarity index 80% rename from libs/libmod/src/mod/lib/IO/Derivation.cpp rename to libs/libmod/src/mod/lib/DG/IO/WriteDerivation.cpp index e3a524b..61d342b 100644 --- a/libs/libmod/src/mod/lib/IO/Derivation.cpp +++ b/libs/libmod/src/mod/lib/DG/IO/WriteDerivation.cpp @@ -1,47 +1,46 @@ -#include "Derivation.hpp" +#include "Write.hpp" #include #include #include -#include +#include #include #include #include #include #include #include +#include #include -#include -#include #include #include #include #include #include #include -#include +#include #include #include -namespace mod::lib::IO::Derivation::Write { +namespace mod::lib::DG::Write { namespace { namespace GM = jla_boost::GraphMorphism; -std::vector findCompleteRules(const lib::DG::NonHyper &dg, - lib::DG::HyperVertex v, +std::vector findCompleteRules(const NonHyper &dg, + HyperVertex v, const lib::Rules::Real &rReal) { - const lib::DG::HyperGraphType &dgGraph = dg.getHyper().getGraph(); + const HyperGraphType &dgGraph = dg.getHyper().getGraph(); assert(v != dgGraph.null_vertex()); - using Vertex = lib::DG::HyperVertex; - assert(dgGraph[v].kind == lib::DG::HyperVertexKind::Edge); + using Vertex = HyperVertex; + assert(dgGraph[v].kind == HyperVertexKind::Edge); LabelledUnionGraph eductUnion, productUnion; - for(const auto vAdj : asRange(inv_adjacent_vertices(v, dgGraph))) + for(const auto vAdj: asRange(inv_adjacent_vertices(v, dgGraph))) eductUnion.push_back(&dgGraph[vAdj].graph->getLabelledGraph()); - for(const auto vAdj : asRange(adjacent_vertices(v, dgGraph))) + for(const auto vAdj: asRange(adjacent_vertices(v, dgGraph))) productUnion.push_back(&dgGraph[vAdj].graph->getLabelledGraph()); - const auto identifyL = lib::Rules::graphToRule(eductUnion, lib::Rules::Membership::Context, "G"); - const auto identifyR = lib::Rules::graphToRule(productUnion, lib::Rules::Membership::Context, "H"); + const auto identifyL = lib::Rules::graphToRule(eductUnion, lib::Rules::Membership::K, "G"); + const auto identifyR = lib::Rules::graphToRule(productUnion, lib::Rules::Membership::K, "H"); std::vector matchingLR; { std::vector matchingL; @@ -49,7 +48,7 @@ std::vector findCompleteRules(const lib::DG::NonHyper &dg, if(getConfig().dg.derivationDebugOutput.get()) { std::cout << "Derivation: compose identifyL -> rReal" << std::endl; std::cout << "Derivation: eductUnion:" << std::endl; - for(const auto vAdj : asRange(inv_adjacent_vertices(v, dgGraph))) + for(const auto vAdj: asRange(inv_adjacent_vertices(v, dgGraph))) std::cout << "Derivation: " << dgGraph[vAdj].graph->getName() << std::endl; } auto reporter = [&matchingL, &dg](std::unique_ptr r) { @@ -61,13 +60,13 @@ std::vector findCompleteRules(const lib::DG::NonHyper &dg, return true; }; if(getConfig().dg.derivationDebugOutput.get()) - lib::IO::Rules::Write::termState(rReal); + Rules::Write::termState(rReal); lib::RC::Super mm( getConfig().dg.derivationVerbosity.get(), IO::Logger(std::cout), false, true); lib::RC::composeRuleRealByMatchMaker(*identifyL, rReal, mm, reporter, dg.getLabelSettings()); } - for(auto *r : matchingL) { + for(auto *r: matchingL) { if(getConfig().dg.derivationDebugOutput.get()) std::cout << "Derivation: compose matchingL -> identifyR" << std::endl; auto reporter = [&matchingLR, &dg](std::unique_ptr r) { @@ -95,10 +94,10 @@ std::vector findCompleteRules(const lib::DG::NonHyper &dg, } template -void forEachMatch(const lib::DG::NonHyper &dg, lib::DG::HyperVertex v, const lib::Rules::Real &rReal, F f) { +void forEachMatch(const NonHyper &dg, HyperVertex v, const lib::Rules::Real &rReal, F f) { bool derivationFound = false; std::vector matchingLR = findCompleteRules(dg, v, rReal); - for(const lib::Rules::Real *rLower : matchingLR) { + for(const lib::Rules::Real *rLower: matchingLR) { auto mr = [&f, &derivationFound, rLower, matchCount = 0](auto &&m, const lib::Rules::GraphType &gUpper, const lib::Rules::GraphType &gLower) mutable { @@ -132,43 +131,41 @@ void forEachMatch(const lib::DG::NonHyper &dg, lib::DG::HyperVertex v, const lib } // namespace std::vector> -summary(const lib::DG::NonHyper &dg, - lib::DG::HyperVertex v, - const IO::Graph::Write::Options &options, - const std::string &nomatchColour, - const std::string &matchColour) { +summaryDerivation(const NonHyper &dg, + HyperVertex v, + const IO::Graph::Write::Options &options, + const std::string &nomatchColour, + const std::string &matchColour) { const auto &dgHyper = dg.getHyper(); const auto &dgGraph = dgHyper.getGraph(); const auto &rules = dgHyper.getRulesFromEdge(v); assert(!rules.empty()); std::vector> res; - for(const lib::Rules::Real *rPtr : rules) { + for(const lib::Rules::Real *rPtr: rules) { const lib::Rules::Real &rReal = *rPtr; const auto printHeader = [v, &dgGraph, &rReal]() { IO::post() << "summarySectionNoEscape \"Derivation " << get(boost::vertex_index_t(), dgGraph, v); IO::post() << ", $r_{" << rReal.getId() << "}$"; IO::post() << ", \\{"; bool first = true; - for(auto vIn : asRange(inv_adjacent_vertices(v, dgGraph))) { + for(auto vIn: asRange(inv_adjacent_vertices(v, dgGraph))) { if(!first) IO::post() << ", "; else first = false; IO::post() << "\\texttt{\\ensuremath{\\mathrm{" << dgGraph[vIn].graph->getName() << "}}}"; } IO::post() << "\\} \\texttt{->} \\{"; first = true; - for(auto vOut : asRange(adjacent_vertices(v, dgGraph))) { + for(auto vOut: asRange(adjacent_vertices(v, dgGraph))) { if(!first) IO::post() << ", "; else first = false; IO::post() << "\\texttt{\\ensuremath{\\mathrm{" << dgGraph[vOut].graph->getName() << "}}}"; } IO::post() << "\\}\"" << std::endl; { - std::string file = getUniqueFilePrefix() + "der_constraints.tex"; + std::string file = IO::makeUniqueFilePrefix() + "der_constraints.tex"; post::FileHandle s(file); - auto vis = lib::IO::MatchConstraint::Write::makeTexPrintVisitor(s, get_left(rReal.getDPORule())); - for(const auto &c : rReal.getDPORule().leftMatchConstraints) { - c->accept(vis); - } + for(const auto &c: get_match_constraints(get_labelled_left(rReal.getDPORule()))) + lib::GraphMorphism::Write::texConstraint(s, getL(rReal.getDPORule().getRule()), *c); IO::post() << "summaryInput \"" << file << "\"" << std::endl; } }; @@ -207,26 +204,26 @@ summary(const lib::DG::NonHyper &dg, }; using Vertex = boost::graph_traits::vertex_descriptor; std::map map; - for(const auto vUpper : asRange(vertices(gUpper))) + for(const auto vUpper: asRange(vertices(gUpper))) map.emplace(vUpper, get(m, gUpper, gLower, vUpper)); rUpperCopy.getDepictionData().copyCoords(rLower.getDepictionData(), map); - std::string fileNoExt1 = IO::Rules::Write::pdf( + std::string fileNoExt1 = Rules::Write::pdf( rUpperCopy, options, strMatch + "_derL", strMatch + "_derK", strMatch + "_derR", - IO::Rules::Write::BaseArgs{visibleRule, vColourRule, eColourRule}); - std::string fileNoExt2 = IO::Rules::Write::pdf( + Rules::Write::BaseArgs{visibleRule, vColourRule, eColourRule}); + std::string fileNoExt2 = Rules::Write::pdf( rLower, options, strMatch + "_derG", strMatch + "_derD", strMatch + "_derH", - IO::Rules::Write::BaseArgs{visibleInstantiation, vColourInstantiation, eColourInstantiation}); + Rules::Write::BaseArgs{visibleInstantiation, vColourInstantiation, eColourInstantiation}); IO::post() << "summaryDerivation \"" << fileNoExt1 << "_" << strMatch << "\" \"" << fileNoExt2 << "_" << strMatch << "\"" << std::endl; res.emplace_back(fileNoExt1 + "_" + strMatch, fileNoExt2 + "_" + strMatch); } - IO::Rules::Write::gml(rLower, false); + Rules::Write::gml(rLower, false); }; forEachMatch(dg, v, rReal, f); } return res; } -} // namespace mod::lib::IO::Derivation::Write +} // namespace mod::lib::DG::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/DGWriteDetail.hpp b/libs/libmod/src/mod/lib/DG/IO/WriteDetail.hpp similarity index 85% rename from libs/libmod/src/mod/lib/IO/DGWriteDetail.hpp rename to libs/libmod/src/mod/lib/DG/IO/WriteDetail.hpp index 9e0b9be..5a42974 100644 --- a/libs/libmod/src/mod/lib/IO/DGWriteDetail.hpp +++ b/libs/libmod/src/mod/lib/DG/IO/WriteDetail.hpp @@ -1,14 +1,14 @@ -#ifndef MOD_LIB_IO_DGWRITEDETAIL_HPP -#define MOD_LIB_IO_DGWRITEDETAIL_HPP +#ifndef MOD_LIB_DG_IO_WRITEDETAIL_HPP +#define MOD_LIB_DG_IO_WRITEDETAIL_HPP #include -#include +#include #include #include -namespace mod::lib::IO::DG::Write { +namespace mod::lib::DG::Write { template std::string toStr(const T &t) { @@ -17,21 +17,21 @@ std::string toStr(const T &t) { namespace detail { -inline std::string hyperEdgeComment(const lib::DG::Hyper &dg, Vertex v) { +inline std::string hyperEdgeComment(const Hyper &dg, Vertex v) { unsigned int vId = get(boost::vertex_index_t(), dg.getGraph(), v); return "id = " + toStr(vId) + toStr(dg.getDerivation(v)); } template void -forEachVertex(const lib::DG::Hyper &dg, const Options &options, SyntaxPrinter &print, bool printHeader, Body body) { +forEachVertex(const Hyper &dg, const Options &options, SyntaxPrinter &print, bool printHeader, Body body) { using DupVertex = Options::DupVertex; const auto &g = dg.getGraph(); const auto &dupGraph = options.dupGraph; DupVertex prevVertex = dupGraph.null_vertex(); for(const DupVertex vDup : asRange(vertices(dupGraph))) { const Vertex v = dupGraph[vDup].v; - if(g[v].kind != lib::DG::HyperVertexKind::Vertex) continue; + if(g[v].kind != HyperVertexKind::Vertex) continue; unsigned int vId = get(boost::vertex_index_t(), g, v); const auto &graph = *g[v].graph; if(printHeader && (prevVertex == dupGraph.null_vertex() || dupGraph[prevVertex].v != dupGraph[vDup].v)) { @@ -48,14 +48,14 @@ forEachVertex(const lib::DG::Hyper &dg, const Options &options, SyntaxPrinter &p } template -void forEachExplicitHyperEdge(const lib::DG::Hyper &dg, const Options &options, SyntaxPrinter &print, Body body) { +void forEachExplicitHyperEdge(const Hyper &dg, const Options &options, SyntaxPrinter &print, Body body) { using DupVertex = Options::DupVertex; const auto &g = dg.getGraph(); const auto &dupGraph = options.dupGraph; DupVertex prevVertex = dupGraph.null_vertex(); for(const DupVertex vDup : asRange(vertices(dupGraph))) { const Vertex v = dupGraph[vDup].v; - if(g[v].kind != lib::DG::HyperVertexKind::Edge) continue; + if(g[v].kind != HyperVertexKind::Edge) continue; if(!options.isHyperedgeVisible(v, dg)) continue; unsigned int inDegreeVisible = options.inDegreeVisible(vDup, dg).first; unsigned int outDegreeVisible = options.outDegreeVisible(vDup, dg).first; @@ -75,7 +75,7 @@ void forEachExplicitHyperEdge(const lib::DG::Hyper &dg, const Options &options, } template -void forEachConnector(const lib::DG::Hyper &dg, const Options &options, SyntaxPrinter &print, +void forEachConnector(const Hyper &dg, const Options &options, SyntaxPrinter &print, TailBody tailBody, HeadBody headBody, ShortcutBody shortcutBody) { using DupVertex = Options::DupVertex; const auto &g = dg.getGraph(); @@ -83,7 +83,7 @@ void forEachConnector(const lib::DG::Hyper &dg, const Options &options, SyntaxPr DupVertex prevVertex = dupGraph.null_vertex(); for(const DupVertex vDup : asRange(vertices(dupGraph))) { const Vertex v = dupGraph[vDup].v; - if(g[v].kind != lib::DG::HyperVertexKind::Edge) continue; + if(g[v].kind != HyperVertexKind::Edge) continue; if(prevVertex == dupGraph.null_vertex() || dupGraph[prevVertex].v != dupGraph[vDup].v) { print.comment(detail::hyperEdgeComment(dg, v)); } else { @@ -146,6 +146,6 @@ void forEachConnector(const lib::DG::Hyper &dg, const Options &options, SyntaxPr } } // namespace detail -} // namespace namespace mod::lib::IO::DG::Write +} // namespace namespace mod::lib::DG::Write -#endif // MOD_LIB_IO_DGWRITEDETAIL_HPP \ No newline at end of file +#endif // MOD_LIB_DG_IO_WRITEDETAIL_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/DG/NonHyper.cpp b/libs/libmod/src/mod/lib/DG/NonHyper.cpp index bd8d57d..7a2ea6a 100644 --- a/libs/libmod/src/mod/lib/DG/NonHyper.cpp +++ b/libs/libmod/src/mod/lib/DG/NonHyper.cpp @@ -13,12 +13,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include @@ -190,6 +190,8 @@ std::pair NonHyper::isDerivation(const GraphMultiset &gmsS std::pair NonHyper::suggestDerivation( const GraphMultiset &gmsSrc, const GraphMultiset &gmsTar, const lib::Rules::Real *r) { + assert(!gmsSrc.empty()); + assert(!gmsTar.empty()); // make vertices for to and from Vertex vSrc = getVertex(gmsSrc), vTar = getVertex(gmsTar); std::pair e = edge(vSrc, vTar, dg); @@ -255,7 +257,7 @@ const std::vector > &NonHyper::getProducts() const void NonHyper::print() const { if(!getHasCalculated()) std::abort(); - std::string fileNoExt = IO::DG::Write::pdfNonHyper(*this); + std::string fileNoExt = Write::pdfNonHyper(*this); IO::post() << "summaryDGNonHyper \"dg_" << getId() << "\" \"" << fileNoExt << "\"" << std::endl; } diff --git a/libs/libmod/src/mod/lib/DG/NonHyper.hpp b/libs/libmod/src/mod/lib/DG/NonHyper.hpp index b086a53..0f50c2a 100644 --- a/libs/libmod/src/mod/lib/DG/NonHyper.hpp +++ b/libs/libmod/src/mod/lib/DG/NonHyper.hpp @@ -22,10 +22,10 @@ namespace mod::lib::Graph { struct PropString; } // namespace mod::lib::Graph namespace mod::lib::DG { -class HyperCreator; +struct HyperCreator; -class NonHyper { - friend class HyperCreator; +struct NonHyper { + friend struct HyperCreator; public: using GraphType = NonHyperGraphType; using Vertex = NonHyperVertex; diff --git a/libs/libmod/src/mod/lib/DG/NonHyperBuilder.cpp b/libs/libmod/src/mod/lib/DG/NonHyperBuilder.cpp index 48e002f..5392bc5 100644 --- a/libs/libmod/src/mod/lib/DG/NonHyperBuilder.cpp +++ b/libs/libmod/src/mod/lib/DG/NonHyperBuilder.cpp @@ -7,11 +7,12 @@ #include #include #include +#include +#include #include #include -#include #include -#include +#include #include #include @@ -64,16 +65,16 @@ std::pair Builder::addDerivation(const Derivations &d, Iso // add graphs switch(graphPolicy) { case IsomorphismPolicy::Check: { - for(const auto &g : d.left) + for(const auto &g: d.left) dg->tryAddGraph(g); - for(const auto &g : d.right) + for(const auto &g: d.right) dg->tryAddGraph(g); break; } case IsomorphismPolicy::TrustMe: - for(const auto &g : d.left) + for(const auto &g: d.left) dg->trustAddGraph(g); - for(const auto &g : d.right) + for(const auto &g: d.right) dg->trustAddGraph(g); break; } @@ -81,7 +82,7 @@ std::pair Builder::addDerivation(const Derivations &d, Iso const auto makeSide = [](const mod::Derivation::GraphList &graphs) -> GraphMultiset { std::vector gPtrs; gPtrs.reserve(graphs.size()); - for(const auto &g : graphs) gPtrs.push_back(&g->getGraph()); + for(const auto &g: graphs) gPtrs.push_back(&g->getGraph()); return GraphMultiset(std::move(gPtrs)); }; auto gmsLeft = makeSide(d.left); @@ -93,7 +94,7 @@ std::pair Builder::addDerivation(const Derivations &d, Iso return dg->suggestDerivation(std::move(gmsLeft), std::move(gmsRight), rule); } else { auto res = dg->suggestDerivation(gmsLeft, gmsRight, &d.rules.front()->getRule()); - for(const auto &r : asRange(d.rules.begin() + 1, d.rules.end())) + for(const auto &r: asRange(d.rules.begin() + 1, d.rules.end())) dg->suggestDerivation(gmsLeft, gmsRight, &r->getRule()); return res; } @@ -120,7 +121,7 @@ struct NonHyperBuilder::ExecutionEnv final : public Strategies::ExecutionEnv { } bool checkLeftPredicate(const mod::Derivation &d) const override { - for(const auto &pred : asRange(leftPredicates.rbegin(), leftPredicates.rend())) { + for(const auto &pred: asRange(leftPredicates.rbegin(), leftPredicates.rend())) { bool result = (*pred)(d); if(!result) return false; } @@ -128,7 +129,7 @@ struct NonHyperBuilder::ExecutionEnv final : public Strategies::ExecutionEnv { } bool checkRightPredicate(const mod::Derivation &d) const override { - for(const auto &pred : asRange(rightPredicates.rbegin(), rightPredicates.rend())) { + for(const auto &pred: asRange(rightPredicates.rbegin(), rightPredicates.rend())) { bool result = (*pred)(d); if(!result) return false; } @@ -233,11 +234,11 @@ Builder::apply(const std::vector> &graphs, dg->rules.insert(rOrig); switch(graphPolicy) { case IsomorphismPolicy::Check: - for(const auto &g : graphs) + for(const auto &g: graphs) dg->tryAddGraph(g); break; case IsomorphismPolicy::TrustMe: - for(const auto &g : graphs) + for(const auto &g: graphs) dg->trustAddGraph(g); break; } @@ -251,7 +252,7 @@ Builder::apply(const std::vector> &graphs, if(graphs.empty()) return {}; std::vector libGraphs; libGraphs.reserve(graphs.size()); - for(const auto &g : graphs) + for(const auto &g: graphs) libGraphs.push_back(&g->getGraph()); std::vector resultRules; @@ -287,13 +288,13 @@ Builder::apply(const std::vector> &graphs, firstGraph, firstGraph + round + 1, inputRules, dg->graphAsRuleCache, ls, onOutput); - for(BoundRule &br : outputRules) { + for(BoundRule &br: outputRules) { // always go to the next graph ++br.nextGraphOffset; } if(round != 0) { // in round 0 the inputRules is the actual original input rule, so don't delete it - for(auto &br : inputRules) + for(auto &br: inputRules) delete br.rule; } std::swap(inputRules, outputRules); @@ -303,10 +304,14 @@ Builder::apply(const std::vector> &graphs, --logger.indentLevel; } } // for each round + // after the last round we may still have rules with connected components in L + // which go unused, so delete them + for(auto &br: inputRules) + delete br.rule; } // end of binding std::vector> res; - for(const BoundRule &br : resultRules) { + for(const BoundRule &br: resultRules) { if(getConfig().dg.applyLimit.get() == res.size()) break; const auto &r = *br.rule; @@ -326,18 +331,26 @@ Builder::apply(const std::vector> &graphs, } } ); - for(const auto &p : products) + if(products.empty()) { + if(verbosity >= V_RuleApplication) { + ++logger.indentLevel; + logger.indent() << "Discarding derivation, empty result." << std::endl; + --logger.indentLevel; + } + continue; + } + for(const auto &p: products) dg->addProduct(p); std::vector rightGraphs; rightGraphs.reserve(products.size()); - for(const auto &p : products) + for(const auto &p: products) rightGraphs.push_back(&p->getGraph()); lib::DG::GraphMultiset gmsLeft(br.boundGraphs), gmsRight(std::move(rightGraphs)); const auto derivationRes = dg->suggestDerivation(gmsLeft, gmsRight, &rOrig->getRule()); res.push_back(derivationRes); } - for(const auto &br : resultRules) + for(const auto &br: resultRules) delete br.rule; return res; @@ -347,16 +360,15 @@ std::vector> Builder::applyRelaxed(const std::vector> &graphs, std::shared_ptr rOrig, int verbosity, IsomorphismPolicy graphPolicy) { - IO::Logger logger(std::cout); dg->rules.insert(rOrig); switch(graphPolicy) { case IsomorphismPolicy::Check: - for(const auto &g : graphs) + for(const auto &g: graphs) dg->tryAddGraph(g); break; case IsomorphismPolicy::TrustMe: - for(const auto &g : graphs) + for(const auto &g: graphs) dg->trustAddGraph(g); break; } @@ -370,7 +382,7 @@ Builder::applyRelaxed(const std::vector> &graphs, if(graphs.empty()) return {}; std::vector libGraphs; libGraphs.reserve(graphs.size()); - for(const auto &g : graphs) + for(const auto &g: graphs) libGraphs.push_back(&g->getGraph()); const auto ls = dg->getLabelSettings(); @@ -390,7 +402,8 @@ Builder::applyRelaxed(const std::vector> &graphs, const auto &r = *br.rule; if(verbosity >= V_RuleApplication_Binding) { logger.indent() << "Splitting " << r.getName() << " into " - << r.getDPORule().numRightComponents << " graphs" << std::endl; + << get_num_connected_components(get_labelled_right(r.getDPORule())) + << " graphs" << std::endl; ++logger.indentLevel; } @@ -410,11 +423,20 @@ Builder::applyRelaxed(const std::vector> &graphs, } } ); - for(const auto &p : products) + if(products.empty()) { + if(verbosity >= V_RuleApplication) { + ++logger.indentLevel; + logger.indent() << "Discarding derivation, empty result." << std::endl; + --logger.indentLevel; + } + delete br.rule; + return true; + } + for(const auto &p: products) dg->addProduct(p); std::vector rightGraphs; rightGraphs.reserve(products.size()); - for(const auto &p : products) + for(const auto &p: products) rightGraphs.push_back(&p->getGraph()); lib::DG::GraphMultiset gmsLeft(br.boundGraphs), gmsRight(std::move(rightGraphs)); const auto derivationRes = dg->suggestDerivation(gmsLeft, gmsRight, &rOrig->getRule()); @@ -432,28 +454,32 @@ Builder::applyRelaxed(const std::vector> &graphs, firstGraph, lastGraph, inputRules, dg->graphAsRuleCache, ls, onOutput); - for(BoundRule &br : outputRules) { + for(BoundRule &br: outputRules) { // always go to the next graph ++br.nextGraphOffset; } if(round != 0) { // in round 0 the inputRules is the actual original input rule, so don't delete it - for(auto &br : inputRules) + for(auto &br: inputRules) delete br.rule; } std::swap(inputRules, outputRules); } // for each round based on numComponents + // the last round should not produce any results with non-empty L, + // as we do exactly |CC(L)| number of rounds. + if(rOrig->getNumLeftComponents() != 0) + assert(inputRules.empty()); return res; } void Builder::addAbstract(const std::string &description) { std::ostringstream err; - auto res = lib::IO::DG::Read::abstract(description, err); + auto res = lib::DG::Read::abstract(description, err); if(!res) throw InputError("Could not parse description of abstract derivations.\n" + err.str()); const auto &derivations = *res; std::unordered_map > strToGraph; - const auto handleSide = [this, &strToGraph](const lib::IO::DG::Read::AbstractDerivation::List &side) { - for(const auto &e : side) { + const auto handleSide = [this, &strToGraph](const lib::DG::Read::AbstractDerivation::List &side) { + for(const auto &e: side) { const auto iter = strToGraph.find(e.second); if(iter != end(strToGraph)) continue; auto gBoost = std::make_unique(); @@ -465,15 +491,15 @@ void Builder::addAbstract(const std::string &description) { strToGraph[e.second] = g; } }; - for(const auto &der : derivations) { + for(const auto &der: derivations) { handleSide(der.left); handleSide(der.right); } using Side = std::unordered_map, unsigned int>; - const auto makeSide = [&strToGraph](const lib::IO::DG::Read::AbstractDerivation::List &side) { + const auto makeSide = [&strToGraph](const lib::DG::Read::AbstractDerivation::List &side) { Side result; - for(const auto &e : side) { + for(const auto &e: side) { const auto g = strToGraph[e.second]; assert(g); auto iter = result.find(g); @@ -482,15 +508,15 @@ void Builder::addAbstract(const std::string &description) { } return result; }; - for(const auto &der : derivations) { + for(const auto &der: derivations) { const Side left = makeSide(der.left); const Side right = makeSide(der.right); std::vector leftGraphs, rightGraphs; - for(const auto &e : left) { + for(const auto &e: left) { for(unsigned int i = 0; i < e.second; i++) leftGraphs.push_back(&e.first->getGraph()); } - for(const auto &e : right) { + for(const auto &e: right) { for(unsigned int i = 0; i < e.second; i++) rightGraphs.push_back(&e.first->getGraph()); } @@ -517,7 +543,7 @@ bool Builder::load(const std::vector> &ruleDatabase, return false; } ifs.close(); - auto jOpt = lib::IO::DG::Read::loadDump(file, err); + auto jOpt = lib::DG::Read::loadDump(file, err); if(!jOpt) return {}; auto &j = *jOpt; @@ -551,12 +577,12 @@ bool Builder::trustLoadDump(nlohmann::json &&j, auto &jVertices = j["vertices"]; std::vector vertices; vertices.reserve(jVertices.size()); - for(auto &jv : jVertices) { + for(auto &jv: jVertices) { Vertex v; v.id = jv[0].get(); const std::string &gml = jv[2].get(); lib::IO::Warnings warnings; - auto gDatasRes = lib::IO::Graph::Read::gml(warnings, gml); + auto gDatasRes = lib::Graph::Read::gml(warnings, gml); err << warnings; if(!gDatasRes) { err << gDatasRes.extractError() << '\n'; @@ -586,7 +612,7 @@ bool Builder::trustLoadDump(nlohmann::json &&j, std::vector> rules; rules.reserve(jRules.size()); const auto ls = dg->getLabelSettings(); - for(const auto &j : jRules) { + for(const auto &j: jRules) { const std::string &jr = j.get(); auto rCand = rule::Rule::fromGMLString(jr, false); const auto iter = std::find_if(ruleDatabase.begin(), ruleDatabase.end(), [rCand, ls](const auto &r) { @@ -628,7 +654,7 @@ bool Builder::trustLoadDump(nlohmann::json &&j, std::vector srcGraphs, tarGraphs; srcGraphs.reserve(e[1].size()); tarGraphs.reserve(e[2].size()); - for(int src : e[1]) { + for(int src: e[1]) { auto gIter = graphFromId.find(src); if(gIter == end(graphFromId)) { err << "Corrupt data for edge " << e[0].get() << ". Source " << src << " is not a yet a vertex."; @@ -636,7 +662,7 @@ bool Builder::trustLoadDump(nlohmann::json &&j, } srcGraphs.push_back(gIter->second); } - for(int tar : e[2]) { + for(int tar: e[2]) { auto gIter = graphFromId.find(tar); if(gIter == end(graphFromId)) { err << "Corrupt data for edge " << e[0].get() << ". Target " << tar << " is not a yet a vertex."; @@ -649,7 +675,7 @@ bool Builder::trustLoadDump(nlohmann::json &&j, if(ruleIds.empty()) { dg->suggestDerivation(std::move(gmsSrc), std::move(gmsTar), nullptr); } else { - for(const int rId : ruleIds) { + for(const int rId: ruleIds) { if(rId < 0 || rId >= rules.size()) { err << "Corrupt data for edge " << e[0].get() << ". Rule offset " << rId << " is not in range."; return false; diff --git a/libs/libmod/src/mod/lib/DG/NonHyperBuilder.hpp b/libs/libmod/src/mod/lib/DG/NonHyperBuilder.hpp index 98ed712..f8f00bf 100644 --- a/libs/libmod/src/mod/lib/DG/NonHyperBuilder.hpp +++ b/libs/libmod/src/mod/lib/DG/NonHyperBuilder.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include namespace mod::lib::DG { diff --git a/libs/libmod/src/mod/lib/DG/RuleApplicationUtils.hpp b/libs/libmod/src/mod/lib/DG/RuleApplicationUtils.hpp index 4f6345f..c2dfd4c 100644 --- a/libs/libmod/src/mod/lib/DG/RuleApplicationUtils.hpp +++ b/libs/libmod/src/mod/lib/DG/RuleApplicationUtils.hpp @@ -185,7 +185,7 @@ struct BoundRuleStorage { logger.indent() << "BoundRules: added <" << r->getName() << ", {"; for(const auto *g : p.boundGraphs) logger.s << " " << g->getName(); - logger.s << " }> onlyRight: " << std::boolalpha << r->isOnlySide(lib::Rules::Membership::Right) + logger.s << " }> onlyRight: " << std::boolalpha << r->isOnlySide(lib::Rules::Membership::R) << std::endl; } } @@ -207,10 +207,9 @@ struct BoundRuleStorage { // =========================================================================== struct GraphData { - using SideVertex = boost::graph_traits::vertex_descriptor; + using SideVertex = boost::graph_traits::vertex_descriptor; public: GraphData() : gPtr(new lib::Graph::GraphType()), pStringPtr(new lib::Graph::PropString(*gPtr)) {} - public: std::unique_ptr gPtr; std::unique_ptr pStringPtr; @@ -224,15 +223,15 @@ std::vector> splitRule(const lib::Rules::LabelledR const bool withStereo, CheckIfNew checkIfNew, OnDup onDup) { - if(rDPO.numRightComponents == 0) MOD_ABORT; // continue; + if(get_num_connected_components(get_labelled_right(rDPO)) == 0) return {}; using Vertex = lib::Graph::Vertex; using Edge = lib::Graph::Edge; - using SideVertex = boost::graph_traits::vertex_descriptor; - using SideEdge = boost::graph_traits::edge_descriptor; + using SideVertex = boost::graph_traits::vertex_descriptor; + using SideEdge = boost::graph_traits::edge_descriptor; - std::vector products(rDPO.numRightComponents); - const auto &compMap = rDPO.rightComponents; - const auto &gRight = get_right(rDPO); + std::vector products(get_num_connected_components(get_labelled_right(rDPO))); + const auto &compMap = get_component(get_labelled_right(rDPO)); + const auto &gRight = get_R_projected(rDPO); auto rpString = get_string(get_labelled_right(rDPO)); assert(num_vertices(gRight) == num_vertices(get_graph(rDPO))); std::vector vertexMap(num_vertices(gRight)); diff --git a/libs/libmod/src/mod/lib/DG/Strategies/Rule.cpp b/libs/libmod/src/mod/lib/DG/Strategies/Rule.cpp index b4e057f..b9915c6 100644 --- a/libs/libmod/src/mod/lib/DG/Strategies/Rule.cpp +++ b/libs/libmod/src/mod/lib/DG/Strategies/Rule.cpp @@ -13,14 +13,17 @@ namespace mod::lib::DG::Strategies { Rule::Rule(std::shared_ptr r) - : Strategy(std::max(r->getRule().getDPORule().numLeftComponents, r->getRule().getDPORule().numRightComponents)), + : Strategy(std::max(get_num_connected_components(get_labelled_left(r->getRule().getDPORule())), + get_num_connected_components(get_labelled_right(r->getRule().getDPORule())))), r(r), rRaw(&r->getRule()) { - assert(rRaw->getDPORule().numLeftComponents > 0); + assert(get_num_connected_components(get_labelled_left(rRaw->getDPORule())) > 0); } Rule::Rule(const lib::Rules::Real *r) - : Strategy(std::max(r->getDPORule().numLeftComponents, r->getDPORule().numRightComponents)), rRaw(r) { - assert(r->getDPORule().numLeftComponents > 0); + : Strategy(std::max(get_num_connected_components(get_labelled_left(r->getDPORule())), + get_num_connected_components(get_labelled_right(r->getDPORule())))), + rRaw(r) { + assert(get_num_connected_components(get_labelled_left(rRaw->getDPORule())) > 0); } std::unique_ptr Rule::clone() const { @@ -41,7 +44,7 @@ void Rule::printInfo(PrintSettings settings) const { settings.indent() << "consumed ="; std::vector temp(begin(consumedGraphs), end(consumedGraphs)); std::sort(begin(temp), end(temp), lib::Graph::Single::nameLess); - for(const auto *g : temp) + for(const auto *g: temp) settings.s << " " << g->getName(); settings.s << '\n'; } @@ -68,7 +71,7 @@ void handleBoundRulePair(int verbosity, IO::Logger logger, Context context, cons // All max component results should be only right side mod::Derivation d; d.r = context.r; - for(const lib::Graph::Single *g : brp.boundGraphs) d.left.push_back(g->getAPIReference()); + for(const lib::Graph::Single *g: brp.boundGraphs) d.left.push_back(g->getAPIReference()); { // left predicate bool result = context.executionEnv.checkLeftPredicate(d); if(!result) { @@ -79,7 +82,8 @@ void handleBoundRulePair(int verbosity, IO::Logger logger, Context context, cons } if(verbosity >= PrintSettings::V_RuleApplication) { logger.indent() << "Splitting " << r.getName() << " into " - << rDPO.numRightComponents << " graphs" << std::endl; + << get_num_connected_components(get_labelled_right(rDPO)) + << " graphs" << std::endl; ++logger.indentLevel; } const std::vector &educts = brp.boundGraphs; @@ -97,6 +101,14 @@ void handleBoundRulePair(int verbosity, IO::Logger logger, Context context, cons ); if(verbosity >= PrintSettings::V_RuleApplication) --logger.indentLevel; + if(d.right.empty()) { + if(verbosity >= V_RuleApplication) { + ++logger.indentLevel; + logger.indent() << "Discarding derivation, empty result." << std::endl; + --logger.indentLevel; + } + return; + } { // right predicates bool result = context.executionEnv.checkRightPredicate(d); @@ -108,24 +120,24 @@ void handleBoundRulePair(int verbosity, IO::Logger logger, Context context, cons } { // now the derivation is good, so add the products to output if(getConfig().dg.putAllProductsInSubset.get()) { - for(const auto &g : d.right) + for(const auto &g: d.right) context.output->addToSubset(&g->getGraph()); } else { - for(const auto &g : d.right) + for(const auto &g: d.right) if(!context.output->isInUniverse(&g->getGraph())) context.output->addToSubset(&g->getGraph()); } - for(const auto &g : d.right) + for(const auto &g: d.right) context.executionEnv.addProduct(g); } std::vector rightGraphs; rightGraphs.reserve(d.right.size()); - for(const std::shared_ptr &g : d.right) + for(const std::shared_ptr &g: d.right) rightGraphs.push_back(&g->getGraph()); lib::DG::GraphMultiset gmsLeft(educts), gmsRight(std::move(rightGraphs)); bool inserted = context.executionEnv.suggestDerivation(gmsLeft, gmsRight, &context.r->getRule()); if(inserted) { - for(const lib::Graph::Single *g : educts) + for(const lib::Graph::Single *g: educts) context.consumedGraphs.insert(g); } } @@ -138,9 +150,9 @@ unsigned int bindGraphs(PrintSettings settings, Context context, Rules::GraphAsRuleCache &graphAsRuleCache) { unsigned int processedRules = 0; - for(const lib::Graph::Single *g : graphRange) { + for(const lib::Graph::Single *g: graphRange) { if(context.executionEnv.doExit()) break; - for(const BoundRule &p : rules) { + for(const BoundRule &p: rules) { if(context.executionEnv.doExit()) break; if(settings.verbosity >= PrintSettings::V_RuleApplication) { settings.indent() << "Trying to bind " << g->getName() << " to " << p.rule->getName() << ":" << std::endl; @@ -163,7 +175,7 @@ unsigned int bindGraphs(PrintSettings settings, Context context, settings, true, true); lib::RC::composeRuleRealByMatchMaker(rFirst, rSecond, mm, reporter, context.executionEnv.labelSettings); - for(const BoundRule &brp : resultRules) { + for(const BoundRule &brp: resultRules) { processedRules++; if(context.executionEnv.doExit()) delete brp.rule; else if(brp.rule->isOnlyRightSide()) { @@ -213,7 +225,8 @@ void Rule::executeImpl(PrintSettings settings, const GraphState &input) { } if(getConfig().dg.useOldRuleApplication.get()) { - std::vector> intermediaryRules(rRaw->getDPORule().numLeftComponents + 1); + std::vector> + intermediaryRules(get_num_connected_components(get_labelled_left(rRaw->getDPORule())) + 1); { BoundRule p; p.rule = rRaw; @@ -222,7 +235,7 @@ void Rule::executeImpl(PrintSettings settings, const GraphState &input) { Context context{r, getExecutionEnv(), output, consumedGraphs}; const auto &subset = input.getSubset(); const auto &universe = input.getUniverse(); - for(unsigned int i = 1; i <= rRaw->getDPORule().numLeftComponents; i++) { + for(unsigned int i = 1; i <= get_num_connected_components(get_labelled_left(rRaw->getDPORule())); i++) { if(settings.verbosity >= PrintSettings::V_RuleBinding) { settings.indent() << "Component bind round " << i << " with "; ++settings.indentLevel; @@ -241,7 +254,7 @@ void Rule::executeImpl(PrintSettings settings, const GraphState &input) { } else { processedRules = bindGraphs(settings, context, universe, intermediaryRules[i - 1], intermediaryRules[i], getExecutionEnv().graphAsRuleCache); - for(BoundRule &p : intermediaryRules[i - 1]) { + for(BoundRule &p: intermediaryRules[i - 1]) { delete p.rule; p.rule = nullptr; } @@ -262,7 +275,7 @@ void Rule::executeImpl(PrintSettings settings, const GraphState &input) { assert(subset.begin()[i] == universe[subset.getIndices()[i]]); std::vector inSubset(universe.size(), false); - for(int idx : subset.getIndices()) + for(int idx: subset.getIndices()) inSubset[idx] = true; std::vector graphs = universe; @@ -278,7 +291,7 @@ void Rule::executeImpl(PrintSettings settings, const GraphState &input) { Context context{r, getExecutionEnv(), output, consumedGraphs}; std::vector inputRules{{rRaw, {}, 0}}; - for(int round = 0; round != rRaw->getDPORule().numLeftComponents; ++round) { + for(int round = 0; round != get_num_connected_components(get_labelled_left(rRaw->getDPORule())); ++round) { const auto firstGraph = graphs.begin(); const auto lastGraph = round == 0 ? subsetEnd : graphs.end(); @@ -299,7 +312,7 @@ void Rule::executeImpl(PrintSettings settings, const GraphState &input) { onOutput); if(round != 0) { // in round 0 the inputRules is the actual original input rule, so don't delete it - for(auto &br : inputRules) + for(auto &br: inputRules) delete br.rule; } std::swap(inputRules, outputRules); diff --git a/libs/libmod/src/mod/lib/DPO/CombinedRule.cpp b/libs/libmod/src/mod/lib/DPO/CombinedRule.cpp new file mode 100644 index 0000000..90af537 --- /dev/null +++ b/libs/libmod/src/mod/lib/DPO/CombinedRule.cpp @@ -0,0 +1,129 @@ +#include "CombinedRule.hpp" + +#include + +namespace mod::lib::DPO { + +BOOST_CONCEPT_ASSERT((RuleConcept)); + +CombinedRule::CombinedRule() : gProjectedK(gCombined, Membership::K), + gProjectedL(gCombined, Membership::L), + gProjectedR(gCombined, Membership::R), + mL(getK(*this), getL(*this)), + mR(getK(*this), getR(*this)), + mKtoCG(getK(*this), gCombined), + mLtoCG(getL(*this), gCombined), + mRtoCG(getR(*this), gCombined) {} + +const CombinedRule::SideGraphType &getL(const CombinedRule &r) { + return r.gProjectedL; +} + +const CombinedRule::KGraphType &getK(const CombinedRule &r) { return r.gProjectedK; } + +const CombinedRule::SideGraphType &getR(const CombinedRule &r) { + return r.gProjectedR; +} + +const CombinedRule::MorphismType &getMorL(const CombinedRule &r) { return r.mL; } +const CombinedRule::MorphismType &getMorR(const CombinedRule &r) { return r.mR; } + +// ====================================================================================== + +void invert(CombinedRule &r) { + auto &g = r.gCombined; + for(const auto v: asRange(vertices(g))) + g[v].membership = lib::DPO::invert(g[v].membership); + for(const auto e: asRange(edges(g))) + g[e].membership = lib::DPO::invert(g[e].membership); + using std::swap; + // don't swap projected, it acts as a reference type + swap(r.mL, r.mR); + swap(r.mLtoCG, r.mRtoCG); +} + +CombinedRule::SideVertex addVertexL(CombinedRule &r) { + const auto vCG = add_vertex(r.gCombined); + r.gCombined[vCG].membership = Membership::L; + return vCG; +} + +CombinedRule::KVertex addVertexK(CombinedRule &r) { + const auto vCG = add_vertex(r.gCombined); + r.gCombined[vCG].membership = Membership::K; + return vCG; +} + +CombinedRule::SideVertex addVertexR(CombinedRule &r) { + const auto vCG = add_vertex(r.gCombined); + r.gCombined[vCG].membership = Membership::R; + return vCG; +} + +CombinedRule::SideVertex promoteVertexL(CombinedRule &r, CombinedRule::SideVertex vL) { + const auto vCG = get(r.getLtoCG(), getL(r), r.getCombinedGraph(), vL); + assert(r.gCombined[vCG].membership == Membership::L); + r.gCombined[vCG].membership = Membership::K; + return vCG; +} + +CombinedRule::SideEdge addEdgeL(CombinedRule &r, CombinedRule::SideVertex v1, CombinedRule::SideVertex v2) { + const auto vc1 = get(r.getLtoCG(), getL(r), r.getCombinedGraph(), v1); + const auto vc2 = get(r.getLtoCG(), getL(r), r.getCombinedGraph(), v2); + const auto[eCG, addedCG] = add_edge(vc1, vc2, r.gCombined); + assert(addedCG); + r.gCombined[eCG].membership = Membership::L; + return eCG; +} + +CombinedRule::KEdge addEdgeK(CombinedRule &r, CombinedRule::KVertex v1, CombinedRule::KVertex v2) { + const auto[eCG, added] = add_edge(v1, v2, r.gCombined); + assert(added); + r.gCombined[eCG].membership = Membership::K; + return eCG; +} + +CombinedRule::SideEdge addEdgeR(CombinedRule &r, CombinedRule::SideVertex v1, CombinedRule::SideVertex v2) { + const auto vc1 = get(r.getRtoCG(), getR(r), r.getCombinedGraph(), v1); + const auto vc2 = get(r.getRtoCG(), getR(r), r.getCombinedGraph(), v2); + const auto[eCG, addedCG] = add_edge(vc1, vc2, r.gCombined); + assert(addedCG); + r.gCombined[eCG].membership = Membership::R; + return eCG; +} + +// ====================================================================================== + +CombinedRule::CombinedGraphType &CombinedRule::getCombinedGraph() { + return gCombined; +} + +const CombinedRule::CombinedGraphType &CombinedRule::getCombinedGraph() const { + return gCombined; +} + +CombinedRule::MembershipPropertyMap CombinedRule::makeMembershipPropertyMap() const { + return {*this}; +} + +const CombinedRule::ToCombinedMorphismSide &CombinedRule::getLtoCG() const { return mLtoCG; } +const CombinedRule::ToCombinedMorphismK &CombinedRule::getKtoCG() const { return mKtoCG; } +const CombinedRule::ToCombinedMorphismSide &CombinedRule::getRtoCG() const { return mRtoCG; } + +// ========================================================================================= + +const CombinedRule::SideProjectedGraphType &CombinedRule::getLProjected() const { + return gProjectedL; +} + +const CombinedRule::KProjectedGraphType &CombinedRule::getKProjected() const { + return gProjectedK; +} + +const CombinedRule::SideProjectedGraphType &CombinedRule::getRProjected() const { + return gProjectedR; +} + +} // namespace mod::lib::DPO + +BOOST_CONCEPT_ASSERT((mod::lib::DPO::WritableRuleConcept)); \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/DPO/CombinedRule.hpp b/libs/libmod/src/mod/lib/DPO/CombinedRule.hpp new file mode 100644 index 0000000..be0dc3e --- /dev/null +++ b/libs/libmod/src/mod/lib/DPO/CombinedRule.hpp @@ -0,0 +1,157 @@ +#ifndef MOD_LIB_DPO_COMBINEDRULE_HPP +#define MOD_LIB_DPO_COMBINEDRULE_HPP + +#include + +#include +#include +#include + +namespace mod::lib::DPO { + +// A DPO rule model which contains a combined graph of the L <- K -> R span. +// That is, besides getting views of L, K, and R, there is a view which represents +// the pushout object C of the rule span. +// There are two views of L and R: an ordinary one, and then a projected one. +// The projected views are adaptations of the combined view C, i.e., +// vertex/edge descriptors from LProjected and RProjected are valid in C. +// The K-view is currently also just a projected view. +struct CombinedRule { +public: // CombinedGraph + struct CombinedVProp { + Membership membership; + }; + + struct CombinedEProp { + Membership membership; + }; + + using CombinedGraphType = jla_boost::EdgeIndexedAdjacencyList; + using CombinedVertex = boost::graph_traits::vertex_descriptor; + using CombinedEdge = boost::graph_traits::edge_descriptor; + + struct MembershipPropertyMap { + MembershipPropertyMap(const CombinedRule &r) : g(r.getCombinedGraph()) {} + friend Membership get(MembershipPropertyMap m, CombinedVertex v) { + return get(&CombinedVProp::membership, m.g, v); + } + + friend Membership get(MembershipPropertyMap m, CombinedEdge e) { + return get(&CombinedEProp::membership, m.g, e); + } + public: + const CombinedGraphType &g; + }; +public: + using SideProjectedGraphType = lib::DPO::FilteredGraphProjection; + using KProjectedGraphType = lib::DPO::FilteredGraphProjection; +public: // RuleConcept + using SideGraphType = SideProjectedGraphType; + using SideVertex = boost::graph_traits::vertex_descriptor; + using SideEdge = boost::graph_traits::edge_descriptor; + using KGraphType = KProjectedGraphType; + using KVertex = boost::graph_traits::vertex_descriptor; + using KEdge = boost::graph_traits::edge_descriptor; + struct MorphismType { + // Both domain and codomain are projected so vertex descriptors mean the same in both. + // But a side-vertex should map to a null-vertex if it is not in K. + using GraphDom = KGraphType; + using GraphCodom = SideGraphType; + using Storable = std::false_type; + public: + MorphismType(const GraphDom &, const GraphCodom &) {} + public: + friend SideVertex get(const MorphismType &, const GraphDom &gDom, const GraphCodom &, KVertex v) { + assert(gDom.gInner[v].membership == Membership::K); + return v; + } + + friend KVertex get_inverse(const MorphismType &, const GraphDom &gDom, const GraphCodom &gCodom, SideVertex v) { + if(gCodom.gInner[v].membership == Membership::K) return v; + else return GraphDom::null_vertex(); + } + public: + friend SideEdge get(const MorphismType &, const GraphDom &gDom, const GraphCodom &, KEdge e) { + assert(gDom.gInner[e].membership == Membership::K); + return e; + } + + friend KEdge get_inverse(const MorphismType &, const GraphDom &gDom, const GraphCodom &gCodom, SideEdge e) { + if(gCodom.gInner[e].membership == Membership::K) return e; + else return {}; + } + }; +public: // Other, {L, K, R} -> Combined Graph + struct ToCombinedMorphismK { + // The identity morphism + using GraphDom = KGraphType; + using GraphCodom = CombinedGraphType; + using Storable = std::false_type; + public: + ToCombinedMorphismK(const GraphDom &, const GraphCodom &) {} + + friend CombinedVertex + get(const ToCombinedMorphismK &, const GraphDom &gDom, const GraphCodom &, CombinedVertex v) { + return v; + } + + friend CombinedVertex + get_inverse(const ToCombinedMorphismK &, const GraphDom &gDom, const GraphCodom &, CombinedVertex v) { + return v; + } + + friend CombinedEdge + get(const ToCombinedMorphismK &, const GraphDom &gDom, const GraphCodom &, CombinedEdge e) { + return e; + } + + friend CombinedEdge + get_inverse(const ToCombinedMorphismK &, const GraphDom &gDom, const GraphCodom &, CombinedEdge e) { + return e; + } + }; + using ToCombinedMorphismSide = ToCombinedMorphismK; +public: + CombinedRule(); + // internally pointers are used, so don't allow moving and copying + CombinedRule(CombinedRule &&) = delete; + CombinedRule &operator=(CombinedRule &&) = delete; +public: // RuleConcept + friend const SideGraphType &getL(const CombinedRule &r); + friend const KGraphType &getK(const CombinedRule &r); + friend const SideGraphType &getR(const CombinedRule &r); + friend const MorphismType &getMorL(const CombinedRule &r); + friend const MorphismType &getMorR(const CombinedRule &r); +public: // WriteableRuleConcept + friend void invert(CombinedRule &r); + friend SideVertex addVertexL(CombinedRule &r); + friend KVertex addVertexK(CombinedRule &r); + friend SideVertex addVertexR(CombinedRule &r); + friend SideVertex promoteVertexL(CombinedRule &r, SideVertex vL); + friend SideEdge addEdgeL(CombinedRule &r, SideVertex v1, SideVertex v2); + friend KEdge addEdgeK(CombinedRule &r, KVertex v1, KVertex v2); + friend SideEdge addEdgeR(CombinedRule &r, SideVertex v1, SideVertex v2); +public: // Other, Combined Graph + CombinedGraphType &getCombinedGraph(); // TODO: remove non-const version + const CombinedGraphType &getCombinedGraph() const; + MembershipPropertyMap makeMembershipPropertyMap() const; + const ToCombinedMorphismSide &getLtoCG() const; + const ToCombinedMorphismK &getKtoCG() const; + const ToCombinedMorphismSide &getRtoCG() const; +public: // Other, Projections of Combined Graph + const SideProjectedGraphType &getLProjected() const; + const KProjectedGraphType &getKProjected() const; + const SideProjectedGraphType &getRProjected() const; +private: + CombinedGraphType gCombined; + KProjectedGraphType gProjectedK; + SideProjectedGraphType gProjectedL, gProjectedR; + MorphismType mL, mR; +private: + ToCombinedMorphismK mKtoCG; + ToCombinedMorphismSide mLtoCG, mRtoCG; +}; + +} // namesapce mod::lib::DPO + +#endif // MOD_LIB_DPO_COMBINEDRULE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/DPO/Concepts.hpp b/libs/libmod/src/mod/lib/DPO/Concepts.hpp new file mode 100644 index 0000000..cb2a186 --- /dev/null +++ b/libs/libmod/src/mod/lib/DPO/Concepts.hpp @@ -0,0 +1,66 @@ +#ifndef MOD_LIB_DPO_CONCEPTS_HPP +#define MOD_LIB_DPO_CONCEPTS_HPP + +#include + +#include + +#include + +namespace mod::lib::DPO { + +template +struct RuleConcept { + using Traits = RuleTraits; + using SideGraphType = typename Traits::SideGraphType; + using KGraphType = typename Traits::KGraphType; + using MorphismType = typename Traits::MorphismType; + + BOOST_CONCEPT_ASSERT((boost::GraphConcept)); + BOOST_CONCEPT_ASSERT((boost::GraphConcept)); + BOOST_CONCEPT_ASSERT((jla_boost::GraphMorphism::InvertibleGraphMapConcept)); + + BOOST_CONCEPT_USAGE(RuleConcept) { + const Rule &rConst = r; + [[maybe_unused]] const SideGraphType &gL = getL(rConst); + [[maybe_unused]] const KGraphType &gK = getK(rConst); + [[maybe_unused]] const SideGraphType &gR = getR(rConst); + [[maybe_unused]] const MorphismType &mL = getMorL(rConst); + [[maybe_unused]] const MorphismType &mR = getMorR(rConst); + } +private: + Rule r; +}; + +template +struct WritableRuleConcept : RuleConcept { + BOOST_CONCEPT_ASSERT((boost::DefaultConstructible)); + + using Traits = RuleTraits; + using SideGraphType = typename Traits::SideGraphType; + using SideVertex = typename boost::graph_traits::vertex_descriptor; + using SideEdge = typename boost::graph_traits::edge_descriptor; + using KGraphType = typename Traits::KGraphType; + using KVertex = typename boost::graph_traits::vertex_descriptor; + using KEdge = typename boost::graph_traits::edge_descriptor; + + BOOST_CONCEPT_USAGE(WritableRuleConcept) { + invert(r); // swap L and R + [[maybe_unused]] const SideVertex vL = addVertexL(r); + [[maybe_unused]] const KVertex vK = addVertexK(r); + [[maybe_unused]] const SideVertex vR = addVertexR(r); + [[maybe_unused]] const SideVertex vRfL = promoteVertexL(r, cvSide); + //[[maybe_unused]] const SideVertex vLfR = promoteVertexR(r, cvSide); + [[maybe_unused]] const SideEdge eL = addEdgeL(r, cvSide, cvSide); + [[maybe_unused]] const KEdge eK = addEdgeK(r, cvK, cvK); + [[maybe_unused]] const SideEdge eR = addEdgeR(r, cvSide, cvSide); + } +private: + Rule r; + static const SideVertex cvSide; + static const KVertex cvK; +}; + +} // namespace mod::lib::DPO + +#endif // MOD_LIB_DPO_CONCEPTS_HPP \ No newline at end of file diff --git a/libs/jla_boost/include/jla_boost/graph/dpo/FilteredGraphProjection.hpp b/libs/libmod/src/mod/lib/DPO/FilteredGraphProjection.hpp similarity index 82% rename from libs/jla_boost/include/jla_boost/graph/dpo/FilteredGraphProjection.hpp rename to libs/libmod/src/mod/lib/DPO/FilteredGraphProjection.hpp index a0a3647..d78744d 100644 --- a/libs/jla_boost/include/jla_boost/graph/dpo/FilteredGraphProjection.hpp +++ b/libs/libmod/src/mod/lib/DPO/FilteredGraphProjection.hpp @@ -1,35 +1,34 @@ -#ifndef JLA_BOOST_GRAPH_DPO_FILTERED_GRAPH_PROJECTION_HPP -#define JLA_BOOST_GRAPH_DPO_FILTERED_GRAPH_PROJECTION_HPP +#ifndef MOD_LIB_DPO_FILTERED_GRAPH_PROJECTION_HPP +#define MOD_LIB_DPO_FILTERED_GRAPH_PROJECTION_HPP + +#include #include -#include -#include +#include #include -namespace jla_boost { -namespace GraphDPO { +namespace mod::lib::DPO { template struct FilteredGraphProjection { using Self = FilteredGraphProjection; struct Filter { + Filter() : gInner(nullptr) {} - Filter() : gInner(nullptr) { } - - Filter(const GraphType &gInner, Membership membership) : gInner(&gInner), membership(membership) { } + Filter(const GraphType &gInner, Membership membership) : gInner(&gInner), membership(membership) {} bool operator()(typename boost::graph_traits::vertex_descriptor v) const { assert(gInner); auto m = (*gInner)[v].membership; - return m == membership || m == Membership::Context; + return m == membership || m == Membership::K; } bool operator()(typename boost::graph_traits::edge_descriptor e) const { assert(gInner); auto m = (*gInner)[e].membership; - return m == membership || m == Membership::Context; + return m == membership || m == Membership::K; } private: const GraphType *gInner; @@ -38,9 +37,8 @@ struct FilteredGraphProjection { using GraphTypeFiltered = boost::filtered_graph; public: - - FilteredGraphProjection(const GraphType &gInner, Membership membership) - : gInner(gInner), membership(membership), g(gInner, Filter(gInner, membership), Filter(gInner, membership)) { } + explicit FilteredGraphProjection(const GraphType &gInner, Membership membership) + : gInner(gInner), membership(membership), g(gInner, Filter(gInner, membership), Filter(gInner, membership)) {} public: // Graph using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; using edge_descriptor = typename boost::graph_traits::edge_descriptor; @@ -113,12 +111,10 @@ struct FilteredGraphProjection { return num_edges(g.g); } public: // "AdjacencyMatrix" (it's not constant time) - friend std::pair edge(vertex_descriptor u, vertex_descriptor v, const Self &g) { return edge(u, v, g.g); } public: // PropertyGraph - template friend decltype(auto) get(PropertyTag t, const Self &g) { return get(t, g.g); @@ -144,7 +140,6 @@ struct FilteredGraphProjection { return g[ve]; } public: // Other - friend vertex_descriptor vertex(vertices_size_type n, const Self &g) { return vertex(n, g.g); } @@ -154,13 +149,14 @@ struct FilteredGraphProjection { const GraphTypeFiltered g; }; -} // namespace GraphDPO +} // namespace mod::lib::DPO +namespace jla_boost { template -struct GraphAdaptorTraits > { +struct GraphAdaptorTraits > { using type = GraphType; - static const type &unwrap(const GraphDPO::FilteredGraphProjection &g) { + static const type &unwrap(const mod::lib::DPO::FilteredGraphProjection &g) { return g.gInner; } }; @@ -172,15 +168,15 @@ namespace boost { //------------------------------------------------------------------------------ template -struct property_map, Property> -: property_map { +struct property_map, Property> + : property_map { }; template -struct property_map, Property> -: property_map { +struct property_map, Property> + : property_map { }; } // namespace boost -#endif /* JLA_BOOST_GRAPH_DPO_FILTERED_GRAPH_PROJECTION_HPP */ \ No newline at end of file +#endif // MOD_LIB_DPO_FILTERED_GRAPH_PROJECTION_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/DPO/Membership.cpp b/libs/libmod/src/mod/lib/DPO/Membership.cpp new file mode 100644 index 0000000..bf9b096 --- /dev/null +++ b/libs/libmod/src/mod/lib/DPO/Membership.cpp @@ -0,0 +1,19 @@ +#include "Membership.hpp" + +#include + +namespace mod::lib::DPO { + +std::ostream &operator<<(std::ostream &s, Membership m) { + switch(m) { + case Membership::L: + return s << "Left"; + case Membership::R: + return s << "Right"; + case Membership::K: + return s << "Context"; + } + __builtin_unreachable(); +} + +} // namespace mod::lib::DPO \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/DPO/Membership.hpp b/libs/libmod/src/mod/lib/DPO/Membership.hpp new file mode 100644 index 0000000..74db80c --- /dev/null +++ b/libs/libmod/src/mod/lib/DPO/Membership.hpp @@ -0,0 +1,28 @@ +#ifndef MOD_LIB_DPO_MEMBERSHIP_HPP +#define MOD_LIB_DPO_MEMBERSHIP_HPP + +#include + +namespace mod::lib::DPO { + +enum class Membership { + L, K, R +}; + +inline Membership invert(Membership m) { + switch(m) { + case Membership::L: + return Membership::R; + case Membership::K: + return Membership::K; + case Membership::R: + return Membership::L; + } + __builtin_unreachable(); +} + +std::ostream &operator<<(std::ostream &s, Membership m); + +} // namespace mod::lib::DPO + +#endif // MOD_LIB_DPO_MEMBERSHIP_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/DPO/Traits.hpp b/libs/libmod/src/mod/lib/DPO/Traits.hpp new file mode 100644 index 0000000..2f6f8ff --- /dev/null +++ b/libs/libmod/src/mod/lib/DPO/Traits.hpp @@ -0,0 +1,15 @@ +#ifndef MOD_LIB_DPO_TRAITS_HPP +#define MOD_LIB_DPO_TRAITS_HPP + +namespace mod::lib::DPO { + +template +struct RuleTraits { + using SideGraphType = typename Rule::SideGraphType; + using KGraphType = typename Rule::KGraphType; + using MorphismType = typename Rule::MorphismType; +}; + +} // namespace mod::lib::DPO + +#endif // MOD_LIB_DPO_TRAITS_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/Collection.cpp b/libs/libmod/src/mod/lib/Graph/Collection.cpp index 5c812ad..351e979 100644 --- a/libs/libmod/src/mod/lib/Graph/Collection.cpp +++ b/libs/libmod/src/mod/lib/Graph/Collection.cpp @@ -40,7 +40,7 @@ struct Collection::Store { // opts.edgesAsBonds = opts.withIndex = true; // optsGraph.collapseHydrogens = optsGraph.edgesAsBonds = optsGraph.raiseCharges = true; // optsGraph.simpleCarbons = optsGraph.withColour = optsGraph.withIndex = true; - // mod::lib::IO::Graph::Write::summary(*gCand, optsGraph, optsGraph); + // mod::lib::Graph::IO::Write::summary(*gCand, optsGraph, optsGraph); // std::shared_ptr gCandWrapped = graph::Graph::makeGraph(std::move(gCand)); // for(auto v : gCandWrapped->vertices()) { // if(v.getStringLabel() == "C") diff --git a/libs/libmod/src/mod/lib/Graph/DFSEncoding.cpp b/libs/libmod/src/mod/lib/Graph/DFSEncoding.cpp deleted file mode 100644 index 1b5a91c..0000000 --- a/libs/libmod/src/mod/lib/Graph/DFSEncoding.cpp +++ /dev/null @@ -1,623 +0,0 @@ -#include "DFSEncoding.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -namespace mod::lib::Graph::DFSEncoding { -namespace { - -void escapeLabel(std::ostream &s, const std::string &label, char escChar) { - for(int i = 0; i != label.size(); i++) { - char c = label[i]; - if(c == escChar) s << "\\" << escChar; - else if(c == '\t') s << "\\t"; - else if(c == '\\' && i + 1 != label.size()) { - char next = label[i + 1]; - if(next == '\\' || next == 't') s << "\\\\"; - else s << '\\'; - } else s << c; - } -} - -} // namespace -namespace detail { -class LabelVertex; -class RingClosure; -class Branch; - -// x3 does not yet fully support std::variant -// https://github.com/boostorg/spirit/issues/321 -using BaseVertex = boost::variant; - -using GVertex = lib::Graph::Vertex; -using GEdge = lib::Graph::Edge; - -struct LabelVertex { - std::string label; - bool implicit = false; - std::optional id; -public: - GVertex gVertex; -public: - friend std::ostream &operator<<(std::ostream &s, const LabelVertex &lv) { - if(lv.implicit) s << lv.label; - else { - s << '['; - escapeLabel(s, lv.label, ']'); - s << ']'; - } - if(lv.id) s << *lv.id; - return s; - } -}; - -struct RingClosure { - // must use unsigned so the parser understands how to store it - unsigned int id = std::numeric_limits::max(); -public: - friend std::ostream &operator<<(std::ostream &s, const RingClosure &rc) { - return s << rc.id; - } -}; - -struct Vertex { - BaseVertex vertex; - std::vector branches; -public: - friend std::ostream &operator<<(std::ostream &s, const Vertex &v); -}; - -struct Edge { - std::string label; -public: - friend std::ostream &operator<<(std::ostream &s, const Edge &e) { - if(e.label.size() > 1) { - s << '{'; - escapeLabel(s, e.label, '}'); - return s << '}'; - } - switch(e.label.front()) { - case '-': - return s; - case '=': - case '#': - case ':': - return s << e.label; - default: - return s << "{" << e.label << "}"; - } - } -}; - -using EVPair = std::pair; - -struct Chain { - Vertex head; - std::vector tail; - bool hasNonSmilesRingClosure; // only set when creating GraphDFS from a graph -public: - friend std::ostream &operator<<(std::ostream &s, const Chain &c) { - s << c.head; - for(const auto &ev : c.tail) s << ev.first << ev.second; - return s; - } -}; - -struct Branch { - std::vector tail; -public: - friend std::ostream &operator<<(std::ostream &s, const Branch &b) { - for(const auto &ev : b.tail) s << ev.first << ev.second; - return s; - } -}; - -std::ostream &operator<<(std::ostream &s, const Vertex &v) { - s << v.vertex; - for(const auto &b : v.branches) s << b; - return s; -} - -namespace { -namespace parser { - -struct SpecialVertexLabels : x3::symbols { - SpecialVertexLabels() { - name("specialVertexLabels"); - for(auto atomId : lib::Chem::getSmilesOrganicSubset()) - add(lib::Chem::symbolFromAtomId(atomId), lib::Chem::symbolFromAtomId(atomId)); - } -} specialVertexLabels; - -struct SpecialEdgeLabels : x3::symbols { - SpecialEdgeLabels() { - name("specialEdgeLabels"); - add("-", "-"); - add(":", ":"); - add("=", "="); - add("#", "#"); - } -} specialEdgeLabelSymbols; - -// no recursion -const auto implicitBackslash = x3::attr('\\'); -const auto explicitBackslash = '\\' >> x3::attr('\\'); -const auto tab = 't' >> x3::attr('\t'); -const auto plainBrace /* */ = (x3::char_ - x3::char_('}')); -const auto plainBracket /**/ = (x3::char_ - x3::char_(']')); -const auto escapedBrace /* */ = '\\' >> (x3::char_('}') | tab | explicitBackslash | implicitBackslash); -const auto escapedBracket /**/ = '\\' >> (x3::char_(']') | tab | explicitBackslash | implicitBackslash); -const auto escapedStringBrace /* */ = x3::lexeme[x3::lit('{') > *(escapedBrace /* */ | plainBrace /* */) > - x3::lit('}')]; -const auto escapedStringBracket /**/ = x3::lexeme[x3::lit('[') > *(escapedBracket /**/ | plainBracket /**/) > - x3::lit(']')]; - -const auto specialEdgeLabels = specialEdgeLabelSymbols | x3::attr(std::string(1, '-')); -const auto edge = x3::rule("edge") = escapedStringBrace | specialEdgeLabels; -const auto defRingId = x3::uint_; -const auto ringClosure = x3::rule("ringClosure") = x3::uint_; -const auto specialLabelVertex = x3::rule("specialLabelVertex") -/* */ = specialVertexLabels >> x3::attr(true) >> -defRingId; -const auto explicitLabelVertex = x3::rule("explicitLabelVertex") -/* */ = escapedStringBracket >> x3::attr(false) >> -defRingId; -const auto labelVertex = explicitLabelVertex | specialLabelVertex; -// part of recursion -const x3::rule vertex = "vertex"; -const auto evPair = x3::rule("edgeVertexPair") = edge >> vertex; -const auto branch = x3::rule("branch") = '(' > +evPair > ')'; -const auto branches = *branch; -const auto vertex_def = (labelVertex | ringClosure) >> branches; -BOOST_SPIRIT_DEFINE(vertex) -// no recursion -const auto chain = vertex > *evPair; -const auto graphDFS = x3::rule("graphDFS") -/* */ = chain; - -} // namespace parser -} // namespace - -class Converter { - struct ConvertRes { - GVertex next; - bool isRingClosure; - }; -public: - Converter(GraphType &g, PropString &pString) : g(g), pString(pString) {} - - lib::IO::Result<> convert(Chain &chain) { - auto subRes = convertTail(chain.head, g.null_vertex()); - if(!subRes) return std::move(subRes); - const auto sub = *subRes; - GVertex vPrev = sub.next; - assert(!sub.isRingClosure); - assert(vPrev != g.null_vertex()); - for(EVPair &ev : chain.tail) { - auto attachRes = attach(ev, vPrev); - if(!attachRes) return std::move(attachRes); - vPrev = *attachRes; - assert(vPrev != g.null_vertex()); - } - return {}; - } - - lib::IO::Result convertTail(Vertex &vertex, GVertex prev) { - auto subRes = boost::apply_visitor(*this, vertex.vertex); - if(!subRes) return subRes; - const auto sub = *subRes; - assert(!(sub.isRingClosure && prev == g.null_vertex())); - GVertex branchRoot = sub.isRingClosure ? prev : sub.next; - for(Branch &branch : vertex.branches) { - GVertex branchPrev = branchRoot; - for(EVPair &ev : branch.tail) { - auto branchPrevRes = attach(ev, branchPrev); - if(!branchPrevRes) return std::move(branchPrevRes); - branchPrev = *branchPrevRes; - assert(branchPrev != g.null_vertex()); - } - } - return sub; - } - - void makeRingClosureBond(GVertex vSrc, GVertex vTar, const std::string &label) { - std::pair e = add_edge(vSrc, vTar, g); - assert(e.second); - pString.addEdge(e.first, label); - } - - lib::IO::Result attach(EVPair &ev, GVertex prev) { - auto subRes = convertTail(ev.second, prev); - if(!subRes) return std::move(subRes); - const auto res = *subRes; - makeRingClosureBond(prev, res.next, ev.first.label); - if(res.isRingClosure) return prev; - else return res.next; - } - - ConvertRes operator()(LabelVertex &vertex) { - GVertex v = add_vertex(g); - pString.addVertex(v, vertex.label); - if(vertex.id) { - const auto iter = idVertexMap.find(*vertex.id); - if(iter == idVertexMap.end()) { - // use the id as definition - idVertexMap[*vertex.id] = v; - } else { - // use the id as ring closure - makeRingClosureBond(v, iter->second, "-"); - } - } - vertex.gVertex = v; - return ConvertRes{v, false}; - } - - lib::IO::Result operator()(const RingClosure &vertex) { - const auto iter = idVertexMap.find(vertex.id); - if(iter == idVertexMap.end()) - return lib::IO::Result<>::Error("Ring closure id " + std::to_string(vertex.id) + " not found."); - return ConvertRes{iter->second, true}; - } -private: - GraphType &g; - PropString &pString; -public: - std::map idVertexMap; -}; - -struct ImplicitHydrogenAdder : public boost::static_visitor { - ImplicitHydrogenAdder(GraphType &g, PropString &pString) : g(g), pString(pString) {} - - void operator()(const Chain &chain) { - (*this)(chain.head); - for(const auto &ev : chain.tail) (*this)(ev.second); - } - - void operator()(const Vertex &vertex) { - boost::apply_visitor(*this, vertex.vertex); - for(const auto &b : vertex.branches) - for(const auto &ev : b.tail) (*this)(ev.second); - } - - void operator()(const RingClosure &) {} - - void operator()(const LabelVertex &vertex) { - if(vertex.implicit) { - // we can only add hydrogens if all incident edges are valid bonds - for(auto eOut : asRange(out_edges(vertex.gVertex, g))) { - if(lib::Chem::decodeEdgeLabel(pString[eOut]) == BondType::Invalid) - return; - } - const auto atomId = lib::Chem::atomIdFromSymbol(vertex.label); - const auto iter = std::find(begin(lib::Chem::getSmilesOrganicSubset()), - end(lib::Chem::getSmilesOrganicSubset()), atomId); - if(iter == end(lib::Chem::getSmilesOrganicSubset())) - MOD_ABORT; - const auto hydrogenAdder = [](lib::Graph::GraphType &g, lib::Graph::PropString &pString, - lib::Graph::Vertex p) { - const GVertex v = add_vertex(g); - pString.addVertex(v, "H"); - const GEdge e = add_edge(v, p, g).first; - pString.addEdge(e, "-"); - }; - lib::Chem::addImplicitHydrogens(g, pString, vertex.gVertex, atomId, hydrogenAdder); - } - } -private: - GraphType &g; - PropString &pString; -}; - -} // namespace detail - -lib::IO::Result parse(const std::string &dfs) { - using IteratorType = std::string::const_iterator; - IteratorType first = dfs.begin(), last = dfs.end(); - detail::Chain chain; - try { - lib::IO::parse(first, last, detail::parser::graphDFS, chain); - } catch(const lib::IO::ParsingError &e) { - return lib::IO::Result<>::Error(e.msg); - } - - auto g = std::make_unique(); - auto pString = std::make_unique(*g); - detail::Converter conv(*g, *pString); - if(auto res = conv.convert(chain); res) { - detail::ImplicitHydrogenAdder adder(*g, *pString); - adder(chain); - // write(*g, *pString); - lib::IO::Graph::Read::Data data; - data.g = std::move(g); - data.pString = std::move(pString); - for(auto &&vp : conv.idVertexMap) - data.externalToInternalIds[vp.first] = get(boost::vertex_index_t(), *g, vp.second); - return std::move(data); // TODO: remove std::move when C++20/P1825R0 is available - } else { - return res; - } -} - -} // namespace mod::lib::Graph::DFSEncoding - -BOOST_FUSION_ADAPT_STRUCT(mod::lib::Graph::DFSEncoding::detail::LabelVertex, - (std::string, label) - (bool, implicit) - (std::optional, id)) -BOOST_FUSION_ADAPT_STRUCT(mod::lib::Graph::DFSEncoding::detail::RingClosure, - (unsigned int, id)) -BOOST_FUSION_ADAPT_STRUCT(mod::lib::Graph::DFSEncoding::detail::Vertex, - (mod::lib::Graph::DFSEncoding::detail::BaseVertex, vertex) - (std::vector, branches)) -BOOST_FUSION_ADAPT_STRUCT(mod::lib::Graph::DFSEncoding::detail::Edge, - (std::string, label)) -BOOST_FUSION_ADAPT_STRUCT(mod::lib::Graph::DFSEncoding::detail::Chain, - (mod::lib::Graph::DFSEncoding::detail::Vertex, head) - (std::vector, tail)) -BOOST_FUSION_ADAPT_STRUCT(mod::lib::Graph::DFSEncoding::detail::Branch, - (std::vector, tail)) - -#include -#include - -#include -#include -#include - -namespace mod::lib::Graph::DFSEncoding { -namespace detail { - -enum class Colour { - White, Grey, Black -}; - -struct Printer { - Printer(std::ostream &s, std::map idMap) : s(s), idMap(idMap) {} - - void operator()(const Chain &chain) { - (*this)(chain.head); - (*this)(chain.tail); - } - - void operator()(const Vertex &v) { - boost::apply_visitor(*this, v.vertex); - for(const Branch &b : v.branches) { - s << "("; - (*this)(b.tail); - s << ")"; - } - } - - void operator()(const LabelVertex &v) { - s << "["; - escapeLabel(s, v.label, ']'); - s << "]"; - if(v.id && idMap.find(*v.id) != idMap.end()) - s << idMap[*v.id]; - } - - void operator()(const RingClosure &v) { - assert(v.id != std::numeric_limits::max()); - s << idMap[v.id]; - } - - void operator()(const std::vector &evPairs) { - for(const EVPair &p : evPairs) { - (*this)(p.first); - (*this)(p.second); - } - } - - void operator()(const Edge &e) { - if(e.label.size() == 1) { - char c = e.label[0]; - switch(c) { - case '-': - return; - case ':': - case '=': - case '#': - s << c; - return; - } - } - s << "{"; - escapeLabel(s, e.label, '}'); - s << "}"; - } -private: - std::ostream &s; - std::map idMap; -}; - -struct Prettyfier { - Prettyfier(const std::vector &targetForRing) : targetForRing(targetForRing) {} - - void operator()(Chain &chain) { - (*this)(chain.head); - (*this)(chain.tail); - if(chain.head.branches.size() > 0 && chain.tail.size() == 0) { - chain.tail = chain.head.branches.back().tail; - chain.head.branches.pop_back(); - } - } - - void operator()(Vertex &v) { - boost::apply_visitor(*this, v.vertex); - for(Branch &b : v.branches) (*this)(b.tail); - } - - void operator()(LabelVertex &v) { - if(v.id) - if(!targetForRing[*v.id]) v.id.reset(); - } - - void operator()(RingClosure &v) { - assert(v.id != std::numeric_limits::max()); - assert(targetForRing[v.id]); - } - - void operator()(std::vector &evPairs) { - for(EVPair &p : evPairs) { - (*this)(p.first); - (*this)(p.second); - } - if(evPairs.size() > 0 && evPairs.back().second.branches.size() > 0) { - unsigned int index = evPairs.size() - 1; - std::vector tempCopy = evPairs.back().second.branches.back().tail; - std::vector newVec = evPairs; - newVec.insert(newVec.end(), tempCopy.begin(), tempCopy.end()); - newVec[index].second.branches.pop_back(); - evPairs = newVec; - } - } - - void operator()(Edge &e) {} -private: - const std::vector targetForRing; -}; - -std::pair> -write(const lib::Graph::GraphType &g, const PropString &pString, bool withIds) { - using namespace detail; - using GEdgeIter = lib::Graph::GraphType::out_edge_iterator; - using VertexInfo = std::pair>; - std::vector realVertices(num_vertices(g), nullptr); - Chain chain; - chain.hasNonSmilesRingClosure = false; - - std::vector colour(num_vertices(g), Colour::White); - // note: if withIds, pretend *all* vertices are targets for ring closures - std::vector targetForRing(num_vertices(g), withIds); - std::map edgeColour; - for(GEdge e : asRange(edges(g))) edgeColour[e] = Colour::White; - std::stack stack; - { // discover root - GVertex cur = *vertices(g).first; - unsigned int curId = get(boost::vertex_index_t(), g, cur); - assert(curId < colour.size()); - colour[curId] = Colour::Grey; - assert(!realVertices[curId]); - realVertices[curId] = &chain.head; - LabelVertex lv; - lv.id = curId; - lv.label = pString[cur]; - realVertices[curId]->vertex = lv; - stack.push(std::make_pair(cur, out_edges(cur, g))); - } - while(!stack.empty()) { - GVertex cur = stack.top().first; - GEdgeIter iter, iterEnd; - boost::tie(iter, iterEnd) = stack.top().second; - stack.pop(); - unsigned int curId = get(boost::vertex_index_t(), g, cur); - assert(realVertices[curId]); - Vertex *curVertex = realVertices[curId]; - // std::cout << "CurVertex: " << pString(cur) << std::endl; - while(iter != iterEnd) { - GVertex next = target(*iter, g); - GEdge test; - unsigned int nextId = get(boost::vertex_index_t(), g, next); - Edge edge; - edge.label = pString[*iter]; - // mark edge - Colour oldEdgeColour = edgeColour[*iter]; - edgeColour[*iter] = Colour::Black; - // std::cout << "\tEdge: " << pString(cur) - // << " ->(" << edge.label << ", " << (oldEdgeColour == Black ? "black" : "white") << ") " - // << pString(next) << "\t"; - if(colour[nextId] == Colour::White) { // tree edge, new vertex - // std::cout << "white" << std::endl; - // create the new vertex - assert(!realVertices[nextId]); - LabelVertex lv; - lv.id = nextId; - lv.label = pString[next]; - Vertex newVertex; - newVertex.vertex = lv; - curVertex->branches.push_back((Branch())); - curVertex->branches.back().tail.push_back(std::make_pair(edge, newVertex)); - Vertex *nextVertex = &curVertex->branches.back().tail.back().second; - realVertices[nextId] = nextVertex; - colour[nextId] = Colour::Grey; - // switch to the new vertex - iter++; - stack.push(std::make_pair(cur, std::make_pair(iter, iterEnd))); - cur = next; - curId = nextId; - curVertex = nextVertex; - boost::tie(iter, iterEnd) = out_edges(next, g); - // std::cout << "CurVertex: " << pString(cur)<< std::endl; - } else if(colour[nextId] == Colour::Grey) { // back edge, maybe an already traversed edge - // std::cout << "grey" << std::endl; - if(oldEdgeColour == Colour::Black) iter++; // already traversed - else { - RingClosure rc; - rc.id = nextId; - Vertex backVertex; - backVertex.vertex = rc; - curVertex->branches.push_back((Branch())); - curVertex->branches.back().tail.push_back(std::make_pair(edge, backVertex)); - if(targetForRing[nextId]) chain.hasNonSmilesRingClosure = true; - targetForRing[nextId] = true; - iter++; - } - } else { - // std::cout << "black" << std::endl; - iter++; - } - } - colour[curId] = Colour::Black; - } - - std::map idMap; - int nextMappedId = 1; - for(int id = 0; id != targetForRing.size(); id++) - if(targetForRing[id]) - idMap[id] = nextMappedId++; - Prettyfier pretty(targetForRing); - pretty(chain); - return std::make_pair(chain, idMap); -} - -} // namespace detail - -std::pair write(const lib::Graph::GraphType &g, const PropString &pString, bool withIds) { - if(num_vertices(g) == 0) return std::make_pair("", false); - using namespace detail; - auto[chain, idMap] = detail::write(g, pString, withIds); - - std::stringstream graphDFS; - Printer p(graphDFS, idMap); - p(chain); - return std::make_pair(graphDFS.str(), chain.hasNonSmilesRingClosure); -} - -} // namespace mod::lib::Graph::DFSEncoding \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/DFSEncoding.hpp b/libs/libmod/src/mod/lib/Graph/DFSEncoding.hpp deleted file mode 100644 index 8a57da6..0000000 --- a/libs/libmod/src/mod/lib/Graph/DFSEncoding.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef MOD_LIB_GRAPH_DFSENCODING_HPP -#define MOD_LIB_GRAPH_DFSENCODING_HPP - -#include -#include - -#include - -namespace mod::lib::Graph { -struct PropString; -} // namespace mod::lib::Graph -namespace mod::lib::Graph::DFSEncoding { - -lib::IO::Result parse(const std::string &dfs); -std::pair write(const GraphType &g, const PropString &pString, bool withIds); - -} // namespace mod::lib::Graph::DFSEncoding - -#endif // MOD_LIB_GRAPH_DFSENCODING_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/Properties/Depiction.cpp b/libs/libmod/src/mod/lib/Graph/IO/DepictionData.cpp similarity index 84% rename from libs/libmod/src/mod/lib/Graph/Properties/Depiction.cpp rename to libs/libmod/src/mod/lib/Graph/IO/DepictionData.cpp index ff91c77..ce8d2a4 100644 --- a/libs/libmod/src/mod/lib/Graph/Properties/Depiction.cpp +++ b/libs/libmod/src/mod/lib/Graph/IO/DepictionData.cpp @@ -1,19 +1,19 @@ -#include "Depiction.hpp" +#include "DepictionData.hpp" #include #include #include #include +#include #include #include #include -#include #include #include -namespace mod::lib::Graph { +namespace mod::lib::Graph::Write { DepictionData::DepictionData(const LabelledGraph &lg) : lg(lg), hasMoleculeEncoding(true) { const auto &g = get_graph(lg); @@ -23,14 +23,14 @@ DepictionData::DepictionData(const LabelledGraph &lg) : lg(lg), hasMoleculeEncod std::vector atomUsed(AtomIds::Max + 1, false); Chem::markSpecialAtomsUsed(atomUsed); std::vector verticesToProcess; - for(Vertex v : asRange(vertices(g))) { + for(Vertex v: asRange(vertices(g))) { unsigned char atomId = pMol[v].getAtomId(); if(atomId != AtomIds::Invalid) atomUsed[atomId] = true; else verticesToProcess.push_back(v); } // map non-atom labels to atoms std::map labelNoStuff; - for(Vertex v : verticesToProcess) { + for(Vertex v: verticesToProcess) { std::string label = std::get<0>(Chem::extractIsotopeChargeRadical(pString[v])); auto iter = labelNoStuff.find(label); if(iter == end(labelNoStuff)) { @@ -47,14 +47,14 @@ DepictionData::DepictionData(const LabelledGraph &lg) : lg(lg), hasMoleculeEncod break; } } - auto atomId = iter->second; + const auto atomId = iter->second; nonAtomToPhonyAtom[v] = AtomData(atomId, pMol[v].getCharge(), pMol[v].getRadical()); } } { // edgeData - for(Edge e : asRange(edges(g))) { - if(pMol[e] == BondType::Invalid) nonBondEdges[e] = pString[e]; - } + for(Edge e: asRange(edges(g))) + if(pMol[e] == BondType::Invalid) + nonBondEdges[e] = pString[e]; } } @@ -91,42 +91,62 @@ BondType DepictionData::getBondData(Edge e) const { return get_molecule(lg)[e]; } -bool DepictionData::hasImportantStereo(Vertex v) const { - if(!has_stereo(lg)) return false; - return !get_stereo(lg)[v]->morphismDynamicOk(); -} - std::string DepictionData::getEdgeLabel(Edge e) const { if(!hasMoleculeEncoding) MOD_ABORT; - auto bt = getBondData(e); + const auto bt = getBondData(e); if(bt != BondType::Invalid) return std::string(1, Chem::bondToChar(bt)); - auto iter = nonBondEdges.find(e); + const auto iter = nonBondEdges.find(e); assert(iter != end(nonBondEdges)); return iter->second; } -const AtomData &DepictionData::operator()(Vertex v) const { - if(get_molecule(lg)[v].getAtomId() != AtomIds::Invalid) return get_molecule(lg)[v]; - else { - auto iter = nonAtomToPhonyAtom.find(v); - assert(iter != end(nonAtomToPhonyAtom)); - return iter->second; - } +bool DepictionData::hasImportantStereo(Vertex v) const { + if(!has_stereo(lg)) return false; + return !get_stereo(lg)[v]->morphismDynamicOk(); } -BondType DepictionData::operator()(Edge e) const { - auto eType = get_molecule(lg)[e]; - return eType != BondType::Invalid ? eType : BondType::Single; +lib::IO::Graph::Write::EdgeFake3DType DepictionData::getEdgeFake3DType(Edge e, bool withHydrogen) const { + if(!has_stereo(lg)) + return lib::IO::Graph::Write::EdgeFake3DType::None; +#ifndef MOD_HAVE_OPENBABEL + throw FatalError(MOD_NO_OPENBABEL_ERROR_STR); +#else + const auto idSrc = get(boost::vertex_index_t(), get_graph(lg), source(e, get_graph(lg))); + const auto idTar = get(boost::vertex_index_t(), get_graph(lg), target(e, get_graph(lg))); + const auto &mol = getOB(withHydrogen); + return mol.getBondFake3D(idSrc, idTar); +#endif +} + +std::string DepictionData::getRawStereoString(Vertex v) const { + const auto &conf = *get_stereo(lg)[v]; + const auto getNeighbourId = [&](const lib::Stereo::EmbeddingEdge &emb) { + const auto &g = get_graph(lg); + return get(boost::vertex_index_t(), g, target(emb.getEdge(v, g), g)); + }; + return conf.asRawString(getNeighbourId); +} + +std::string DepictionData::getPrettyStereoString(Vertex v) const { + const auto &conf = *get_stereo(lg)[v]; + const auto getNeighbourId = [&](const lib::Stereo::EmbeddingEdge &emb) { + const auto &g = get_graph(lg); + return get(boost::vertex_index_t(), g, target(emb.getEdge(v, g), g)); + }; + return conf.asPrettyString(getNeighbourId); +} + +std::string DepictionData::getStereoString(Edge e) const { + const auto cat = get_stereo(lg)[e]; + return boost::lexical_cast(cat); } bool DepictionData::getHasCoordinates() const { #ifdef MOD_HAVE_OPENBABEL - if(getConfig().io.useOpenBabelCoords.get()) - return hasMoleculeEncoding; - else return false; + return hasMoleculeEncoding; #else return false; -#endif +#endif } double DepictionData::getX(Vertex v, bool withHydrogen) const { @@ -143,7 +163,7 @@ double DepictionData::getX(Vertex v, bool withHydrogen) const { double DepictionData::getY(Vertex v, bool withHydrogen) const { if(!getHasCoordinates()) MOD_ABORT; #ifdef MOD_HAVE_OPENBABEL - unsigned int vId = get(boost::vertex_index_t(), get_graph(lg), v); + const auto vId = get(boost::vertex_index_t(), get_graph(lg), v); const auto &mol = getOB(withHydrogen); return mol.getAtomY(vId); #else @@ -151,40 +171,23 @@ double DepictionData::getY(Vertex v, bool withHydrogen) const { #endif } -lib::IO::Graph::Write::EdgeFake3DType DepictionData::getEdgeFake3DType(Edge e, bool withHydrogen) const { - if(!has_stereo(lg)) - return lib::IO::Graph::Write::EdgeFake3DType::None; -#ifdef MOD_HAVE_OPENBABEL - auto idSrc = get(boost::vertex_index_t(), get_graph(lg), source(e, get_graph(lg))); - auto idTar = get(boost::vertex_index_t(), get_graph(lg), target(e, get_graph(lg))); - const auto &mol = getOB(withHydrogen); - return mol.getBondFake3D(idSrc, idTar); -#else - MOD_ABORT; -#endif -} - -std::string DepictionData::getRawStereoString(Vertex v) const { - const auto &conf = *get_stereo(lg)[v]; - const auto getNeighbourId = [&](const lib::Stereo::EmbeddingEdge & emb) { - const auto &g = get_graph(lg); - return get(boost::vertex_index_t(), g, target(emb.getEdge(v, g), g)); - }; - return conf.asRawString(getNeighbourId); +int DepictionData::getOutputId(Vertex v) const { + return get(boost::vertex_index_t(), get_graph(lg), v); } -std::string DepictionData::getPrettyStereoString(Vertex v) const { - const auto &conf = *get_stereo(lg)[v]; - const auto getNeighbourId = [&](const lib::Stereo::EmbeddingEdge & emb) { - const auto &g = get_graph(lg); - return get(boost::vertex_index_t(), g, target(emb.getEdge(v, g), g)); - }; - return conf.asPrettyString(getNeighbourId); +const AtomData &DepictionData::operator()(Vertex v) const { + if(get_molecule(lg)[v].getAtomId() != AtomIds::Invalid) { + return get_molecule(lg)[v]; + } else { + auto iter = nonAtomToPhonyAtom.find(v); + assert(iter != end(nonAtomToPhonyAtom)); + return iter->second; + } } -std::string DepictionData::getStereoString(Edge e) const { - const auto cat = get_stereo(lg)[e]; - return boost::lexical_cast(cat); +BondType DepictionData::operator()(Edge e) const { + const auto eType = get_molecule(lg)[e]; + return eType != BondType::Invalid ? eType : BondType::Single; } void DepictionData::setImage(std::shared_ptr> image) { @@ -203,9 +206,9 @@ std::string DepictionData::getImageCommand() const { return imageCmd; } +#ifdef MOD_HAVE_OPENBABEL const lib::Chem::OBMolHandle &DepictionData::getOB(bool withHydrogen) const { if(!hasMoleculeEncoding) MOD_ABORT; -#ifdef MOD_HAVE_OPENBABEL if(!obMolAll) { const auto &g = get_graph(lg); const auto *pStereo = has_stereo(lg) ? &get_stereo(lg) : nullptr; @@ -217,7 +220,7 @@ const lib::Chem::OBMolHandle &DepictionData::getOB(bool withHydrogen) const { obMolNoHydrogen = Chem::makeOBMol(g, std::cref(*this), std::cref(*this), hasImportantStereo, false, pStereo); } return withHydrogen ? obMolAll : obMolNoHydrogen; -#endif } +#endif -} // namespace mod::lib::Graph \ No newline at end of file +} // namespace mod::lib::Graph::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/Properties/Depiction.hpp b/libs/libmod/src/mod/lib/Graph/IO/DepictionData.hpp similarity index 70% rename from libs/libmod/src/mod/lib/Graph/Properties/Depiction.hpp rename to libs/libmod/src/mod/lib/Graph/IO/DepictionData.hpp index c133808..caeb42f 100644 --- a/libs/libmod/src/mod/lib/Graph/Properties/Depiction.hpp +++ b/libs/libmod/src/mod/lib/Graph/IO/DepictionData.hpp @@ -1,7 +1,8 @@ -#ifndef MOD_LIB_GRAPH_DEPICTION_HPP -#define MOD_LIB_GRAPH_DEPICTION_HPP +#ifndef MOD_LIB_GRAPH_IO_DEPICTIONDATA_HPP +#define MOD_LIB_GRAPH_IO_DEPICTIONDATA_HPP #include +#include #include #include @@ -14,34 +15,42 @@ template class Function; } // namespace mod namespace mod::lib::IO::Graph::Write { -enum class EdgeFake3DType; +enum struct EdgeFake3DType; } // namespace mod::lib::IO::Graph::Write namespace mod::lib::Graph { struct PropMolecule; struct PropString; +} // namespace mod::lib::Grpah +namespace mod::lib::Graph::Write { -class DepictionData { +struct DepictionData { DepictionData(const DepictionData &) = delete; DepictionData &operator=(const DepictionData &) = delete; public: DepictionData(const LabelledGraph &lg); - AtomId getAtomId(Vertex v) const; // shortcut to moleculeState - Isotope getIsotope(Vertex v) const; // shortcut to moleculeState - Charge getCharge(Vertex v) const; // shortcut to moleculeState - bool getRadical(Vertex v) const; // shortcut to moleculeState +public: // used in GraphWriteGeneric + AtomId getAtomId(Vertex v) const; // shortcut to PropMolecule + Isotope getIsotope(Vertex v) const; // shortcut to PropMolecule + Charge getCharge(Vertex v) const; // shortcut to PropMolecule + bool getRadical(Vertex v) const; // shortcut to PropMolecule std::string getVertexLabelNoIsotopeChargeRadical(Vertex v) const; - BondType getBondData(Edge e) const; // shortcut to moleculeState - bool hasImportantStereo(Vertex v) const; + BondType getBondData(Edge e) const; // shortcut to PropMolecule std::string getEdgeLabel(Edge e) const; - const AtomData &operator()(Vertex v) const; // fake data - BondType operator()(Edge e) const; // fake data - bool getHasCoordinates() const; - double getX(Vertex v, bool withHydrogen) const; - double getY(Vertex v, bool withHydrogen) const; + bool hasImportantStereo(Vertex v) const; lib::IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(Edge e, bool withHydrogen) const; std::string getRawStereoString(Vertex v) const; std::string getPrettyStereoString(Vertex v) const; std::string getStereoString(Edge e) const; + bool getHasCoordinates() const; + // pre: getHasCoordinates() + double getX(Vertex v, bool withHydrogen) const; + // pre: getHasCoordinates() + double getY(Vertex v, bool withHydrogen) const; +public: + int getOutputId(Vertex v) const; +public: // used for Coordinate handling + const AtomData &operator()(Vertex v) const; // fake data + BondType operator()(Edge e) const; // fake data public: // custom depiction void setImage(std::shared_ptr > image); std::shared_ptr > getImage() const; @@ -64,6 +73,6 @@ class DepictionData { std::string imageCmd; }; -} // namespace mod::lib::Graph +} // namespace mod::lib::Graph::Write -#endif // MOD_LIB_GRAPH_DEPICTION_HPP \ No newline at end of file +#endif // MOD_LIB_GRAPH_IO_DEPICTIONDATA_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/IO/Read.cpp b/libs/libmod/src/mod/lib/Graph/IO/Read.cpp new file mode 100644 index 0000000..5b29061 --- /dev/null +++ b/libs/libmod/src/mod/lib/Graph/IO/Read.cpp @@ -0,0 +1,524 @@ +#include "Read.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace mod::lib::Graph::Read { + +namespace GML = lib::IO::GML; +using lib::IO::Result; + +Data::Data() = default; + +Data::Data(Data &&other) : g(std::move(other.g)), pString(std::move(other.pString)), pStereo(std::move(other.pStereo)), + externalToInternalIds(std::move(other.externalToInternalIds)) {} + +Data::~Data() { + if(std::uncaught_exceptions() != 0) return; + if(g) MOD_ABORT; + if(pString) MOD_ABORT; + if(pStereo) MOD_ABORT; +} + +void Data::reset() { + pStereo.reset(); + pString.reset(); + g.reset(); +} + +Result> gml(lib::IO::Warnings &warnings, std::string_view src) { + GML::Graph gGML; + { + gml::ast::KeyValue ast; + try { + ast = gml::parser::parse(src); + } catch(const gml::parser::error &e) { + return lib::IO::Result<>::Error(e.what()); + } + using namespace gml::converter::edsl; + auto cVertex = GML::makeVertexConverter(1); + auto cEdge = GML::makeEdgeConverter(1); + auto cGraph = list("graph")(cVertex)(cEdge); + auto iterBegin = * + auto iterEnd = iterBegin + 1; + try { + gml::converter::convert(iterBegin, iterEnd, cGraph, gGML); + } catch(const gml::converter::error &e) { + return Result<>::Error(e.what()); + } + } + + std::sort(begin(gGML.vertices), end(gGML.vertices), [](const GML::Vertex &v1, const GML::Vertex &v2) -> bool { + return v1.id < v2.id; + }); + + // Check that is mathematically is a graph + + std::unordered_map globalIddFromExtID; + { + int i = 0; + for(const auto &vGML: gGML.vertices) { + if(globalIddFromExtID.find(vGML.id) != end(globalIddFromExtID)) + return Result<>::Error("Vertex id " + std::to_string(vGML.id) + " used multiple times."); + globalIddFromExtID.emplace(vGML.id, i++); + } + } + + for(const auto &eGML: gGML.edges) { + if(eGML.source == eGML.target) + return Result<>::Error("Loop edge (on " + std::to_string(eGML.source) + ") is not allowed."); + if(globalIddFromExtID.find(eGML.source) == end(globalIddFromExtID)) + return Result<>::Error("Source " + std::to_string(eGML.source) + " does not exist in '" + + boost::lexical_cast(eGML) + "'."); + if(globalIddFromExtID.find(eGML.target) == end(globalIddFromExtID)) + return Result<>::Error("Target " + std::to_string(eGML.target) + " does not exist in '" + + boost::lexical_cast(eGML) + "'."); + } + + // Now we can calculate connected components and start converting data + + ConnectedComponents components(gGML.vertices.size()); + for(const auto &e: gGML.edges) { + const auto iterSrc = globalIddFromExtID.find(e.source); + const auto iterTar = globalIddFromExtID.find(e.target); + components.join(iterSrc->second, iterTar->second); + } + const auto numComponents = components.finalize(); + + std::vector datas(numComponents); + std::vector> extIDFromVertex(numComponents); + for(auto &d: datas) { + d.g = std::make_unique(); + d.pString = std::make_unique(*d.g); + } + + const auto atError = [&datas](std::string msg) -> Result<> { + for(auto &d: datas) d.reset(); + return Result<>::Error(std::move(msg)); + }; + + for(const auto &vGML: gGML.vertices) { + const auto comp = components[globalIddFromExtID.find(vGML.id)->second]; + auto &g = *datas[comp].g; + const auto v = add_vertex(g); + assert(vGML.label); + datas[comp].pString->addVertex(v, *vGML.label); + datas[comp].externalToInternalIds.emplace(vGML.id, get(boost::vertex_index_t(), g, v)); + extIDFromVertex[comp].emplace(v, vGML.id); + } + const auto vFromVertexId = [&](int id) { + const auto globalIDIter = globalIddFromExtID.find(id); + assert(globalIDIter != end(globalIddFromExtID)); + const auto comp = components[globalIDIter->second]; + const auto vIdIter = datas[comp].externalToInternalIds.find(id); + assert(vIdIter != end(datas[comp].externalToInternalIds)); + return std::pair(comp, vertex(vIdIter->second, *datas[comp].g)); + }; + for(const auto &eGML: gGML.edges) { + const auto[comp, vSrc] = vFromVertexId(eGML.source); + const auto[compTar, vTar] = vFromVertexId(eGML.target); + assert(comp == compTar); + auto &g = *datas[comp].g; + const auto eQuery = edge(vSrc, vTar, g); + if(eQuery.second) + return atError("Duplicate edge with source " + std::to_string(eGML.source) + + " and target " + std::to_string(eGML.target) + "."); + const auto e = add_edge(vSrc, vTar, g); + assert(eGML.label); + datas[comp].pString->addEdge(e.first, *eGML.label); + } + + bool doStereo = false; + for(const auto &vGML: gGML.vertices) doStereo = doStereo || vGML.stereo; + for(const auto &eGML: gGML.edges) doStereo = doStereo || eGML.stereo; + if(!doStereo) return std::move(datas); // TODO: remove std::move when C++20/P1825R0 is available + // Stereo + //============================================================================ + std::vector molStates; + std::vector> stereoInferences; + molStates.reserve(numComponents); + stereoInferences.reserve(numComponents); + for(int i = 0; i != numComponents; ++i) { + const auto &g = *datas[i].g; + molStates.emplace_back(g, *datas[i].pString); + stereoInferences.push_back(lib::Stereo::Inference(g, molStates.back(), false)); + } + + const auto &gGeometry = lib::Stereo::getGeometryGraph(); + // Set the explicitly defined edge categories. + //---------------------------------------------------------------------------- + for(const auto &eGML: gGML.edges) { + const auto[comp, vSrc] = vFromVertexId(eGML.source); + const auto[compTar, vTar] = vFromVertexId(eGML.target); + assert(comp == compTar); + const auto &g = *datas[comp].g; + const auto ePair = edge(vSrc, vTar, g); + assert(ePair.second); + if(!eGML.stereo) continue; + const std::string &s = *eGML.stereo; + if(s.size() != 1) + return atError( + "Error in stereo data for edge (" + std::to_string(eGML.source) + + ", " + std::to_string(eGML.target) + "). Parsing error in stereo data '" + s + "'."); + lib::Stereo::EdgeCategory cat; + switch(s.front()) { + case '*': + cat = lib::Stereo::EdgeCategory::Any; + break; + default: + return atError("Error in stereo data for edge (" + std::to_string(eGML.source) + + ", " + std::to_string(eGML.target) + "). Parsing error in stereo data '" + s + "'."); + } + if(auto res = stereoInferences[comp].assignEdgeCategory(ePair.first, cat); !res) + return atError("Error in stereo data for edge (" + std::to_string(eGML.source) + + ", " + std::to_string(eGML.target) + "). " + + res.extractError()); + } + // Set the explicitly stereo data. + //---------------------------------------------------------------------------- + for(auto &vGML: gGML.vertices) { + if(!vGML.stereo) continue; + const auto[comp, v] = vFromVertexId(vGML.id); + if(auto res = lib::Stereo::Read::parseEmbedding(*vGML.stereo)) { + vGML.parsedEmbedding = std::move(*res); + } else { + return atError("Error in stereo data for vertex " + std::to_string(vGML.id) + ". " + + res.extractError()); + } + // Geometry + //.......................................................................... + const auto &embGML = *vGML.parsedEmbedding; + if(embGML.geometry) { + const auto vGeo = gGeometry.findGeometry(*embGML.geometry); + if(vGeo == gGeometry.nullGeometry()) + return atError("Error in stereo data for vertex " + std::to_string(vGML.id) + + ". Invalid gGeometry '" + *embGML.geometry + "'."); + if(auto res = stereoInferences[comp].assignGeometry(v, vGeo); !res) + return atError("Error in stereo data for vertex " + std::to_string(vGML.id) + ". " + + res.extractError()); + } + // Edges + //.......................................................................... + if(embGML.edges) { + stereoInferences[comp].initEmbedding(v); + for(const auto &e: *embGML.edges) { + if(const int *idPtr = std::get_if(&e)) { + const auto extIDNeighbour = *idPtr; + if(globalIddFromExtID.find(extIDNeighbour) == end(globalIddFromExtID)) + return atError("Neighbour vertex " + std::to_string(extIDNeighbour) + + " in stereo embedding for vertex " + std::to_string(vGML.id) + " does not exist."); + const auto[compNeighbour, vNeighbour] = vFromVertexId(extIDNeighbour); + const auto ePair = edge(v, vNeighbour, *datas[comp].g); + if(!ePair.second) + return atError("Error in graph GML. Vertex " + std::to_string(extIDNeighbour) + + " in stereo embedding for vertex " + std::to_string(vGML.id) + " is not a neighbour."); + assert(compNeighbour == comp); + stereoInferences[comp].addEdge(v, ePair.first); + } else if(const char *virtPtr = std::get_if(&e)) { + switch(*virtPtr) { + case 'e': + stereoInferences[comp].addLonePair(v); + break; + case 'r': + stereoInferences[comp].addRadical(v); + break; + default: + MOD_ABORT; // the parser should know what is allowed, nope, not any more +// return Result<>::Error( +// "Error in graph GML. Virtual neighbour in stereo embedding for vertex " + +// std::to_string(vId) + " in " + side + " has unknown type '" + *virtPtr + "'."); + } + } else { + MOD_ABORT; // the parser should know what is allowed + } + } + } + // Fixation + //.......................................................................... + if(embGML.fixation) { + // TODO: expand this when more complicated geometries are implemented + const bool isFixed = *embGML.fixation; + if(isFixed) stereoInferences[comp].fixSimpleGeometry(v); + } + } // end of explicit stereo data + + for(int comp = 0; comp != numComponents; ++comp) { + // TODO: the warning should only be printed once, instead of for each connected component + lib::IO::Warnings stereoWarnings; + auto stereoResult = stereoInferences[comp].finalize( + stereoWarnings, [comp, &extIDFromVertex](lib::Graph::Vertex v) { + const auto iter = extIDFromVertex[comp].find(v); + assert(iter != extIDFromVertex[comp].end()); + return std::to_string(iter->second); + }); + warnings.addFrom(std::move(stereoWarnings), !getConfig().stereo.silenceDeductionWarnings.get()); + if(!stereoResult) + return atError(stereoResult.extractError()); + datas[comp].pStereo = std::make_unique(*datas[comp].g, std::move(stereoInferences[comp])); + } + return std::move(datas); // TODO: remove std::move when C++20/P1825R0 is available +} + +namespace { +namespace dfsDetail { +using namespace IO::DFS; +using Vertex = IO::DFS::Vertex; +using Edge = IO::DFS::Edge; + +using GVertex = lib::Graph::Vertex; +using GEdge = lib::Graph::Edge; + +struct JoinConnected { + using ConvertRes = std::pair; +public: + JoinConnected(ConnectedComponents &components) : components(components) {} + + void operator()(const Chain &chain) { + auto[prev, isRingClosure] = (*this)(chain.head, nullptr); + assert(!isRingClosure); + assert(prev); + for(const EVPair &ev: chain.tail) { + prev = (*this)(ev, prev); + assert(prev); + } + } + + ConvertRes operator()(const Vertex &vertex, const LabelVertex *prev) { + const auto[subNext, subIsRingClosure] = boost::apply_visitor(*this, vertex.vertex); + assert(!(subIsRingClosure && prev == nullptr)); + const auto *branchRoot = subIsRingClosure ? prev : subNext; + for(const Branch &branch: vertex.branches) { + const auto *branchPrev = branchRoot; + for(const EVPair &ev: branch.tail) { + branchPrev = (*this)(ev, branchPrev); + assert(branchPrev); + } + } + return {subNext, subIsRingClosure}; + } + + const LabelVertex *operator()(const EVPair &ev, const LabelVertex *prev) { + const auto[next, isRingClosure] = (*this)(ev.second, prev); + join(prev, next, ev.first.label); + if(isRingClosure) return prev; + else return next; + } + + ConvertRes operator()(const LabelVertex &vertex) { + if(vertex.ringClosure) + join(&vertex, vertex.ringClosure, "-"); + return {&vertex, false}; + } + + ConvertRes operator()(const RingClosure &vertex) { + return {vertex.other, true}; + } +private: + void join(const LabelVertex *src, const LabelVertex *tar, const std::string &label) { + if(label.empty()) return; // a dot edge for no-edge + components.join(src->connectedComponentID, tar->connectedComponentID); + } +private: + ConnectedComponents &components; +}; + +struct Converter { + struct ConvertRes { + int component; + GVertex next; + bool isRingClosure; + }; +public: + Converter(std::vector> &gPtrs, + std::vector> &pStringPtrs, + const ConnectedComponents &components) + : gPtrs(gPtrs), pStringPtrs(pStringPtrs), components(components) {} + + void operator()(Chain &chain) { + const auto sub = (*this)(chain.head, -1, lib::Graph::GraphType::null_vertex()); + int component = sub.component; + GVertex vPrev = sub.next; + assert(!sub.isRingClosure); + assert(vPrev != lib::Graph::GraphType::null_vertex()); + for(EVPair &ev: chain.tail) { + std::tie(component, vPrev) = (*this)(ev, component, vPrev); + assert(vPrev != lib::Graph::GraphType::null_vertex()); + } + } + + ConvertRes operator()(Vertex &vertex, int component, GVertex prev) { + const auto sub = boost::apply_visitor(*this, vertex.vertex); + assert(!(sub.isRingClosure && prev == lib::Graph::GraphType::null_vertex())); + const GVertex branchRoot = sub.isRingClosure ? prev : sub.next; + const int branchRootComponent = sub.isRingClosure ? component : sub.component; + for(Branch &branch: vertex.branches) { + int componentPrev = branchRootComponent; + GVertex branchPrev = branchRoot; + for(EVPair &ev: branch.tail) { + std::tie(componentPrev, branchPrev) = (*this)(ev, componentPrev, branchPrev); + assert(branchPrev != lib::Graph::GraphType::null_vertex()); + } + } + return sub; + } + + std::pair operator()(EVPair &ev, int component, GVertex prev) { + const auto res = (*this)(ev.second, component, prev); + makeEdge(component, prev, res.component, res.next, ev.first.label); + if(res.isRingClosure) return {component, prev}; + else return {res.component, res.next}; + } + + ConvertRes operator()(LabelVertex &vDFS) { + const int component = components[vDFS.connectedComponentID]; + const GVertex v = add_vertex(*gPtrs[component]); + pStringPtrs[component]->addVertex(v, vDFS.label); + if(vDFS.ringClosure) { + const auto componentRing = components[vDFS.ringClosure->connectedComponentID]; + const auto vRing = vertex(vDFS.ringClosure->gVertexId, *gPtrs[componentRing]); + makeEdge(component, v, componentRing, vRing, "-"); + } + vDFS.gVertexId = get(boost::vertex_index_t(), *gPtrs[component], v); + return {component, v, false}; + } + + ConvertRes operator()(RingClosure &rc) { + const int component = components[rc.other->connectedComponentID]; + return {component, vertex(rc.other->gVertexId, *gPtrs[component]), true}; + } +private: + void makeEdge(int srcComponent, GVertex vSrc, int tarComponent, GVertex vTar, const std::string &label) { + if(label.empty()) return; // a dot edge for no-edge + assert(srcComponent == tarComponent); + assert(vSrc != vTar); + assert(!edge(vSrc, vTar, *gPtrs[srcComponent]).second); + std::pair e = add_edge(vSrc, vTar, *gPtrs[srcComponent]); + assert(e.second); + pStringPtrs[srcComponent]->addEdge(e.first, label); + } +private: + std::vector> &gPtrs; + std::vector> &pStringPtrs; + const ConnectedComponents &components; +}; + +struct ImplicitHydrogenAdder { + ImplicitHydrogenAdder(std::vector> &gPtrs, + std::vector> &pStringPtrs, + const ConnectedComponents &components) + : gPtrs(gPtrs), pStringPtrs(pStringPtrs), components(components) {} + + void operator()(const Chain &chain) { + (*this)(chain.head); + for(const auto &ev: chain.tail) (*this)(ev.second); + } + + void operator()(const Vertex &vertex) { + boost::apply_visitor(*this, vertex.vertex); + for(const auto &b: vertex.branches) + for(const auto &ev: b.tail) (*this)(ev.second); + } + + void operator()(const RingClosure &) {} + + void operator()(const LabelVertex &vDFS) { + if(!vDFS.implicit) return; + // we can only add hydrogens if all incident edges are valid bonds + const int component = components[vDFS.connectedComponentID]; + auto &g = *gPtrs[component]; + const auto gVertex = vertex(vDFS.gVertexId, g); + auto &pString = *pStringPtrs[component]; + for(auto eOut: asRange(out_edges(gVertex, g))) + if(lib::Chem::decodeEdgeLabel(pString[eOut]) == BondType::Invalid) + return; + const auto atomId = lib::Chem::atomIdFromSymbol(vDFS.label); + const auto iter = std::find(begin(lib::Chem::getSmilesOrganicSubset()), + end(lib::Chem::getSmilesOrganicSubset()), atomId); + if(iter == end(lib::Chem::getSmilesOrganicSubset())) + MOD_ABORT; + const auto hydrogenAdder = [](lib::Graph::GraphType &g, lib::Graph::PropString &pString, + lib::Graph::Vertex p) { + const GVertex v = add_vertex(g); + pString.addVertex(v, "H"); + const GEdge e = add_edge(v, p, g).first; + pString.addEdge(e, "-"); + }; + lib::Chem::addImplicitHydrogens(g, pString, gVertex, atomId, hydrogenAdder); + } +private: + std::vector> &gPtrs; + std::vector> &pStringPtrs; + const ConnectedComponents &components; +}; + +} // namespace dfsDetail +} // namespace + +Result> dfs(lib::IO::Warnings &warnings, std::string_view src) { + auto astRes = lib::IO::DFS::Read::graph(src); + if(!astRes) return lib::IO::Result<>::Error(astRes.extractError()); + auto &[astPtr, numVertices, vertexFromId] = *astRes; + auto &ast = *astPtr; + + ConnectedComponents components(numVertices); + (dfsDetail::JoinConnected(components)(ast)); + const int numComponents = components.finalize(); + for(int i = 0; i != numVertices; ++i) { + assert(components[i] >= 0); + assert(components[i] < numComponents); + } + + std::vector> gPtrs(numComponents); + std::vector> pStringPtrs(numComponents); + for(int i = 0; i != numComponents; ++i) { + gPtrs[i] = std::make_unique(); + pStringPtrs[i] = std::make_unique(*gPtrs[i]); + } + + dfsDetail::Converter(gPtrs, pStringPtrs, components)(ast); + dfsDetail::ImplicitHydrogenAdder(gPtrs, pStringPtrs, components)(ast); + std::vector datas(numComponents); + for(int i = 0; i != numComponents; ++i) { + assert(gPtrs[i]); + datas[i].g = std::move(gPtrs[i]); + datas[i].pString = std::move(pStringPtrs[i]); + } + for(auto &&vp: vertexFromId) { + const int component = components[vp.second->connectedComponentID]; + datas[component].externalToInternalIds[vp.first] = vp.second->gVertexId; + } + return std::move(datas); // TODO: remove std::move when C++20/P1825R0 is available +} + +Result> smiles(lib::IO::Warnings &warnings, std::string_view src, const bool allowAbstract, + SmilesClassPolicy classPolicy) { + return lib::Chem::readSmiles(warnings, src, allowAbstract, classPolicy); +} + +Result> MDLMOL(lib::IO::Warnings &warnings, std::string_view src, const MDLOptions &options) { + return lib::Chem::readMDLMOL(warnings, src, options); +} + +Result>> +MDLSD(lib::IO::Warnings &warnings, std::string_view src, const MDLOptions &options) { + return lib::Chem::readMDLSD(warnings, src, options); +} + +} // namespace mod::lib::Graph::Read \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/IO/Read.hpp b/libs/libmod/src/mod/lib/Graph/IO/Read.hpp new file mode 100644 index 0000000..6d42165 --- /dev/null +++ b/libs/libmod/src/mod/lib/Graph/IO/Read.hpp @@ -0,0 +1,52 @@ +#ifndef MOD_LIB_GRAPH_IO_READ_HPP +#define MOD_LIB_GRAPH_IO_READ_HPP + +#include +#include + +#include +#include +#include +#include + +namespace mod { +enum class SmilesClassPolicy; +struct MDLOptions; +} // namespace mod +namespace mod::lib::Graph { +struct PropStereo; +struct PropString; +} // namespace mod::lib::Graph +namespace mod::lib::Graph::Read { + +struct Data { + Data(); + Data(std::unique_ptr graph, std::unique_ptr label); + Data(Data &&other); + ~Data(); + void reset(); +public: + std::unique_ptr g; + std::unique_ptr pString; + std::unique_ptr pStereo; + std::map externalToInternalIds; +}; + +struct RXNFileData { + std::vector> reactants, products; + // maps external IDs to )productOffset, external ID) + // if no map, then (-1, -1) + // std::vector>> aamap; +}; + +lib::IO::Result> gml(lib::IO::Warnings &warnings, std::string_view src); +lib::IO::Result> dfs(lib::IO::Warnings &warnings, std::string_view src); +lib::IO::Result> smiles(lib::IO::Warnings &warnings, std::string_view smiles, bool allowAbstract, + SmilesClassPolicy classPolicy); +lib::IO::Result> MDLMOL(lib::IO::Warnings &warnings, std::string_view src, const MDLOptions &options); +lib::IO::Result>> +MDLSD(lib::IO::Warnings &warnings, std::string_view src, const MDLOptions &options); + +} // namespace mod::lib::Graph::Read + +#endif // MOD_LIB_GRAPH_IO_READ_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/IO/Write.cpp b/libs/libmod/src/mod/lib/Graph/IO/Write.cpp new file mode 100644 index 0000000..56d2828 --- /dev/null +++ b/libs/libmod/src/mod/lib/Graph/IO/Write.cpp @@ -0,0 +1,665 @@ +#include "Write.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +namespace mod::lib::Graph::Write { +namespace { + +// returns the filename _without_ extension + +std::string getFilePrefix(const std::size_t gId) { + return lib::IO::makeUniqueFilePrefix() + "g_" + boost::lexical_cast(gId); +} + +void escapeLabelForDot(const std::string &label, std::ostream &s) { + for(char c: label) { + if(c == '"') s << "\\\""; + else if(c == '\\') s << "\\\\"; + else s << c; + } +} + +} // namespace + +void gml(const LabelledGraph &gLabelled, const DepictionData &depict, const std::size_t gId, + bool withCoords, std::ostream &s) { + if(!depict.getHasCoordinates() && withCoords) MOD_ABORT; + const auto &g = get_graph(gLabelled); + const auto &pString = get_string(gLabelled); + s << "graph [\n"; + for(auto v: asRange(vertices(g))) { + s << "\tnode [ id " << get(boost::vertex_index_t(), g, v) << " label \"" << pString[v] << "\""; + if(withCoords) + s << " vis2d [ x " << depict.getX(v, true) << " y " << depict.getY(v, true) << " ]"; + s << " ]\n"; + } + for(auto e: asRange(edges(g))) { + s << "\tedge [ source " << get(boost::vertex_index_t(), g, source(e, g)) + << " target " << get(boost::vertex_index_t(), g, target(e, g)) + << " label \"" << pString[e] << "\" ]\n"; + } + s << "]\n"; +} + +std::string gml(const Single &g, bool withCoords) { + static std::map, std::string> cache; + const auto iter = cache.find({g.getId(), withCoords}); + if(iter != end(cache)) return iter->second; + + std::string fileNoExt = getFilePrefix(g.getId()); + post::FileHandle s(fileNoExt + ".gml"); + gml(g.getLabelledGraph(), g.getDepictionData(), g.getId(), withCoords, s); + + cache[{g.getId(), withCoords}] = s; + return s; +} + +namespace { +namespace dfsDetail { +using namespace IO::DFS; +using Vertex = IO::DFS::Vertex; +using Edge = IO::DFS::Edge; + +using GVertex = lib::Graph::Vertex; +using GEdge = lib::Graph::Edge; + +enum class Colour { + White, Grey, Black +}; + +struct Printer { + Printer(std::ostream &s, const std::map &idMap) : s(s), idMap(idMap) {} + + void operator()(const Chain &chain) { + (*this)(chain.head); + (*this)(chain.tail); + } + + void operator()(const Vertex &v) { + boost::apply_visitor(*this, v.vertex); + for(const Branch &b: v.branches) { + s << "("; + (*this)(b.tail); + s << ")"; + } + } + + void operator()(const LabelVertex &v) { + s << "["; + escapeLabel(s, v.label, ']'); + s << "]"; + if(v.id) + if(const auto iter = idMap.find(*v.id); iter != idMap.end()) + s << iter->second; + } + + void operator()(const RingClosure &v) { + assert(v.id != std::numeric_limits::max()); + const auto iter = idMap.find(v.id); + assert(iter != idMap.end()); + s << iter->second; + } + + void operator()(const std::vector &evPairs) { + for(const EVPair &p: evPairs) { + (*this)(p.first); + (*this)(p.second); + } + } + + void operator()(const Edge &e) { + if(e.label.size() == 1) { + char c = e.label[0]; + switch(c) { + case '-': + return; + case ':': + case '=': + case '#': + s << c; + return; + } + } + s << "{"; + escapeLabel(s, e.label, '}'); + s << "}"; + } +private: + std::ostream &s; + const std::map &idMap; +}; + +struct Prettyfier { + struct EndsWithNumber { + bool operator()(const LabelVertex &v) const { + return v.id.has_value(); + } + + bool operator()(const RingClosure &v) const { + return true; + } + }; + + struct IsRingClorsure { + bool operator()(const LabelVertex &v) const { + return false; + } + + bool operator()(const RingClosure &v) const { + return true; + } + }; +public: + Prettyfier(const std::vector &targetForRing) : targetForRing(targetForRing) {} + + void operator()(Chain &chain) { + (*this)(chain.head); + (*this)(chain.tail); + if(chain.head.branches.size() > 0 && chain.tail.size() == 0) { + // V(B)..(B)(B) + chain.tail = std::move(chain.head.branches.back().tail); + chain.head.branches.pop_back(); + // V(B)..(B)B + } + } + + void operator()(Vertex &v) { + boost::apply_visitor(*this, v.vertex); + for(Branch &b: v.branches) (*this)(b.tail); + } + + void operator()(LabelVertex &v) { + if(v.id) + if(!targetForRing[*v.id]) v.id.reset(); + } + + void operator()(RingClosure &v) { + assert(v.id != std::numeric_limits::max()); + assert(targetForRing[v.id]); + } + + void operator()(std::vector &evPairs) { + for(EVPair &p: evPairs) { + (*this)(p.first); + (*this)(p.second); + } + if(evPairs.empty()) return; + // -EV...-EV + auto &lastVertex = evPairs.back().second; + if(lastVertex.branches.empty()) return; + // -EV...-EV(B)..(B)(B) + // pull up the last B to be more EV-pairs + if(lastVertex.branches.size() == 1 && boost::apply_visitor(EndsWithNumber(), lastVertex.vertex)) { + // -EV...-EVN(B) + // or + // -EV...-N(B) + assert(!lastVertex.branches.front().tail.empty()); + const auto &firstBranchEV = lastVertex.branches.front().tail.front(); + if(firstBranchEV.first.label == "-" && boost::apply_visitor(IsRingClorsure(), firstBranchEV.second.vertex)) { + // -EV...-EVN(M) + // or + // -EV...-N(M) + return; + } + } + std::vector lastTail = std::move(evPairs.back().second.branches.back().tail); + evPairs.back().second.branches.pop_back(); + evPairs.insert(end(evPairs), std::move_iterator(lastTail.begin()), std::move_iterator(lastTail.end())); + } + + void operator()(Edge &e) {} +private: + const std::vector &targetForRing; +}; + +std::pair> +write(const GraphType &g, const PropString &pString, bool withIds) { + using namespace detail; + using GEdgeIter = GraphType::out_edge_iterator; + using VertexInfo = std::pair>; + std::vector realVertices(num_vertices(g), nullptr); + Chain chain; + chain.hasNonSmilesRingClosure = false; + + std::vector colour(num_vertices(g), Colour::White); + // note: if withIds, pretend *all* vertices are targets for ring closures + std::vector targetForRing(num_vertices(g), withIds); + std::map edgeColour; + for(GEdge e: asRange(edges(g))) edgeColour[e] = Colour::White; + std::stack stack; + { // discover root + GVertex cur = *vertices(g).first; + unsigned int curId = get(boost::vertex_index_t(), g, cur); + assert(curId < colour.size()); + colour[curId] = Colour::Grey; + assert(!realVertices[curId]); + realVertices[curId] = &chain.head; + LabelVertex lv; + lv.id = curId; + lv.label = pString[cur]; + realVertices[curId]->vertex = lv; + stack.push(std::make_pair(cur, out_edges(cur, g))); + } + while(!stack.empty()) { + GVertex cur = stack.top().first; + GEdgeIter iter, iterEnd; + boost::tie(iter, iterEnd) = stack.top().second; + stack.pop(); + unsigned int curId = get(boost::vertex_index_t(), g, cur); + assert(realVertices[curId]); + Vertex *curVertex = realVertices[curId]; + // std::cout << "CurVertex: " << pString(cur) << std::endl; + while(iter != iterEnd) { + GVertex next = target(*iter, g); + GEdge test; + unsigned int nextId = get(boost::vertex_index_t(), g, next); + Edge edge; + edge.label = pString[*iter]; + // mark edge + Colour oldEdgeColour = edgeColour[*iter]; + edgeColour[*iter] = Colour::Black; + // std::cout << "\tEdge: " << pString(cur) + // << " ->(" << edge.label << ", " << (oldEdgeColour == Black ? "black" : "white") << ") " + // << pString(next) << "\t"; + if(colour[nextId] == Colour::White) { // tree edge, new vertex + // std::cout << "white" << std::endl; + // create the new vertex + assert(!realVertices[nextId]); + LabelVertex lv; + lv.id = nextId; + lv.label = pString[next]; + Vertex newVertex; + newVertex.vertex = lv; + curVertex->branches.push_back((Branch())); + curVertex->branches.back().tail.push_back(std::make_pair(edge, newVertex)); + Vertex *nextVertex = &curVertex->branches.back().tail.back().second; + realVertices[nextId] = nextVertex; + colour[nextId] = Colour::Grey; + // switch to the new vertex + iter++; + stack.push(std::make_pair(cur, std::make_pair(iter, iterEnd))); + cur = next; + curId = nextId; + curVertex = nextVertex; + boost::tie(iter, iterEnd) = out_edges(next, g); + // std::cout << "CurVertex: " << pString(cur)<< std::endl; + } else if(colour[nextId] == Colour::Grey) { // back edge, maybe an already traversed edge + // std::cout << "grey" << std::endl; + if(oldEdgeColour == Colour::Black) iter++; // already traversed + else { + RingClosure rc; + rc.id = nextId; + Vertex backVertex; + backVertex.vertex = rc; + curVertex->branches.push_back((Branch())); + curVertex->branches.back().tail.push_back(std::make_pair(edge, backVertex)); + if(targetForRing[nextId]) chain.hasNonSmilesRingClosure = true; + targetForRing[nextId] = true; + iter++; + } + } else { + // std::cout << "black" << std::endl; + iter++; + } + } + colour[curId] = Colour::Black; + } + + std::map idMap; + int nextMappedId = 1; + for(int id = 0; id != targetForRing.size(); id++) + if(targetForRing[id]) + idMap[id] = nextMappedId++; + Prettyfier pretty(targetForRing); + pretty(chain); + return std::make_pair(chain, idMap); +} + +} // namespace dfsDetail +} // namespace + +std::pair dfs(const LabelledGraph &gLabelled, bool withIds) { + const auto &g = get_graph(gLabelled); + const auto &pString = get_string(gLabelled); + if(num_vertices(g) == 0) return std::pair("", false); + auto[chain, idMap] = dfsDetail::write(g, pString, withIds); + + std::stringstream graphDFS; + dfsDetail::Printer p(graphDFS, idMap); + p(chain); + return std::pair(graphDFS.str(), chain.hasNonSmilesRingClosure); +} + +namespace { + +struct DotCacheEntry { + std::size_t id; + std::string prefix; +public: + friend bool operator<(const DotCacheEntry &a, const DotCacheEntry &b) { + return std::tie(a.id, a.prefix) < std::tie(b.id, b.prefix); + } +}; + +} // namespace + +std::string dot(const LabelledGraph &gLabelled, const std::size_t gId, const Options &options) { + static std::map cache; + const auto iter = cache.find({gId, options.graphvizPrefix}); + if(iter != end(cache)) return iter->second; + + // use _gv suffix so a coord file will not clash with non-gv coord files + post::FileHandle s(getFilePrefix(gId) + "_gv.dot"); + const auto &g = get_graph(gLabelled); + const auto &pString = get_string(gLabelled); + s << "graph G {\n"; + s << "\tnode [shape=plaintext]\n"; + if(!options.graphvizPrefix.empty()) + s << "\t" << options.graphvizPrefix << '\n'; + + for(const auto v: asRange(vertices(g))) { + s << '\t' << get(boost::vertex_index_t(), g, v) << " [ label=\""; + escapeLabelForDot(pString[v], s); + s << "\" ]\n"; + } + for(const auto e: asRange(edges(g))) { + const auto srcId = get(boost::vertex_index_t(), g, source(e, g)); + const auto tarId = get(boost::vertex_index_t(), g, target(e, g)); + const std::string &label = pString[e]; + if(label == "-" || label == "=" || label == "#") { + s << srcId << " -- " << tarId << '\n'; + if(label != "-") s << srcId << " -- " << tarId << '\n'; + if(label == "#") s << srcId << " -- " << tarId << '\n'; + } else { + s << srcId << " -- " << tarId << " [ label=\""; + escapeLabelForDot(label, s); + s << "\" ]\n"; + } + } + s << "}\n"; + cache[{gId, options.graphvizPrefix}] = s; + return s; +} + +namespace { + +struct OpenBabelCoordsCacheEntry { + std::size_t id; + bool collapseHydrogens; + int rotation; + bool mirror; +public: + friend bool operator<(const OpenBabelCoordsCacheEntry &a, const OpenBabelCoordsCacheEntry &b) { + return std::tie(a.id, a.collapseHydrogens, a.rotation, a.mirror) + < std::tie(b.id, b.collapseHydrogens, b.rotation, b.mirror); + } +}; + +} // namespace + +std::string coords(const LabelledGraph &gLabelled, const DepictionData &depict, + const std::size_t gId, const Options &options) { + if(options.withGraphvizCoords || !depict.getHasCoordinates()) { + // we map 1-to-1 a dot file to a coord file, so cache by the dot filename + static std::map cache; + + auto fileNoExt = dot(gLabelled, gId, options); + fileNoExt.erase(fileNoExt.end() - 4, fileNoExt.end()); + const auto iter = cache.find(fileNoExt); + if(iter != end(cache)) return iter->second; + + lib::IO::post() << "coordsFromGV graph \"" << fileNoExt << "\"\n"; + + // the coord file is still for the tex coord file which is just then created in post + std::string file = fileNoExt + "_coord.tex"; + cache[fileNoExt] = file; + return file; + } else { + static std::map cache; + const auto iter = cache.find({gId, options.collapseHydrogens, options.rotation, options.mirror}); + if(iter != end(cache)) return iter->second; + + const auto &g = get_graph(gLabelled); + std::string f = getFilePrefix(gId); + if(options.collapseHydrogens) f += "_mol"; + if(options.rotation != 0) f += "_r" + std::to_string(options.rotation); + if(options.mirror) f += "_m" + std::to_string(options.mirror); + post::FileHandle s(f + "_coord.tex"); + s << "% dummy\n"; + for(const auto v: asRange(vertices(g))) { + const auto vId = get(boost::vertex_index_t(), g, v); + if(options.collapseHydrogens && Chem::isCollapsibleHydrogen(v, g, depict, depict, [&depict](const auto v) { + return depict.hasImportantStereo(v); + })) + continue; + double x, y; + std::tie(x, y) = pointTransform( + depict.getX(v, !options.collapseHydrogens), + depict.getY(v, !options.collapseHydrogens), + options.rotation, options.mirror); + s << "\\coordinate[overlay] (\\modIdPrefix v-coord-" << vId << ") at (" + << std::fixed << x << ", " << y << ") {};\n"; + } + std::string file = s; + cache[{gId, options.collapseHydrogens, options.rotation, options.mirror}] = file; + return file; + } +} + +namespace { + +struct TikzCacheEntry { + std::size_t id; + std::string coordFile; + std::string options; + bool asInline; + std::string idPrefix; +public: + friend bool operator<(const TikzCacheEntry &a, const TikzCacheEntry &b) { + return std::tie(a.id, a.coordFile, a.options, a.asInline, a.idPrefix) + < std::tie(b.id, b.coordFile, b.options, b.asInline, b.idPrefix); + } +}; + +} // namespace + +std::pair +tikz(const LabelledGraph &gLabelled, const DepictionData &depict, const std::size_t gId, + const Options &options, + bool asInline, const std::string &idPrefix) { + static std::map cache; + + std::string strOptions = options.getStringEncoding(); + std::string fileCoordsExt = coords(gLabelled, depict, gId, options); + + const auto iter = cache.find({gId, fileCoordsExt, strOptions, asInline, idPrefix}); + if(iter != end(cache)) return std::pair(iter->second, fileCoordsExt); + + std::string file = getFilePrefix(gId) + "_" + strOptions; + if(asInline) file += "i"; + file += ".tex"; + post::FileHandle s(file); + tikz(s, options, get_graph(gLabelled), depict, fileCoordsExt, asInline, idPrefix); + + cache[{gId, fileCoordsExt, strOptions, asInline, idPrefix}] = file; + return std::pair(file, fileCoordsExt); +} + +std::string +pdf(const LabelledGraph &gLabelled, const DepictionData &depict, const std::size_t gId, + const Options &options) { + { // user-specified depiction + static std::map userCache; + auto iter = userCache.find(gId); + if(iter != end(userCache)) return iter->second; + auto fImage = depict.getImage(); + if(fImage) { + std::string imageNoExt = (*fImage)(); + if(imageNoExt.empty()) { + std::cout << "User-specified depiction file for graph with id " << gId << " can not be empty." << std::endl; + throw 0; + } + std::string cmd = depict.getImageCommand(); + if(!cmd.empty()) lib::IO::post() << cmd << '\n'; + std::string image = imageNoExt + ".pdf"; + userCache[gId] = image; + return image; + } + } + // auto-generated depiction + // maps 1-to-1 a (coord, tikz) pair to a PDF + static std::map, std::string> cache; + auto tikzFiles = tikz(gLabelled, depict, gId, options, false, ""); + + const auto iter = cache.find(tikzFiles); + if(iter != end(cache)) return iter->second; + + std::string fileNoExt = tikzFiles.first.substr(0, tikzFiles.first.size() - 4); + std::string fileCoordsNoExt = tikzFiles.second.substr(0, tikzFiles.second.size() - 4); + lib::IO::post() << "compileTikz \"" << fileNoExt << "\" \"" << fileCoordsNoExt << "\"\n"; + + fileNoExt += ".pdf"; + cache[tikzFiles] = fileNoExt; + return fileNoExt; +} + +std::string svg(const LabelledGraph &gLabelled, const DepictionData &depict, const std::size_t gId, + const Options &options) { + // maps 1-to-1 a PDF to an SVG + static std::map cache; + + std::string pdfFile = pdf(gLabelled, depict, gId, options); + + const auto iter = cache.find(pdfFile); + if(iter != end(cache)) return iter->second; + + std::string fileNoExt = pdfFile.substr(0, pdfFile.size() - 4); + lib::IO::post() << "pdfToSvg \"" << fileNoExt << "\" \"" << fileNoExt << "\"\n"; + + std::string file = fileNoExt + ".svg"; + cache[pdfFile] = file; + return file; +} + +std::pair summary(const Single &g, const Options &first, const Options &second) { + std::string graphLike = pdf(g, first); + std::string molLike = first == second ? "" : pdf(g, second); + lib::IO::post() << "summaryGraph \"" << g.getName() << "\" \"" + << std::string(begin(graphLike), end(graphLike) - 4) << "\" \""; + + if(!molLike.empty()) + lib::IO::post() << std::string(begin(molLike), end(molLike) - 4); + lib::IO::post() << "\"\n"; + + if(molLike.empty()) + return std::pair(graphLike, graphLike); + else + return std::pair(graphLike, molLike); +} + +void termState(const Single &g) { + using namespace lib::Term; + lib::IO::post() << "summarySubsection \"Term State for " << g.getName() << "\"\n"; + post::FileHandle s(lib::IO::makeUniqueFilePrefix() + "termState.tex"); + s << "\\begin{verbatim}\n"; + const auto &termState = get_term(g.getLabelledGraph()); + if(isValid(termState)) { + std::unordered_map > addrToVertex; + std::unordered_map > addrToEdge; + for(Vertex v: asRange(vertices(g.getGraph()))) { + Address a{AddressType::Heap, termState[v]}; + addrToVertex[a].insert(v); + } + for(Edge e: asRange(edges(g.getGraph()))) { + Address a{AddressType::Heap, termState[e]}; + addrToEdge[a].insert(e); + } + lib::Term::Write::wam(getMachine(termState), lib::Term::getStrings(), s, [&](Address addr, std::ostream &s) { + s << " "; + bool first = true; + for(auto v: addrToVertex[addr]) { + if(!first) s << ", "; + first = false; + s << "v" << get(boost::vertex_index_t(), g.getGraph(), v); + } + for(auto e: addrToEdge[addr]) { + if(!first) s << ", "; + first = false; + s << "e(" + << get(boost::vertex_index_t(), g.getGraph(), source(e, g.getGraph())) + << ", " + << get(boost::vertex_index_t(), g.getGraph(), target(e, g.getGraph())) + << ")"; + } + }); + } else { + std::string msg = "Parsing failed for graph '" + g.getName() + "'. " + termState.getParsingError(); + throw TermParsingError(std::move(msg)); + } + s << "\\end{verbatim}\n"; + lib::IO::post() << "summaryInput \"" << std::string(s) << "\"\n"; +} + +std::string stereoSummary(const Single &gLib, Vertex v, const lib::Stereo::Configuration &conf, + const IO::Graph::Write::Options &options, int shownIdOffset, const std::string &nameSuffix) { + const auto &g = gLib.getGraph(); + const auto vId = get(boost::vertex_index_t(), g, v); + std::string name = "g_" + boost::lexical_cast(gLib.getId()) + "_stereo_" + + boost::lexical_cast(vId); + IO::post() << "summarySubsection \"Stereo, g " << gLib.getId() << ", v " << vId + << nameSuffix << "\"\n"; + std::string f = lib::Stereo::Write::pdf(g, v, conf, name, gLib.getDepictionData(), options, + [shownIdOffset](const auto &g, const auto v) { + return get(boost::vertex_index_t(), g, v) + shownIdOffset; + }); + post::FileHandle s(IO::makeUniqueFilePrefix() + "stereo.tex"); + s << "\\begin{center}\n"; + s << "\\includegraphics{" << f << "}\\\\\n"; + s << "File: \\texttt{" << IO::escapeForLatex(f) << "}\n"; + s << "\\end{center}\n"; + IO::post() << "summaryInput \"" << std::string(s) << "\"\n"; + return f; +} + +//------------------------------------------------------------------------------ +// Simplified interface for Single +//------------------------------------------------------------------------------ + +void gml(const Single &g, bool withCoords, std::ostream &s) { + gml(g.getLabelledGraph(), g.getDepictionData(), g.getId(), withCoords, s); +} + +std::string tikz(const Single &g, const Options &options, bool asInline, const std::string &idPrefix) { + auto res = tikz(g.getLabelledGraph(), g.getDepictionData(), g.getId(), options, asInline, idPrefix); + return res.first; +} + +std::string pdf(const Single &g, const Options &options) { + return pdf(g.getLabelledGraph(), g.getDepictionData(), g.getId(), options); +} + +std::string svg(const Single &g, const Options &options) { + return svg(g.getLabelledGraph(), g.getDepictionData(), g.getId(), options); +} + +} // namespace mod::lib::Graph::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/IO/Write.hpp b/libs/libmod/src/mod/lib/Graph/IO/Write.hpp new file mode 100644 index 0000000..7e6cd1f --- /dev/null +++ b/libs/libmod/src/mod/lib/Graph/IO/Write.hpp @@ -0,0 +1,52 @@ +#ifndef MOD_LIB_GRAPH_IO_WRITE_HPP +#define MOD_LIB_GRAPH_IO_WRITE_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace mod::lib::Graph { +struct LabelledGraph; +struct Single; +} // namespace mod::lib::Graph +namespace mod::lib::Graph::Write { +struct DepictionData; +using Options = lib::IO::Graph::Write::Options; + +// all return the filename _with_ extension +void gml(const LabelledGraph &gLabelled, const DepictionData &depict, + const std::size_t gId, bool withCoords, std::ostream &s); +std::string gml(const Single &g, bool withCoords); +std::pair dfs(const LabelledGraph &gLabelled, bool withIds); + +std::string dot(const LabelledGraph &gLabelled, const std::size_t gId, const Options &options); +std::string coords(const LabelledGraph &gLabelled, const DepictionData &depict, + const std::size_t gId, const Options &options); +std::pair tikz(const LabelledGraph &gLabelled, + const DepictionData &depict, + const std::size_t gId, const Options &options, + bool asInline, const std::string &idPrefix); +std::string pdf(const LabelledGraph &gLabelled, const DepictionData &depict, + const std::size_t gId, const Options &options); +std::string svg(const LabelledGraph &gLabelled, const DepictionData &depict, + const std::size_t gId, const Options &options); +std::pair summary(const Single &g, const Options &first, const Options &second); +void termState(const Single &g); + +std::string stereoSummary(const Single &g, Vertex v, const lib::Stereo::Configuration &conf, + const IO::Graph::Write::Options &options, int shownIdOffset, const std::string &nameSuffix); + +// simplified interface for lib::Graph::Single +void gml(const Single &g, bool withCoords, std::ostream &s); +std::string tikz(const Single &g, const Options &options, bool asInline, const std::string &idPrefix); +std::string pdf(const Single &g, const Options &options); +std::string svg(const Single &g, const Options &options); + +} // namespace mod::lib::Graph::Write + +#endif // MOD_LIB_GRAPH_IO_WRITE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Graph/LabelledGraph.cpp b/libs/libmod/src/mod/lib/Graph/LabelledGraph.cpp index c229cb4..374093c 100644 --- a/libs/libmod/src/mod/lib/Graph/LabelledGraph.cpp +++ b/libs/libmod/src/mod/lib/Graph/LabelledGraph.cpp @@ -68,7 +68,7 @@ bool has_stereo(const LabelledGraph &g) { const LabelledGraph::PropStereoType &get_stereo(const LabelledGraph &g) { if(!has_stereo(g)) { - auto inference = lib::Stereo::makeInference(get_graph(g), get_molecule(g), false); + auto inference = lib::Stereo::Inference(get_graph(g), get_molecule(g), false); lib::IO::Warnings warnings; auto result = inference.finalize(warnings, [&g](Vertex v) { return std::to_string(get(boost::vertex_index_t(), get_graph(g), v)); diff --git a/libs/libmod/src/mod/lib/Graph/LabelledGraph.hpp b/libs/libmod/src/mod/lib/Graph/LabelledGraph.hpp index dc3a192..b4e51f9 100644 --- a/libs/libmod/src/mod/lib/Graph/LabelledGraph.hpp +++ b/libs/libmod/src/mod/lib/Graph/LabelledGraph.hpp @@ -21,6 +21,7 @@ struct LabelledGraph { // models a mod::lib::LabelledGraphConcept std::unique_ptr pStereo); LabelledGraph(const LabelledGraph &other); ~LabelledGraph(); +public: // LabelledGraphConcept friend GraphType &get_graph(LabelledGraph &g); friend const GraphType &get_graph(const LabelledGraph &g); friend PropStringType &get_string(LabelledGraph &g); diff --git a/libs/libmod/src/mod/lib/Graph/Properties/Molecule.cpp b/libs/libmod/src/mod/lib/Graph/Properties/Molecule.cpp index fb07437..e92e6b3 100644 --- a/libs/libmod/src/mod/lib/Graph/Properties/Molecule.cpp +++ b/libs/libmod/src/mod/lib/Graph/Properties/Molecule.cpp @@ -1,5 +1,6 @@ #include "Molecule.hpp" +#include #include #include #include @@ -9,6 +10,7 @@ #include #include +#include namespace mod::lib::Graph { @@ -47,7 +49,7 @@ bool PropMolecule::getIsMolecule() const { const lib::Chem::OBMolHandle &PropMolecule::getOBMol() const { if(!isMolecule) { std::cout << "MoleculeState: Trying to create OpenBabel::OBMol from non-molecule." << std::endl - << "Should DepictionData be used instead?" << std::endl; + << "Should DepictionData be used instead?" << std::endl; MOD_ABORT; } if(!obMol) { @@ -77,12 +79,11 @@ double PropMolecule::getExactMass() const { double PropMolecule::getEnergy() const { if(!energy) { #ifndef MOD_HAVE_OPENBABEL - MOD_NO_OPENBABEL_ERROR - std::cout << "Energy calculation is not possible without Open Babel." << std::endl; - std::cout << "Energy values can be manually cached on graphs if calculation is not desired." << std::endl; - std::exit(1); + throw FatalError(MOD_NO_OPENBABEL_ERROR_STR + + "\nEnergy calculation is not possible without Open Babel.\n" + + "Energy values can be manually cached on graphs if calculation is not desired."); #else - energy = getOBMol().getEnergy(); + energy = getOBMol().getEnergy(false); #endif } return *energy; diff --git a/libs/libmod/src/mod/lib/Graph/Properties/Term.cpp b/libs/libmod/src/mod/lib/Graph/Properties/Term.cpp index c36cf5f..a2f4dd5 100644 --- a/libs/libmod/src/mod/lib/Graph/Properties/Term.cpp +++ b/libs/libmod/src/mod/lib/Graph/Properties/Term.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include @@ -23,7 +23,7 @@ PropTerm::PropTerm(const GraphType &g, const PropString &pString, const StringSt } lib::Term::RawTerm rawTerm; try { - rawTerm = lib::IO::Term::Read::rawTerm(label, stringStore); + rawTerm = Term::Read::rawTerm(label, stringStore); } catch(const lib::IO::ParsingError &e) { parsingError = e.msg; return std::numeric_limits::max(); diff --git a/libs/libmod/src/mod/lib/Graph/Single.cpp b/libs/libmod/src/mod/lib/Graph/Single.cpp index 0a6eb9b..fa97fb4 100644 --- a/libs/libmod/src/mod/lib/Graph/Single.cpp +++ b/libs/libmod/src/mod/lib/Graph/Single.cpp @@ -1,12 +1,15 @@ #include "Single.hpp" #include +#include #include +#include #include #include #include -#include -#include +#include +#include +#include #include #include #include @@ -14,12 +17,14 @@ #include #include #include -#include #include #include #include #include +#include +#include +#include #include #include @@ -38,7 +43,7 @@ const std::string getGraphName(unsigned int id) { bool sanityCheck(const GraphType &g, const PropString &pString, std::ostream &s) { std::vector > edgesSorted; edgesSorted.reserve(num_edges(g)); - for(Edge e : asRange(edges(g))) { + for(Edge e: asRange(edges(g))) { Vertex v1 = source(e, g), v2 = target(e, g); if(get(boost::vertex_index_t(), g, v2) > get(boost::vertex_index_t(), g, v1)) std::swap(v1, v2); edgesSorted.emplace_back(v1, v2); @@ -115,12 +120,12 @@ void Single::setName(std::string name) { } const std::pair Single::getGraphDFS() const { - if(!dfs) std::tie(dfs, dfsHasNonSmilesRingClosure) = DFSEncoding::write(getGraph(), getStringState(), false); + if(!dfs) std::tie(dfs, dfsHasNonSmilesRingClosure) = Write::dfs(getLabelledGraph(), false); return std::pair(*dfs, dfsHasNonSmilesRingClosure); } const std::string &Single::getGraphDFSWithIds() const { - if(!dfsWithIds) dfsWithIds = DFSEncoding::write(getGraph(), getStringState(), true).first; + if(!dfsWithIds) dfsWithIds = Write::dfs(getLabelledGraph(), true).first; return *dfsWithIds; } @@ -167,7 +172,7 @@ const std::string &Single::getSmilesWithIds() const { unsigned int Single::getVertexLabelCount(const std::string &label) const { unsigned int count = 0; - for(Vertex v : asRange(vertices(getGraph()))) { + for(Vertex v: asRange(vertices(getGraph()))) { const std::string &vLabel = getStringState()[v]; if(vLabel == label) count++; } @@ -177,20 +182,20 @@ unsigned int Single::getVertexLabelCount(const std::string &label) const { unsigned int Single::getEdgeLabelCount(const std::string &label) const { unsigned int count = 0; - for(Edge e : asRange(edges(getGraph()))) { + for(Edge e: asRange(edges(getGraph()))) { const std::string &eLabel = getStringState()[e]; if(eLabel == label) count++; } return count; } -DepictionData &Single::getDepictionData() { - if(!depictionData) depictionData.reset(new DepictionData(getLabelledGraph())); +Write::DepictionData &Single::getDepictionData() { + if(!depictionData) depictionData.reset(new Write::DepictionData(getLabelledGraph())); return *depictionData; } -const DepictionData &Single::getDepictionData() const { - if(!depictionData) depictionData.reset(new DepictionData(getLabelledGraph())); +const Write::DepictionData &Single::getDepictionData() const { + if(!depictionData) depictionData.reset(new Write::DepictionData(getLabelledGraph())); return *depictionData; } @@ -239,13 +244,24 @@ namespace { namespace GM = jla_boost::GraphMorphism; namespace GM_MOD = lib::GraphMorphism; +template +void morphism(const Single &gDomain, + const Single &gCodomain, + LabelSettings labelSettings, + Finder finder, + Callback callback) { + lib::GraphMorphism::morphismSelectByLabelSettings(gDomain.getLabelledGraph(), gCodomain.getLabelledGraph(), + labelSettings, finder, callback); +} + template -std::size_t -morphism(const Single &gDomain, const Single &gCodomain, std::size_t maxNumMatches, LabelSettings labelSettings, - Finder finder) { +std::size_t morphismMax(const Single &gDomain, + const Single &gCodomain, + std::size_t maxNumMatches, + LabelSettings labelSettings, + Finder finder) { auto mr = GM::makeLimit(maxNumMatches); - lib::GraphMorphism::morphismSelectByLabelSettings(gDomain.getLabelledGraph(), gCodomain.getLabelledGraph(), - labelSettings, finder, std::ref(mr)); + morphism(gDomain, gCodomain, labelSettings, finder, std::ref(mr)); return mr.getNumHits(); } @@ -270,7 +286,7 @@ std::size_t isomorphismSmilesOrCanonOrVF2(const Single &gDom, const Single &gCod std::size_t Single::isomorphismVF2(const Single &gDom, const Single &gCodom, std::size_t maxNumMatches, LabelSettings labelSettings) { - return morphism(gDom, gCodom, maxNumMatches, labelSettings, GM_MOD::VF2Isomorphism()); + return morphismMax(gDom, gCodom, maxNumMatches, labelSettings, GM_MOD::VF2Isomorphism()); } bool Single::isomorphic(const Single &gDom, const Single &gCodom, LabelSettings labelSettings) { @@ -284,10 +300,8 @@ bool Single::isomorphic(const Single &gDom, const Single &gCodom, LabelSettings return gDom.getName() == gCodom.getName(); if(&gDom == &gCodom) return true; switch(getConfig().graph.isomorphismAlg.get()) { - case Config::IsomorphismAlg::SmilesCanonVF2: - return isomorphismSmilesOrCanonOrVF2(gDom, gCodom, labelSettings); - case Config::IsomorphismAlg::VF2: - return isomorphismVF2(gDom, gCodom, 1, labelSettings); + case Config::IsomorphismAlg::SmilesCanonVF2: return isomorphismSmilesOrCanonOrVF2(gDom, gCodom, labelSettings); + case Config::IsomorphismAlg::VF2: return isomorphismVF2(gDom, gCodom, 1, labelSettings); case Config::IsomorphismAlg::Canon: if(labelSettings.relation != LabelRelation::Isomorphism) throw LogicError("Can only do isomorphism via canonicalisation with the isomorphism relation."); @@ -315,7 +329,44 @@ Single::isomorphism(const Single &gDom, const Single &gCodom, std::size_t maxNum std::size_t Single::monomorphism(const Single &gDom, const Single &gCodom, std::size_t maxNumMatches, LabelSettings labelSettings) { - return morphism(gDom, gCodom, maxNumMatches, labelSettings, GM_MOD::VF2Monomorphism()); + return morphismMax(gDom, gCodom, maxNumMatches, labelSettings, GM_MOD::VF2Monomorphism()); +} + +void Single::enumerateMonomorphisms(const Single &gDom, const Single &gCodom, + std::function)> callback, + LabelSettings labelSettings) { + if(labelSettings.type != LabelType::String) MOD_ABORT; + if(labelSettings.withStereo) MOD_ABORT; + morphism(gDom, gCodom, labelSettings, GM_MOD::VF2Monomorphism(), + GM::makeSliceProps( // Slice away the properties for now + GM::makeTransform( + GM::ToInvertibleVectorVertexMap(), + [&gDom, &gCodom, callback](auto &&mVal, const auto &dom, const auto &codom) -> bool { + auto mPtr = std::make_shared>( + std::move(mVal)); + auto gDomAPI = gDom.getAPIReference(); + auto gCodomAPI = gCodom.getAPIReference(); + auto m = VertexMap( + gDomAPI, gCodomAPI, + [gDomAPI, gCodomAPI, mPtr](graph::Graph::Vertex vDom) -> graph::Graph::Vertex { + const auto &gDom = gDomAPI->getGraph().getGraph(); + const auto &gCodom = gCodomAPI->getGraph().getGraph(); + assert(vDom.getId() < num_vertices(gDom)); + const auto v = vertices(gDom).first[vDom.getId()]; + const auto vRes = get(*mPtr, gDom, gCodom, v); + return gCodomAPI->vertices()[get(boost::vertex_index_t(), gCodom, vRes)]; + }, + [gDomAPI, gCodomAPI, mPtr](graph::Graph::Vertex vCodom) -> graph::Graph::Vertex { + const auto &gDom = gDomAPI->getGraph().getGraph(); + const auto &gCodom = gCodomAPI->getGraph().getGraph(); + assert(vCodom.getId() < num_vertices(gCodom)); + const auto v = vertices(gCodom).first[vCodom.getId()]; + const auto vRes = get_inverse(*mPtr, gDom, gCodom, v); + return gDomAPI->vertices()[get(boost::vertex_index_t(), gDom, vRes)]; + } + ); + return callback(std::move(m)); + }))); } bool Single::nameLess(const Single *g1, const Single *g2) { @@ -351,14 +402,14 @@ Single makePermutation(const Single &g) { {LabelType::String, LabelRelation::Isomorphism, false, LabelRelation::Isomorphism}); if(!iso) { - IO::Graph::Write::Options graphLike, molLike; + Write::Options graphLike, molLike; graphLike.EdgesAsBonds(true).RaiseCharges(true).CollapseHydrogens(true).WithIndex(true); molLike.CollapseHydrogens(true).EdgesAsBonds(true).RaiseCharges(true).SimpleCarbons(true).WithColour( true).WithIndex(true); - IO::Graph::Write::summary(g, graphLike, molLike); - IO::Graph::Write::summary(gPerm, graphLike, molLike); - IO::Graph::Write::gml(g, false); - IO::Graph::Write::gml(gPerm, false); + Write::summary(g, graphLike, molLike); + Write::summary(gPerm, graphLike, molLike); + Write::gml(g, false); + Write::gml(gPerm, false); std::cout << "g: " << g.getSmiles() << std::endl; std::cout << "gPerm: " << gPerm.getSmiles() << std::endl; MOD_ABORT; diff --git a/libs/libmod/src/mod/lib/Graph/Single.hpp b/libs/libmod/src/mod/lib/Graph/Single.hpp index 18ca335..86e78e6 100644 --- a/libs/libmod/src/mod/lib/Graph/Single.hpp +++ b/libs/libmod/src/mod/lib/Graph/Single.hpp @@ -16,9 +16,15 @@ #include #include +namespace mod { +template +struct VertexMap; +} // namespace mod namespace mod::lib::Graph { struct PropMolecule; +namespace Write { struct DepictionData; +} // namespace Write struct Single { using CanonIdxMap = boost::iterator_property_map::const_iterator, @@ -44,8 +50,8 @@ struct Single { const std::string &getSmilesWithIds() const; unsigned int getVertexLabelCount(const std::string &label) const; unsigned int getEdgeLabelCount(const std::string &label) const; - DepictionData &getDepictionData(); - const DepictionData &getDepictionData() const; + Write::DepictionData &getDepictionData(); + const Write::DepictionData &getDepictionData() const; public: // deprecated interface const GraphType &getGraph() const; const PropString &getStringState() const; @@ -65,7 +71,7 @@ struct Single { mutable std::vector canon_perm_string; mutable std::unique_ptr canon_form_string; mutable std::unique_ptr aut_group_string; - mutable std::unique_ptr depictionData; + mutable std::unique_ptr depictionData; public: static std::size_t isomorphismVF2(const Single &gDom, const Single &gCodom, std::size_t maxNumMatches, LabelSettings labelSettings); @@ -74,6 +80,9 @@ struct Single { isomorphism(const Single &gDom, const Single &gCodom, std::size_t maxNumMatches, LabelSettings labelSettings); static std::size_t monomorphism(const Single &gDom, const Single &gCodom, std::size_t maxNumMatches, LabelSettings labelSettings); + static void enumerateMonomorphisms(const Single &gDom, const Single &gCodom, + std::function)> callback, + LabelSettings labelSettings); static bool nameLess(const Single *g1, const Single *g2); static bool canonicalCompare(const Single &g1, const Single &g2, LabelType labelType, bool withStereo); public: diff --git a/libs/libmod/src/mod/lib/GraphMorphism/CommonSubgraphFinder.hpp b/libs/libmod/src/mod/lib/GraphMorphism/CommonSubgraphFinder.hpp index 1673d82..0ccb175 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/CommonSubgraphFinder.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/CommonSubgraphFinder.hpp @@ -2,10 +2,9 @@ #define JLA_BOOST_GRAPH_MORPHISM_MCGREGORCOMMONFINDER_HPP #include +#include #include -#include - namespace mod::lib::GraphMorphism { namespace detail { diff --git a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/AllVisitor.hpp b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/AllVisitor.hpp index 344b3a3..2c24a22 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/AllVisitor.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/AllVisitor.hpp @@ -1,22 +1,19 @@ -#ifndef MOD_LIB_GRAPHMORPHISM_ALLVISITOR_H -#define MOD_LIB_GRAPHMORPHISM_ALLVISITOR_H +#ifndef MOD_LIB_GRAPHMORPHISM_ALLVISITOR_HPP +#define MOD_LIB_GRAPHMORPHISM_ALLVISITOR_HPP #include #include #include -namespace mod { -namespace lib { -namespace GraphMorphism { -namespace Constraints { +namespace mod::lib::GraphMorphism::Constraints { namespace detail { template class ...Cs> -struct AllVisitor : BaseVisitor, Visitor >... { +struct AllVisitor : BaseVisitor, Visitor > ... { }; template class ...Cs> -struct AllVisitorNonConst : BaseVisitorNonConst, Visitor >... { +struct AllVisitorNonConst : BaseVisitorNonConst, Visitor > ... { }; } // namespace detail @@ -35,9 +32,6 @@ struct AllVisitorNonConst : detail::AllVisitorNonConst { }; -} // namespace Constraints -} // namespace GraphMorphism -} // namespace lib -} // namespace mod +} // namespace mod::lib::GraphMorphism::Constraints -#endif /* MOD_LIB_GRAPHMORPHISM_ALLVISITOR_H */ \ No newline at end of file +#endif // MOD_LIB_GRAPHMORPHISM_ALLVISITOR_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/CheckVisitor.hpp b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/CheckVisitor.hpp index 16ce4c7..b085908 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/CheckVisitor.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/CheckVisitor.hpp @@ -1,24 +1,20 @@ -#ifndef MOD_LIB_GRAPHMORPHISM_MATCHESVISITOR_H -#define MOD_LIB_GRAPHMORPHISM_MATCHESVISITOR_H +#ifndef MOD_LIB_GRAPHMORPHISM_MATCHESVISITOR_HPP +#define MOD_LIB_GRAPHMORPHISM_MATCHESVISITOR_HPP #include -namespace mod { -namespace lib { -namespace GraphMorphism { -namespace Constraints { +namespace mod::lib::GraphMorphism::Constraints { template struct CheckVisitor : AllVisitor { - CheckVisitor(const GraphDom &gDom, const LabelledGraphCodom &lgCodom, Morphism &m, const LabelSettings ls) - : gDom(gDom), lgCodom(lgCodom), m(m), ls(ls) { } + : gDom(gDom), lgCodom(lgCodom), m(m), ls(ls) {} - virtual void operator()(const VertexAdjacency &c) override { + virtual void operator()(const VertexAdjacency &c) override { result = c.matches(*this, gDom, lgCodom, m, ls); } - virtual void operator()(const ShortestPath &c) override { + virtual void operator()(const ShortestPath &c) override { result = c.matches(*this, gDom, lgCodom, m, ls); } public: @@ -31,15 +27,14 @@ struct CheckVisitor : AllVisitor { template struct Checker { - Checker(ConstraintRange constraints, const LabelledGraphCodom &lgCodom, LabelSettings ls, Next next) - : constraints(constraints), lgCodom(lgCodom), ls(ls), next(next) { } + : constraints(constraints), lgCodom(lgCodom), ls(ls), next(next) {} template bool operator()(Morphism &&m, const GraphDom &gDom, const GraphCodom &gCodom) const { assert(&gCodom == &get_graph(lgCodom)); CheckVisitor visitor(gDom, lgCodom, m, ls); - for(const auto &c : constraints) { + for(const auto &c: constraints) { c->accept(visitor); if(!visitor.result) return true; } @@ -54,14 +49,11 @@ struct Checker { template Checker -makeChecker(ConstraintRange constraints, const LabelledGraphCodom &lgCodom, LabelSettings ls, Next next = jla_boost::AlwaysTrue()) { +makeChecker(ConstraintRange constraints, const LabelledGraphCodom &lgCodom, LabelSettings ls, + Next next = jla_boost::AlwaysTrue()) { return Checker(constraints, lgCodom, ls, next); } -} // namespace Constraints -} // namespace GraphMorphism -} // namespace lib -} // namespace mod - -#endif /* MOD_LIB_GRAPHMORPHISM_MATCHESVISITOR_H */ +} // namespace mod::lib::GraphMorphism::Constraints +#endif // MOD_LIB_GRAPHMORPHISM_MATCHESVISITOR_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/Constraint.hpp b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/Constraint.hpp index e512771..a0a328a 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/Constraint.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/Constraint.hpp @@ -14,18 +14,17 @@ struct Constraint { virtual ~Constraint() = default; virtual void accept(BaseVisitorNonConst &v) = 0; virtual void accept(BaseVisitor &v) const = 0; - virtual std::unique_ptr > clone() const = 0; + virtual std::unique_ptr> clone() const = 0; virtual std::string name() const = 0; - virtual bool supportsTerm() const = 0; protected: template static void acceptDispatch(C &c, BaseVisitorNonConst &visitor) { - dynamic_cast &> (visitor)(c); + dynamic_cast &>(visitor)(c); } template static void acceptDispatch(C &c, BaseVisitor &visitor) { - dynamic_cast &> (visitor)(c); + dynamic_cast &>(visitor)(c); } #define MOD_VISITABLE() \ diff --git a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/ShortestPath.hpp b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/ShortestPath.hpp index 5384c7b..86ca796 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/ShortestPath.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/ShortestPath.hpp @@ -1,29 +1,26 @@ -#ifndef MOD_LIB_GRAPHMORPHISM_SHORTESTPATH_H -#define MOD_LIB_GRAPHMORPHISM_SHORTESTPATH_H +#ifndef MOD_LIB_GRAPHMORPHISM_SHORTESTPATH_HPP +#define MOD_LIB_GRAPHMORPHISM_SHORTESTPATH_HPP #include #include #include -#include +#include #include -namespace mod { -namespace lib { -namespace GraphMorphism { -namespace Constraints { +namespace mod::lib::GraphMorphism::Constraints { template struct ShortestPath : Constraint { MOD_VISITABLE(); using Vertex = typename boost::graph_traits::vertex_descriptor; public: - ShortestPath(Vertex vSrc, Vertex vTar, Operator op, int length) - : vSrc(vSrc), vTar(vTar), op(op), length(length) { } + : vSrc(vSrc), vTar(vTar), op(op), length(length) {} - virtual std::unique_ptr > clone() const override { + virtual std::unique_ptr > + clone() const override { return std::make_unique(vSrc, vTar, op, length); } @@ -31,15 +28,15 @@ struct ShortestPath : Constraint { return "ShortestPath"; } - virtual bool supportsTerm() const override { - return true; - } - template - bool matches(Visitor &vis, const Graph &gDom, const LabelledGraphCodom &lgCodom, const VertexMap &m, const LabelSettings ls) const { + bool matches(Visitor &vis, const Graph &gDom, const LabelledGraphCodom &lgCodom, const VertexMap &m, + const LabelSettings ls) const { using GraphCodom = typename LabelledGraphTraits::GraphType; - static_assert(std::is_same::GraphDom>::value, ""); - static_assert(std::is_same::GraphCodom>::value, ""); + static_assert(std::is_same::GraphDom>::value, + ""); + static_assert( + std::is_same::GraphCodom>::value, + ""); const GraphCodom &gCodom = get_graph(lgCodom); { // verify #ifndef NDEBUG @@ -50,11 +47,16 @@ struct ShortestPath : Constraint { } const auto check = [this](int length) -> bool { switch(op) { - case Operator::EQ: return length == this->length; - case Operator::LT: return length < this->length; - case Operator::GT: return length > this->length; - case Operator::LEQ: return length <= this->length; - case Operator::GEQ: return length >= this->length; + case Operator::EQ: + return length == this->length; + case Operator::LT: + return length < this->length; + case Operator::GT: + return length > this->length; + case Operator::LEQ: + return length <= this->length; + case Operator::GEQ: + return length >= this->length; } assert(false); std::abort(); @@ -84,9 +86,6 @@ struct ShortestPath : Constraint { int length; }; -} // namespace Constraints -} // namespace GraphMorphism -} // namespace lib -} // namespace mod +} // namespace mod::lib::GraphMorphism::Constraints -#endif /* MOD_LIB_GRAPHMORPHISM_SHORTESTPATH_H */ \ No newline at end of file +#endif // MOD_LIB_GRAPHMORPHISM_SHORTESTPATH_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/VertexAdjacency.hpp b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/VertexAdjacency.hpp index a01fcc3..1900d68 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/VertexAdjacency.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/VertexAdjacency.hpp @@ -1,29 +1,25 @@ -#ifndef MOD_LIB_GRAPHMORPHISM_ADJACENCY_H -#define MOD_LIB_GRAPHMORPHISM_ADJACENCY_H +#ifndef MOD_LIB_GRAPHMORPHISM_ADJACENCY_HPP +#define MOD_LIB_GRAPHMORPHISM_ADJACENCY_HPP #include #include #include #include -#include +#include #include -namespace mod { -namespace lib { -namespace GraphMorphism { -namespace Constraints { +namespace mod::lib::GraphMorphism::Constraints { template struct VertexAdjacency : Constraint { MOD_VISITABLE(); using Vertex = typename boost::graph_traits::vertex_descriptor; public: - VertexAdjacency(Vertex vConstrained, Operator op, int count) - : vConstrained(vConstrained), op(op), count(count), vertexLabels(1), edgeLabels(1) { } + : vConstrained(vConstrained), op(op), count(count), vertexLabels(1), edgeLabels(1) {} - virtual std::unique_ptr > clone() const override { + virtual std::unique_ptr> clone() const override { auto c = std::make_unique(vConstrained, op, count); c->vertexLabels = vertexLabels; c->edgeLabels = edgeLabels; @@ -33,21 +29,17 @@ struct VertexAdjacency : Constraint { virtual std::string name() const override { return "VertexAdjacency"; } - - virtual bool supportsTerm() const override { - return false; - } private: - template - int matchesImpl(Visitor &vis, const Graph &gDom, const LabelledGraphCodom &lgCodom, VertexMap &m, const LabelSettings ls, std::false_type) const { + int matchesImpl(Visitor &vis, const Graph &gDom, const LabelledGraphCodom &lgCodom, VertexMap &m, + const LabelSettings ls, std::false_type) const { assert(ls.type == LabelType::String); // otherwise someone forgot to add the TermData prop using GraphCodom = typename LabelledGraphTraits::GraphType; const GraphCodom &gCodom = get_graph(lgCodom); const auto vCodom = get(m, gDom, gCodom, vConstrained); int count = 0; const auto &string = get_string(lgCodom); - for(const auto eOutCodom : asRange(out_edges(vCodom, gCodom))) { + for(const auto eOutCodom: asRange(out_edges(vCodom, gCodom))) { if(!edgeLabels.empty()) { const auto iter = edgeLabels.find(string[eOutCodom]); if(iter == edgeLabels.end()) continue; @@ -62,7 +54,8 @@ struct VertexAdjacency : Constraint { } template - int matchesImpl(Visitor &vis, const Graph &gDom, const LabelledGraphCodom &lgCodom, VertexMap &m, const LabelSettings ls, std::true_type) const { + int matchesImpl(Visitor &vis, const Graph &gDom, const LabelledGraphCodom &lgCodom, VertexMap &m, + const LabelSettings ls, std::true_type) const { assert(ls.type == LabelType::Term); // otherwise someone did something very strange using GraphCodom = typename LabelledGraphTraits::GraphType; const GraphCodom &gCodom = get_graph(lgCodom); @@ -74,7 +67,7 @@ struct VertexAdjacency : Constraint { if(vertexTerms.empty()) { ++count; } else { - for(const auto t : vertexTerms) { + for(const auto t: vertexTerms) { lib::Term::MGU mgu = machine.unifyHeapTemp(h, t); if(mgu.status == lib::Term::MGU::Status::Exists) { ++count; @@ -87,7 +80,7 @@ struct VertexAdjacency : Constraint { if(edgeTerms.empty()) { countPerVertexTerms(hVertex); } else { - for(const auto t : edgeTerms) { + for(const auto t: edgeTerms) { lib::Term::MGU mgu = machine.unifyHeapTemp(hEdge, t); if(mgu.status == lib::Term::MGU::Status::Exists) { countPerVertexTerms(hVertex); @@ -96,19 +89,22 @@ struct VertexAdjacency : Constraint { } } }; - for(const auto eOutCodom : asRange(out_edges(vCodom, gCodom))) { + for(const auto eOutCodom: asRange(out_edges(vCodom, gCodom))) { const auto vOut = target(eOutCodom, gCodom); countPerEdgeTerms(term[eOutCodom], term[vOut]); } return count; } public: - template - bool matches(Visitor &vis, const Graph &gDom, const LabelledGraphCodom &lgCodom, VertexMap &m, const LabelSettings ls) const { + bool matches(Visitor &vis, const Graph &gDom, const LabelledGraphCodom &lgCodom, VertexMap &m, + const LabelSettings ls) const { using GraphCodom = typename LabelledGraphTraits::GraphType; - static_assert(std::is_same::GraphDom>::value, ""); - static_assert(std::is_same::GraphCodom>::value, ""); + static_assert(std::is_same::GraphDom>::value, + ""); + static_assert( + std::is_same::GraphCodom>::value, + ""); { // verify #ifndef NDEBUG const auto vs = vertices(gDom); @@ -123,11 +119,16 @@ struct VertexAdjacency : Constraint { using HasTerm = GraphMorphism::HasTermData; const int count = matchesImpl(vis, gDom, lgCodom, m, ls, HasTerm()); switch(op) { - case Operator::EQ: return count == this->count; - case Operator::LT: return count < this->count; - case Operator::GT: return count > this->count; - case Operator::LEQ: return count <= this->count; - case Operator::GEQ: return count >= this->count; + case Operator::EQ: + return count == this->count; + case Operator::LT: + return count < this->count; + case Operator::GT: + return count > this->count; + case Operator::LEQ: + return count <= this->count; + case Operator::GEQ: + return count >= this->count; } assert(false); std::abort(); @@ -140,9 +141,6 @@ struct VertexAdjacency : Constraint { std::unordered_set vertexTerms, edgeTerms; }; -} // namespace Constraints -} // namespace GraphMorphism -} // namespace lib -} // namespace mod +} // namespace mod::lib::GraphMorphism::Constraints -#endif /* MOD_LIB_GRAPHMORPHISM_ADJACENCY_H */ \ No newline at end of file +#endif // MOD_LIB_GRAPHMORPHISM_ADJACENCY_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/Visitor.hpp b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/Visitor.hpp index 717791d..df4c736 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/Constraints/Visitor.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/Constraints/Visitor.hpp @@ -1,10 +1,7 @@ -#ifndef MOD_LIB_GRAPHMORPHISM_VISITOR_H -#define MOD_LIB_GRAPHMORPHISM_VISITOR_H +#ifndef MOD_LIB_GRAPHMORPHISM_VISITOR_HPP +#define MOD_LIB_GRAPHMORPHISM_VISITOR_HPP -namespace mod { -namespace lib { -namespace GraphMorphism { -namespace Constraints { +namespace mod::lib::GraphMorphism::Constraints { template struct BaseVisitor { @@ -21,10 +18,6 @@ struct Visitor { virtual void operator()(C &c) = 0; }; -} // namespace Constraints -} // namespace GraphMorphism -} // namespace lib -} // namespace mod - -#endif /* MOD_LIB_GRAPHMORPHISM_VISITOR_H */ +} // namespace mod::lib::GraphMorphism::Constraints +#endif // MOD_LIB_GRAPHMORPHISM_VISITOR_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/GraphMorphism/Finder.hpp b/libs/libmod/src/mod/lib/GraphMorphism/Finder.hpp index 5306f40..7ba28bf 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/Finder.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/Finder.hpp @@ -3,21 +3,16 @@ #include -namespace mod { -namespace lib { -namespace GraphMorphism { +namespace mod::lib::GraphMorphism { struct DefaultFinderArgsProvider { - template friend std::vector::vertex_descriptor> - get_vertex_order(const DefaultFinderArgsProvider&, const Graph &g) { + get_vertex_order(const DefaultFinderArgsProvider &, const Graph &g) { return jla_boost::GraphMorphism::vertex_order_by_mult(g); } }; -} // namespace GraphMorphism -} // namespace lib -} // namespace mod +} // namespace mod::lib::GraphMorphism -#endif /* MOD_LIB_GRAPH_MORPHISM_FINDER_HPP */ \ No newline at end of file +#endif // MOD_LIB_GRAPH_MORPHISM_FINDER_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/GraphMorphism/IO/WriteConstraints.hpp b/libs/libmod/src/mod/lib/GraphMorphism/IO/WriteConstraints.hpp new file mode 100644 index 0000000..97327d9 --- /dev/null +++ b/libs/libmod/src/mod/lib/GraphMorphism/IO/WriteConstraints.hpp @@ -0,0 +1,152 @@ +#ifndef MOD_LIB_GRAPHMORPHISM_IO_WRITECONSTRAINTS_HPP +#define MOD_LIB_GRAPHMORPHISM_IO_WRITECONSTRAINTS_HPP + +#include +#include +#include + +namespace mod::lib::GraphMorphism::Write { +namespace detail { + +template +struct ConstraintsTexPrintVisitor : Constraints::AllVisitor { + ConstraintsTexPrintVisitor(std::ostream &s, const Graph &g) : s(s), g(g) {} + + void printOp(Constraints::Operator op) { + using Constraints::Operator; + switch(op) { + case Operator::EQ: + s << "="; + break; + case Operator::LT: + s << "<"; + break; + case Operator::GT: + s << ">"; + break; + case Operator::LEQ: + s << "\\leq"; + break; + case Operator::GEQ: + s << "\\geq"; + break; + } + } + + virtual void operator()(const Constraints::VertexAdjacency &c) override { + s << "\\begin{align*}\n"; + s << "&|\\{e \\in \\mathrm{outEdges}(" << get(boost::vertex_index_t(), g, c.vConstrained) << ") "; + if(!c.vertexLabels.empty() || !c.edgeLabels.empty()) s << " \\mid \\\\\n"; + if(!c.vertexLabels.empty()) { + s << "&\\quad \\mathrm{label}(\\mathrm{target}(e)) \\in \\{"; + bool first = true; + for(const std::string &str: c.vertexLabels) { + if(!first) s << ", "; + first = false; + s << " \\text{`\\texttt{" << IO::escapeForLatex(str) << "}'}"; + } + s << " \\}\\\\\n"; + } + if(!c.vertexLabels.empty() && !c.edgeLabels.empty()) s << "&\\quad \\wedge\\\\\n"; + if(!c.edgeLabels.empty()) { + s << "&\\quad \\mathrm{label}(e) \\in \\{"; + bool first = true; + for(const std::string &str: c.edgeLabels) { + if(!first) s << ", "; + first = false; + s << " \\text{`\\texttt{" << IO::escapeForLatex(str) << "}'}"; + } + s << " \\}\\\\\n"; + } + if(!c.vertexLabels.empty() || !c.edgeLabels.empty()) s << "&"; + s << "\\}| "; + printOp(c.op); + s << " " << c.count << "\n\\end{align*}\n"; + } + + virtual void operator()(const Constraints::ShortestPath &c) override { + s << "$\\mathrm{shortestPath}(" << get(boost::vertex_index_t(), g, c.vSrc) << ", " + << get(boost::vertex_index_t(), g, c.vTar) << ") "; + printOp(c.op); + s << " " << c.length << "$\n"; + } +private: + std::ostream &s; + const Graph &g; +}; + +template +struct ConstraintsGMLPrintVisitor : Constraints::AllVisitor { + ConstraintsGMLPrintVisitor(std::ostream &s, const Graph &g, const std::string &prefix) + : s(s), g(g), prefix(prefix) {} + + void printOp(Constraints::Operator op) { + using Constraints::Operator; + switch(op) { + case Operator::EQ: + s << "="; + break; + case Operator::LT: + s << "<"; + break; + case Operator::GT: + s << ">"; + break; + case Operator::LEQ: + s << "<="; + break; + case Operator::GEQ: + s << ">="; + break; + } + } + + virtual void operator()(const Constraints::VertexAdjacency &c) { + s << prefix << "constrainAdj [\n"; + s << prefix << " id " << get(boost::vertex_index_t(), g, c.vConstrained) << "\n"; + s << prefix << " op \""; + printOp(c.op); + s << "\"\n"; + s << prefix << " count " << c.count << "\n"; + s << prefix << " nodeLabels ["; + for(const auto &str: c.vertexLabels) s << " label \"" << str << "\""; + s << " ]\n"; + s << prefix << " edgeLabels ["; + for(const auto &str: c.edgeLabels) s << " label \"" << str << "\""; + s << " ]\n"; + s << prefix << "]\n"; + } + + virtual void operator()(const Constraints::ShortestPath &c) { + s << prefix << "constrainShortestPath [\n"; + s << prefix << " source " << get(boost::vertex_index_t(), g, c.vSrc) + << " target " << get(boost::vertex_index_t(), g, c.vTar) << "\n"; + s << prefix << " op \""; + printOp(c.op); + s << "\" length " << c.length << "\n"; + s << prefix << "]\n"; + } +private: + std::ostream &s; + const Graph &g; + const std::string &prefix; +}; + +} // namespace detail + +template +void texConstraint(std::ostream &s, const Graph &g, const Constraints::Constraint &c) { + auto vis = detail::ConstraintsTexPrintVisitor(s, g); + c.accept(vis); +} + +template +void gmlConstraint(std::ostream &s, const Graph &g, const std::string &prefix, + const Constraints::Constraint &c) { + auto vis = detail::ConstraintsGMLPrintVisitor(s, g, prefix); + c.accept(vis); +} + +} // namespace mod::lib::GraphMorphism::Write + +#endif // MOD_LIB_GRAPHMORPHISM_IO_WRITECONSTRAINTS_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/GraphMorphism/StereoVertexMap.hpp b/libs/libmod/src/mod/lib/GraphMorphism/StereoVertexMap.hpp index 350abbb..617cf34 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/StereoVertexMap.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/StereoVertexMap.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include // LocalIso diff --git a/libs/libmod/src/mod/lib/GraphMorphism/TermVertexMap.hpp b/libs/libmod/src/mod/lib/GraphMorphism/TermVertexMap.hpp index 1a06286..e1447c0 100644 --- a/libs/libmod/src/mod/lib/GraphMorphism/TermVertexMap.hpp +++ b/libs/libmod/src/mod/lib/GraphMorphism/TermVertexMap.hpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include // - TermPredConstants (compare terms, variables are equal to everything) @@ -14,7 +14,8 @@ // for debugging #include -#include +#include + #include namespace mod::lib::GraphMorphism { @@ -48,8 +49,8 @@ struct TermPredConstants { return res && next(veDom, veCodom, gDom, gCodom); } private: - bool compare(std::size_t addrLeft, std::size_t addrRight, const lib::Term::Wam &machineLeft, - const lib::Term::Wam &machineRight) const { + bool compare(std::size_t addrLeft, std::size_t addrRight, const Term::Wam &machineLeft, + const Term::Wam &machineRight) const { assert(stack.empty()); // maybe_unused to silence warnings on GCC < 9 // (perhaps this bug? https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85827) @@ -58,18 +59,18 @@ struct TermPredConstants { stack.emplace_back(addrLeft, addrRight); if constexpr(DEBUG) { std::cout << "TermConstEqual:\n"; - lib::IO::Term::Write::wam(machineLeft, lib::Term::getStrings(), std::cout); - lib::IO::Term::Write::wam(machineRight, lib::Term::getStrings(), std::cout); + Term::Write::wam(machineLeft, Term::getStrings(), std::cout); + Term::Write::wam(machineRight, Term::getStrings(), std::cout); } while(!stack.empty()) { std::size_t l, r; std::tie(l, r) = stack.back(); stack.pop_back(); if constexpr(DEBUG) std::cout << "comp(" << l << ", " << r << ")\n"; - using AddressType = lib::Term::AddressType; - using Address = lib::Term::Address; - using Cell = lib::Term::Cell; - using CellTag = lib::Term::Cell::Tag; + using AddressType = Term::AddressType; + using Address = Term::Address; + using Cell = Term::Cell; + using CellTag = Term::Cell::Tag; Address addrLhs = machineLeft.deref({AddressType::Heap, l}); Address addrRhs = machineRight.deref({AddressType::Heap, r}); if constexpr(DEBUG) std::cout << "compDeref(" << addrLhs.addr << ", " << addrRhs.addr << ")\n"; @@ -119,34 +120,34 @@ struct TermDataT { }; struct TermData { - lib::Term::Wam machine; - lib::Term::MGU mgu; + Term::Wam machine; + Term::MGU mgu; }; struct TermAssociationHandlerUnify { template bool operator()(std::size_t l, std::size_t r, const OuterGraphDom &gDom, const OuterGraphCodom &gCodom, - lib::Term::Wam &res, lib::Term::MGU &mgu) const { + Term::Wam &res, Term::MGU &mgu) const { constexpr bool DEBUG = false; if(DEBUG) { auto &s = std::cout; s << "TermAssociationHandlerUnify:\n"; - lib::IO::Term::Write::wam(res, lib::Term::getStrings(), s); + Term::Write::wam(res, Term::getStrings(), s); } res.unifyHeapTemp(r, l, mgu); if(DEBUG) { auto &s = std::cout; s << "\tunifyHeapTemp(" << r << ", " << l << ")\n"; switch(mgu.status) { - case lib::Term::MGU::Status::Exists: + case Term::MGU::Status::Exists: s << "\tExists\n"; break; - case lib::Term::MGU::Status::Fail: + case Term::MGU::Status::Fail: s << "\tFail\n"; break; } } - if(mgu.status != lib::Term::MGU::Status::Exists) return false; + if(mgu.status != Term::MGU::Status::Exists) return false; else { res.verify(); return true; @@ -173,11 +174,11 @@ struct ToTermVertexMap { const auto &pCodomain = get_term(lgCodom); assert(isValid(pDomain)); assert(isValid(pCodomain)); - lib::Term::Wam machine(getMachine(pCodomain)); + Term::Wam machine(getMachine(pCodomain)); machine.setTemp(getMachine(pDomain)); - lib::Term::MGU mgu(machine.getHeap().size()); + Term::MGU mgu(machine.getHeap().size()); using Handler = typename LabGraphDom::PropTermType::Handler; - for(const auto vDom : asRange(vertices(gDom))) { + for(const auto vDom: asRange(vertices(gDom))) { const auto vCodom = get(m, gDom, gCodom, vDom); if(vCodom == boost::graph_traits::null_vertex()) continue; const bool ok = Handler::reduce( @@ -187,7 +188,7 @@ struct ToTermVertexMap { )); if(!ok) return true; } - for(const auto eDom : asRange(edges(gDom))) { + for(const auto eDom: asRange(edges(gDom))) { const auto vDomSrc = source(eDom, gDom); const auto vDomTar = target(eDom, gDom); const auto vCodomSrc = get(m, gDom, gCodom, vDomSrc); @@ -195,6 +196,7 @@ struct ToTermVertexMap { if(vCodomSrc == boost::graph_traits::null_vertex()) continue; if(vCodomTar == boost::graph_traits::null_vertex()) continue; const auto peCodom = edge(vCodomSrc, vCodomTar, gCodom); + if(!peCodom.second) continue; // no edge in the other side assert(peCodom.second); const auto eCodom = peCodom.first; const bool ok = Handler::reduce( @@ -224,8 +226,8 @@ auto makeToTermVertexMap(const LabGraphDom &gDom, const LabGraphCodom &gCodom, N struct TermFilterRenaming { template bool operator()(const VertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom) const { - // lib::IO::Term::Write::wam(m.machine, lib::Term::getStrings(), std::cout); - // lib::IO::Term::Write::mgu(m.machine, m.mgu, lib::Term::getStrings(), std::cout) << "\n"; + // Term::Write::wam(m.machine, Term::getStrings(), std::cout); + // Term::Write::mgu(m.machine, m.mgu, Term::getStrings(), std::cout) << "\n"; const auto &data = get_prop(TermDataT(), m); bool res = data.mgu.isRenaming(data.machine); // std::cout << "Result: " << std::boolalpha << res << "\n"; @@ -236,8 +238,8 @@ struct TermFilterRenaming { struct TermFilterSpecialisation { template bool operator()(const VertexMap &m, const GraphDom &gDom, const GraphCodom &gCodom) const { - // lib::IO::Term::Write::wam(m.machine, lib::Term::getStrings(), std::cout); - // lib::IO::Term::Write::mgu(m.machine, m.mgu, lib::Term::getStrings(), std::cout) << "\n"; + // Term::Write::wam(m.machine, Term::getStrings(), std::cout); + // Term::Write::mgu(m.machine, m.mgu, Term::getStrings(), std::cout) << "\n"; const auto &data = get_prop(TermDataT(), m); bool res = data.mgu.isSpecialisation(data.machine); // std::cout << "Result: " << std::boolalpha << res << "\n"; diff --git a/libs/libmod/src/mod/lib/GraphPimpl.hpp b/libs/libmod/src/mod/lib/GraphPimpl.hpp index f5b5277..5cbfab8 100644 --- a/libs/libmod/src/mod/lib/GraphPimpl.hpp +++ b/libs/libmod/src/mod/lib/GraphPimpl.hpp @@ -22,15 +22,22 @@ Graph getGraphFromStore(std::optional g) { } // namespace -#define MOD_GRAPHPIMPL_Define_Vertex(GraphClass, GraphName, getMacroGraph, g, VertePrint) \ - MOD_GRAPHPIMPL_Define_Vertex_noGraph(GraphClass, GraphName, getMacroGraph, g, VertePrint) \ +#define MOD_GRAPHPIMPL_Define_Vertex(GraphClass, GraphName, getMacroGraph, g, VertexPrint) \ + MOD_GRAPHPIMPL_Define_Vertex_noGraph(GraphClass, GraphName, getMacroGraph, g, VertexPrint) \ + MOD_GRAPHPIMPL_Define_Vertex_graph(GraphClass, g) + +#define MOD_GRAPHPIMPL_Define_Vertex_graph(GraphClass, g) \ \ GraphHandle GraphClass::Vertex::getGraph() const { \ if(!*this) throw LogicError("Can not get graph on a null vertex."); \ return lib::getGraphFromStore(g); \ } -#define MOD_GRAPHPIMPL_Define_Vertex_noGraph(GraphClass, GraphName, getMacroGraph, g, VertePrint)\ +#define MOD_GRAPHPIMPL_Define_Vertex_noGraph(GraphClass, GraphName, getMacroGraph, g, VertexPrint) \ + MOD_GRAPHPIMPL_Define_Vertex_noGraph_noId(GraphClass, GraphName, getMacroGraph, g, VertexPrint) \ + MOD_GRAPHPIMPL_Define_Vertex_id(GraphClass, getMacroGraph) + +#define MOD_GRAPHPIMPL_Define_Vertex_noGraph_noId(GraphClass, GraphName, getMacroGraph, g, VertexPrint)\ \ GraphClass::Vertex::Vertex(GraphHandle g, std::size_t vId) : g(g), vId(vId) { \ using boost::vertices; \ @@ -47,7 +54,7 @@ GraphClass::Vertex::Vertex() : vId(0) { } std::ostream &operator<<(std::ostream &s, const GraphClass::Vertex &v) { \ s << #GraphName "Vertex("; \ if(!v) s << "null"; \ - else s << *(v.g VertePrint) << ", " << v.getId(); \ + else s << *(v.g VertexPrint) << ", " << v.getId(); \ return s << ")"; \ } \ \ @@ -74,7 +81,9 @@ GraphClass::Vertex::operator bool() const { \ bool GraphClass::Vertex::isNull() const { \ return *this == GraphClass::Vertex(); \ -} \ +} + +#define MOD_GRAPHPIMPL_Define_Vertex_id(GraphClass, getMacroGraph) \ \ std::size_t GraphClass::Vertex::getId() const { \ if(!*this) throw LogicError("Can not get id on a null vertex."); \ diff --git a/libs/libmod/src/mod/lib/IO/Config.hpp b/libs/libmod/src/mod/lib/IO/Config.hpp index 72c992c..11005e4 100644 --- a/libs/libmod/src/mod/lib/IO/Config.hpp +++ b/libs/libmod/src/mod/lib/IO/Config.hpp @@ -2,7 +2,7 @@ #define MOD_LIB_IO_CONFIG_HPP #include -#include +#include namespace mod { diff --git a/libs/libmod/src/mod/lib/IO/DFS.cpp b/libs/libmod/src/mod/lib/IO/DFS.cpp new file mode 100644 index 0000000..971c69e --- /dev/null +++ b/libs/libmod/src/mod/lib/IO/DFS.cpp @@ -0,0 +1,412 @@ +#include "DFS.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace mod::lib::IO::DFS { + +void escapeLabel(std::ostream &s, const std::string &label, char escChar) { + for(int i = 0; i != label.size(); i++) { + char c = label[i]; + if(c == escChar) s << "\\" << escChar; + else if(c == '\t') s << "\\t"; + else if(c == '\\' && i + 1 != label.size()) { + char next = label[i + 1]; + if(next == '\\' || next == 't') s << "\\\\"; + else s << '\\'; + } else s << c; + } +} + +std::ostream &operator<<(std::ostream &s, const LabelVertex &lv) { + if(lv.implicit) s << lv.label; + else { + s << '['; + escapeLabel(s, lv.label, ']'); + s << ']'; + } + if(lv.id) s << *lv.id; + return s; +} + +std::ostream &operator<<(std::ostream &s, const RingClosure &rc) { + return s << rc.id; +} + +std::ostream &operator<<(std::ostream &s, const Vertex &v) { + s << v.vertex; + for(const auto &b: v.branches) s << b; + return s; +} + +std::ostream &operator<<(std::ostream &s, const Edge &e) { + if(e.label.size() > 1) { + s << '{'; + escapeLabel(s, e.label, '}'); + return s << '}'; + } + switch(e.label.front()) { + case '-': + return s; + case '=': + case '#': + case ':': + return s << e.label; + default: + return s << "{" << e.label << "}"; + } +} + +std::ostream &operator<<(std::ostream &s, const Chain &c) { + s << c.head; + for(const auto &ev: c.tail) s << ev.first << ev.second; + return s; +} + +std::ostream &operator<<(std::ostream &s, const Branch &b) { + for(const auto &ev: b.tail) s << '(' << ev.first << ev.second << ')'; + return s; +} + +std::ostream &operator<<(std::ostream &s, const Rule &r) { + if(r.left) s << *r.left; + s << "<<"; + if(r.right) s << *r.right; + return s; +} + +namespace Read { +namespace { +namespace parser { + +struct SpecialVertexLabels : x3::symbols { + SpecialVertexLabels() { + name("specialVertexLabels"); + for(auto atomId: lib::Chem::getSmilesOrganicSubset()) + add(lib::Chem::symbolFromAtomId(atomId), lib::Chem::symbolFromAtomId(atomId)); + } +} specialVertexLabels; + +struct SpecialEdgeLabels : x3::symbols { + SpecialEdgeLabels() { + name("specialEdgeLabels"); + add("-", "-"); + add(":", ":"); + add("=", "="); + add("#", "#"); + add(".", ""); + } +} specialEdgeLabelSymbols; + +// no recursion +const auto implicitBackslash = x3::attr('\\'); +const auto explicitBackslash = '\\' >> x3::attr('\\'); +const auto tab = 't' >> x3::attr('\t'); +const auto plainBrace /* */ = (x3::char_ - x3::char_('}')); +const auto plainBracket /**/ = (x3::char_ - x3::char_(']')); +const auto escapedBrace /* */ = '\\' >> (x3::char_('}') | tab | explicitBackslash | implicitBackslash); +const auto escapedBracket /**/ = '\\' >> (x3::char_(']') | tab | explicitBackslash | implicitBackslash); +const auto escapedStringBrace /* */ = x3::lexeme[x3::lit('{') > *(escapedBrace /* */ | plainBrace /* */) > + x3::lit('}')]; +const auto escapedStringBracket /**/ = x3::lexeme[x3::lit('[') > *(escapedBracket /**/ | plainBracket /**/) > + x3::lit(']')]; + +const auto specialEdgeLabels = specialEdgeLabelSymbols | x3::attr(std::string(1, '-')); +const auto edge = x3::rule("edge") = escapedStringBrace | specialEdgeLabels; +const auto defRingId = x3::uint_; +const auto ringClosure = x3::rule("ringClosure") = x3::uint_; +const auto specialLabelVertex = x3::rule("specialLabelVertex") +/* */ = specialVertexLabels >> x3::attr(true) >> -defRingId; +const auto explicitLabelVertex = x3::rule("explicitLabelVertex") +/* */ = escapedStringBracket >> x3::attr(false) >> -defRingId; +const auto labelVertex = explicitLabelVertex | specialLabelVertex; +// part of recursion +const x3::rule vertex = "vertex"; +const auto evPair = x3::rule("edgeVertexPair") = edge >> vertex; +const auto branch = x3::rule("branch") = '(' > +evPair > ')'; +const auto branches = *branch; +const auto vertex_def = (labelVertex | ringClosure) >> branches; +BOOST_SPIRIT_DEFINE(vertex) +// no recursion +const auto chain = vertex > *evPair; +const auto graphDFS = x3::rule("graphDFS") +/* */ = chain; +const auto ruleDFS = x3::rule("ruleDFS") +/* */ = -graphDFS > x3::lit(">>") > -graphDFS; + +} // namespace parser + +struct Phase1 { + // - Set connectedComponentIDs + // - Record vertex IDs + using Map = std::map; + // - Resolve rings +public: + Phase1(std::string errorVariation) : errorVariation(std::move(errorVariation)) {} + lib::IO::Result> operator()(Chain &chain) { + (*this)(chain.head); + for(EVPair &ev: chain.tail) + (*this)(ev.second); + if(error.empty()) + return std::pair(nextID, std::move(vertexFromId)); + else + return lib::IO::Result<>::Error(std::move(error)); + } + + void operator()(Vertex &vertex) { + boost::apply_visitor(*this, vertex.vertex); + for(Branch &branch: vertex.branches) + for(EVPair &ev: branch.tail) + (*this)(ev.second); + } + + void operator()(LabelVertex &vertex) { + vertex.connectedComponentID = nextID; + ++nextID; + + if(vertex.id) { + const auto iter = vertexFromId.find(*vertex.id); + if(iter == vertexFromId.end()) { + // use the id as definition + vertexFromId[*vertex.id] = &vertex; + } else { + // use the id as ring closure + vertex.ringClosure = iter->second; + } + } + } + + void operator()(RingClosure &vertex) { + const auto iter = vertexFromId.find(vertex.id); + if(iter != vertexFromId.end()) { + vertex.other = iter->second; + } else { + // there may have been a previous error + if(error.empty()) + error = "Ring closure ID " + std::to_string(vertex.id) + " not found" + errorVariation + "."; + } + } +public: + int nextID = 0; + Map vertexFromId; + std::string error; + std::string errorVariation; +}; + +struct CheckLoop { + // - Check for loop edges, by ring resolution to previous vertex + CheckLoop(std::string errorVariation) : errorVariation(std::move(errorVariation)) {} + + lib::IO::Result<> operator()(const Chain &chain) { + const LabelVertex *prevVertex = (*this)(nullptr, nullptr, chain.head); + for(const EVPair &ev: chain.tail) { + prevVertex = (*this)(prevVertex, &ev.first, ev.second); + if(!error.empty()) break; + } + + if(error.empty()) + return {}; + else + return lib::IO::Result<>::Error(std::move(error)); + } + + const LabelVertex *operator()(const LabelVertex *pVertex, const Edge *edge, const Vertex &vertex) { + assert((pVertex == nullptr) == (edge == nullptr)); + auto [thisVertex, ringId] = boost::apply_visitor(*this, vertex.vertex); + assert(thisVertex); + if(ringId >= 0 && thisVertex == pVertex && !edge->label.empty()) { + assert(error.empty()); + error = "Loop edge in DFS on vertex" + errorVariation + " with ID " + std::to_string(ringId) + "."; + return pVertex; + } + + const LabelVertex *baseVertex = ringId < 0 ? thisVertex : pVertex; + + for(const Branch &branch: vertex.branches) { + const LabelVertex *pBranchVertex = baseVertex; + for(const EVPair &ev: branch.tail) { + pBranchVertex = (*this)(pBranchVertex, &ev.first, ev.second); + if(!error.empty()) break; + } + } + + return baseVertex; + } + + std::pair operator()(const LabelVertex &vertex) { + return {&vertex, -1}; + } + + std::pair operator()(const RingClosure &vertex) { + return {vertex.other, vertex.id}; + } +public: + std::string error; + std::string errorVariation; +}; + +struct CheckParallel { + // - Check for parallel edges, by ring-closure to just previous vertex + CheckParallel(std::string errorVariation) : errorVariation(std::move(errorVariation)) {} + + lib::IO::Result<> operator()(const Chain &chain) { + auto [ppVertex, pVertex] = (*this)(nullptr, nullptr, nullptr, chain.head); + for(const EVPair &ev: chain.tail) { + std::tie(ppVertex, pVertex) = (*this)(ppVertex, pVertex, &ev.first, ev.second); + if(!error.empty()) break; + } + + if(error.empty()) + return {}; + else + return lib::IO::Result<>::Error(std::move(error)); + } + + std::pair + operator()(const LabelVertex *ppVertex, const LabelVertex *pVertex, const Edge *edge, const Vertex &vertex) { + assert((pVertex == nullptr) == (edge == nullptr)); + auto [thisVertex, ringId] = boost::apply_visitor(*this, vertex.vertex); + assert(thisVertex); + if(edge && !edge->label.empty()) { + // not [A].[B] with (edge, vertex) being (., [B]) + if(ringId >= 0 && thisVertex == ppVertex) { + // [A]1[B]-1 with (edge, vertex) being (-, 1) + assert(error.empty()); + error = "Parallel edge in DFS" + errorVariation + ". Back-edge is to vertex with ID " + + std::to_string(ringId) + "."; + return {nullptr, nullptr}; + } + if(ringId < 0 && thisVertex->ringClosure && thisVertex->ringClosure == pVertex) { + // [A]1[B]1 with (edge, vertex) being ('-', [B]1) + assert(error.empty()); + error = "Parallel edge in DFS" + errorVariation + ". Back-edge is to vertex with ID " + + std::to_string(*thisVertex->id) + "."; + return {nullptr, nullptr}; + } + } + + if(ringId < 0) { + // thisVertex is an actual vertex + if(!edge || !edge->label.empty()) { + // [ppVertex][pVertex][thisVertex] + ppVertex = pVertex; + pVertex = thisVertex; + } else { + // [ppVertex][pVertex].[thisVertex] + // so semi-reset + ppVertex = nullptr; + pVertex = thisVertex; + } + } else { + // thisVertex is just a ringClosure, i.e., an implicit branch + // keep ppVertex and pVertex + } + for(const Branch &branch: vertex.branches) { + const LabelVertex *ppBranchVertex = ppVertex; + const LabelVertex *pBranchVertex = pVertex; + for(const EVPair &ev: branch.tail) { + std::tie(ppBranchVertex, pBranchVertex) = (*this)(ppBranchVertex, pBranchVertex, &ev.first, ev.second); + if(!error.empty()) break; + } + } + + return {ppVertex, pVertex}; + } + + std::pair operator()(const LabelVertex &vertex) { + return {&vertex, -1}; + } + + std::pair operator()(const RingClosure &vertex) { + return {vertex.other, vertex.id}; + } +public: + std::string error; + std::string errorVariation; +}; + +} // namespace + +Result graph(std::string_view data) { + auto ast = std::make_unique(); + try { + lib::IO::parse(data.begin(), data.end(), parser::graphDFS, *ast, true, x3::ascii::space); + } catch(const lib::IO::ParsingError &e) { + return lib::IO::Result<>::Error(e.msg); + } + + auto resPhase1 = Phase1("")(*ast); + if(!resPhase1) return lib::IO::Result<>::Error(resPhase1.extractError()); + auto [numVertices, vertexFromId] = *resPhase1; + if(auto res = CheckLoop("")(*ast); !res) return res; + if(auto res = CheckParallel("")(*ast); !res) return res; + return GraphResult{std::move(ast), numVertices, std::move(vertexFromId)}; +} + +Result rule(std::string_view data) { + Rule ast; + try { + lib::IO::parse(data.begin(), data.end(), parser::ruleDFS, ast, true, x3::ascii::space); + } catch(const lib::IO::ParsingError &e) { + return lib::IO::Result<>::Error(e.msg); + } + RuleResult res; + if(ast.left) { + res.left.ast = std::make_unique(std::move(*ast.left)); + auto resPhase1 = Phase1(", in the left side")(*res.left.ast); + if(!resPhase1) return lib::IO::Result<>::Error(resPhase1.extractError()); + std::tie(res.left.numVertices, res.left.vertexFromId) = *resPhase1; + if(auto resLoop = CheckLoop(" in the left side")(*res.left.ast); !resLoop) return resLoop; + if(auto resPar = CheckParallel(" in the left side")(*res.left.ast); !resPar) return resPar; + } + if(ast.right) { + res.right.ast = std::make_unique(std::move(*ast.right)); + auto resPhase1 = Phase1(", in the right side")(*res.right.ast); + if(!resPhase1) return lib::IO::Result<>::Error(resPhase1.extractError()); + std::tie(res.right.numVertices, res.right.vertexFromId) = *resPhase1; + if(auto resLoop = CheckLoop(" in the right side")(*res.right.ast); !resLoop) return resLoop; + if(auto resPar = CheckParallel(" in the right side")(*res.right.ast); !resPar) return resPar; + } + return std::move(res); // TODO: remove std::move when C++20/P1825R0 is available +} + +} // namespace Read +} // namespace mod::lib::IO::DFS + +BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::DFS::LabelVertex, + (std::string, label) + (bool, implicit) + (std::optional, id)) +BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::DFS::RingClosure, + (unsigned int, id)) +BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::DFS::Vertex, + (mod::lib::IO::DFS::BaseVertex, vertex) + (std::vector, branches)) +BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::DFS::Edge, + (std::string, label)) +BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::DFS::Chain, + (mod::lib::IO::DFS::Vertex, head) + (std::vector, tail)) +BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::DFS::Branch, + (std::vector, tail)) +BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::DFS::Rule, + (std::optional, left) + (std::optional, right)) \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/DFS.hpp b/libs/libmod/src/mod/lib/IO/DFS.hpp new file mode 100644 index 0000000..fcde79d --- /dev/null +++ b/libs/libmod/src/mod/lib/IO/DFS.hpp @@ -0,0 +1,103 @@ +#ifndef MOD_LIB_IO_DFS_HPP +#define MOD_LIB_IO_DFS_HPP + +#include + +#include + +#include +#include +#include +#include +#include + +namespace mod::lib::IO::DFS { + +void escapeLabel(std::ostream &s, const std::string &label, char escChar); + +struct LabelVertex; +struct RingClosure; +struct Branch; + +// x3 does not yet fully support std::variant +// https://github.com/boostorg/spirit/issues/321 +using BaseVertex = boost::variant; + +struct LabelVertex { + std::string label; + bool implicit = false; + std::optional id; +public: // set by Phase1 + int connectedComponentID; + LabelVertex *ringClosure = nullptr; // if id turns out to be a ring-closure, then this gets set +public: // used by consumer + int gVertexId = -1; +public: + friend std::ostream &operator<<(std::ostream &s, const LabelVertex &lv); +}; + +struct RingClosure { + // must use unsigned so the parser understands how to store it + unsigned int id = std::numeric_limits::max(); +public: + LabelVertex *other; +public: + friend std::ostream &operator<<(std::ostream &s, const RingClosure &rc); +}; + +struct Vertex { + BaseVertex vertex; + std::vector branches; +public: + friend std::ostream &operator<<(std::ostream &s, const Vertex &v); +}; + +struct Edge { + std::string label; +public: + friend std::ostream &operator<<(std::ostream &s, const Edge &e); +}; + +using EVPair = std::pair; + +struct Chain { + Vertex head; + std::vector tail; + bool hasNonSmilesRingClosure; // only set when creating GraphDFS from a graph +public: + friend std::ostream &operator<<(std::ostream &s, const Chain &c); +}; + +struct Branch { + std::vector tail; +public: + friend std::ostream &operator<<(std::ostream &s, const Branch &b); +}; + +struct Rule { + std::optional left, right; +public: + friend std::ostream &operator<<(std::ostream &s, const Rule &r); +}; + +namespace Read { + +struct GraphResult { + // returns a pointer such that all resolved rings have stable pointers + std::unique_ptr ast; + int numVertices; + std::map vertexFromId; +}; + +struct RuleResult { + // the ast may be null if that side doesn't exist + GraphResult left, right; +}; + +Result graph(std::string_view data); +Result rule(std::string_view data); + +} // namespace Read +} // namespace mod::lib::IO::DF + +#endif // MOD_LIB_IO_DFS_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/Derivation.hpp b/libs/libmod/src/mod/lib/IO/Derivation.hpp deleted file mode 100644 index fd16ce1..0000000 --- a/libs/libmod/src/mod/lib/IO/Derivation.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MOD_LIB_IO_DERIVATION_H -#define MOD_LIB_IO_DERIVATION_H - -#include - -namespace mod { -namespace lib { -namespace DG { -class NonHyper; -} // namespace DG -namespace IO { -namespace Graph { -namespace Write { -struct Options; -} // namespace Write -} // namespace Graph -namespace Derivation { -namespace Write { -std::vector> -summary(const lib::DG::NonHyper &dg, lib::DG::HyperVertex v, const IO::Graph::Write::Options &options, - const std::string &nomatchColour, const std::string &matchColour); -} // namespace Write -} // namespace Derivation -} // namespace IO -} // namespace lib -} // namespace mod - -#endif /* MOD_LIB_IO_DERIVATION_H */ \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/GMLUtil.cpp b/libs/libmod/src/mod/lib/IO/GML.cpp similarity index 90% rename from libs/libmod/src/mod/lib/IO/GMLUtil.cpp rename to libs/libmod/src/mod/lib/IO/GML.cpp index a15c605..be89a7d 100644 --- a/libs/libmod/src/mod/lib/IO/GMLUtil.cpp +++ b/libs/libmod/src/mod/lib/IO/GML.cpp @@ -1,4 +1,4 @@ -#include +#include "GML.hpp" #include diff --git a/libs/libmod/src/mod/lib/IO/GMLUtils.hpp b/libs/libmod/src/mod/lib/IO/GML.hpp similarity index 89% rename from libs/libmod/src/mod/lib/IO/GMLUtils.hpp rename to libs/libmod/src/mod/lib/IO/GML.hpp index af9dac2..a93ee10 100644 --- a/libs/libmod/src/mod/lib/IO/GMLUtils.hpp +++ b/libs/libmod/src/mod/lib/IO/GML.hpp @@ -1,7 +1,8 @@ -#ifndef MOD_LIB_IO_GMLUTILS_HPP -#define MOD_LIB_IO_GMLUTILS_HPP +#ifndef MOD_LIB_IO_GML_HPP +#define MOD_LIB_IO_GML_HPP + +#include -#include #include #include @@ -16,7 +17,7 @@ struct Vertex { std::optional label; std::optional stereo; public: - std::optional parsedEmbedding; + std::optional parsedEmbedding; }; struct Edge { @@ -77,4 +78,4 @@ static const auto makeEdgeConverter = [](std::size_t lowerBound) { } // namespace mod::lib::IO::GML -#endif // MOD_LIB_IO_GMLUTILS_HPP \ No newline at end of file +#endif // MOD_LIB_IO_GML_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/Graph.hpp b/libs/libmod/src/mod/lib/IO/Graph.hpp deleted file mode 100644 index 328ef83..0000000 --- a/libs/libmod/src/mod/lib/IO/Graph.hpp +++ /dev/null @@ -1,254 +0,0 @@ -#ifndef MOD_LIB_IO_GRAPH_HPP -#define MOD_LIB_IO_GRAPH_HPP - -#include -#include - -#include -#include -#include -#include - -namespace mod { -enum class SmilesClassPolicy; -} // namespace mod -namespace mod::lib::Graph { -struct DepictionData; -struct LabelledGraph; -struct PropStereo; -struct PropString; -struct Single; -} // namespace mod::lib::Graph -namespace mod::lib::IO::Graph::Read { - -struct Data { - Data(); - Data(std::unique_ptr graph, std::unique_ptr label); - Data(Data &&other); - ~Data(); - void reset(); -public: - std::unique_ptr g; - std::unique_ptr pString; - std::unique_ptr pStereo; - std::map externalToInternalIds; -}; - -struct ConnectedComponents { - ConnectedComponents(int n) : components(n) { - for(int i = 0; i != n; ++i) - components[i] = i; - } - - int findRoot(int i) { - while(components[i] != i) - i = components[i]; - return i; - } - - void join(int a, int b) { - a = findRoot(a); - b = findRoot(b); - // always make the smallest the root - if(a < b) components[b] = a; - else if(b < a) components[a] = b; - } -public: - int finalize() { - int next = 0; - for(int i = 0; i != components.size(); ++i) { - // everything below i has been changed to component numbers - // and joining always goes downward, so just to a single root-finding step - const int root = components[i]; - if(root == i) { - components[i] = next; - ++next; - } else { - assert(root < i); - components[i] = components[root]; - } - } - return next; - } -public: - int operator[](int i) const { - return components[i]; - } -private: - std::vector components; -}; - -auto gml(lib::IO::Warnings &warnings, std::string_view src) -> Result>; -auto dfs(const std::string &dfs) -> Result; -auto smiles(lib::IO::Warnings &warnings, const std::string &smiles, bool allowAbstract, - SmilesClassPolicy classPolicy) -> Result>; -} // namespace mod::lib::IO::Graph::Read -namespace mod::lib::IO::Graph::Write { - -enum class EdgeFake3DType { - None, WedgeSL, WedgeLS, HashSL, HashLS -}; - -EdgeFake3DType invertEdgeFake3DType(EdgeFake3DType t); - -struct Options { - Options &Non() { - return EdgesAsBonds(false).CollapseHydrogens(false).RaiseIsotopes(false).RaiseCharges(false).SimpleCarbons( - false).Thick(false).WithColour(false).WithIndex(false); - } - - Options &All() { - return EdgesAsBonds(true).CollapseHydrogens(true).RaiseIsotopes(true).RaiseCharges(true).SimpleCarbons( - true).Thick(true).WithColour(true).WithIndex(true); - } - - Options &EdgesAsBonds(bool v) { - edgesAsBonds = v; - return *this; - } - - Options &CollapseHydrogens(bool v) { - collapseHydrogens = v; - return *this; - } - - Options &RaiseIsotopes(bool v) { - raiseIsotopes = v; - return *this; - } - - Options &RaiseCharges(bool v) { - raiseCharges = v; - return *this; - } - - Options &SimpleCarbons(bool v) { - simpleCarbons = v; - return *this; - } - - Options &Thick(bool v) { - thick = v; - return *this; - } - - Options &WithColour(bool v) { - withColour = v; - return *this; - } - - Options &WithIndex(bool v) { - withIndex = v; - return *this; - } - - Options &WithTexttt(bool v) { - withTexttt = v; - return *this; - } - - Options &WithRawStereo(bool v) { - withRawStereo = v; - return *this; - } - - Options &WithPrettyStereo(bool v) { - withPrettyStereo = v; - return *this; - } - - Options &Rotation(int degrees) { - rotation = degrees; - return *this; - } - - Options &Mirror(bool v) { - mirror = v; - return *this; - } - - std::string getStringEncoding() const { - auto toChar = [](bool b) { - return b ? '1' : '0'; - }; - std::string res; - res += toChar(edgesAsBonds); - res += toChar(collapseHydrogens); - char raise = 0; - if(raiseCharges) raise += 1; - if(raiseIsotopes) raise += 2; - res += '0' + raise; - res += toChar(simpleCarbons); - res += toChar(thick); - res += toChar(withColour); - res += toChar(withIndex); - res += toChar(withTexttt); - char stereo = 0; - if(withRawStereo) stereo += 1; - if(withPrettyStereo) stereo += 2; - if(stereo != 0) res += '0' + stereo; - if(rotation != 0 || mirror) { - res += "_"; - res += std::to_string(rotation); - if(mirror) { - res += "_"; - res += toChar(mirror); - } - } - return res; - } - - friend bool operator==(const Options &a, const Options &b) { - return a.getStringEncoding() == b.getStringEncoding(); - } - - friend bool operator!=(const Options &a, const Options &b) { - return !(a == b); - } - -public: - bool edgesAsBonds = false; - bool collapseHydrogens = false; - bool raiseCharges = false; - bool raiseIsotopes = false; - bool simpleCarbons = false; - bool thick = false; - bool withColour = false; - bool withIndex = false; - bool withTexttt = false; - bool withRawStereo = false; - bool withPrettyStereo = false; - int rotation = 0; - bool mirror = false; -}; - -// all return the filename _with_ extension -void gml(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - bool withCoords, std::ostream &s); -std::string gml(const lib::Graph::Single &g, bool withCoords); -std::string dot(const lib::Graph::LabelledGraph &gLabelled, const std::size_t gId); -std::string -coords(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - const Options &options, bool asInline); -std::pair -tikz(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - const Options &options, - bool asInline, const std::string &idPrefix); -std::string -pdf(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - const Options &options); -std::string -svg(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - const Options &options); -std::pair summary(const lib::Graph::Single &g, const Options &first, const Options &second); -void termState(const lib::Graph::Single &g); - -// simplified interface for lib::Graph::Single -void gml(const lib::Graph::Single &g, bool withCoords, std::ostream &s); -std::string tikz(const lib::Graph::Single &g, const Options &options, bool asInline, const std::string &idPrefix); -std::string pdf(const lib::Graph::Single &g, const Options &options); -std::string svg(const lib::Graph::Single &g, const Options &options); - -} // namespace mod::lib::IO::Graph::Write - -#endif // MOD_LIB_IO_GRAPH_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/GraphRead.cpp b/libs/libmod/src/mod/lib/IO/GraphRead.cpp deleted file mode 100644 index 8c04bcc..0000000 --- a/libs/libmod/src/mod/lib/IO/GraphRead.cpp +++ /dev/null @@ -1,282 +0,0 @@ -#include "Graph.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include - -namespace mod::lib::IO::Graph::Read { - -Data::Data() {} - -Data::Data(Data &&other) : g(std::move(other.g)), pString(std::move(other.pString)), pStereo(std::move(other.pStereo)), - externalToInternalIds(std::move(other.externalToInternalIds)) {} - -Data::~Data() { - if(std::uncaught_exceptions() != 0) return; - if(g) MOD_ABORT; - if(pString) MOD_ABORT; - if(pStereo) MOD_ABORT; -} - -void Data::reset() { - pStereo.reset(); - pString.reset(); - g.reset(); -} - -Result> gml(lib::IO::Warnings &warnings, std::string_view src) { - GML::Graph gGML; - { - gml::ast::KeyValue ast; - try { - ast = gml::parser::parse(src); - } catch(const gml::parser::error &e) { - return Result<>::Error(e.what()); - } - using namespace gml::converter::edsl; - auto cVertex = GML::makeVertexConverter(1); - auto cEdge = GML::makeEdgeConverter(1); - auto cGraph = list("graph")(cVertex)(cEdge); - auto iterBegin = * - auto iterEnd = iterBegin + 1; - try { - gml::converter::convert(iterBegin, iterEnd, cGraph, gGML); - } catch(const gml::converter::error &e) { - return Result<>::Error(e.what()); - } - } - - std::sort(begin(gGML.vertices), end(gGML.vertices), [](const GML::Vertex &v1, const GML::Vertex &v2) -> bool { - return v1.id < v2.id; - }); - - // Check that is mathematically is a graph - - std::unordered_map globalIddFromExtID; - { - int i = 0; - for(const auto &vGML : gGML.vertices) { - if(globalIddFromExtID.find(vGML.id) != end(globalIddFromExtID)) - return Result<>::Error("Vertex id " + std::to_string(vGML.id) + " used multiple times."); - globalIddFromExtID.emplace(vGML.id, i++); - } - } - - for(const auto &eGML : gGML.edges) { - if(eGML.source == eGML.target) - return Result<>::Error("Loop edge (on " + std::to_string(eGML.source) + ") is not allowed."); - if(globalIddFromExtID.find(eGML.source) == end(globalIddFromExtID)) - return Result<>::Error("Source " + std::to_string(eGML.source) + " does not exist in '" - + boost::lexical_cast(eGML) + "'."); - if(globalIddFromExtID.find(eGML.target) == end(globalIddFromExtID)) - return Result<>::Error("Target " + std::to_string(eGML.target) + " does not exist in '" - + boost::lexical_cast(eGML) + "'."); - } - - // Now we can calculate connected components and start converting data - - ConnectedComponents components(gGML.vertices.size()); - for(const auto &e : gGML.edges) { - const auto iterSrc = globalIddFromExtID.find(e.source); - const auto iterTar = globalIddFromExtID.find(e.target); - components.join(iterSrc->second, iterTar->second); - } - const auto numComponents = components.finalize(); - - std::vector datas(numComponents); - std::vector> extIDFromVertex(numComponents); - for(auto &d : datas) { - d.g = std::make_unique(); - d.pString = std::make_unique(*d.g); - } - - const auto atError = [&datas](std::string msg) -> Result<> { - for(auto &d : datas) d.reset(); - return Result<>::Error(std::move(msg)); - }; - - for(const auto &vGML : gGML.vertices) { - const auto comp = components[globalIddFromExtID.find(vGML.id)->second]; - auto &g = *datas[comp].g; - const auto v = add_vertex(g); - assert(vGML.label); - datas[comp].pString->addVertex(v, *vGML.label); - datas[comp].externalToInternalIds.emplace(vGML.id, get(boost::vertex_index_t(), g, v)); - extIDFromVertex[comp].emplace(v, vGML.id); - } - const auto vFromVertexId = [&](int id) { - const auto globalIDIter = globalIddFromExtID.find(id); - assert(globalIDIter != end(globalIddFromExtID)); - const auto comp = components[globalIDIter->second]; - const auto vIdIter = datas[comp].externalToInternalIds.find(id); - assert(vIdIter != end(datas[comp].externalToInternalIds)); - return std::pair(comp, vertex(vIdIter->second, *datas[comp].g)); - }; - for(const auto &eGML : gGML.edges) { - const auto[comp, vSrc] = vFromVertexId(eGML.source); - const auto[compTar, vTar] = vFromVertexId(eGML.target); - assert(comp == compTar); - auto &g = *datas[comp].g; - const auto eQuery = edge(vSrc, vTar, g); - if(eQuery.second) - return atError("Duplicate edge with source " + std::to_string(eGML.source) - + " and target " + std::to_string(eGML.target) + "."); - const auto e = add_edge(vSrc, vTar, g); - assert(eGML.label); - datas[comp].pString->addEdge(e.first, *eGML.label); - } - - bool doStereo = false; - for(const auto &vGML : gGML.vertices) doStereo = doStereo || vGML.stereo; - for(const auto &eGML : gGML.edges) doStereo = doStereo || eGML.stereo; - if(!doStereo) return std::move(datas); // TODO: remove std::move when C++20/P1825R0 is available - // Stereo - //============================================================================ - std::vector molStates; - std::vector> stereoInferences; - molStates.reserve(numComponents); - stereoInferences.reserve(numComponents); - for(int i = 0; i != numComponents; ++i) { - const auto &g = *datas[i].g; - molStates.emplace_back(g, *datas[i].pString); - stereoInferences.push_back(lib::Stereo::makeInference(g, molStates.back(), false)); - } - - const auto &gGeometry = lib::Stereo::getGeometryGraph(); - // Set the explicitly defined edge categories. - //---------------------------------------------------------------------------- - for(const auto &eGML : gGML.edges) { - const auto[comp, vSrc] = vFromVertexId(eGML.source); - const auto[compTar, vTar] = vFromVertexId(eGML.target); - assert(comp == compTar); - const auto &g = *datas[comp].g; - const auto ePair = edge(vSrc, vTar, g); - assert(ePair.second); - if(!eGML.stereo) continue; - const std::string &s = *eGML.stereo; - if(s.size() != 1) - return atError( - "Error in stereo data for edge (" + std::to_string(eGML.source) - + ", " + std::to_string(eGML.target) + "). Parsing error in stereo data '" + s + "'."); - lib::Stereo::EdgeCategory cat; - switch(s.front()) { - case '*': - cat = lib::Stereo::EdgeCategory::Any; - break; - default: - return atError("Error in stereo data for edge (" + std::to_string(eGML.source) - + ", " + std::to_string(eGML.target) + "). Parsing error in stereo data '" + s + "'."); - } - if(auto res = stereoInferences[comp].assignEdgeCategory(ePair.first, cat); !res) - return atError("Error in stereo data for edge (" + std::to_string(eGML.source) - + ", " + std::to_string(eGML.target) + "). " - + res.extractError()); - } - // Set the explicitly stereo data. - //---------------------------------------------------------------------------- - for(auto &vGML : gGML.vertices) { - if(!vGML.stereo) continue; - const auto[comp, v] = vFromVertexId(vGML.id); - if(auto res = lib::IO::Stereo::Read::parseEmbedding(*vGML.stereo)) { - vGML.parsedEmbedding = std::move(*res); - } else { - return atError("Error in stereo data for vertex " + std::to_string(vGML.id) + ". " - + res.extractError()); - } - // Geometry - //.......................................................................... - const auto &embGML = *vGML.parsedEmbedding; - if(embGML.geometry) { - const auto vGeo = gGeometry.findGeometry(*embGML.geometry); - if(vGeo == gGeometry.nullGeometry()) - return atError("Error in stereo data for vertex " + std::to_string(vGML.id) - + ". Invalid gGeometry '" + *embGML.geometry + "'."); - if(auto res = stereoInferences[comp].assignGeometry(v, vGeo); !res) - return atError("Error in stereo data for vertex " + std::to_string(vGML.id) + ". " - + res.extractError()); - } - // Edges - //.......................................................................... - if(embGML.edges) { - stereoInferences[comp].initEmbedding(v); - for(const auto &e : *embGML.edges) { - if(const int *idPtr = std::get_if(&e)) { - const auto extIDNeighbour = *idPtr; - if(globalIddFromExtID.find(extIDNeighbour) == end(globalIddFromExtID)) - return atError("Neighbour vertex " + std::to_string(extIDNeighbour) - + " in stereo embedding for vertex " + std::to_string(vGML.id) + " does not exist."); - const auto[compNeighbour, vNeighbour] = vFromVertexId(extIDNeighbour); - const auto ePair = edge(v, vNeighbour, *datas[comp].g); - if(!ePair.second) - return atError("Error in graph GML. Vertex " + std::to_string(extIDNeighbour) + - " in stereo embedding for vertex " + std::to_string(vGML.id) + " is not a neighbour."); - assert(compNeighbour == comp); - stereoInferences[comp].addEdge(v, ePair.first); - } else if(const char *virtPtr = std::get_if(&e)) { - switch(*virtPtr) { - case 'e': - stereoInferences[comp].addLonePair(v); - break; - case 'r': - stereoInferences[comp].addRadical(v); - break; - default: - MOD_ABORT; // the parser should know what is allowed - } - } else { - MOD_ABORT; // the parser should know what is allowed - } - } - } - // Fixation - //.......................................................................... - if(embGML.fixation) { - // TODO: expand this when more complicated geometries are implemented - const bool isFixed = *embGML.fixation; - if(isFixed) stereoInferences[comp].fixSimpleGeometry(v); - } - } // end of explicit stereo data - - for(int comp = 0; comp != numComponents; ++comp) { - // TODO: the warning should only be printed once, instead of for each connected component - lib::IO::Warnings stereoWarnings; - auto stereoResult = stereoInferences[comp].finalize( - stereoWarnings, [comp, &extIDFromVertex](lib::Graph::Vertex v) { - const auto iter = extIDFromVertex[comp].find(v); - assert(iter != extIDFromVertex[comp].end()); - return std::to_string(iter->second); - }); - warnings.addFrom(std::move(stereoWarnings), !getConfig().stereo.silenceDeductionWarnings.get()); - if(!stereoResult) - return atError(stereoResult.extractError()); - datas[comp].pStereo = std::make_unique(*datas[comp].g, std::move(stereoInferences[comp])); - } - return std::move(datas); // TODO: remove std::move when C++20/P1825R0 is available -} - -Result dfs(const std::string &dfs) { - return lib::Graph::DFSEncoding::parse(dfs); -} - -Result> -smiles(lib::IO::Warnings &warnings, const std::string &smiles, const bool allowAbstract, SmilesClassPolicy classPolicy) { - return lib::Chem::readSmiles(warnings, smiles, allowAbstract, classPolicy); -} - -} // namespace mod::lib::IO::Graph::Read \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/GraphWrite.cpp b/libs/libmod/src/mod/lib/IO/GraphWrite.cpp index 3f8f1dc..5007935 100644 --- a/libs/libmod/src/mod/lib/IO/GraphWrite.cpp +++ b/libs/libmod/src/mod/lib/IO/GraphWrite.cpp @@ -1,49 +1,6 @@ -#include "Graph.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include +#include "GraphWrite.hpp" namespace mod::lib::IO::Graph::Write { -namespace { - -// returns the filename _without_ extension - -std::string getFilePrefix(const std::size_t gId, bool useCache) { - static std::map cache; - auto iter = cache.find(gId); - if(!useCache || iter == end(cache)) { - std::string prefix = IO::getUniqueFilePrefix() + "g_" + boost::lexical_cast(gId); - if(useCache) cache[gId] = prefix; - return prefix; - } else return iter->second; -} - -void escapeLabelForDot(const std::string &label, std::ostream &s) { - for(char c : label) { - if(c == '"') s << "\\\""; - else if(c == '\\') s << "\\\\"; - else s << c; - } -} - -} // namespace EdgeFake3DType invertEdgeFake3DType(EdgeFake3DType t) { switch(t) { @@ -58,295 +15,7 @@ EdgeFake3DType invertEdgeFake3DType(EdgeFake3DType t) { case EdgeFake3DType::HashLS: return EdgeFake3DType::HashSL; } - MOD_ABORT; // no, GCC, shh -} - -void gml(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - bool withCoords, std::ostream &s) { - if(!depict.getHasCoordinates() && withCoords) MOD_ABORT; - const auto &g = get_graph(gLabelled); - const auto &pString = get_string(gLabelled); - s << "graph [\n"; - for(auto v : asRange(vertices(g))) { - s << "\tnode [ id " << get(boost::vertex_index_t(), g, v) << " label \"" << pString[v] << "\""; - if(withCoords) - s << " vis2d [ x " << depict.getX(v, true) << " y " << depict.getY(v, true) << " ]"; - s << " ]\n"; - } - for(auto e : asRange(edges(g))) { - s << "\tedge [ source " << get(boost::vertex_index_t(), g, source(e, g)) - << " target " << get(boost::vertex_index_t(), g, target(e, g)) - << " label \"" << pString[e] << "\" ]\n"; - } - s << "]\n"; -} - -std::string gml(const lib::Graph::Single &g, bool withCoords) { - static std::set > cache; - auto iter = cache.find(std::make_pair(g.getId(), withCoords)); - std::string fileNoExt = getFilePrefix(g.getId(), true); - if(iter != end(cache)) return fileNoExt; - cache.emplace(g.getId(), withCoords); - post::FileHandle s(fileNoExt + ".gml"); - gml(g.getLabelledGraph(), g.getDepictionData(), g.getId(), withCoords, s); - return s; -} - -std::string dot(const lib::Graph::LabelledGraph &gLabelled, const std::size_t gId) { - static std::set cache; - auto iter = cache.find(gId); - std::string file = getFilePrefix(gId, true) + ".dot"; - if(iter != end(cache)) return file; - cache.insert(gId); - post::FileHandle s(file); - const auto &g = get_graph(gLabelled); - const auto &pString = get_string(gLabelled); - { - s << "graph g {\n"; - s << "\tnode [shape=plaintext]\n"; - s << getConfig().io.dotCoordOptions.get() << "\n"; - - for(auto v : asRange(vertices(g))) { - unsigned int id = get(boost::vertex_index_t(), g, v); - const std::string &label = pString[v]; - s << "\t" << id << " [ label=\""; - escapeLabelForDot(label, s); - s << "\" ]\n"; - } - - for(auto e : asRange(edges(g))) { - auto fromId = get(boost::vertex_index_t(), g, source(e, g)); - auto toId = get(boost::vertex_index_t(), g, target(e, g)); - const std::string &label = pString[e]; - if(label == "-" || label == "=" || label == "#") { - s << fromId << " -- " << toId << std::endl; - if(label != "-") s << fromId << " -- " << toId << std::endl; - if(label == "#") s << fromId << " -- " << toId << std::endl; - } else { - s << fromId << " -- " << toId << " [ label=\""; - escapeLabelForDot(label, s); - s << "\" ]\n"; - } - } - s << "}\n"; - } - return s; -} - -struct CoordsCacheEntry { - std::size_t id; - bool collapseHydrogens; - int rotation; - bool mirror; -public: - friend bool operator<(const CoordsCacheEntry &a, const CoordsCacheEntry &b) { - return std::tie(a.id, a.collapseHydrogens, a.rotation, a.mirror) - < std::tie(b.id, b.collapseHydrogens, b.rotation, b.mirror); - } -}; - -std::string coords(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, - const std::size_t gId, const Options &options, bool asInline) { - static std::map cache; - if(!asInline) { - const auto iter = cache.find({gId, options.collapseHydrogens, options.rotation, options.mirror}); - if(iter != end(cache)) return iter->second; - } - if(!depict.getHasCoordinates()) { - dot(gLabelled, gId); - std::string fileNoExt = getFilePrefix(gId, !asInline); - IO::post() << "coordsFromGV graph \"" << fileNoExt << "\"\n"; - std::string file = std::move(fileNoExt) + "_coord.tex"; - if(!asInline) { - for(bool collapse :{true, false}) { - for(bool mirror :{true, false}) { - cache[{gId, collapse, options.rotation, mirror} - ] = file; - } - } - } - return file; - } else { - const auto &g = get_graph(gLabelled); - std::string f = getFilePrefix(gId, !asInline); - if(options.collapseHydrogens) f += "_mol"; - if(options.rotation != 0) f += "_r" + std::to_string(options.rotation); - if(options.mirror) f += "_m" + std::to_string(options.mirror); - if(asInline) f += "i"; - post::FileHandle s(f + "_coord.tex"); - s << "% dummy\n"; - for(const auto v : asRange(vertices(g))) { - const auto vId = get(boost::vertex_index_t(), g, v); - if(options.collapseHydrogens && Chem::isCollapsible(v, g, depict, depict, [&depict](const auto v) { - return depict.hasImportantStereo(v); - })) - continue; - double x, y; - std::tie(x, y) = pointTransform( - depict.getX(v, !options.collapseHydrogens), - depict.getY(v, !options.collapseHydrogens), - options.rotation, options.mirror); - s << "\\coordinate[overlay] (\\modIdPrefix v-coord-" << vId << ") at (" << std::fixed << x << ", " << y - << ") {};\n"; - } - std::string file = s; - if(!asInline) { - cache[{gId, options.collapseHydrogens, options.rotation, options.mirror} - ] = file; - } - return file; - } -} - -std::pair -tikz(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - const Options &options, - bool asInline, const std::string &idPrefix) { - static std::set > cache; - std::string strOptions = options.getStringEncoding(); - std::string file = getFilePrefix(gId, !asInline) + "_" + strOptions; - if(asInline) file += "i"; - file += ".tex"; - std::string fileCoordsExt = coords(gLabelled, depict, gId, options, asInline); - if(!asInline) { - const auto iter = cache.find(std::make_pair(gId, strOptions)); - if(iter != end(cache)) return std::make_pair(file, fileCoordsExt); - cache.insert(std::make_pair(gId, strOptions)); - } - post::FileHandle s(file); - tikz(s, options, get_graph(gLabelled), depict, fileCoordsExt, asInline, idPrefix); - return std::make_pair(file, fileCoordsExt); -} - -std::string -pdf(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - const Options &options) { - { // user-specified depiction - static std::map userCache; - auto iter = userCache.find(gId); - if(iter != end(userCache)) return iter->second; - auto image = depict.getImage(); - if(image) { - std::string imageNoExt = (*image)(); - if(imageNoExt.empty()) { - std::cout << "User-specified depiction file for graph with id " << gId << " can not be empty." << std::endl; - throw 0; - } - std::string cmd = depict.getImageCommand(); - if(!cmd.empty()) IO::post() << cmd << std::endl; - std::string image = imageNoExt + ".pdf"; - userCache[gId] = image; - return image; - } - } - // auto-generated depiction - static std::set > cache; - std::string strOptions = options.getStringEncoding(); - std::string fileNoExt = getFilePrefix(gId, true) + "_" + strOptions; - std::string file = fileNoExt + ".pdf"; - auto iter = cache.find(std::make_pair(gId, strOptions)); - if(iter != end(cache)) return file; - cache.insert(std::make_pair(gId, strOptions)); - auto tikzFiles = tikz(gLabelled, depict, gId, options, false, ""); - std::string fileCoordsNoExt = tikzFiles.second.substr(0, tikzFiles.second.length() - 4); - IO::post() << "compileTikz \"" << fileNoExt << "\" \"" << fileCoordsNoExt << "\"" << std::endl; - return file; -} - -std::string -svg(const lib::Graph::LabelledGraph &gLabelled, const lib::Graph::DepictionData &depict, const std::size_t gId, - const Options &options) { - static std::set > cache; - std::string strOptions = options.getStringEncoding(); - std::string fileNoExt = getFilePrefix(gId, true) + "_" + strOptions; - std::string file = fileNoExt + ".svg"; - auto iter = cache.find(std::make_pair(gId, strOptions)); - if(iter != end(cache)) return file; - cache.insert(std::make_pair(gId, strOptions)); - std::string pdfFileNoExt = pdf(gLabelled, depict, gId, options); - pdfFileNoExt.erase(end(pdfFileNoExt) - 4, end(pdfFileNoExt)); - IO::post() << "pdfToSvg \"" << pdfFileNoExt << "\" \"" << fileNoExt << "\"" << std::endl; - return file; -} - -std::pair summary(const lib::Graph::Single &g, const Options &first, const Options &second) { - std::string graphLike = pdf(g, first); - std::string molLike = first == second ? "" : pdf(g, second); - IO::post() << "summaryGraph \"" << g.getName() << "\" \"" - << std::string(begin(graphLike), end(graphLike) - 4) << "\" \""; - if(!molLike.empty()) - IO::post() << std::string(begin(molLike), end(molLike) - 4); - IO::post() << "\"" << std::endl; - if(molLike.empty()) - return std::make_pair(graphLike, graphLike); - else - return std::make_pair(graphLike, molLike); -} - -void termState(const lib::Graph::Single &g) { - using Vertex = lib::Graph::Vertex; - using Edge = lib::Graph::Edge; - using namespace lib::Term; - IO::post() << "summarySubsection \"Term State for " << g.getName() << "\"" << std::endl; - post::FileHandle s(getUniqueFilePrefix() + "termState.tex"); - s << "\\begin{verbatim}\n"; - const auto &termState = get_term(g.getLabelledGraph()); - if(isValid(termState)) { - std::unordered_map > addrToVertex; - std::unordered_map > addrToEdge; - for(Vertex v : asRange(vertices(g.getGraph()))) { - Address a{AddressType::Heap, termState[v]}; - addrToVertex[a].insert(v); - } - for(Edge e : asRange(edges(g.getGraph()))) { - Address a{AddressType::Heap, termState[e]}; - addrToEdge[a].insert(e); - } - lib::IO::Term::Write::wam(getMachine(termState), lib::Term::getStrings(), s, [&](Address addr, std::ostream &s) { - s << " "; - bool first = true; - for(auto v : addrToVertex[addr]) { - if(!first) s << ", "; - first = false; - s << "v" << get(boost::vertex_index_t(), g.getGraph(), v); - } - for(auto e : addrToEdge[addr]) { - if(!first) s << ", "; - first = false; - s << "e(" - << get(boost::vertex_index_t(), g.getGraph(), source(e, g.getGraph())) - << ", " - << get(boost::vertex_index_t(), g.getGraph(), target(e, g.getGraph())) - << ")"; - } - }); - } else { - std::string msg = "Parsing failed for graph '" + g.getName() + "'. " + termState.getParsingError(); - throw TermParsingError(std::move(msg)); - } - s << "\\end{verbatim}\n"; - IO::post() << "summaryInput \"" << std::string(s) << "\"" << std::endl; -} - -//------------------------------------------------------------------------------ -// Simplified interface for lib::Graph::Single -//------------------------------------------------------------------------------ - -void gml(const lib::Graph::Single &g, bool withCoords, std::ostream &s) { - gml(g.getLabelledGraph(), g.getDepictionData(), g.getId(), withCoords, s); -} - -std::string tikz(const lib::Graph::Single &g, const Options &options, bool asInline, const std::string &idPrefix) { - auto res = tikz(g.getLabelledGraph(), g.getDepictionData(), g.getId(), options, asInline, idPrefix); - return res.first; -} - -std::string pdf(const lib::Graph::Single &g, const Options &options) { - return pdf(g.getLabelledGraph(), g.getDepictionData(), g.getId(), options); -} - -std::string svg(const lib::Graph::Single &g, const Options &options) { - return svg(g.getLabelledGraph(), g.getDepictionData(), g.getId(), options); + __builtin_unreachable(); } } // namespace mod::lib::IO::Graph::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/GraphWrite.hpp b/libs/libmod/src/mod/lib/IO/GraphWrite.hpp new file mode 100644 index 0000000..0c1a7ff --- /dev/null +++ b/libs/libmod/src/mod/lib/IO/GraphWrite.hpp @@ -0,0 +1,155 @@ +#ifndef MOD_LIB_IO_GRAPHWRITE_HPP +#define MOD_LIB_IO_GRAPHWRITE_HPP + +#include + +namespace mod::lib::IO::Graph::Write { + +struct Options { + Options &Non() { + return EdgesAsBonds(false).CollapseHydrogens(false).RaiseIsotopes(false).RaiseCharges(false) + .SimpleCarbons(false).Thick(false).WithColour(false).WithIndex(false); + } + + Options &All() { + return EdgesAsBonds(true).CollapseHydrogens(true).RaiseIsotopes(true).RaiseCharges(true) + .SimpleCarbons(true).Thick(true).WithColour(true).WithIndex(true); + } + + Options &EdgesAsBonds(bool v) { + edgesAsBonds = v; + return *this; + } + + Options &CollapseHydrogens(bool v) { + collapseHydrogens = v; + return *this; + } + + Options &RaiseIsotopes(bool v) { + raiseIsotopes = v; + return *this; + } + + Options &RaiseCharges(bool v) { + raiseCharges = v; + return *this; + } + + Options &SimpleCarbons(bool v) { + simpleCarbons = v; + return *this; + } + + Options &Thick(bool v) { + thick = v; + return *this; + } + + Options &WithColour(bool v) { + withColour = v; + return *this; + } + + Options &WithIndex(bool v) { + withIndex = v; + return *this; + } + + Options &WithTexttt(bool v) { + withTexttt = v; + return *this; + } + + Options &WithRawStereo(bool v) { + withRawStereo = v; + return *this; + } + + Options &WithPrettyStereo(bool v) { + withPrettyStereo = v; + return *this; + } + + Options &Rotation(int degrees) { + rotation = degrees; + return *this; + } + + Options &Mirror(bool v) { + mirror = v; + return *this; + } + + Options &WithGraphvizCoords(bool v) { + withGraphvizCoords = v; + return *this; + } + + std::string getStringEncoding() const { + auto toChar = [](bool b) { + return b ? '1' : '0'; + }; + std::string res; + res += toChar(edgesAsBonds); + res += toChar(collapseHydrogens); + char raise = 0; + if(raiseCharges) raise += 1; + if(raiseIsotopes) raise += 2; + res += '0' + raise; + res += toChar(simpleCarbons); + res += toChar(thick); + res += toChar(withColour); + res += toChar(withIndex); + res += toChar(withTexttt); + char stereo = 0; + if(withRawStereo) stereo += 1; + if(withPrettyStereo) stereo += 2; + if(stereo != 0) res += '0' + stereo; + if(rotation != 0 || mirror) { + res += "_"; + res += std::to_string(rotation); + if(mirror) { + res += "_"; + res += toChar(mirror); + } + } + return res; + } + + friend bool operator==(const Options &a, const Options &b) { + return a.getStringEncoding() == b.getStringEncoding() + && a.graphvizPrefix == b.graphvizPrefix; + } + + friend bool operator!=(const Options &a, const Options &b) { + return !(a == b); + } +public: + bool edgesAsBonds = false; + bool collapseHydrogens = false; + bool raiseCharges = false; + bool raiseIsotopes = false; + bool simpleCarbons = false; + bool thick = false; + bool withColour = false; + bool withIndex = false; + bool withTexttt = false; + bool withRawStereo = false; + bool withPrettyStereo = false; + int rotation = 0; + bool mirror = false; + bool withGraphvizCoords = false; +public: // not participating in string encoding + std::string graphvizPrefix; +}; + +enum class EdgeFake3DType { + None, WedgeSL, WedgeLS, HashSL, HashLS +}; + +EdgeFake3DType invertEdgeFake3DType(EdgeFake3DType t); + +} // namespace mod::lib::IO::Graph::Write + +#endif // MOD_LIB_IO_GRAPHWRITE_HPP diff --git a/libs/libmod/src/mod/lib/IO/GraphWriteDetail.hpp b/libs/libmod/src/mod/lib/IO/GraphWriteGeneric.hpp similarity index 80% rename from libs/libmod/src/mod/lib/IO/GraphWriteDetail.hpp rename to libs/libmod/src/mod/lib/IO/GraphWriteGeneric.hpp index cf1677c..d81cb8d 100644 --- a/libs/libmod/src/mod/lib/IO/GraphWriteDetail.hpp +++ b/libs/libmod/src/mod/lib/IO/GraphWriteGeneric.hpp @@ -1,9 +1,9 @@ -#ifndef MOD_LIB_IO_GRAPHWRITEDETAIL_HPP -#define MOD_LIB_IO_GRAPHWRITEDETAIL_HPP +#ifndef MOD_LIB_IO_GRAPHWRITEGENERIC_HPP +#define MOD_LIB_IO_GRAPHWRITEGENERIC_HPP #include +#include #include -#include #include #include @@ -46,36 +46,14 @@ static constexpr unsigned int } // namespace Loc } // namespace -namespace mod::lib::IO { - -constexpr double pi = 3.14159265358979323846; - -inline std::pair pointRotation(double xRaw, double yRaw, int rotation) { - double angle = rotation * pi / 180; - auto s = std::sin(angle); - auto c = std::cos(angle); - double x = xRaw * c - yRaw * s; - double y = xRaw * s + yRaw * c; - return std::make_pair(x, y); -} - -inline std::pair pointTransform(double xRaw, double yRaw, int rotation, bool mirror) { - if(mirror) xRaw *= -1; - return pointRotation(xRaw, yRaw, rotation); -} - -} // mod::lib::IO namespace mod::lib::IO::Graph::Write { template void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict &depict, const std::string &fileCoords, const AdvOptions &advOptions, BonusWriter bonusWriter, const std::string &idPrefix) { - typedef typename boost::graph_traits::vertex_descriptor Vertex; - typedef typename boost::graph_traits::edge_descriptor Edge; - [[maybe_unused]] const auto printBlocked = [&s](auto b) { - auto at = [&b](int i) { + const auto at = [&b](int i) { return bool((b & (1 << i)) != 0); }; s << "% " << at(6) << " " << at(5) << at(4) << at(3) << " " << at(2) << "\n"; @@ -91,26 +69,27 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict std::vector implicitHydrogenCount(num_vertices(g), 0); std::vector hasBondBlockingAtChargeRight(num_vertices(g), false); std::vector hasBondBlockingAtChargeLeft(num_vertices(g), false); + + const auto localIdx = get(boost::vertex_index_t(), g); + { // visibility from adv - for(Vertex v : asRange(vertices(g))) { - unsigned int vId = get(boost::vertex_index_t(), g, v); - isVisible[vId] = advOptions.isVisible(v); - } + for(const auto v: asRange(vertices(g))) + isVisible[get(localIdx, v)] = advOptions.isVisible(v); } { // stuff not depending on coordinates being available now if(options.simpleCarbons) { - for(Vertex v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { if(depict.getAtomId(v) != AtomIds::Carbon) continue; - unsigned int adjCount = 0; - for(Vertex vAdj : asRange(adjacent_vertices(v, g))) { - // for now we allow it even though the edges are not bonds - auto adjAtomId = depict.getAtomId(vAdj); + int adjCount = 0; + for(const auto vAdj: asRange(adjacent_vertices(v, g))) { + // for now, we allow it even though the edges are not bonds + const auto adjAtomId = depict.getAtomId(vAdj); switch(adjAtomId) { case AtomIds::Carbon: case AtomIds::Oxygen: case AtomIds::Nitrogen: case AtomIds::Sulfur: - adjCount++; + ++adjCount; } } if(adjCount < 2) continue; @@ -121,23 +100,22 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict } } - if(options.collapseHydrogens) { // collapse most hydrogens to it's neighbour - for(Vertex v : asRange(vertices(g))) { - unsigned int vId = get(boost::vertex_index_t(), g, v); + if(options.collapseHydrogens) { // collapse most hydrogen atoms to it's neighbour + for(const auto v: asRange(vertices(g))) { + const auto vId = get(localIdx, v); if(!isVisible[vId]) continue; const auto hasImportantStereo = [&depict](const auto v) { return depict.hasImportantStereo(v); }; - if(Chem::isCollapsible(v, g, depict, depict, hasImportantStereo)) { - assert(out_degree(v, g) == 1); - Edge e = *out_edges(v, g).first; - Vertex vAdj = target(e, g); // TODO: just use adjacent_vertices, we don't need the edge - unsigned int vAdjId = get(boost::vertex_index_t(), g, vAdj); - if(!isVisible[vAdjId]) continue; - if(advOptions.disallowCollapse(v)) continue; - implicitHydrogenCount[vAdjId]++; - isVisible[vId] = false; - } + if(!Chem::isCollapsibleHydrogen(v, g, depict, depict, hasImportantStereo)) continue; + assert(out_degree(v, g) == 1); + const auto e = *out_edges(v, g).first; + const auto vAdj = target(e, g); // TODO: just use adjacent_vertices, we don't need the edge + const auto vAdjId = get(boost::vertex_index_t(), g, vAdj); + if(!isVisible[vAdjId]) continue; + if(advOptions.disallowHydrogenCollapse(v)) continue; + ++implicitHydrogenCount[vAdjId]; + isVisible[vId] = false; } } } @@ -146,22 +124,21 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict return (b & (Loc::R_down * 2 - 1)) == (Loc::R_down * 2 - 1); }; std::vector auxLabelBlocked(num_vertices(g), 0); - if(depict.getHasCoordinates()) { // stuff where we absolutely need coordinates right now + // stuff where we absolutely need coordinates right now + if(!options.withGraphvizCoords && depict.getHasCoordinates()) { // see where we can have aux data - for(Vertex v : asRange(vertices(g))) { - unsigned int vId = get(boost::vertex_index_t(), g, v); + for(const auto v: asRange(vertices(g))) { + const auto vId = get(localIdx, v); if(!isVisible[vId]) continue; - double x, y; - std::tie(x, y) = pointTransform( + const auto[x, y] = pointTransform( depict.getX(v, !options.collapseHydrogens), depict.getY(v, !options.collapseHydrogens), options.rotation, options.mirror); - for(const Edge eOut : asRange(out_edges(v, g))) { - const Vertex vAdj = target(eOut, g); - const unsigned int vAdjId = get(boost::vertex_index_t(), g, vAdj); + for(const auto eOut: asRange(out_edges(v, g))) { + const auto vAdj = target(eOut, g); + const auto vAdjId = get(localIdx, vAdj); if(!isVisible[vAdjId]) continue; - double xAdj, yAdj; - std::tie(xAdj, yAdj) = pointTransform( + const auto[xAdj, yAdj] = pointTransform( depict.getX(vAdj, !options.collapseHydrogens), depict.getY(vAdj, !options.collapseHydrogens), options.rotation, options.mirror); @@ -172,7 +149,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict double angle = std::atan2(yDiff, xDiff); // [-pi; pi], later shifted to [0; 2*pi] const BondType bType = depict.getBondData(eOut); - // a single bond blocks less than a double/aromatic and they less than a triple + // a single bond blocks less than a double/aromatic, and they block less than a triple const double f = [&]() { switch(bType) { case BondType::Single: @@ -184,7 +161,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict case BondType::Triple: return 3; } - std::abort(); + __builtin_unreachable(); }(); // check if a bond blocks a charge, using angle in [-pi/2; 3/2*pi] { // right @@ -234,7 +211,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict } } } else { // we don't have coordinates, block all - for(auto &c : auxLabelBlocked) c = ~0L; + for(auto &c: auxLabelBlocked) c = ~0L; } // Tikz code @@ -251,22 +228,22 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict s << "\\begin{tikzpicture}[remember picture, scale=\\modGraphScale"; s << R"XXX(, baseline={([yshift={-0.5ex}]current bounding box)})XXX"; if(options.thick) s << ", thick"; - s - << ", solid"; // circumvent the strange inherited 'dashed' http://tex.stackexchange.com/questions/115887/pattern-in-addplot-inherits-dashed-option-from-previous-draw + // circumvent the strange inherited 'dashed' http://tex.stackexchange.com/questions/115887/pattern-in-addplot-inherits-dashed-option-from-previous-draw + s << ", solid"; s << "]\n"; if(!idPrefix.empty()) s << "\\renewcommand\\modIdPrefix{" << idPrefix << "}\n"; s << "\\input{\\modInputPrefix/" << fileCoords << "}\n"; - for(Vertex v : asRange(vertices(g))) { - const auto vId = get(boost::vertex_index_t(), g, v); + for(const auto v: asRange(vertices(g))) { + const auto vId = get(localIdx, v); if(!isVisible[vId]) continue; const auto atomId = depict.getAtomId(v); - const auto createDummy = [&s, vId, &advOptions, &textModifiersBegin, &textModifiersEnd]( + const auto createDummy = [&s, v, &advOptions, &textModifiersBegin, &textModifiersEnd]( std::string suffix, bool subscript, bool superscript) { // create dummy vertex to make sure the bounding box is large enough - s << "\\node[modStyleGraphVertex, at=(\\modIdPrefix v-" << (vId + advOptions.idOffset) << suffix << ")"; + s << "\\node[modStyleGraphVertex, at=(\\modIdPrefix v-" << advOptions.getOutputId(v) << suffix << ")"; if(subscript) s << ", text depth=.25ex"; if(superscript) s << ", text height=2.25ex"; s << "] {\\phantom{" << textModifiersBegin << "H"; @@ -308,25 +285,25 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict if(indexString.empty()) s << ", modStyleCarbon"; else s << ", modStyleCarbonIndex"; } - s << ", at=(\\modIdPrefix v-coord-" << (vId + advOptions.idOffset) << ")"; + s << ", at=(\\modIdPrefix v-coord-" << advOptions.getOutputId(v) << ")"; std::string userOpts = advOptions.getOpts(v); if(!userOpts.empty()) s << ", " << userOpts; - s << "] (\\modIdPrefix v-" << (vId + advOptions.idOffset) << ") "; + s << "] (\\modIdPrefix v-" << advOptions.getOutputId(v) << ") "; auto auxBlocked = auxLabelBlocked[vId]; if(isSimpleCarbon[vId]) { // Simple Cs s << "{" << textModifiersBegin << indexString << textModifiersEnd << "};\n"; if(!indexString.empty()) createDummy("", false, false); } else { // not simple carbon - const Isotope isotope = depict.getIsotope(v); - const char charge = depict.getCharge(v); - const unsigned int hCount = implicitHydrogenCount[vId]; + const auto isotope = depict.getIsotope(v); + const auto charge = depict.getCharge(v); + const auto hCount = implicitHydrogenCount[vId]; const bool allAuxBlocked = areAllBlocked(auxBlocked); const bool hInLabel = !options.collapseHydrogens || allAuxBlocked; const bool chargeOnLeft = hasBondBlockingAtChargeRight[vId] && !hasBondBlockingAtChargeLeft[vId]; // && isotope == Isotope(); - std::string labelNoAux = escapeForLatex(depict.getVertexLabelNoIsotopeChargeRadical(v)); + std::string labelNoAux = lib::IO::escapeForLatex(depict.getVertexLabelNoIsotopeChargeRadical(v)); std::string isotopeString; if(isotope != Isotope()) { if(options.raiseIsotopes) isotopeString += "$^{"; @@ -440,7 +417,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict if(!chargeInAux && charge != 0) { s << "\\node[modStyleGraphVertex" << colourString << ", at=(\\modIdPrefix v-" - << (vId + advOptions.idOffset); + << advOptions.getOutputId(v); if(chargeOnLeft) s << ".west), anchor=east"; else s << ".east), anchor=west"; s << "] {"; @@ -452,7 +429,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict } if(!isotopeInAux && isotope != Isotope()) { s << "\\node[modStyleGraphVertex" << colourString << ", at=(\\modIdPrefix v-" - << (vId + advOptions.idOffset); + << advOptions.getOutputId(v); s << ".west), anchor=east"; s << "] {"; s << textModifiersBegin; @@ -463,7 +440,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict } if(auxHPosition != -1) { - s << "\\node[modStyleGraphVertex" << colourString << ", at=(\\modIdPrefix v-" << (vId + advOptions.idOffset) + s << "\\node[modStyleGraphVertex" << colourString << ", at=(\\modIdPrefix v-" << advOptions.getOutputId(v) << "."; /**/ if(auxHPosition == Loc::R_narrow) s << "east), anchor=west"; else if(auxHPosition == Loc::L_narrow) s << "west), anchor=east"; @@ -471,7 +448,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict else if(auxHPosition == Loc::B_narrow) s << "south), anchor=north, yshift=-1pt"; else MOD_ABORT; - s << "] (\\modIdPrefix v-" << (vId + advOptions.idOffset) << "-aux) {"; + s << "] (\\modIdPrefix v-" << advOptions.getOutputId(v) << "-aux) {"; s << textModifiersBegin; if(chargeInAux && chargeOnLeft) s << chargeString; std::string output; @@ -493,7 +470,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict createDummy("-aux", hCount > 1, isotopeInAux && isotope != Isotope()); if(isAuxVertical && hCount > 1) { s << "\\node[modStyleGraphVertex" << colourString << ", at=(\\modIdPrefix v-" - << (vId + advOptions.idOffset) << "-aux.east), anchor=west] {"; + << advOptions.getOutputId(v) << "-aux.east), anchor=west] {"; s << textModifiersBegin; s << "$_{" << hCount << "}$"; s << textModifiersEnd << "};\n"; @@ -517,7 +494,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict auxBlocked |= (1 << auxRadicalPosition); const double atAngle = auxRadicalPosition * 22.5; s << "\\node[outer sep=0, inner sep=1, minimum size=0, fill=black, circle, " - << "at=(\\modIdPrefix v-" << (vId + advOptions.idOffset) << "." << atAngle << "), " + << "at=(\\modIdPrefix v-" << advOptions.getOutputId(v) << "." << atAngle << "), " << "shift=(" << atAngle << ":3pt)] {};\n"; } // end if has radical } // end if simpleCarbon @@ -527,7 +504,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict + (options.withPrettyStereo ? advOptions.getPrettyStereoString(v) : std::string()); if(!strStereo.empty()) { const auto stereoPosition = [&]() { - for(const unsigned int loc :{0, 8, 4, 12, 1, 15, 7, 9}) { + for(const unsigned int loc: {0, 8, 4, 12, 1, 15, 7, 9}) { if((auxBlocked & (1 << loc)) == 0) return loc; } for(unsigned int loc = 16; loc > 0; --loc) { @@ -537,7 +514,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict }(); auxBlocked |= (1u << stereoPosition); double atAngle = stereoPosition * 22.5; - s << "\\node[outer sep=0, inner sep=1, at=(\\modIdPrefix v-" << (vId + advOptions.idOffset) + s << "\\node[outer sep=0, inner sep=1, at=(\\modIdPrefix v-" << advOptions.getOutputId(v) << "." << atAngle << "), anchor="; [&s, stereoPosition]() -> std::ostream & { switch(stereoPosition) { @@ -556,7 +533,7 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict if(stereoPosition > 4 && stereoPosition < 12) return s << " east"; else return s << " west"; }(); - s << "] (\\modIdPrefix v-" << (vId + advOptions.idOffset) << "-stereoId) "; + s << "] (\\modIdPrefix v-" << advOptions.getOutputId(v) << "-stereoId) "; s << "{\\tiny " << textModifiersBegin << strStereo << textModifiersEnd << "};\n"; } } // end if with any kind of stereo @@ -584,9 +561,9 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict auxBlocked |= (1 << auxIndexPosition); const double atAngle = auxIndexPosition * 22.5; s << "\\node[outer sep=0, inner sep=1" << colourString - << ", at=(\\modIdPrefix v-" << (vId + advOptions.idOffset) + << ", at=(\\modIdPrefix v-" << advOptions.getOutputId(v) << "." << atAngle << "), anchor=" << (atAngle + 180) - << "] (\\modIdPrefix v-" << (vId + advOptions.idOffset) << "-auxId) {"; + << "] (\\modIdPrefix v-" << advOptions.getOutputId(v) << "-auxId) {"; s << textModifiersBegin; s << indexString; s << textModifiersEnd << "};\n"; @@ -594,11 +571,13 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict } // end if withIndex } // foreach vertex - for(Edge e : asRange(edges(g))) { - unsigned int fromId = get(boost::vertex_index_t(), g, source(e, g)); - unsigned int toId = get(boost::vertex_index_t(), g, target(e, g)); - if(!isVisible[fromId]) continue; - if(!isVisible[toId]) continue; + for(const auto e: asRange(edges(g))) { + const auto vSrc = source(e, g); + const auto vTar = target(e, g); + const auto vSrcId = get(localIdx, vSrc); + const auto vTarId = get(localIdx, vTar); + if(!isVisible[vSrcId]) continue; + if(!isVisible[vTarId]) continue; std::string colourString = advOptions.getColour(e); if(!colourString.empty()) colourString += ", text=" + colourString; BondType bType = depict.getBondData(e); @@ -641,17 +620,17 @@ void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict drawCommand += "HashLS"; break; } - unsigned int fromOffset = 1, toOffset = 1; + int fromOffset = 1, toOffset = 1; if(!options.withIndex) { - if(isSimpleCarbon[fromId]) fromOffset = 0; - if(isSimpleCarbon[toId]) toOffset = 0; + if(isSimpleCarbon[vSrcId]) fromOffset = 0; + if(isSimpleCarbon[vTarId]) toOffset = 0; } - s << drawCommand << "{" << (fromId + advOptions.idOffset) << "}{" << (toId + advOptions.idOffset) << "}{" + s << drawCommand << "{" << advOptions.getOutputId(vSrc) << "}{" << advOptions.getOutputId(vTar) << "}{" << fromOffset << "}{" << toOffset << "}"; s << "{" << colourString << "}"; // find the label text const std::string label = (!options.edgesAsBonds || bType == BondType::Invalid) - ? escapeForLatex(depict.getEdgeLabel(e)) : std::string(); + ? lib::IO::escapeForLatex(depict.getEdgeLabel(e)) : std::string(); const std::string rawStereo = options.withRawStereo ? "{\\tiny " + advOptions.getStereoString(e) + "}" : std::string(); const std::string extraAnnotation = advOptions.getEdgeAnnotation(e); @@ -676,16 +655,15 @@ template struct DefaultAdvancedOptions { using Vertex = typename boost::graph_traits::vertex_descriptor; using Edge = typename boost::graph_traits::edge_descriptor; - +public: DefaultAdvancedOptions(const Graph &g, const Depict &depict) : g(g), depict(depict) {} - template - std::string getColour(T) const { + template + std::string getColour(VE) const { return ""; } - template - bool isVisible(T) const { + bool isVisible(Vertex) const { return true; } @@ -705,33 +683,29 @@ struct DefaultAdvancedOptions { return ""; } - template - std::string getRawStereoString(VE ve) const { - return depict.getRawStereoString(ve); + std::string getRawStereoString(Vertex v) const { + return depict.getRawStereoString(v); } - template - std::string getPrettyStereoString(VE ve) const { - return depict.getPrettyStereoString(ve); + std::string getPrettyStereoString(Vertex v) const { + return depict.getPrettyStereoString(v); } - template - std::string getStereoString(VE ve) const { - return depict.getStereoString(ve); + std::string getStereoString(Edge e) const { + return depict.getStereoString(e); } std::string getOpts(Vertex v) const { return std::string(); } -public: - - template - bool disallowCollapse(T) const { + bool disallowHydrogenCollapse(Vertex) const { return false; } - unsigned int idOffset = 0; + auto getOutputId(Vertex v) const { + return get(boost::vertex_index_t(), g, v); + } private: const Graph &g; const Depict &depict; @@ -741,10 +715,9 @@ template void tikz(std::ostream &s, const Options &options, const Graph &g, const Depict &depict, const std::string &fileCoords, bool asInline, const std::string &idPrefix) { DefaultAdvancedOptions adv(g, depict); - tikz(s, options, g, depict, fileCoords, adv, [](std::ostream &s) { - }, idPrefix); + tikz(s, options, g, depict, fileCoords, adv, [](std::ostream &s) {}, idPrefix); } } // namespace mod::lib::IO::Graph::Write -#endif // MOD_LIB_IO_GRAPHWRITEDETAIL_HPP \ No newline at end of file +#endif // MOD_LIB_IO_GRAPHWRITEGENERIC_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/IO.cpp b/libs/libmod/src/mod/lib/IO/IO.cpp index d232106..04b2f6b 100644 --- a/libs/libmod/src/mod/lib/IO/IO.cpp +++ b/libs/libmod/src/mod/lib/IO/IO.cpp @@ -33,6 +33,7 @@ struct PostStream { std::cerr << "Does '" << prefix << "' exist?" << std::endl; std::exit(1); } + private: bool enabled; std::ofstream s; @@ -44,7 +45,7 @@ PostStream postStream; } // namespace -std::string getUniqueFilePrefix() { +std::string makeUniqueFilePrefix() { static int count = 0; std::string strCount; @@ -61,7 +62,7 @@ std::string getUniqueFilePrefix() { std::string escapeForLatex(const std::string &str) { std::string res; res.reserve(str.size()); - for(char c : str) { + for(char c: str) { switch(c) { case '#': case '_': @@ -82,9 +83,10 @@ std::string escapeForLatex(const std::string &str) { std::string asLatexMath(const std::string &str) { std::string res = "$\\mathrm{"; - for(char c : str) { + for(char c: str) { switch(c) { case ' ': + case '#': res += "\\"; res += c; break; @@ -103,6 +105,7 @@ std::ostream &nullStream() { }; struct NullStream : public std::ostream { NullStream() : std::ostream(&buffer) {} + private: NullBuffer buffer; }; @@ -115,14 +118,18 @@ std::ostream &post() { return postStream.getStream(); } -void postReset() { - postStream.resetStream(); -} - void postDisable() { postStream.dynamicallyEnabled = false; } +void postEnable() { + postStream.dynamicallyEnabled = true; +} + +void postReopenCommandFile() { + postStream.resetStream(); +} + std::ostream &Logger::indent() const { assert(indentLevel >= 0); return s << std::string(indentLevel * 2, ' '); diff --git a/libs/libmod/src/mod/lib/IO/IO.hpp b/libs/libmod/src/mod/lib/IO/IO.hpp index 94db330..7fe7ab4 100644 --- a/libs/libmod/src/mod/lib/IO/IO.hpp +++ b/libs/libmod/src/mod/lib/IO/IO.hpp @@ -6,15 +6,16 @@ namespace mod::lib::IO { -std::string getUniqueFilePrefix(); +std::string makeUniqueFilePrefix(); std::string escapeForLatex(const std::string &str); std::string asLatexMath(const std::string &str); std::ostream &nullStream(); std::ostream &post(); -void postReset(); void postDisable(); +void postEnable(); +void postReopenCommandFile(); struct Logger { explicit Logger(std::ostream &s) : s(s) {} diff --git a/libs/libmod/src/mod/lib/IO/JsonUtils.cpp b/libs/libmod/src/mod/lib/IO/Json.cpp similarity index 98% rename from libs/libmod/src/mod/lib/IO/JsonUtils.cpp rename to libs/libmod/src/mod/lib/IO/Json.cpp index fab65f8..114cc34 100644 --- a/libs/libmod/src/mod/lib/IO/JsonUtils.cpp +++ b/libs/libmod/src/mod/lib/IO/Json.cpp @@ -1,4 +1,4 @@ -#include "JsonUtils.hpp" +#include "Json.hpp" #include #include diff --git a/libs/libmod/src/mod/lib/IO/JsonUtils.hpp b/libs/libmod/src/mod/lib/IO/Json.hpp similarity index 88% rename from libs/libmod/src/mod/lib/IO/JsonUtils.hpp rename to libs/libmod/src/mod/lib/IO/Json.hpp index 29a5ea4..d4d3c6f 100644 --- a/libs/libmod/src/mod/lib/IO/JsonUtils.hpp +++ b/libs/libmod/src/mod/lib/IO/Json.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_LIB_IO_JSONUTILS_HPP -#define MOD_LIB_IO_JSONUTILS_HPP +#ifndef MOD_LIB_IO_JSON_HPP +#define MOD_LIB_IO_JSON_HPP // We don't control the symbol visibility of the json library and json-schema library, // so we need to restore the default when using their header files. @@ -24,4 +24,4 @@ bool validateJson(const nlohmann::json &j, } // namespace mod::lib::IO -#endif // MOD_LIB_IO_JSONUTILS_HPP +#endif // MOD_LIB_IO_JSON_HPP diff --git a/libs/libmod/src/mod/lib/IO/MorphismConstraints.hpp b/libs/libmod/src/mod/lib/IO/MorphismConstraints.hpp deleted file mode 100644 index 8612883..0000000 --- a/libs/libmod/src/mod/lib/IO/MorphismConstraints.hpp +++ /dev/null @@ -1,144 +0,0 @@ -#ifndef MOD_LIB_IO_MORPHISMCONSTRAINTS_H -#define MOD_LIB_IO_MORPHISMCONSTRAINTS_H - -#include -#include -#include - -namespace mod { -namespace lib { -namespace IO { -namespace MatchConstraint { -namespace Write { - -template -struct TexPrintVisitor : lib::GraphMorphism::Constraints::AllVisitor { - - TexPrintVisitor(std::ostream &s, const Graph &g) : s(s), g(g) { } - - void printOp(lib::GraphMorphism::Constraints::Operator op) { - using lib::GraphMorphism::Constraints::Operator; - switch(op) { - case Operator::EQ: s << "="; - break; - case Operator::LT: s << "<"; - break; - case Operator::GT: s << ">"; - break; - case Operator::LEQ: s << "\\leq"; - break; - case Operator::GEQ: s << "\\geq"; - break; - } - } - - virtual void operator()(const lib::GraphMorphism::Constraints::VertexAdjacency &c) override { - s << "\\begin{align*}\n"; - s << "&|\\{e \\in \\mathrm{outEdges}(" << get(boost::vertex_index_t(), g, c.vConstrained) << ") "; - if(!c.vertexLabels.empty() || !c.edgeLabels.empty()) s << " \\mid \\\\\n"; - if(!c.vertexLabels.empty()) { - s << "&\\quad \\mathrm{label}(\\mathrm{target}(e)) \\in \\{"; - bool first = true; - for(const std::string &str : c.vertexLabels) { - if(!first) s << ", "; - first = false; - s << " \\text{`\\texttt{" << escapeForLatex(str) << "}'}"; - } - s << " \\}\\\\\n"; - } - if(!c.vertexLabels.empty() && !c.edgeLabels.empty()) s << "&\\quad \\wedge\\\\\n"; - if(!c.edgeLabels.empty()) { - s << "&\\quad \\mathrm{label}(e) \\in \\{"; - bool first = true; - for(const std::string &str : c.edgeLabels) { - if(!first) s << ", "; - first = false; - s << " \\text{`\\texttt{" << escapeForLatex(str) << "}'}"; - } - s << " \\}\\\\\n"; - } - if(!c.vertexLabels.empty() || !c.edgeLabels.empty()) s << "&"; - s << "\\}| "; - printOp(c.op); - s << " " << c.count << "\n\\end{align*}\n"; - } - - virtual void operator()(const lib::GraphMorphism::Constraints::ShortestPath &c) override { - s << "$\\mathrm{shortestPath}(" << get(boost::vertex_index_t(), g, c.vSrc) << ", " << get(boost::vertex_index_t(), g, c.vTar) << ") "; - printOp(c.op); - s << " " << c.length << "$\n"; - } -private: - std::ostream &s; - const Graph &g; -}; - -template -auto makeTexPrintVisitor(std::ostream &s, const Graph &g) { - return TexPrintVisitor(s, g); -} - -template -struct GMLPrintVisitor : lib::GraphMorphism::Constraints::AllVisitor { - - GMLPrintVisitor(std::ostream &s, const Graph &g, std::string prefix) : s(s), g(g), prefix(std::move(prefix)) { } - - void printOp(lib::GraphMorphism::Constraints::Operator op) { - using lib::GraphMorphism::Constraints::Operator; - switch(op) { - case Operator::EQ: s << "="; - break; - case Operator::LT: s << "<"; - break; - case Operator::GT: s << ">"; - break; - case Operator::LEQ: s << "<="; - break; - case Operator::GEQ: s << ">="; - break; - } - } - - virtual void operator()(const lib::GraphMorphism::Constraints::VertexAdjacency &c) { - s << prefix << "constrainAdj [\n"; - s << prefix << " id " << get(boost::vertex_index_t(), g, c.vConstrained) << "\n"; - s << prefix << " op \""; - printOp(c.op); - s << "\"\n"; - s << prefix << " count " << c.count << "\n"; - s << prefix << " nodeLabels ["; - for(const auto &str : c.vertexLabels) s << " label \"" << str << "\""; - s << " ]\n"; - s << prefix << " edgeLabels ["; - for(const auto &str : c.edgeLabels) s << " label \"" << str << "\""; - s << " ]\n"; - s << prefix << "]\n"; - } - - virtual void operator()(const lib::GraphMorphism::Constraints::ShortestPath &c) { - s << prefix << "constrainShortestPath [\n"; - s << prefix << " source " << get(boost::vertex_index_t(), g, c.vSrc) - << " target " << get(boost::vertex_index_t(), g, c.vTar) << "\n"; - s << prefix << " op \""; - printOp(c.op); - s << "\" length " << c.length << "\n"; - s << prefix << "]\n"; - } -private: - std::ostream &s; - const Graph &g; - std::string prefix; -}; - -template -auto makeGMLPrintVisitor(std::ostream &s, const Graph &g, std::string prefix) { - return GMLPrintVisitor(s, g, prefix); -} - -} // namespace Write -} // namespace MatchConstraint -} // namespace IO -} // namespace lib -} // namespace mod - -#endif /* MOD_LIB_IO_MORPHISMCONSTRAINTS_H */ \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/ParsingUtil.hpp b/libs/libmod/src/mod/lib/IO/Parsing.hpp similarity index 72% rename from libs/libmod/src/mod/lib/IO/ParsingUtil.hpp rename to libs/libmod/src/mod/lib/IO/Parsing.hpp index 15fc893..3672c45 100644 --- a/libs/libmod/src/mod/lib/IO/ParsingUtil.hpp +++ b/libs/libmod/src/mod/lib/IO/Parsing.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_LIB_IO_PARSINGUTIL_HPP -#define MOD_LIB_IO_PARSINGUTIL_HPP +#ifndef MOD_LIB_IO_PARSING_HPP +#define MOD_LIB_IO_PARSING_HPP #include @@ -36,19 +36,22 @@ struct ParseDispatch { }; template -std::string makeParserError(const TextIter &textIter, const PosIter &iter, const PosIter &iterEnd) { +std::string +makeParserError(const TextIter &textIter, const PosIter &iter, const PosIter &iterEnd, const bool withPosition) { const auto lineNumber = iter.position(); const auto lineRange = get_current_line(PosIter(textIter), iter, iterEnd); const auto column = get_column(lineRange.begin(), iter); - return "Parsing failed at " + std::to_string(lineNumber) - + ":" + std::to_string(column) + ":\n" - + boost::lexical_cast(lineRange) + "\n" + std::string(column - 1, '-') + "^"; + std::string msg = "Parsing failed"; + if(withPosition) + msg += " at " + std::to_string(lineNumber) + ":" + std::to_string(column); + msg += ":\n" + boost::lexical_cast(lineRange) + "\n" + std::string(column - 1, '-') + "^"; + return msg; } template std::string makeParserExpectationError(const x3::expectation_failure &e, const TextIter &textIter, - const PosIter &iterEnd) { - return detail::makeParserError(textIter, e.where(), iterEnd) + const PosIter &iterEnd, const bool withPosition) { + return detail::makeParserError(textIter, e.where(), iterEnd, withPosition) + "\nExpected " + e.which() + "."; } @@ -59,24 +62,24 @@ using PositionIter = spirit::line_pos_iterator; template void parse(const TextIter &textFirst, PositionIter &first, const PositionIter &last, - const Parser &p, Attr &attr, const Skipper &...skipper) { + const Parser &p, Attr &attr, const bool withPosition, const Skipper &...skipper) { bool res; try { res = detail::ParseDispatch::parse(first, last, p, attr, skipper...); } catch(const x3::expectation_failure > &e) { - throw ParsingError{detail::makeParserExpectationError(e, textFirst, last)}; + throw ParsingError{detail::makeParserExpectationError(e, textFirst, last, withPosition)}; } if(!res || first != last) - throw ParsingError{detail::makeParserError(textFirst, first, last)}; + throw ParsingError{detail::makeParserError(textFirst, first, last, withPosition)}; } template void parse(const TextIter &textFirst, const TextIter &textLast, - const Parser &p, Attr &attr, const Skipper &...skipper) { + const Parser &p, Attr &attr, const bool withPosition, const Skipper &...skipper) { PositionIter first(textFirst); - return parse(textFirst, first, PositionIter(textLast), p, attr, skipper...); + return parse(textFirst, first, PositionIter(textLast), p, attr, withPosition, skipper...); } } // namespace mod::lib::IO -#endif // MOD_LIB_IO_PARSINGUTIL_HPP \ No newline at end of file +#endif // MOD_LIB_IO_PARSING_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/Result.hpp b/libs/libmod/src/mod/lib/IO/Result.hpp index 20beb7a..19962a6 100644 --- a/libs/libmod/src/mod/lib/IO/Result.hpp +++ b/libs/libmod/src/mod/lib/IO/Result.hpp @@ -9,7 +9,7 @@ namespace mod::lib::IO { struct Warnings { Warnings() = default; - Warnings(const Warnings &) = delete; + explicit Warnings(const Warnings &warnings) = default; Warnings &operator=(const Warnings &) = delete; Warnings(Warnings &&) = default; Warnings &operator=(Warnings &&) = default; @@ -23,6 +23,7 @@ struct Warnings { warnings.emplace_back(std::move(msg), print); } public: + std::size_t empty() const { return warnings.empty(); } friend std::ostream &operator<<(std::ostream &s, const Warnings &ws); public: std::vector> extractWarnings() { return std::move(warnings); } @@ -66,6 +67,7 @@ struct [[nodiscard]] Result : Result { // TODO: change to by-value when C++20/P1825R0 is available Result(Result &&other) : Result(std::move(other)) {} T &operator*() { return *value; } + T *operator->() { return &*value; } private: Result() = default; private: diff --git a/libs/libmod/src/mod/lib/IO/Rule.hpp b/libs/libmod/src/mod/lib/IO/Rule.hpp deleted file mode 100644 index 477fa72..0000000 --- a/libs/libmod/src/mod/lib/IO/Rule.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef MOD_LIB_IO_RULE_HPP -#define MOD_LIB_IO_RULE_HPP - -#include -#include // to make sure the write options are defined -#include -#include - -#include -#include -#include -#include - -namespace mod::lib::Rules { -struct Real; -} // namespace mod::lib::Rules -namespace mod::lib::IO::Rules { -namespace Read { - -struct Data { - std::optional rule; - std::optional name; - std::optional labelType; - std::map externalToInternalIds; -}; - -Result gml(lib::IO::Warnings &warnings, std::string_view input); - -} // namespace Read -namespace Write { - -using Options = IO::Graph::Write::Options; -using CoreVertex = lib::Rules::Vertex; -using CoreEdge = lib::Rules::Edge; - -struct BaseArgs { - std::function visible; - std::function vColour; - std::function eColour; -}; - -// returns the filename _with_ extension -void gml(const lib::Rules::Real &r, bool withCoords, std::ostream &s); -std::string gml(const lib::Rules::Real &r, bool withCoords); -// returns the filename without extension -std::string dotCombined(const lib::Rules::Real &r); -std::string svgCombined(const lib::Rules::Real &r); -std::string pdfCombined(const lib::Rules::Real &r); -// returns the filename _without_ extension -std::string dot(const lib::Rules::Real &r); // does not handle labels correctly, is for coordinate generation -std::string coords(const lib::Rules::Real &r, unsigned int idOffset, const Options &options, - std::function disallowCollapse_); -std::pair -tikz(const std::string &fileCoordsNoExt, const lib::Rules::Real &r, unsigned int idOffset, const Options &options, - const std::string &suffixL, const std::string &suffixK, const std::string &suffixR, const BaseArgs &args, - std::function disallowCollapse); -std::pair tikz(const lib::Rules::Real &r, unsigned int idOffset, const Options &options, - const std::string &suffixL, const std::string &suffixK, - const std::string &suffixR, const BaseArgs &args, - std::function disallowCollapse); -std::string pdf(const lib::Rules::Real &r, const Options &options, - const std::string &suffixL, const std::string &suffixK, const std::string &suffixR, - const BaseArgs &args); -std::pair -tikzTransitionState(const std::string &fileCoordsNoExt, const lib::Rules::Real &r, unsigned int idOffset, - const Options &options, - const std::string &suffix, const BaseArgs &args); -std::pair -tikzTransitionState(const lib::Rules::Real &r, unsigned int idOffset, const Options &options, - const std::string &suffix, const BaseArgs &args); -std::string pdfTransitionState(const lib::Rules::Real &r, const Options &options, - const std::string &suffix, const BaseArgs &args); -//std::string pdfCombined(const lib::Rules::Real &r, const Options &options); // TODO -std::pair summary(const lib::Rules::Real &r, bool printCombined); -std::pair -summary(const lib::Rules::Real &r, const Options &first, const Options &second, bool printCombined); -void termState(const lib::Rules::Real &r); -} // namespace Write -} // namespace mod::lib::IO::Rules - -#endif // MOD_LIB_IO_RULE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/RuleRead.cpp b/libs/libmod/src/mod/lib/IO/RuleRead.cpp deleted file mode 100644 index 8ae77b8..0000000 --- a/libs/libmod/src/mod/lib/IO/RuleRead.cpp +++ /dev/null @@ -1,538 +0,0 @@ -#include "Rule.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -namespace mod::lib::IO::Rules::Read { -namespace { - -template -struct Label { - std::optional left, context, right; -}; - -} // namespace - -Result gml(lib::IO::Warnings &warnings, std::string_view input) { - GML::Rule rule; - { // GML parsing and conversion - gml::ast::KeyValue ast; - try { - ast = gml::parser::parse(input); - } catch(const gml::parser::error &e) { - return Result<>::Error(e.what()); - } - using namespace gml::converter::edsl; - auto cVertex = GML::makeVertexConverter(0); - auto cEdge = GML::makeEdgeConverter(0); - auto nodeLabels = list("nodeLabels") - (string("label", &GML::AdjacencyConstraint::nodeLabels)); - auto edgeLabels = list("edgeLabels") - (string("label", &GML::AdjacencyConstraint::edgeLabels)); - auto constrainAdj = list("constrainAdj", &GML::Rule::matchConstraints) - (int_("id", &GML::AdjacencyConstraint::id), 1, 1) - (string("op", &GML::AdjacencyConstraint::op), 1, 1) - (int_("count", &GML::AdjacencyConstraint::count), 1, 1) - (nodeLabels)(edgeLabels); - auto constrainShortestPath = list("constrainShortestPath", - &GML::Rule::matchConstraints) - (int_("source", &GML::ShortestPathConstraint::source), 1, 1) - (int_("target", &GML::ShortestPathConstraint::target), 1, 1) - (string("op", &GML::ShortestPathConstraint::op), 1, 1) - (int_("length", &GML::ShortestPathConstraint::length), 1, 1); - auto makeSide = [&](std::string name, GML::Graph GML::Rule::*side) { - return list(name, side)(cVertex)(cEdge); - }; - auto cRule = list("rule") - (string("ruleID", &GML::Rule::id), 0, 1) - (string("labelType", &GML::Rule::labelType), 0, 1) - (makeSide("left", &GML::Rule::left), 0, 1) - (makeSide("context", &GML::Rule::context), 0, 1) - (makeSide("right", &GML::Rule::right), 0, 1) - (constrainAdj)(constrainShortestPath); - auto iterBegin = * - auto iterEnd = iterBegin + 1; - try { - gml::converter::convert(iterBegin, iterEnd, cRule, rule); - } catch(const gml::converter::error &e) { - return Result<>::Error(e.what()); - } - } // end of GML parsing - - const auto checkSide = [](const GML::Graph &side, std::string name) -> Result<> { - std::unordered_set vertexIds; - for(const GML::Vertex &v : side.vertices) { - if(vertexIds.find(v.id) != end(vertexIds)) - return Result<>::Error("Duplicate vertex " + std::to_string(v.id) + " in " + name + " graph."); - vertexIds.insert(v.id); - } - std::set > edgeIds; - for(const GML::Edge &e : side.edges) { - if(e.source == e.target) - return Result<>::Error("Loop edge (on " + std::to_string(e.source) + ", in " + name + ") is not allowed."); - auto eSorted = std::minmax(e.source, e.target); - if(edgeIds.find(eSorted) != end(edgeIds)) - return Result<>::Error( - "Duplicate edge (" + std::to_string(e.source) + ", " + std::to_string(e.target) + ") in " - + name + " graph."); - edgeIds.insert(eSorted); - } - return Result<>(); - }; - - if(auto res = checkSide(rule.left, "left"); !res) return res; - if(auto res = checkSide(rule.context, "context"); !res) return res; - if(auto res = checkSide(rule.right, "right"); !res) return res; - - using Vertex = lib::Rules::Vertex; - using Edge = lib::Rules::Edge; - Data data; - data.rule = lib::Rules::LabelledRule(); - data.name = rule.id; - if(rule.labelType) { - const std::string <String = *rule.labelType; - if(ltString == "string") data.labelType = LabelType::String; - else if(ltString == "term") data.labelType = LabelType::Term; - else return Result<>::Error("Error in rule GML. Unknown labelType '" + ltString + "'."); - } - - auto &dpoResult = *data.rule; - auto &g = get_graph(dpoResult); - dpoResult.pString = std::make_unique(g); - auto &pString = *dpoResult.pString; - - struct VertexLabels { - bool inLeft = false, inContext = false, inRight = false; - Label string, stereo; - public: - Vertex v; - std::optional parsedEmbeddingLeft, parsedEmbeddingRight; - }; - - struct EdgeLabels { - bool inLeft = false, inContext = false, inRight = false; - Label string, stereo; - public: - Edge e; - }; - std::map idMapVertex; - std::map vertexMapId; - std::map, EdgeLabels> idMapEdge; - for(const GML::Vertex &vGML : rule.left.vertices) { - auto &v = idMapVertex[vGML.id]; - v.inLeft = true; - v.string.left = vGML.label; - v.stereo.left = vGML.stereo; - } - for(const GML::Vertex &vGML : rule.context.vertices) { - auto &v = idMapVertex[vGML.id]; - v.inContext = true; - v.string.context = vGML.label; - v.stereo.context = vGML.stereo; - } - for(const GML::Vertex &vGML : rule.right.vertices) { - auto &v = idMapVertex[vGML.id]; - v.inRight = true; - v.string.right = vGML.label; - v.stereo.right = vGML.stereo; - } - for(const GML::Edge &eGML : rule.left.edges) { - auto eSorted = std::minmax(eGML.source, eGML.target); - auto &e = idMapEdge[eSorted]; - e.inLeft = true; - e.string.left = eGML.label; - e.stereo.left = eGML.stereo; - } - for(const GML::Edge &eGML : rule.context.edges) { - auto eSorted = std::minmax(eGML.source, eGML.target); - auto &e = idMapEdge[eSorted]; - e.inContext = true; - e.string.context = eGML.label; - e.stereo.context = eGML.stereo; - } - for(const GML::Edge &eGML : rule.right.edges) { - auto eSorted = std::minmax(eGML.source, eGML.target); - auto &e = idMapEdge[eSorted]; - e.inRight = true; - e.string.right = eGML.label; - e.stereo.right = eGML.stereo; - } - - for(auto &p : idMapVertex) { - int id = p.first; - auto &vData = p.second; - // First find the right membership: - // inContext <=> inLeft && inRight - if(vData.inContext) vData.inLeft = vData.inRight = true; - else if(vData.inLeft && vData.inRight) vData.inContext = true; - - // Check labels and make (left, right) the correct labels - if(vData.string.context) { - if(vData.string.left) - return Result<>::Error( - "Error in rule GML. Vertex " + std::to_string(id) + " has a label both in 'context' and 'left'."); - if(vData.string.right) - return Result<>::Error( - "Error in rule GML. Vertex " + std::to_string(id) + " has a label both in 'context' and 'right'."); - // Note: terms follow the same semantics as string, i.e., the same string in L and R becomes the exact same terms. - vData.string.left = vData.string.right = vData.string.context; - } - if(vData.stereo.context) { - if(vData.stereo.left) - return Result<>::Error( - "Error in rule GML. Vertex " + std::to_string(id) + " has stereo both in 'context' and 'left'."); - if(vData.stereo.right) - return Result<>::Error( - "Error in rule GML. Vertex " + std::to_string(id) + " has stereo both in 'context' and 'right'."); - // for stereo it matters if it's L+R or it's K - } - - // Check that there is a string/stereo in left/right when inLeft/inRight - if(vData.inLeft && !vData.string.left) - return Result<>::Error("Error in rule GML. Vertex " + std::to_string(id) + " is in L, but has no label."); - if(vData.inRight && !vData.string.right) - return Result<>::Error("Error in rule GML. Vertex " + std::to_string(id) + " is in R, but has no label."); - - vData.v = add_vertex(g); - vertexMapId[vData.v] = id; - data.externalToInternalIds[id] = get(boost::vertex_index_t(), g, vData.v); - if(vData.inContext) { - put_membership(dpoResult, vData.v, lib::Rules::Membership::Context); - pString.add(vData.v, *vData.string.left, *vData.string.right); - } else if(vData.inLeft) { - assert(!vData.inRight); - put_membership(dpoResult, vData.v, lib::Rules::Membership::Left); - pString.add(vData.v, *vData.string.left, ""); - } else { - assert(vData.inRight); - put_membership(dpoResult, vData.v, lib::Rules::Membership::Right); - pString.add(vData.v, "", *vData.string.right); - } - } // for each vertex - - for(auto &p : idMapEdge) { - const int src = p.first.first; - const int tar = p.first.second; - if(idMapVertex.find(src) == end(idMapVertex)) - return Result<>::Error( - "Error in rule GML. Edge endpoint '" + std::to_string(src) + "' does not exist for edge (" - + std::to_string(src) + ", " + std::to_string(tar) + ")."); - if(idMapVertex.find(tar) == end(idMapVertex)) - return Result<>::Error( - "Error in rule GML. Edge endpoint '" + std::to_string(tar) + "' does not exist for edge (" - + std::to_string(src) + ", " + std::to_string(tar) + ")."); - Vertex vSrc = idMapVertex[src].v, vTar = idMapVertex[tar].v; - auto &eData = p.second; - // First find the right membership: - // inContext <=> inLeft && inRight - if(eData.inContext) eData.inLeft = eData.inRight = true; - else if(eData.inLeft && eData.inRight) eData.inContext = true; - - // Check labels and make (left, right) the correct labels - if(eData.string.context) { - if(eData.string.left) - return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + - ") has a label both in 'context' and 'left'."); - if(eData.string.right) - return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + - ") has a label both in 'context' and 'right'."); - eData.string.left = eData.string.right = eData.string.context; - } - if(eData.stereo.context) { - if(eData.stereo.left) - return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + - ") has stereo both in 'context' and 'left'."); - if(eData.stereo.right) - return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + - ") has stereo both in 'context' and 'right'."); - // for stereo it matters if it's L+R or it's K - } - - // Check that there is a string in left/right when inLeft/inRight - if(eData.inLeft && !eData.string.left) - return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + - ") is in L, but has no label."); - if(eData.inRight && !eData.string.right) - return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + - ") is in R, but has no label."); - - eData.e = add_edge(vSrc, vTar, g).first; - if(eData.inContext) { - put_membership(dpoResult, eData.e, lib::Rules::Membership::Context); - pString.add(eData.e, *eData.string.left, *eData.string.right); - } else if(eData.inLeft) { - assert(!eData.inRight); - put_membership(dpoResult, eData.e, lib::Rules::Membership::Left); - pString.add(eData.e, *eData.string.left, ""); - } else { - assert(eData.inRight); - put_membership(dpoResult, eData.e, lib::Rules::Membership::Right); - pString.add(eData.e, "", *eData.string.right); - } - } // for each edge - // the graph is set, so initialise the component storage - dpoResult.initComponents(); - - // constraints - for(const GML::MatchConstraint &cGML : rule.matchConstraints) { - struct MatchConstraintConverter { - MatchConstraintConverter(lib::Rules::LabelledRule &dpoResult, const std::map &idMapVertex) - : dpoResult(dpoResult), idMapVertex(idMapVertex) {} - - Result<> operator()(const GML::AdjacencyConstraint &cGML) { - const auto iter = idMapVertex.find(cGML.id); - if(iter == end(idMapVertex)) - return Result<>::Error("Error in rule GML. Vertex " + std::to_string(cGML.id) + - " in adjacency constraint does not exist."); - const Vertex vConstrained = iter->second.v; - lib::GraphMorphism::Constraints::Operator op; - { - const auto &s = cGML.op; - using namespace lib::GraphMorphism::Constraints; - if(s == "<") op = Operator::LT; - else if(s == "<=") op = Operator::LEQ; - else if(s == "=") op = Operator::EQ; - else if(s == ">=") op = Operator::GEQ; - else if(s == ">") op = Operator::GT; - else return Result<>::Error("Error in rule GML. Unknown operator '" + s + "' in adjacency constraint."); - } - auto c = std::make_unique< - lib::GraphMorphism::Constraints::VertexAdjacency < lib::Rules::LabelledRule::LeftGraphType> - > (vConstrained, op, cGML.count); - c->vertexLabels.insert(cGML.nodeLabels.begin(), cGML.nodeLabels.end()); - c->edgeLabels.insert(cGML.edgeLabels.begin(), cGML.edgeLabels.end()); - dpoResult.leftMatchConstraints.push_back(std::move(c)); - return Result<>(); - } - - Result<> operator()(const GML::ShortestPathConstraint &cGML) { - const auto iterSrc = idMapVertex.find(cGML.source); - const auto iterTar = idMapVertex.find(cGML.target); - if(iterSrc == end(idMapVertex)) - return Result<>::Error("Error in rule GML. Vertex " + std::to_string(cGML.source) + - " in shortest path constraint does not exist."); - if(iterTar == end(idMapVertex)) - return Result<>::Error("Error in rule GML. Vertex " + std::to_string(cGML.target) + - " in shortest path constraint does not exist."); - const Vertex vSrc = iterSrc->second.v; - const Vertex vTar = iterTar->second.v; - lib::GraphMorphism::Constraints::Operator op; - { - const auto &s = cGML.op; - using namespace lib::GraphMorphism::Constraints; - if(s == "<") op = Operator::LT; - else if(s == "<=") op = Operator::LEQ; - else if(s == "=") op = Operator::EQ; - else if(s == ">=") op = Operator::GEQ; - else if(s == ">") op = Operator::GT; - else - return Result<>::Error( - "Error in rule GML. Unknown operator '" + s + "' in shortest path constraint."); - } - const auto compSrc = dpoResult.leftComponents[get(boost::vertex_index_t(), get_graph(dpoResult), vSrc)]; - const auto compTar = dpoResult.leftComponents[get(boost::vertex_index_t(), get_graph(dpoResult), vTar)]; - if(compSrc != compTar) - return Result<>::Error( - "Error in rule GML. Vertex " + std::to_string(cGML.source) + " and " + std::to_string(cGML.target) - + " are in different connected components of the left graph. " - + "This is currently not supported for the shortest path constraint."); - auto c = std::make_unique< - lib::GraphMorphism::Constraints::ShortestPath < lib::Rules::LabelledRule::LeftGraphType> - > (vSrc, vTar, op, cGML.length); - dpoResult.leftMatchConstraints.push_back(std::move(c)); - return Result<>(); - } - public: - lib::Rules::LabelledRule &dpoResult; - const std::map &idMapVertex; - } visitor(dpoResult, idMapVertex); - if(auto res = std::visit(visitor, cGML); !res) return res; - } // for each constriant - bool doStereo = false; - for(const auto &v : rule.left.vertices) doStereo = doStereo || v.stereo; - for(const auto &v : rule.context.vertices) doStereo = doStereo || v.stereo; - for(const auto &v : rule.right.vertices) doStereo = doStereo || v.stereo; - for(const auto &e : rule.left.edges) doStereo = doStereo || e.stereo; - for(const auto &e : rule.context.edges) doStereo = doStereo || e.stereo; - for(const auto &e : rule.right.edges) doStereo = doStereo || e.stereo; - if(!doStereo) return std::move(data); // TODO: remove std::move when C++20/P1825R0 is available - - // Stereo - //========================================================================== - const auto vFromVertexId = [&idMapVertex](int id) { - auto iter = idMapVertex.find(id); - assert(iter != end(idMapVertex)); - return iter->second.v; - }; - lib::Rules::PropMoleculeCore mol(g, pString); // temporary for doing the inference - lib::Rules::DPOProjection gLeft(g, lib::Rules::Membership::Left), gRight(g, lib::Rules::Membership::Right); - auto molLeft = mol.getLeft(); - auto molRight = mol.getRight(); - auto leftInference = lib::Stereo::makeInference(gLeft, molLeft, true); - auto rightInference = lib::Stereo::makeInference(gRight, molRight, true); - const auto &gGeometry = lib::Stereo::getGeometryGraph(); - // Set the explicitly defined edge categories. - //---------------------------------------------------------------------------- - for(const auto &p : idMapEdge) { - const auto handleSide = [&p](const std::optional &os, const std::string &side, - auto &inference) -> Result<> { - if(!os) return Result<>(); - const std::string &s = *os; - if(s.size() != 1) - return Result<>::Error("Error in stereo data for edge (" + std::to_string(p.first.first) - + ", " + std::to_string(p.first.second) + ") in " + side - + ". Parsing error in stereo data '" + s + "'."); - lib::Stereo::EdgeCategory cat; - switch(s.front()) { - case '*': - cat = lib::Stereo::EdgeCategory::Any; - break; - default: - return Result<>::Error("Error in stereo data for edge (" + std::to_string(p.first.first) + ", " - + std::to_string(p.first.second) + ") in " + side - + ". Parsing error in stereo data '" + s + "'."); - } - auto res = inference.assignEdgeCategory(p.second.e, cat); - if(!res) { - res.setError("Error in stereo data for edge (" + std::to_string(p.first.first) + ", " - + std::to_string(p.first.second) + ") in " + side + ". " + res.extractError()); - return res; - } - return res; - }; - if(auto res = handleSide(p.second.stereo.left, "L", leftInference); !res) return res; - if(auto res = handleSide(p.second.stereo.right, "R", rightInference); !res) return res; - } // for each edge - // Set the explicitly stereo data. - //---------------------------------------------------------------------------- - for(auto &p : idMapVertex) { - const auto handleSide = [&](const std::optional &os, const std::string &side, auto &inference, - auto &parsedEmbedding, const auto &gSide) { - if(!os) return Result<>(); - const auto &v = p.second.v; - if(auto parsedEmbeddingRes = lib::IO::Stereo::Read::parseEmbedding(*os)) { - parsedEmbedding = std::move(*parsedEmbeddingRes); - } else { - return Result<>::Error( - "Error in stereo data for vertex " + std::to_string(p.first) + " in " + side + ". " + - parsedEmbeddingRes.extractError()); - } - // Geometry - //.......................................................................... - const auto &embGML = *parsedEmbedding; - if(embGML.geometry) { - auto vGeo = gGeometry.findGeometry(*embGML.geometry); - if(vGeo == gGeometry.nullGeometry()) - return Result<>::Error("Error in stereo data for vertex " + std::to_string(p.first) + " in " + side + - ". Invalid gGeometry '" + *embGML.geometry + "'."); - if(auto res = inference.assignGeometry(v, vGeo); !res) { - return Result<>::Error( - "Error in stereo data for vertex " + std::to_string(p.first) + " in " + side + ". " + - res.extractError()); - } - } - // Edges - //.......................................................................... - if(embGML.edges) { - inference.initEmbedding(v); - for(const auto &e : *embGML.edges) { - if(const int *idPtr = std::get_if(&e)) { - int idNeighbour = *idPtr; - if(idMapVertex.find(idNeighbour) == end(idMapVertex)) - return Result<>::Error("Error in graph GML. Neighbour vertex " + std::to_string(idNeighbour) + - " in stereo embedding for vertex " - + std::to_string(p.first) + " in " + side + " does not exist."); - auto ePair = edge(v, vFromVertexId(idNeighbour), gSide); - if(!ePair.second) - return Result<>::Error("Error in graph GML. Vertex " + std::to_string(idNeighbour) + - " in stereo embedding for vertex " - + std::to_string(p.first) + " in " + side + " is not a neighbour."); - inference.addEdge(v, ePair.first); - } else if(const char *virtPtr = std::get_if(&e)) { - switch(*virtPtr) { - case 'e': - inference.addLonePair(v); - break; - default: - MOD_ABORT; // the parser should know what is allowed - } - } else { - MOD_ABORT; // the parser should know what is allowed - } - } - } - // Fixation - //.......................................................................... - if(embGML.fixation) { - // TODO: expand this when more complicated geometries are implemented - const bool isFixed = *embGML.fixation; - if(isFixed) inference.fixSimpleGeometry(v); - } - return Result<>(); - }; - if(auto res = handleSide(p.second.stereo.left, "L", leftInference, p.second.parsedEmbeddingLeft, - gLeft); !res) - return res; - if(auto res = handleSide(p.second.stereo.right, "R", rightInference, p.second.parsedEmbeddingRight, - gRight); !res) - return res; - } // for each vertex - - const auto finalize = [&warnings, &vertexMapId](auto &inference, const std::string &side) { - return inference.finalize(warnings, [&vertexMapId, &side](Vertex v) { - auto iter = vertexMapId.find(v); - assert(iter != vertexMapId.end()); - return std::to_string(iter->second) + " in " + side; - }); - }; - if(auto resLeft = finalize(leftInference, "L"); !resLeft) return resLeft; - if(auto resRight = finalize(rightInference, "R"); !resRight) return resRight; - - const auto vertexInContext = [&](Vertex v) -> bool { - const auto idIter = vertexMapId.find(v); - assert(idIter != end(vertexMapId)); - const auto lIter = idMapVertex.find(idIter->second); - assert(lIter != end(idMapVertex)); - assert(lIter->second.inContext); - const auto &stereo = lIter->second.stereo; - // if there is any stereo data, maybe we are in context - if(stereo.left.has_value() || stereo.context.has_value() || stereo.right.has_value()) - return stereo.context.has_value(); - else // otherwise, default to be in context - return true; - }; - const auto edgeInContext = [&](Edge e) -> bool { - const auto vSrc = source(e, g); - const auto vTar = target(e, g); - const auto idSrcIter = vertexMapId.find(vSrc); - const auto idTarIter = vertexMapId.find(vTar); - assert(idSrcIter != end(vertexMapId)); - assert(idTarIter != end(vertexMapId)); - const auto lIter = idMapEdge.find(std::make_pair(idSrcIter->second, idTarIter->second)); - assert(lIter != end(idMapEdge)); - assert(lIter->second.inContext); - const auto &stereo = lIter->second.stereo; - // if there is any stereo data, maybe we are in context - if(stereo.left.has_value() || stereo.context.has_value() || stereo.right.has_value()) - return stereo.context.has_value(); - else // otherwise, default to be in context - return true; - }; - dpoResult.pStereo = std::make_unique(g, std::move(leftInference), - std::move(rightInference), vertexInContext, - edgeInContext); - return std::move(data); // TODO: remove std::move when C++20/P1825R0 is available -} - -} // namespace mod::lib::IO::Rules::Read \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/RuleWrite.cpp b/libs/libmod/src/mod/lib/IO/RuleWrite.cpp deleted file mode 100644 index 0d51eb1..0000000 --- a/libs/libmod/src/mod/lib/IO/RuleWrite.cpp +++ /dev/null @@ -1,832 +0,0 @@ -#include "Rule.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace mod::lib::IO::Rules::Write { -namespace { - -// returns the filename _without_ extension - -const std::string &getFilePrefix(const lib::Rules::Real &r) { - static std::map cache; - auto iter = cache.find(r.getId()); - if(iter == end(cache)) { - std::string prefix = IO::getUniqueFilePrefix() + "r_" + boost::lexical_cast(r.getId()); - return cache[r.getId()] = prefix; - } else return iter->second; -} - -void gmlSide(const lib::Rules::Real &r, std::ostream &s, lib::Rules::Membership printMembership, bool withCoords) { - if(withCoords) { - const auto &depict = r.getDepictionData(); - if(!depict.getHasCoordinates()) MOD_ABORT; - } - using Vertex = lib::Rules::Vertex; - using Edge = lib::Rules::Edge; - const lib::Rules::GraphType &core = r.getGraph(); - const lib::Rules::PropStringCore &labelState = r.getStringState(); - - for(Vertex v : asRange(vertices(core))) { - auto vMembership = core[v].membership; - if(printMembership == lib::Rules::Membership::Context) { - if(vMembership != lib::Rules::Membership::Context) continue; - if(labelState.isChanged(v)) continue; - } else { - if(vMembership == lib::Rules::Membership::Context) { - if(!labelState.isChanged(v)) continue; - } else { - if(printMembership != vMembership) continue; - } - } - s << "\t\tnode [ id " << get(boost::vertex_index_t(), core, v) << " label \""; - switch(printMembership) { - case lib::Rules::Membership::Left: - s << labelState.getLeft()[v]; - break; - case lib::Rules::Membership::Context: - s << labelState.getLeft()[v]; - break; - case lib::Rules::Membership::Right: - s << labelState.getRight()[v]; - break; - } - s << "\""; - if(withCoords) { - const auto &depict = r.getDepictionData(); - s << " vis2d [ x " << depict.getX(v, true) << " y " << depict.getY(v, true) << " ]"; - } - s << " ]\n"; - } - - for(Edge e : asRange(edges(core))) { - auto eMembership = core[e].membership; - if(printMembership == lib::Rules::Membership::Context) { - if(eMembership != lib::Rules::Membership::Context) continue; - if(labelState.isChanged(e)) continue; - } else { - if(eMembership == lib::Rules::Membership::Context) { - if(!labelState.isChanged(e)) continue; - } else { - if(printMembership != eMembership) continue; - } - } - s << "\t\tedge [ source " << get(boost::vertex_index_t(), core, source(e, core)) - << " target " << get(boost::vertex_index_t(), core, target(e, core)) - << " label \""; - switch(printMembership) { - case lib::Rules::Membership::Left: - s << labelState.getLeft()[e]; - break; - case lib::Rules::Membership::Context: - s << labelState.getLeft()[e]; - break; - case lib::Rules::Membership::Right: - s << labelState.getRight()[e]; - break; - } - s << "\" ]\n"; - } -} - -void printEdgeStyle(std::ostream &s, lib::Rules::Membership eSide, int src, int tar) { - s << "\t" << src << " -- " << tar << " [ "; - switch(eSide) { - case lib::Rules::Membership::Left: - s << "style=dashed "; - break; - case lib::Rules::Membership::Right: - s << "style=dotted "; - break; - default: - break; - } -} - -} // namespace - -void gml(const lib::Rules::Real &r, bool withCoords, std::ostream &s) { - s << "rule [" << std::endl; - s << "\truleID \"" << r.getName() << "\"\n"; - if(r.getLabelType()) { - s << "\tlabelType \""; - switch(*r.getLabelType()) { - case LabelType::String: - s << "string"; - break; - case LabelType::Term: - s << "term"; - break; - } - s << "\"\n"; - } - { - std::stringstream str; - gmlSide(r, str, lib::Rules::Membership::Left, withCoords); - if(str.str().size() > 0) s << "\tleft [\n" << str.str() << "\t]\n"; - } - { - std::stringstream str; - gmlSide(r, str, lib::Rules::Membership::Context, withCoords); - if(str.str().size() > 0) s << "\tcontext [\n" << str.str() << "\t]\n"; - } - { - std::stringstream str; - gmlSide(r, str, lib::Rules::Membership::Right, withCoords); - if(str.str().size() > 0) s << "\tright [\n" << str.str() << "\t]\n"; - } - auto printer = lib::IO::MatchConstraint::Write::makeGMLPrintVisitor(s, get_left(r.getDPORule()), "\t"); - for(const auto &c : r.getDPORule().leftMatchConstraints) { - c->accept(printer); - } - s << "]"; -} - -std::string gml(const lib::Rules::Real &r, bool withCoords) { - post::FileHandle s(getFilePrefix(r) + ".gml"); - gml(r, withCoords, s); - return s; -} - -std::string dotCombined(const lib::Rules::Real &r) { - std::stringstream fileName; - fileName << "r_" << r.getId() << "_combined.dot"; - post::FileHandle s(getUniqueFilePrefix() + fileName.str()); - std::string fileNoExt = s; - fileNoExt.erase(fileNoExt.end() - 4, fileNoExt.end()); - using Vertex = lib::Rules::Vertex; - using Edge = lib::Rules::Edge; - const lib::Rules::GraphType &g = r.getGraph(); - const lib::Rules::PropStringCore &labelState = r.getStringState(); - s << "graph G {" << std::endl; - for(Vertex v : asRange(vertices(g))) { - auto membership = g[v].membership; - s << "\t" << get(boost::vertex_index_t(), g, v) << " [ label=\""; - switch(membership) { - case lib::Rules::Membership::Left: - s << labelState.getLeft()[v]; - break; - case lib::Rules::Membership::Context: - s << labelState.getLeft()[v] << " | " << labelState.getRight()[v]; - break; - case lib::Rules::Membership::Right: - s << labelState.getRight()[v]; - break; - } - s << "\""; - switch(membership) { - case lib::Rules::Membership::Left: - s << " style=dashed"; - break; - case lib::Rules::Membership::Right: - s << " style=dotted"; - break; - default: - break; - } - s << " ]" << std::endl; - } - - for(Edge e : asRange(edges(g))) { - auto membership = g[e].membership; - auto vSrcId = get(boost::vertex_index_t(), g, source(e, g)); - auto vTarId = get(boost::vertex_index_t(), g, target(e, g)); - std::string label; - switch(membership) { - case lib::Rules::Membership::Left: - label = labelState.getLeft()[e]; - break; - case lib::Rules::Membership::Context: - label = labelState.getLeft()[e] + " | " + labelState.getRight()[e]; - break; - case lib::Rules::Membership::Right: - label = labelState.getRight()[e]; - break; - } - switch(label[0]) { - // case '=': // fall through to make two edges - // // assert(false); - // printEdgeStyle(s, membership, vSrcId, vTarId); - // s << "]" << std::endl; - // case '-': // print the rest of the label - // printEdgeStyle(s, membership, vSrcId, vTarId); - // s << "label=\"" << (label.c_str() + 1) << "\" ]" << std::endl; - // break; - default: - printEdgeStyle(s, membership, vSrcId, vTarId); - s << "label=\"" << label << "\" ]" << std::endl; - break; - } - } - s << "}" << std::endl; - return fileNoExt; -} - -std::string svgCombined(const lib::Rules::Real &r) { - std::string fileNoExt = dotCombined(r); - IO::post() << "gv ruleCombined \"" << fileNoExt << "\" svg" << std::endl; - return fileNoExt; -} - -std::string pdfCombined(const lib::Rules::Real &r) { - std::string fileNoExt = svgCombined(r); - IO::post() << "svgToPdf \"" << fileNoExt << "\"" << std::endl; - return fileNoExt; -} - -std::string dot(const lib::Rules::Real &r) { - static std::set cache; - std::string fileNoExt = getFilePrefix(r); - auto iter = cache.find(r.getId()); - if(iter != end(cache)) return fileNoExt; - - using Vertex = lib::Rules::Vertex; - using Edge = lib::Rules::Edge; - const lib::Rules::GraphType &g = r.getGraph(); - const lib::Rules::PropStringCore &labelState = r.getStringState(); - - post::FileHandle s(fileNoExt + ".dot"); - s << "graph g {" << std::endl; - s << getConfig().io.dotCoordOptions.get() << std::endl; - for(Vertex v : asRange(vertices(g))) { - unsigned int vId = get(boost::vertex_index_t(), g, v); - s << vId << " [ label=\""; - auto membership = g[v].membership; - switch(membership) { - case lib::Rules::Membership::Left: - s << labelState.getLeft()[v]; - break; - case lib::Rules::Membership::Context: - s << labelState.getLeft()[v] << " | " << labelState.getRight()[v]; - break; - case lib::Rules::Membership::Right: - s << labelState.getRight()[v]; - break; - } - s << "\" ];" << std::endl; - } - for(Edge e : asRange(edges(g))) { - unsigned int vSrcId = get(boost::vertex_index_t(), g, source(e, g)); - unsigned int vTarId = get(boost::vertex_index_t(), g, target(e, g)); - s << vSrcId << " -- " << vTarId << " [ label=\""; - auto membership = g[e].membership; - switch(membership) { - case lib::Rules::Membership::Left: - s << labelState.getLeft()[e]; - break; - case lib::Rules::Membership::Context: - s << labelState.getLeft()[e] << " | " << labelState.getRight()[e]; - break; - case lib::Rules::Membership::Right: - s << labelState.getRight()[e]; - break; - } - s << "\" ];" << std::endl; - } - s << "}" << std::endl; - return fileNoExt; -} - -struct CoordsCacheEntry { - std::size_t id; - bool collapseHydrogens; - int rotation; - bool mirror; -public: - friend bool operator<(const CoordsCacheEntry &a, const CoordsCacheEntry &b) { - return std::tie(a.id, a.collapseHydrogens, a.rotation, a.mirror) - < std::tie(b.id, b.collapseHydrogens, b.rotation, b.mirror); - } -}; - -namespace { - -bool disallowVertexCollapse(const lib::Rules::Real &r, const CoreVertex v, - std::function disallowCollapse) { - if(getConfig().rule.collapseChangedHydrogens.get()) - return disallowCollapse(v); - if(r.getGraph()[v].membership != lib::Rules::Membership::Context) - return true; - for(const auto e : asRange(out_edges(v, r.getGraph()))) { - if(r.getGraph()[e].membership != lib::Rules::Membership::Context) - return true; - } - return disallowCollapse(v); -} - -} // namespace - -std::string coords(const lib::Rules::Real &r, unsigned int idOffset, const Options &options, - std::function disallowCollapse_) { - static std::map cache; - const auto iter = cache.find({r.getId(), options.collapseHydrogens, options.rotation, options.mirror}); - if(iter != end(cache)) return iter->second; - std::string fileNoExt = getFilePrefix(r); - const auto &depict = r.getDepictionData(); - if(!depict.getHasCoordinates()) { - if(idOffset != 0) { - std::cout << "Blame the lazy programmer. Offset " << idOffset << " not yet supported in dot coords." - << std::endl; - MOD_ABORT; - } - dot(r); - IO::post() << "coordsFromGV rule \"" << fileNoExt << "\" noOverlay" << std::endl; - fileNoExt = fileNoExt + "_coord"; - cache[{r.getId(), true, options.rotation, options.mirror} - ] = fileNoExt; - cache[{r.getId(), false, options.rotation, options.mirror} - ] = fileNoExt; - return fileNoExt; - } else { - const auto &g = r.getGraph(); - const std::string molString = options.collapseHydrogens ? "_mol" : ""; - const std::string fileNoExt = getFilePrefix(r) + molString + "_coord"; - post::FileHandle s(fileNoExt + ".tex"); - s << "% dummy\n"; - const bool useCollapsedCoords = [&]() { - if(!options.collapseHydrogens) return false; - for(const auto v : asRange(vertices(g))) { - if(disallowCollapse_(v)) return false; - } - return true; - }(); - - for(const auto v : asRange(vertices(g))) { - const auto vId = get(boost::vertex_index_t(), g, v); - const bool printCoord = [&]() { - if(!useCollapsedCoords) return true; - if(disallowVertexCollapse(r, v, disallowCollapse_)) return true; - return !Chem::isCollapsible(v, g, depict, depict, [&depict](const auto v) { - return depict.hasImportantStereo(v); - }); - }(); - if(!printCoord) continue; - double x, y; - std::tie(x, y) = pointTransform( - depict.getX(v, !useCollapsedCoords), - depict.getY(v, !useCollapsedCoords), - options.rotation, options.mirror); - s << "\\coordinate[overlay] (v-coord-" << (vId + idOffset) << ") at (" << std::fixed << x << ", " << y - << ") {};" << std::endl; - } - if(options.collapseHydrogens && !useCollapsedCoords) { - // don't cache these as the user predicate influences it - } else { - cache[{r.getId(), options.collapseHydrogens, options.rotation, options.mirror} - ] = fileNoExt; - } - return fileNoExt; - } -} - -namespace { - -template -struct AdvOptions { - AdvOptions(const lib::Rules::Real &r, unsigned int idOffset, const BaseArgs &args, - std::function disallowCollapse) - : idOffset(idOffset), changeColour(changeColourFromMembership()), r(r), args(args), - disallowCollapse_(disallowCollapse) {} -private: - static std::string changeColourFromMembership() { - std::string side = []() { - switch(membership) { - case lib::Rules::Membership::Left: - return getConfig().rule.changeColourL.get(); - case lib::Rules::Membership::Context: - return getConfig().rule.changeColourK.get(); - case lib::Rules::Membership::Right: - return getConfig().rule.changeColourR.get(); - } - }(); - if(side.empty()) return getConfig().rule.changeColour.get(); - else return side; - } - -public: - std::string getColour(CoreVertex v) const { - bool isChanged = r.getGraph()[v].membership != lib::Rules::Membership::Context - || r.getStringState().isChanged(v); - if(isChanged) { - return changeColour; - } else return args.vColour(v); - } - - std::string getColour(CoreEdge e) const { - bool isChanged = r.getGraph()[e].membership != lib::Rules::Membership::Context - || r.getStringState().isChanged(e); - if(isChanged) { - return changeColour; - } else return args.eColour(e); - } - - bool isVisible(CoreVertex v) const { - return args.visible(v); - } - - std::string getShownId(CoreVertex v) const { - return boost::lexical_cast(get(boost::vertex_index_t(), r.getGraph(), v)); - } - - bool overwriteWithIndex(CoreVertex) const { - return false; - } - - lib::IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(CoreEdge e, bool withHydrogen) const { - const auto get = [&](const auto &depict) { - return depict.getEdgeFake3DType(e, withHydrogen); - }; - const auto &depict = r.getDepictionData(); - switch(membership) { - case lib::Rules::Membership::Left: - return get(depict.getLeft()); - case lib::Rules::Membership::Context: - return get(depict.getContext()); - case lib::Rules::Membership::Right: - return get(depict.getRight()); - } - } - - std::string getOpts(CoreVertex v) const { - return std::string(); - } - -private: - - template - std::string getStereoStringVertex(const CoreVertex v, const F f) const { - const auto apply = [&](const auto &lg) { - const auto &conf = *get_stereo(lg)[v]; - const auto getNeighbourId = [&](const lib::Stereo::EmbeddingEdge &emb) { - const auto &g = get_graph(lg); - return get(boost::vertex_index_t(), g, target(emb.getEdge(v, g), g)); - }; - std::string res = f(conf, getNeighbourId); - if(!get_stereo(r.getDPORule()).inContext(v)) { - res = "{\\color{" + getConfig().rule.changeColourL.get() + "}" + res + "}"; - } - return res; - }; - - const auto &lr = r.getDPORule(); - switch(membership) { - case lib::Rules::Membership::Left: - return apply(get_labelled_left(lr)); - case lib::Rules::Membership::Right: - return apply(get_labelled_right(lr)); - case lib::Rules::Membership::Context: - if(get_stereo(lr).inContext(v)) { - const auto &lg = get_labelled_left(r.getDPORule()); - const auto &conf = *get_stereo(lg)[v]; - const auto getNeighbourId = [&](const lib::Stereo::EmbeddingEdge &emb) { - const auto &g = get_graph(lg); - return get(boost::vertex_index_t(), g, target(emb.getEdge(v, g), g)); - }; - return f(conf, getNeighbourId); - } else return ""; - } - } - -public: - - std::string getRawStereoString(CoreVertex v) const { - return getStereoStringVertex(v, [&](const auto &conf, auto getNId) { - return conf.asRawString(getNId); - }); - } - - std::string getPrettyStereoString(CoreVertex v) const { - return getStereoStringVertex(v, [&](const auto &conf, auto getNId) { - return conf.asPrettyString(getNId); - }); - } - - std::string getStereoString(CoreEdge e) const { - const auto apply = [&](const auto &lg) { - const auto cat = get_stereo(lg)[e]; - std::string res = boost::lexical_cast(cat); - if(!get_stereo(r.getDPORule()).inContext(e)) { - res = "{\\color{" + getConfig().rule.changeColourL.get() + "}" + res + "}"; - } - return res; - }; - - const auto &lr = r.getDPORule(); - switch(membership) { - case lib::Rules::Membership::Left: - return apply(get_labelled_left(lr)); - case lib::Rules::Membership::Right: - return apply(get_labelled_right(lr)); - case lib::Rules::Membership::Context: - if(get_stereo(lr).inContext(e)) { - const auto &lg = get_labelled_left(r.getDPORule()); - const auto cat = get_stereo(lg)[e]; - return boost::lexical_cast(cat); - } else return ""; - } - } - - std::string getEdgeAnnotation(CoreEdge) const { - return ""; - } - - bool disallowCollapse(CoreVertex v) const { - return disallowVertexCollapse(r, v, disallowCollapse_); - } - -public: - const unsigned int idOffset; -private: - const std::string changeColour; - const lib::Rules::Real &r; - const BaseArgs &args; - std::function disallowCollapse_; -}; - -} // namespace - -std::pair -tikz(const std::string &fileCoordsNoExt, const lib::Rules::Real &r, unsigned int idOffset, const Options &options, - const std::string &suffixL, const std::string &suffixK, const std::string &suffixR, const BaseArgs &args, - std::function disallowCollapse) { - std::string strOptions = options.getStringEncoding(); - std::string fileNoExt = IO::getUniqueFilePrefix() + "r_" + boost::lexical_cast(r.getId()); - fileNoExt += "_" + strOptions; - - std::string fileCoords = fileCoordsNoExt + ".tex"; - { // left - post::FileHandle s(fileNoExt + "_" + suffixL + ".tex"); - const auto &g = get_left(r.getDPORule()); - const auto &depict = r.getDepictionData().getLeft(); - const auto adv = AdvOptions(r, idOffset, args, disallowCollapse); - IO::Graph::Write::tikz(s, options, g, depict, fileCoords, adv, jla_boost::Nop<>(), ""); - } - { // context - post::FileHandle s(fileNoExt + "_" + suffixK + ".tex"); - const auto &g = get_context(r.getDPORule()); - const auto &depict = r.getDepictionData().getContext(); - - struct EdgeVisible { - EdgeVisible() = default; - - EdgeVisible(const lib::Rules::Real &r) : r(&r) {} - - bool operator()(const CoreEdge e) const { - if(getConfig().rule.printChangedEdgesInContext.get()) return true; - return !r->getStringState().isChanged(e); - } - - private: - const lib::Rules::Real *r = nullptr; - }; - boost::filtered_graph gFiltered(g, EdgeVisible(r)); - const auto adv = AdvOptions(r, idOffset, args, disallowCollapse); - IO::Graph::Write::tikz(s, options, gFiltered, depict, fileCoords, adv, jla_boost::Nop<>(), ""); - } - { // right - post::FileHandle s(fileNoExt + "_" + suffixR + ".tex"); - const auto &g = get_right(r.getDPORule()); - const auto &depict = r.getDepictionData().getRight(); - const auto adv = AdvOptions(r, idOffset, args, disallowCollapse); - IO::Graph::Write::tikz(s, options, g, depict, fileCoords, adv, jla_boost::Nop<>(), ""); - } - return std::make_pair(fileNoExt, fileCoordsNoExt); -} - -std::pair tikz(const lib::Rules::Real &r, unsigned int idOffset, const Options &options, - const std::string &suffixL, const std::string &suffixK, - const std::string &suffixR, const BaseArgs &args, - std::function disallowCollapse) { - std::string fileCoordsNoExt = coords(r, idOffset, options, disallowCollapse); - return tikz(fileCoordsNoExt, r, idOffset, options, suffixL, suffixK, suffixR, args, disallowCollapse); -} - -std::string pdf(const lib::Rules::Real &r, const Options &options, - const std::string &suffixL, const std::string &suffixK, const std::string &suffixR, - const BaseArgs &args) { - std::string fileNoExt, fileCoordsNoExt; - const unsigned int idOffset = 0; - std::tie(fileNoExt, fileCoordsNoExt) = tikz(r, idOffset, options, suffixL, suffixK, suffixR, args, - jla_boost::AlwaysFalse()); - IO::post() << "compileTikz \"" << fileNoExt << "_" << suffixL << "\" \"" << fileCoordsNoExt << "\"\n"; - IO::post() << "compileTikz \"" << fileNoExt << "_" << suffixK << "\" \"" << fileCoordsNoExt << "\"\n"; - IO::post() << "compileTikz \"" << fileNoExt << "_" << suffixR << "\" \"" << fileCoordsNoExt << "\"\n"; - IO::post().flush(); - return fileNoExt; -} - -std::pair -tikzTransitionState(const std::string &fileCoordsNoExt, const lib::Rules::Real &r, unsigned int idOffset, - const Options &options, - const std::string &suffix, const BaseArgs &args) { - MOD_ABORT; -} - -std::pair -tikzTransitionState(const lib::Rules::Real &r, unsigned int idOffset, const Options &options, - const std::string &suffix, const BaseArgs &args) { - MOD_ABORT; -} - -std::string pdfTransitionState(const lib::Rules::Real &r, const Options &options, - const std::string &suffix, const BaseArgs &args) { - std::string fileNoExt, fileCoordsNoExt; - const unsigned int idOffset = 0; - std::tie(fileNoExt, fileCoordsNoExt) = tikzTransitionState(r, idOffset, options, suffix, args); - IO::post() << "compileTikz \"" << fileNoExt << "_" << suffix << "\" \"" << fileCoordsNoExt << "\"" << std::endl; - return fileNoExt; -} - -std::string pdfCombined(const lib::Rules::Real &r, const Options &options) { - MOD_ABORT; -} - -std::pair summary(const lib::Rules::Real &r, bool printCombined) { - graph::Printer first; - graph::Printer second; - second.setReactionDefault(); - return summary(r, first.getOptions(), second.getOptions(), printCombined); -} - -std::pair -summary(const lib::Rules::Real &r, const Options &first, const Options &second, bool printCombined) { - auto visible = jla_boost::AlwaysTrue(); - auto vColour = jla_boost::Nop(); - auto eColour = jla_boost::Nop(); - const BaseArgs args{visible, vColour, eColour}; - std::string graphLike = pdf(r, first, "L", "K", "R", args); - std::string molLike = first == second ? "" : pdf(r, second, "L", "K", "R", args); - std::string combined = printCombined - ? pdfCombined(r /*, Options().EdgesAsBonds().RaiseCharges()*/) - : ""; - std::string constraints = - getUniqueFilePrefix() + "r_" + boost::lexical_cast(r.getId()) + "_constraints.tex"; - { - post::FileHandle s(constraints); - auto printer = lib::IO::MatchConstraint::Write::makeTexPrintVisitor(s, get_left(r.getDPORule())); - for(const auto &c : r.getDPORule().leftMatchConstraints) { - c->accept(printer); - s << "\n"; - } - } - IO::post() << "summaryRule \"" << r.getName() << "\" \"" << graphLike << "\" \"" << molLike << "\" \"" << combined - << "\" \"" << constraints << "\"" << std::endl; - if(molLike.empty()) - return std::make_pair(graphLike, graphLike); - else - return std::make_pair(graphLike, molLike); -} - -void termState(const lib::Rules::Real &r) { - using Vertex = lib::Rules::Vertex; - using Edge = lib::Rules::Edge; - using Membership = lib::Rules::Membership; - using namespace lib::Term; - IO::post() << "summarySubsection \"Term State for " << r.getName() << "\"" << std::endl; - post::FileHandle s(getUniqueFilePrefix() + "termState.tex"); - s << "\\begin{verbatim}\n"; - const auto &termState = r.getTermState(); - if(isValid(termState)) { - std::unordered_map > > addrToVertex; - std::unordered_map > > addrToEdge; - std::unordered_map > addrToConstraintInfo; - auto insertVertex = [&addrToVertex](std::size_t addr, Vertex v, Membership membership) { - Address a{AddressType::Heap, addr}; - addrToVertex[a].insert({v, membership}); - }; - auto insertEdge = [&addrToEdge](std::size_t addr, Edge e, Membership membership) { - Address a{AddressType::Heap, addr}; - addrToEdge[a].insert({e, membership}); - }; - for(Vertex v : asRange(vertices(r.getGraph()))) { - switch(r.getGraph()[v].membership) { - case Membership::Left: - insertVertex(termState.getLeft()[v], v, Membership::Left); - break; - case Membership::Right: - insertVertex(termState.getRight()[v], v, Membership::Right); - break; - case Membership::Context: - insertVertex(termState.getLeft()[v], v, Membership::Left); - insertVertex(termState.getRight()[v], v, Membership::Right); - break; - } - } - for(Edge e : asRange(edges(r.getGraph()))) { - switch(r.getGraph()[e].membership) { - case Membership::Left: - insertEdge(termState.getLeft()[e], e, Membership::Left); - break; - case Membership::Right: - insertEdge(termState.getRight()[e], e, Membership::Right); - break; - case Membership::Context: - insertEdge(termState.getLeft()[e], e, Membership::Left); - insertEdge(termState.getRight()[e], e, Membership::Right); - break; - } - } - - struct Visitor : lib::GraphMorphism::Constraints::AllVisitor { - Visitor(std::unordered_map > &addrMap, const lib::Rules::GraphType &g) - : addrMap(addrMap), g(g) {} - - virtual void - operator()(const lib::GraphMorphism::Constraints::VertexAdjacency &c) override { - const auto vStr = boost::lexical_cast(get(boost::vertex_index_t(), g, c.vConstrained)); - for(const auto a : c.vertexTerms) { - Address addr{AddressType::Heap, a}; - std::string msg = "VertexAdj(" + vStr + ", " + side + ", V)"; - addrMap[addr].insert(std::move(msg)); - } - for(const auto a : c.edgeTerms) { - Address addr{AddressType::Heap, a}; - std::string msg = "VertexAdj(" + vStr + ", " + side + ", E)"; - addrMap[addr].insert(std::move(msg)); - } - } - - virtual void - operator()(const lib::GraphMorphism::Constraints::ShortestPath &c) override {} - public: - std::unordered_map > &addrMap; - const lib::Rules::GraphType &g; - std::string side; - }; - Visitor vis(addrToConstraintInfo, r.getGraph()); - vis.side = "L"; - for(const auto &c : r.getDPORule().leftMatchConstraints) - c->accept(vis); - vis.side = "R"; - for(const auto &c : r.getDPORule().rightMatchConstraints) - c->accept(vis); - - lib::IO::Term::Write::wam(getMachine(termState), lib::Term::getStrings(), s, [&](Address addr, std::ostream &s) { - s << " "; - bool first = true; - for(auto vm : addrToVertex[addr]) { - if(!first) s << ", "; - first = false; - s << "v(" << get(boost::vertex_index_t(), r.getGraph(), vm.first) << ", "; - switch(vm.second) { - case Membership::Left: - s << "L"; - break; - case Membership::Right: - s << "R"; - break; - case Membership::Context: - s << "K"; - break; - } - s << ")"; - } - for(auto em : addrToEdge[addr]) { - if(!first) s << ", "; - first = false; - s << "e(" - << get(boost::vertex_index_t(), r.getGraph(), source(em.first, r.getGraph())) - << ", " - << get(boost::vertex_index_t(), r.getGraph(), target(em.first, r.getGraph())) - << ", "; - switch(em.second) { - case Membership::Left: - s << "L"; - break; - case Membership::Right: - s << "R"; - break; - case Membership::Context: - s << "K"; - break; - } - s << ")"; - } - for(auto &msg : addrToConstraintInfo[addr]) { - if(!first) s << ", "; - first = false; - s << msg; - } - }); - } else { - std::string msg = "Parsing failed for rule '" + r.getName() + "'. " + termState.getParsingError(); - throw TermParsingError(std::move(msg)); - } - s << "\\end{verbatim}\n"; - IO::post() << "summaryInput \"" << std::string(s) << "\"" << std::endl; -} - -} // namespace mod::lib::IO::Rules::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/Stereo.hpp b/libs/libmod/src/mod/lib/IO/Stereo.hpp deleted file mode 100644 index 6fb2433..0000000 --- a/libs/libmod/src/mod/lib/IO/Stereo.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MOD_LIB_IO_STEREO_HPP -#define MOD_LIB_IO_STEREO_HPP - -#include -#include - -#include -#include -#include - -namespace mod::lib::Rules { -struct PropStereoCore; -} // namespace mod::lib::Rules -namespace mod::lib::Stereo { -struct Configuration; -struct GeometryGraph; -} // namespace mod::lib::Stereo -namespace mod::lib::IO::Stereo::Read { -using ParsedEmbeddingEdge = std::variant; - -struct ParsedEmbedding { - std::optional geometry; - std::optional > edges; - std::optional fixation; -}; - -lib::IO::Result parseEmbedding(const std::string &str); - -} // namesapce mod::lib::IO::Stereo::Read -namespace mod::lib::IO::Stereo::Write { - -std::string summary(const lib::Graph::Single &g, lib::Graph::Vertex v, const lib::Stereo::Configuration &conf, - const IO::Graph::Write::Options &options, int shownIdOffset, const std::string &nameSuffix); -std::string summary(const lib::Rules::Real &r, lib::Rules::Vertex v, lib::Rules::Membership m, - const IO::Graph::Write::Options &options); - -// old/new delimiter - -void summary(const lib::Stereo::GeometryGraph &g); - -} // namesapce mod::lib::IO::Stereo::Write - -#endif // MOD_LIB_IO_STEREO_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/StereoWrite.cpp b/libs/libmod/src/mod/lib/IO/StereoWrite.cpp deleted file mode 100644 index 3739d83..0000000 --- a/libs/libmod/src/mod/lib/IO/StereoWrite.cpp +++ /dev/null @@ -1,581 +0,0 @@ -#include "Stereo.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -namespace mod::lib::Stereo { -namespace { - -#define MOD_anyPrintCoords() \ - Configuration::printCoords(s, vIds); \ - if(vIds.size() == 1) return; \ - auto centerId = vIds.back(); \ - double angle = 360.0 / (vIds.size() - 1); \ - for(std::size_t i = 0; i < vIds.size() - 1; i++) \ - printSateliteCoord(s, centerId, angle * i + 90, vIds[i]); - -void printSateliteCoord(std::ostream &s, std::size_t centerId, double angle, std::size_t id) { - s << "\\coordinate[overlay, at=($(v-coord-" << centerId << ")+(" << angle << ":1)"; - s << "$)] (v-coord-" << id << ") {};" << std::endl; -} - -} // namespace - -// Configuration -//------------------------------------------------------------------------------ - -IO::Graph::Write::EdgeFake3DType Configuration::getEdgeDepiction(std::size_t i) const { - return IO::Graph::Write::EdgeFake3DType::None; -} - -void Configuration::printCoords(std::ostream &s, const std::vector &vIds) const { - const std::string &name = getGeometryGraph().getGraph()[getGeometryVertex()].name; - s << "\\coordinate[overlay] (v-coord-" << vIds.back() << ") {};\n"; - s << "\\node[at=($(v-coord-" << vIds.back() << ")+(-90:1)+(-90:12pt)$)] {" << name << "};\n"; -} - -std::string Configuration::getEdgeAnnotation(std::size_t i) const { - return ""; -} - -// Any -//------------------------------------------------------------------------------ - -void Any::printCoords(std::ostream &s, const std::vector &vIds) const { - MOD_anyPrintCoords(); -} - -// Linear -//------------------------------------------------------------------------------ - -void Linear::printCoords(std::ostream &s, const std::vector &vIds) const { - Configuration::printCoords(s, vIds); - printSateliteCoord(s, vIds.back(), 180, vIds[0]); - printSateliteCoord(s, vIds.back(), 0, vIds[1]); -} - -// TrigonalPlanar -//------------------------------------------------------------------------------ - -void TrigonalPlanar::printCoords(std::ostream &s, const std::vector &vIds) const { - Configuration::printCoords(s, vIds); - printSateliteCoord(s, vIds.back(), 180, vIds[0]); - printSateliteCoord(s, vIds.back(), 300, vIds[1]); - printSateliteCoord(s, vIds.back(), 60, vIds[2]); -} - -std::string TrigonalPlanar::getEdgeAnnotation(std::size_t i) const { - if(fixed) return ""; - else return "?"; -} - -// Tetrahedral -//------------------------------------------------------------------------------ - -IO::Graph::Write::EdgeFake3DType Tetrahedral::getEdgeDepiction(std::size_t i) const { - switch(i) { - case 0: - case 1: - return lib::IO::Graph::Write::EdgeFake3DType::None; - case 2: - return lib::IO::Graph::Write::EdgeFake3DType::WedgeSL; - case 3: - return lib::IO::Graph::Write::EdgeFake3DType::HashSL; - default: - MOD_ABORT; - } -} - -void Tetrahedral::printCoords(std::ostream &s, const std::vector &vIds) const { - Configuration::printCoords(s, vIds); - printSateliteCoord(s, vIds.back(), 90, vIds[0]); - printSateliteCoord(s, vIds.back(), 210, vIds[1]); - printSateliteCoord(s, vIds.back(), -60, vIds[2]); - printSateliteCoord(s, vIds.back(), -10, vIds[3]); -} - -std::string Tetrahedral::getEdgeAnnotation(std::size_t i) const { - if(fixed) return ""; - else return "?"; -} - -} // namespace mod::lib::Stereo -namespace mod::lib::IO::Stereo::Read { -} // namesapce mod::lib::IO::Stereo::Read -namespace mod::lib::IO::Stereo::Write { -namespace { - -template -std::string coords(const GraphInner &gStereo, const lib::Stereo::Configuration &conf, const std::string &name, - std::map::vertex_descriptor, int> &vMap) { - post::FileHandle s(getUniqueFilePrefix() + name + "_coord.tex"); - std::vector vIds(num_vertices(gStereo)); - using SVertex = typename boost::graph_traits::vertex_descriptor; - for(SVertex vStereo : asRange(vertices(gStereo))) { - auto vId = get(boost::vertex_index_t(), gStereo, vStereo); - auto iter = vMap.find(vStereo); - assert(iter != end(vMap)); - if(iter->second == -1) vIds[vIds.size() - 1] = vId; - else vIds[iter->second] = vId; - } - conf.printCoords(s, vIds); - return s; -} - -template -std::pair -tikz(const GraphPrint &g, typename boost::graph_traits::vertex_descriptor v, - const lib::Stereo::Configuration &conf, const std::string &name, const Depict &depict, - const IO::Graph::Write::Options &options, ShownId shownId) { - const bool printLonePairs = true; - using GVertex = typename boost::graph_traits::vertex_descriptor; - using GEdge = typename boost::graph_traits::edge_descriptor; - using GraphStereo = lib::Graph::GraphType; - using SVertex = lib::Graph::Vertex; - using SEdge = lib::Graph::Edge; - - // we make a new graph with copies and then the extra lone pairs - GraphStereo gStereo; - std::map vMap; // the order id, though -1 => the center - SVertex vCenter = add_vertex(gStereo); - vMap[vCenter] = -1; - std::map, IO::Graph::Write::EdgeFake3DType> edgeDepiction; - for(std::size_t i = 0; i < conf.degree(); ++i) { - SVertex vStereo = add_vertex(gStereo); - vMap[vStereo] = i; - add_edge(vCenter, vStereo, gStereo); - auto edge3Dtype = conf.getEdgeDepiction(i); - edgeDepiction[std::make_pair(vCenter, vStereo)] = edge3Dtype; - edgeDepiction[std::make_pair(vStereo, vCenter)] = IO::Graph::Write::invertEdgeFake3DType(edge3Dtype); - } - - std::string coordFile = coords(gStereo, conf, name, vMap); - post::FileHandle s(getUniqueFilePrefix() + name + ".tex"); - - struct DepictorAndAdvOptions { - DepictorAndAdvOptions(const GraphPrint &gOuter, GVertex vOuterCenter, const lib::Graph::GraphType &g, - const Depict &depict, bool printLonePairs, const std::map &vMap, - const lib::Stereo::Configuration &conf, - const std::map, IO::Graph::Write::EdgeFake3DType> &edgeDepiction, - ShownId shownId) - : gOuter(gOuter), vOuterCenter(vOuterCenter), - nullVertexOuter(boost::graph_traits::null_vertex()), gInner(g), - depict(depict), printLonePairs(printLonePairs), vMap(vMap), conf(conf), edgeDepiction(edgeDepiction), - shownId(shownId) {} - - GVertex getOuterVertexFromInnerVertex(SVertex vInner) const { - auto iter = vMap.find(vInner); - assert(iter != end(vMap)); - if(iter->second == -1) return vOuterCenter; - const auto &emb = conf.begin()[iter->second]; - if(emb.type != lib::Stereo::EmbeddingEdge::Type::Edge) return nullVertexOuter; - auto eOuter = emb.getEdge(vOuterCenter, gOuter); - return target(eOuter, gOuter); - } - - GEdge getOuterEdgeFromInnerEdge(SEdge eInner) const { - GVertex vSrcOuter = getOuterVertexFromInnerVertex(source(eInner, gInner)); - GVertex vTarOuter = getOuterVertexFromInnerVertex(target(eInner, gInner)); - assert(vSrcOuter != nullVertexOuter); - assert(vTarOuter != nullVertexOuter); - auto p = edge(vSrcOuter, vTarOuter, gOuter); - assert(p.second); - return p.first; - } - - unsigned char getAtomId(SVertex vInner) const { - GVertex vOuter = getOuterVertexFromInnerVertex(vInner); - if(vOuter == nullVertexOuter) return AtomIds::Invalid; - else return depict.getAtomId(vOuter); - } - - Isotope getIsotope(SVertex vInner) const { - GVertex vOuter = getOuterVertexFromInnerVertex(vInner); - if(vOuter == nullVertexOuter) return Isotope(); - else return depict.getIsotope(vOuter); - } - - char getCharge(SVertex vInner) const { - GVertex vOuter = getOuterVertexFromInnerVertex(vInner); - if(vOuter == nullVertexOuter) return 0; - else return depict.getCharge(vOuter); - } - - bool getRadical(SVertex vInner) const { - GVertex vOuter = getOuterVertexFromInnerVertex(vInner); - if(vOuter == nullVertexOuter) return false; - else return depict.getRadical(vOuter); - } - - BondType getBondData(SEdge eInner) const { - GVertex vSrcOuter = getOuterVertexFromInnerVertex(source(eInner, gInner)); - GVertex vTarOuter = getOuterVertexFromInnerVertex(target(eInner, gInner)); - if(vSrcOuter == nullVertexOuter || vTarOuter == nullVertexOuter) return BondType::Invalid; - else return depict.getBondData(getOuterEdgeFromInnerEdge(eInner)); - } - - AtomData operator()(SVertex v) const { - GVertex vOuter = getOuterVertexFromInnerVertex(v); - if(vOuter == nullVertexOuter) return AtomData(); - else return depict(vOuter); - } - - BondType operator()(SEdge eInner) const { - GVertex vSrcOuter = getOuterVertexFromInnerVertex(source(eInner, gInner)); - GVertex vTarOuter = getOuterVertexFromInnerVertex(target(eInner, gInner)); - if(vSrcOuter == nullVertexOuter || vTarOuter == nullVertexOuter) return BondType::Invalid; - else return depict(getOuterEdgeFromInnerEdge(eInner)); - } - - std::string getVertexLabelNoIsotopeChargeRadical(SVertex vInner) const { - GVertex vOuter = getOuterVertexFromInnerVertex(vInner); - if(vOuter != nullVertexOuter)return depict.getVertexLabelNoIsotopeChargeRadical(vOuter); - auto iter = vMap.find(vInner); - assert(iter != end(vMap)); - assert(iter->second != -1); - const auto &emb = conf.begin()[iter->second]; - switch(emb.type) { - case lib::Stereo::EmbeddingEdge::Type::Edge: - MOD_ABORT; - case lib::Stereo::EmbeddingEdge::Type::LonePair: - return "e"; - case lib::Stereo::EmbeddingEdge::Type::Radical: - return "r"; - } - MOD_ABORT; - } - - std::string getEdgeLabel(SEdge eInner) const { - GVertex vSrcOuter = getOuterVertexFromInnerVertex(source(eInner, gInner)); - GVertex vTarOuter = getOuterVertexFromInnerVertex(target(eInner, gInner)); - if(vSrcOuter == nullVertexOuter || vTarOuter == nullVertexOuter) return ""; - else return depict.getEdgeLabel(getOuterEdgeFromInnerEdge(eInner)); - } - - bool hasImportantStereo(SVertex vInner) const { - return true; - } - - bool getHasCoordinates() const { - return false; - } - - double getX(SVertex v, bool b) const { - return 0; - } - - double getY(SVertex v, bool b) const { - return 0; - } - - bool isVisible(SVertex v) const { - if(printLonePairs) return true; - else - MOD_ABORT; - return true; - } - - std::string getColour(SVertex) const { - return ""; - } - - std::string getColour(SEdge) const { - return ""; - } - - std::string getShownId(SVertex vInner) const { - GVertex vOuter = getOuterVertexFromInnerVertex(vInner); - if(vOuter == nullVertexOuter) MOD_ABORT; - else return boost::lexical_cast(shownId(gOuter, vOuter)); - } - - bool overwriteWithIndex(SVertex vInner) const { - auto vOuter = getOuterVertexFromInnerVertex(vInner); - return vOuter == nullVertexOuter; - } - - IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(SEdge eInner, bool withHydrogen) const { - auto iter = edgeDepiction.find(std::make_pair(source(eInner, gInner), target(eInner, gInner))); - assert(iter != end(edgeDepiction)); - return iter->second; - } - - std::string getRawStereoString(SVertex vInner) const { - return ""; - } - - std::string getPrettyStereoString(SVertex vInner) const { - return ""; - } - - std::string getStereoString(SEdge eInner) const { - return ""; - } - - std::string getEdgeAnnotation(SEdge eInner) const { - SVertex vSrcInner = source(eInner, gInner), vTarInner = target(eInner, gInner); - auto iterSrc = vMap.find(vSrcInner), iterTar = vMap.find(vTarInner); - assert(iterSrc != end(vMap)); - assert(iterTar != end(vMap)); - assert(iterSrc->second == -1 || iterTar->second == -1); - bool swapped = false; - if(iterSrc->second != -1) { - std::swap(vSrcInner, vTarInner); - std::swap(iterSrc, iterTar); - swapped = true; - } - std::string res; - // if the edge category does not correspond to the bond type, then print it - const lib::Stereo::EmbeddingEdge &emb = conf.begin()[iterTar->second]; - if(emb.type == lib::Stereo::EmbeddingEdge::Type::Edge) { - auto eOuter = getOuterEdgeFromInnerEdge(eInner); - auto eCatFromBt = lib::Stereo::bondTypeToEdgeCategory(depict.getBondData(eOuter)); - if(eCatFromBt != emb.cat) { - res += " node[auto] {"; - res += boost::lexical_cast(emb.cat); - res += "}"; - } - } - - // print the offset and whatever the configuration wants - res += " node[auto, pos="; - if(swapped) res += "0.25"; - else res += "0.75"; - res += "] {{\\tiny "; - res += boost::lexical_cast(iterTar->second); - res += conf.getEdgeAnnotation(iterTar->second); - res += "}} "; - return res; - } - - bool disallowCollapse(SVertex) const { - return false; - } - - bool disallowCollapse(SEdge) const { - return false; - } - - std::string getOpts(SVertex v) const { - return std::string(); - } - public: - unsigned int idOffset = 0; - private: - const GraphPrint &gOuter; - GVertex vOuterCenter, nullVertexOuter; - const lib::Graph::GraphType &gInner; - const Depict &depict; - bool printLonePairs; - const std::map &vMap; - const lib::Stereo::Configuration &conf; - const std::map, IO::Graph::Write::EdgeFake3DType> &edgeDepiction; - ShownId shownId; - } depictAndAdvOptions(g, v, gStereo, depict, printLonePairs, vMap, conf, edgeDepiction, shownId); - auto bonusWriter = [&](std::ostream &s) { - }; - lib::IO::Graph::Write::tikz(s, options, gStereo, depictAndAdvOptions, coordFile, depictAndAdvOptions, bonusWriter, - ""); - return std::pair(s, coordFile); -} - -template -std::string pdf(const Graph &g, typename boost::graph_traits::vertex_descriptor v, - const lib::Stereo::Configuration &conf, const std::string &name, const Depict &depict, - const IO::Graph::Write::Options &options, ShownId shownId) { - const auto p = tikz(g, v, conf, name, depict, options, shownId); - std::string fileTikz = p.first, fileCoords = p.second; - std::string fileNoExt = fileTikz.substr(0, fileTikz.size() - 4); - std::string fileCoordsNoExt = fileCoords.substr(0, fileCoords.size() - 4); - IO::post() << "compileTikz \"" << fileNoExt << "\" \"" << fileCoordsNoExt << "\"" << std::endl; - return fileNoExt + ".pdf"; -} - -} // namespace - -std::string summary(const lib::Graph::Single &gLib, lib::Graph::Vertex v, const lib::Stereo::Configuration &conf, - const IO::Graph::Write::Options &options, int shownIdOffset, const std::string &nameSuffix) { - const auto &g = gLib.getGraph(); - const auto vId = get(boost::vertex_index_t(), g, v); - std::string name = "g_" + boost::lexical_cast(gLib.getId()) + "_stereo_" + - boost::lexical_cast(vId); - IO::post() << "summarySubsection \"Stereo, g " << gLib.getId() << ", v " << vId - << nameSuffix << "\"\n"; - std::string f = pdf(g, v, conf, name, gLib.getDepictionData(), options, [shownIdOffset](const auto &g, const auto v) { - return get(boost::vertex_index_t(), g, v) + shownIdOffset; - }); - post::FileHandle s(getUniqueFilePrefix() + "stereo.tex"); - s << "\\begin{center}\n"; - s << "\\includegraphics{" << f << "}\\\\\n"; - s << "File: \\texttt{" << escapeForLatex(f) << "}\n"; - s << "\\end{center}\n"; - IO::post() << "summaryInput \"" << std::string(s) << "\"\n"; - return f; -} - -std::string summary(const lib::Rules::Real &r, lib::Rules::Vertex v, lib::Rules::Membership m, - const IO::Graph::Write::Options &options) { - assert(m != lib::Rules::Membership::Context); - if(m == lib::Rules::Membership::Left) assert(membership(r.getDPORule(), v) != lib::Rules::Membership::Right); - if(m == lib::Rules::Membership::Right) assert(membership(r.getDPORule(), v) != lib::Rules::Membership::Left); - const std::string side = m == lib::Rules::Membership::Left ? "L" : "R"; - const auto &g = get_graph(r.getDPORule()); - std::string name = "r_" + std::to_string(r.getId()) + "_" + side + "_stereo_" + - std::to_string(get(boost::vertex_index_t(), g, v)); - IO::post() << "summarySubsection \"Stereo, r " << r.getId() << ", v " << get(boost::vertex_index_t(), g, v) << " " - << side << "\"" << std::endl; - const auto handler = [&](const auto &gLabelled, const auto &depict) { - const auto &g = get_graph(gLabelled); - return pdf(g, v, *get_stereo(gLabelled)[v], name, depict, options, [](const auto &g, const auto v) { - return get(boost::vertex_index_t(), g, v); - }); - }; - std::string f = m == lib::Rules::Membership::Left - ? handler(get_labelled_left(r.getDPORule()), r.getDepictionData().getLeft()) - : handler(get_labelled_right(r.getDPORule()), r.getDepictionData().getRight()); - post::FileHandle s(getUniqueFilePrefix() + "stereo.tex"); - s << "\\begin{center}\n"; - s << "\\includegraphics{" << f << "}\\\\\n"; - s << "File: \\texttt{" << escapeForLatex(f) << "}\n"; - s << "\\end{center}\n"; - IO::post() << "summaryInput \"" << std::string(s) << "\"\n"; - return f; -} - -//------------------------------------------------------------------------------ - -void summary(const lib::Stereo::GeometryGraph &graph) { - const auto &g = graph.getGraph(); - const auto n = num_vertices(g); - std::string fileDot = [&]() { - post::FileHandle s(IO::getUniqueFilePrefix() + "geometryGraph.dot"); - s << "digraph g {\nrankdir=LR;\n"; - for(auto v : asRange(vertices(g))) { - s << get(boost::vertex_index_t(), g, v) << " [ label=\"" << g[v].name << "\" ];\n"; - } - for(auto e : asRange(edges(g))) { - auto vSrc = source(e, g); - auto vTar = target(e, g); - s << get(boost::vertex_index_t(), g, vSrc) << " -> " << get(boost::vertex_index_t(), g, vTar); - s << " [];\n"; - } - for(std::size_t i = 0; i < graph.chemValids.size(); ++i) { - auto &cv = graph.chemValids[i]; - s << (i + n) << " [ label=\"" << lib::Chem::symbolFromAtomId(cv.atomId); - if(cv.charge != 0) { - if(cv.charge > 0) s << "+"; - else s << "-"; - if(std::abs(cv.charge) != 1) s << std::abs(cv.charge); - } - if(cv.catCount.sum() > 0) s << ", " << cv.catCount; - if(cv.lonePair > 0) s << ", e = " << cv.lonePair; - s << "\" ];\n"; - s << get(boost::vertex_index_t(), g, cv.geometry) << " -> " << (i + n) << " [];\n"; - } - s << "}\n"; - std::string f = s; - return std::string(f.begin(), f.end() - 4); - }(); - IO::post() << "coordsFromGV dgHyperDot \"" << fileDot << "\"\n"; - std::string fileCoords = fileDot + "_coord"; - std::string fileFig = [&]() { - post::FileHandle s(IO::getUniqueFilePrefix() + "geometryGraph.tex"); - s << "\\begin{tikzpicture}[scale=\\modDGHyperScale]\n"; - s << "\\input{" << fileCoords << ".tex}\n"; - for(auto v : asRange(vertices(g))) { - auto vId = get(boost::vertex_index_t(), g, v); - s << "\\node[draw] (v-" << vId << ") at (v-coord-" << vId << "){"; - s << g[v].name; - s << "};\n"; - } - for(auto e : asRange(edges(g))) { - auto vSrc = source(e, g); - auto vTar = target(e, g); - s << "\\path[draw, ->, >=stealth] (v-" << get(boost::vertex_index_t(), g, vSrc) - << ") to (v-" << get(boost::vertex_index_t(), g, vTar) - << ");\n"; - } - for(std::size_t i = 0; i < graph.chemValids.size(); ++i) { - auto &cv = graph.chemValids[i]; - s << "\\node[draw, ellipse] (v-" << (i + n) << ") at (v-coord-" << (i + n) << "){"; - s << lib::Chem::symbolFromAtomId(cv.atomId); - if(cv.charge != 0) { - s << "$^{"; - if(cv.charge > 0) s << "+"; - else s << "-"; - if(std::abs(cv.charge) != 1) s << std::abs(cv.charge); - s << "}$"; - } - bool hasMore = cv.catCount.sum() > 0 || cv.lonePair > 0; - if(hasMore) s << "$"; - if(cv.catCount.sum() > 0) s << ", " << cv.catCount; - if(cv.lonePair > 0) s << ", e = " << cv.lonePair; - if(hasMore) s << "$"; - s << "};\n"; - s << "\\path[draw] (v-" << get(boost::vertex_index_t(), g, cv.geometry) << ") to (v-" << (i + n) << ");\n"; - } - s << "\\end{tikzpicture}"; - std::string f = s; - return std::string(f.begin(), f.end() - 4); - }(); - std::string fileTex = [&]() { - post::FileHandle s(IO::getUniqueFilePrefix() + "geometryGraphSummary.tex"); - s - << "\\begin{center}\n" - << "\\includegraphics{" << fileFig << "}\n\n" - << "File: \\texttt{" << escapeForLatex(fileFig) << "}\n" - << "\\end{center}\n" - << "\n" - << "\\section{Stereo, Chemically Valid Configurations}\n" - << "\\begin{center}\n" - << "\\begin{longtable}{@{}lllllll@{}}\n" - << "\\toprule\n" - << "Atom & S & D & T & A & LP & Geometry\\\\\n" - << "\\midrule\n"; - for(std::size_t i = 0; i < graph.chemValids.size(); ++i) { - auto &cv = graph.chemValids[i]; - s << lib::Chem::symbolFromAtomId(cv.atomId); - if(cv.charge != 0) { - s << "$^{"; - if(cv.charge > 0) s << "+"; - else s << "-"; - if(std::abs(cv.charge) != 1) s << std::abs(cv.charge); - s << "}$"; - } - for(auto cat :{lib::Stereo::EdgeCategory::Single, lib::Stereo::EdgeCategory::Double, - lib::Stereo::EdgeCategory::Triple, lib::Stereo::EdgeCategory::Aromatic}) { - s << " & "; - if(auto count = cv.catCount[cat]) { - s << int(count); - } - } - s << " & "; - if(cv.lonePair > 0) s << cv.lonePair; - s << " & " << g[cv.geometry].name << " \\\\\n"; - } - s - << "\\bottomrule\n" - << "\\end{longtable}\n" - << "\\end{center}\n"; - return std::string(s); - }(); - - IO::post() << "compileTikz \"" << fileFig << "\" \"" << fileCoords << "\"" << std::endl; - IO::post() << "summarySection \"Stereo, Geometry Graph\"" << std::endl; - IO::post() << "summaryInput \"" << fileTex << "\"" << std::endl; -} - -} // namesapce mod::lib::IO::Stereo::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/Term.cpp b/libs/libmod/src/mod/lib/IO/Term.cpp deleted file mode 100644 index 6269894..0000000 --- a/libs/libmod/src/mod/lib/IO/Term.cpp +++ /dev/null @@ -1,306 +0,0 @@ -#include "Term.hpp" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include - -#include - -namespace mod::lib::IO::Term::Read { -namespace detail { - -struct Structure; - -struct Variable { - std::string name; -}; - -struct Term : x3::variant> { - using base_type::base_type; - using base_type::operator=; -}; - -struct Structure { - std::string name; - std::vector arguments; -}; - -namespace { -namespace parser { -#define FIRST "A-Za-z0-9=#:.+-" -#define SECOND "_" - -const x3::rule term = "term"; -const x3::rule function = "function"; -const x3::rule > termList = "term list"; -const x3::rule variable = "variable"; -const x3::rule identifier = "identifier"; - -const auto term_def = function | variable; -const auto function_def = identifier >> -('(' > termList > ')'); -const auto termList_def = term % ','; -const auto variable_def = (x3::lexeme['_' > identifier] >> x3::eps) | x3::string("*"); -const auto identifier_def = x3::lexeme[x3::char_(FIRST) > *x3::char_(SECOND FIRST)]; - -BOOST_SPIRIT_DEFINE(term, function, termList, variable, identifier) -} // namespace parser -} // namespace - -struct Converter : public boost::static_visitor { - Converter(const Converter &) = delete; - Converter &operator=(const Converter &) = delete; - - Converter(const StringStore &stringStore) : stringStore(stringStore) {} - - lib::Term::RawTerm operator()(const Variable &var) { - std::size_t stringId; - if(var.name == "*") { - for(;; ++nextVar) { - std::string name = "X" + std::to_string(nextVar) + "_"; - if(!stringStore.hasString(name)) { - stringId = stringStore.getIndex(name); - break; - } - } - } else { - stringId = stringStore.getIndex(var.name); - } - return lib::Term::RawVariable{stringId}; - } - - lib::Term::RawTerm operator()(const Structure &str) { - auto stringId = stringStore.getIndex(str.name); - std::vector arguments; - for(const auto &a : str.arguments) - arguments.push_back(boost::apply_visitor(*this, a)); - return lib::Term::RawStructure{stringId, std::move(arguments)}; - } -private: - const StringStore &stringStore; - std::size_t nextVar = 0; -}; - -} // namespace detail - -lib::Term::RawTerm rawTerm(const std::string &data, const StringStore &stringStore) { - detail::Term term; - parse(data.begin(), data.end(), detail::parser::term, term, x3::space); - detail::Converter converter(stringStore); - return boost::apply_visitor(converter, term); -} - -} // namespace mod::lib::IO::Term::Read -namespace mod::lib::IO::Term::Write { -namespace { - -std::ostream &rawVarFromCell(std::ostream &s, lib::Term::Cell cell) { - using namespace lib::Term; - assert(cell.tag == Cell::Tag::REF); - switch(cell.REF.addr.type) { - case AddressType::Heap: - return s << "_H" << cell.REF.addr.addr; - case AddressType::Temp: - return s << "_T" << cell.REF.addr.addr; - } - MOD_ABORT; -} - -} // namespace - -std::ostream &rawTerm(const lib::Term::RawTerm &term, const StringStore &strings, std::ostream &s) { - struct Printer { - void operator()(lib::Term::RawVariable v) const { - s << "_" << strings.getString(v.name); - } - - void operator()(const lib::Term::RawStructure &str) const { - s << strings.getString(str.name); - if(!str.args.empty()) { - s << '('; - std::visit(*this, str.args.front()); - for(int i = 1; i != str.args.size(); i++) { - s << ", "; - std::visit(*this, str.args[i]); - } - s << ')'; - } - } - public: - std::ostream &s; - const StringStore &strings; - }; - std::visit(Printer{s, strings}, term); - return s; -} - -std::ostream &element(lib::Term::Cell cell, const StringStore &strings, std::ostream &s) { - using namespace lib::Term; - switch(cell.tag) { - case Cell::Tag::STR: - return s << "STR " << cell.STR.addr; - case Cell::Tag::Structure: - s << strings.getString(cell.Structure.name); - if(cell.Structure.arity > 0) - s << "/" << cell.Structure.arity; - return s; - case Cell::Tag::REF: - return s << "REF " << cell.REF.addr; - } - __builtin_unreachable(); -} - -void wam(const lib::Term::Wam &machine, const StringStore &strings, std::ostream &s) { - wam(machine, strings, s, [](lib::Term::Address, std::ostream &) {}); -} - -void wam(const lib::Term::Wam &machine, const StringStore &strings, std::ostream &s, - std::function addressCallback) { - using namespace lib::Term; - s << "Heap:" << std::endl; - for(std::size_t i = 0; i < machine.getHeap().size(); i++) { - Cell cell = machine.getHeap()[i]; - s << std::setw(5) << std::left << i; - element(cell, strings, s); - addressCallback({AddressType::Heap, i}, s); - s << std::endl; - } - s << "-------------------------------------------------" << std::endl; - s << "Temp:" << std::endl; - for(std::size_t i = 0; i < machine.getTemp().size(); i++) { - Cell cell = machine.getTemp()[i]; - s << std::setw(5) << std::left << i; - element(cell, strings, s); - addressCallback({AddressType::Temp, i}, s); - s << std::endl; - } - s << "-------------------------------------------------" << std::endl; -} - -std::ostream & -term(const lib::Term::Wam &machine, lib::Term::Address addr, const StringStore &strings, std::ostream &s) { - using namespace lib::Term; - - struct Printer { - Printer(const Wam &machine, const StringStore &strings, std::ostream &s) - : machine(machine), strings(strings), s(s) { - occurred[0].resize(machine.getHeap().size(), 0); - occurred[1].resize(machine.getTemp().size(), 0); - } - - void operator()(Address addr) { - Cell cell = machine.getCell(addr); - switch(cell.tag) { - case Cell::Tag::REF: - if(cell.REF.addr == addr - || occurred[static_cast(cell.REF.addr.type)][cell.REF.addr.addr] != 0 - ) { - rawVarFromCell(s, cell); - } else (*this)(cell.REF.addr); - break; - case Cell::Tag::STR: - (*this)(cell.STR.addr); - break; - case Cell::Tag::Structure: - if(occurred[static_cast(addr.type)][addr.addr] != 0) { - wam(machine, strings, std::cout); - std::cout << "addr.addr = " << addr.addr << std::endl; - std::cout << "occurred:" << std::endl; - for(int aType : {0, 1}) { - for(const auto o : occurred[aType]) { - if(o == 0) continue; - std::cout << " [" << aType << "]: " << o << std::endl; - } - } - } - assert(occurred[static_cast(addr.type)][addr.addr] == 0); - s << strings.getString(cell.Structure.name); - if(cell.Structure.arity > 0) { - ++occurred[static_cast(addr.type)][addr.addr]; - s << "("; - (*this)(addr + 1); - for(std::size_t i = 2; i <= cell.Structure.arity; i++) { - s << ", "; - (*this)(addr + i); - } - s << ")"; - --occurred[static_cast(addr.type)][addr.addr]; - } - break; - } - } - private: - const Wam &machine; - const StringStore &strings; - std::ostream &s; - std::array, 2> occurred; - }; - Printer(machine, strings, s)(addr); - return s; -} - -std::ostream & -mgu(const lib::Term::Wam &machine, const lib::Term::MGU &mgu, const StringStore &strings, std::ostream &s) { - using namespace lib::Term; - switch(mgu.status) { - case MGU::Status::Exists: - s << "Exists: "; - break; - case MGU::Status::Fail: - term(machine, mgu.errorLeft, strings, s << "Fail(") << " != "; - term(machine, mgu.errorRight, strings, s) << ")"; - return s; - } - bool first = true; - for(auto binding : mgu.bindings) { - if(binding.type == AddressType::Heap && binding.addr >= mgu.preHeapSize) continue; - if(!first) s << ", "; - first = false; - Cell cell; - cell.tag = Cell::Tag::REF; - cell.REF.addr = binding; - rawVarFromCell(s, cell) << " = "; - term(machine, binding, strings, s); - } - return s; -} - -} // namespace mod::lib::IO::Term::Write -namespace mod::lib::Term { - -std::ostream &operator<<(std::ostream &s, Address addr) { - switch(addr.type) { - case AddressType::Heap: - s << "H"; - break; - case AddressType::Temp: - s << "T"; - break; - } - return s << "[" << addr.addr << "]"; -} - -} // namespace mod::lib::Term - -BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::Term::Read::detail::Variable, - (std::string, name)) -BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::Term::Read::detail::Structure, - (std::string, name) - (std::vector, arguments)) \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/Term.hpp b/libs/libmod/src/mod/lib/IO/Term.hpp deleted file mode 100644 index b7362cf..0000000 --- a/libs/libmod/src/mod/lib/IO/Term.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MOD_LIB_IO_TERM_HPP -#define MOD_LIB_IO_TERM_HPP - -#include -#include - -#include -#include - -namespace mod::lib { -struct StringStore; -} // namespace mod::lib -namespace mod::lib::IO::Term::Read { -// throws lib::IO::ParsingError on error -lib::Term::RawTerm rawTerm(const std::string &data, const StringStore &stringStore); -} // namespace mod::lib::IO::Term::Read -namespace mod::lib::IO::Term::Write { -std::ostream &rawTerm(const lib::Term::RawTerm &term, const StringStore &strings, std::ostream &s); -std::ostream &element(lib::Term::Cell cell, const StringStore &strings, std::ostream &s); -void wam(const lib::Term::Wam &machine, const StringStore &strings, std::ostream &s); -void wam(const lib::Term::Wam &machine, const StringStore &strings, std::ostream &s, - std::function addressCallback); -std::ostream &term(const lib::Term::Wam &machine, lib::Term::Address addr, const StringStore &strings, std::ostream &s); -std::ostream &mgu(const lib::Term::Wam &machine, const lib::Term::MGU &mgu, const StringStore &strings, std::ostream &s); -} // namespace mod::lib::IO::Term::Write - -#endif // MOD_LIB_IO_TERM_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/LabelledGraph.hpp b/libs/libmod/src/mod/lib/LabelledGraph.hpp index 90b6178..4346563 100644 --- a/libs/libmod/src/mod/lib/LabelledGraph.hpp +++ b/libs/libmod/src/mod/lib/LabelledGraph.hpp @@ -10,6 +10,7 @@ struct LabelledGraphTraits { using GraphType = typename G::GraphType; using PropStringType = typename G::PropStringType; using PropTermType = typename G::PropTermType; + using PropStereoType = typename G::PropStereoType; }; template @@ -17,16 +18,16 @@ struct LabelledGraphConcept { using GraphType = typename LabelledGraphTraits::GraphType; using PropStringType = typename LabelledGraphTraits::PropStringType; using PropTermType = typename LabelledGraphTraits::PropTermType; + using PropStereoType = typename LabelledGraphTraits::PropStereoType; public: BOOST_CONCEPT_USAGE(LabelledGraphConcept) { const G &gOuterConst = gOuter; - const GraphType &gConst = get_graph(gOuterConst); - (void) gConst; - const PropStringType &pString = get_string(gOuterConst); - (void) pString; - const PropTermType &pTerm = get_term(gOuterConst); - (void) pTerm; + [[maybe_unused]] const GraphType &gConst = get_graph(gOuterConst); + [[maybe_unused]] const PropStringType &pString = get_string(gOuterConst); + [[maybe_unused]] const PropTermType &pTerm = get_term(gOuterConst); + [[maybe_unused]] const bool hasStereo = has_stereo(gOuterConst); + [[maybe_unused]] const PropStereoType &pStereo = get_stereo(gOuterConst); } private: G gOuter; diff --git a/libs/libmod/src/mod/lib/RC/Compose.hpp b/libs/libmod/src/mod/lib/RC/Compose.hpp index a799beb..8d46179 100644 --- a/libs/libmod/src/mod/lib/RC/Compose.hpp +++ b/libs/libmod/src/mod/lib/RC/Compose.hpp @@ -6,28 +6,28 @@ // The graphs may not have parallel edges. #include +#include #include #include -#include -#include +#include namespace mod::lib::RC { -template -using DPOTraits = jla_boost::GraphDPO::PushoutRuleTraits; - template -std::optional compose(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, VisitorT visitor) { +bool compose(Result &result, + const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + VisitorT visitor) { // match must be rSecond -> rFirst - using RuleResult = typename Result::RuleResult; using WrappedVisitor = Visitor::Compound; - BOOST_CONCEPT_ASSERT((jla_boost::GraphDPO::WritablePushoutRuleConcept)); - BOOST_CONCEPT_ASSERT((jla_boost::GraphDPO::PushoutRuleConcept)); - BOOST_CONCEPT_ASSERT((jla_boost::GraphDPO::PushoutRuleConcept)); + // TODO: enable these concepts again +// BOOST_CONCEPT_ASSERT((lib::DPO::WritableRuleConcept)); + BOOST_CONCEPT_ASSERT((lib::DPO::RuleConcept)); + BOOST_CONCEPT_ASSERT((lib::DPO::RuleConcept)); BOOST_CONCEPT_ASSERT((jla_boost::GraphMorphism::InvertibleVertexMapConcept)); return detail::CompositionHelper(rFirst, rSecond, match, Visitor::makeVisitor(std::move(visitor)))(); + InvertibleVertexMap, WrappedVisitor>(result, rFirst, rSecond, match, + Visitor::makeVisitor(std::move(visitor)))(); } } // namespace mod::lib::RC diff --git a/libs/libmod/src/mod/lib/RC/ComposeRuleReal.cpp b/libs/libmod/src/mod/lib/RC/ComposeRuleReal.cpp deleted file mode 100644 index 37a9d87..0000000 --- a/libs/libmod/src/mod/lib/RC/ComposeRuleReal.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "ComposeRuleReal.hpp" - -#include -#include -#include -#include -#include - -namespace mod::lib::RC { - -#define MOD_RC_COMPOSE_BY_MATCH_MAKER(MM) \ - void composeRuleRealByMatchMaker(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, \ - const MM &mm, std::function) > rr, LabelSettings labelSettings) { \ - composeRuleRealByMatchMakerGeneric(rFirst, rSecond, mm, rr, labelSettings); \ - } - -MOD_RC_COMPOSE_BY_MATCH_MAKER(Common) -MOD_RC_COMPOSE_BY_MATCH_MAKER(Parallel) -MOD_RC_COMPOSE_BY_MATCH_MAKER(Sub) -MOD_RC_COMPOSE_BY_MATCH_MAKER(Super) -#undef MOD_RC_COMPOSE_BY_MATCH_MAKER - -} // namespace mod::lib::RC \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/ComposeRuleReal.hpp b/libs/libmod/src/mod/lib/RC/ComposeRuleReal.hpp index c14ee45..73cd564 100644 --- a/libs/libmod/src/mod/lib/RC/ComposeRuleReal.hpp +++ b/libs/libmod/src/mod/lib/RC/ComposeRuleReal.hpp @@ -28,6 +28,12 @@ MOD_RC_COMPOSE_BY_MATCH_MAKER(Sub) MOD_RC_COMPOSE_BY_MATCH_MAKER(Super) #undef MOD_RC_COMPOSE_BY_MATCH_MAKER +#define MOD_RC_COMPOSE_BY_MATCH_MAKER_IMPL(MM) \ + void composeRuleRealByMatchMaker(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, \ + const MM &mm, std::function) > rr, LabelSettings labelSettings) { \ + composeRuleRealByMatchMakerGeneric(rFirst, rSecond, mm, rr, labelSettings); \ + } + } // namespace mod::lib::RC #endif // MOD_LIB_RC_COMPOSE_RULE_REAL_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/ComposeRuleRealCommon.cpp b/libs/libmod/src/mod/lib/RC/ComposeRuleRealCommon.cpp new file mode 100644 index 0000000..d398011 --- /dev/null +++ b/libs/libmod/src/mod/lib/RC/ComposeRuleRealCommon.cpp @@ -0,0 +1,10 @@ +#include "ComposeRuleReal.hpp" + +#include +#include + +namespace mod::lib::RC { + +MOD_RC_COMPOSE_BY_MATCH_MAKER_IMPL(Common) + +} // namespace mod::lib::RC \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/ComposeRuleRealGeneric.hpp b/libs/libmod/src/mod/lib/RC/ComposeRuleRealGeneric.hpp index 0120fe3..082c6a3 100644 --- a/libs/libmod/src/mod/lib/RC/ComposeRuleRealGeneric.hpp +++ b/libs/libmod/src/mod/lib/RC/ComposeRuleRealGeneric.hpp @@ -4,9 +4,9 @@ #include #include #include -#include -#include #include +#include +#include #include #include #include @@ -20,15 +20,23 @@ struct Sub; struct Super; template -auto composeRuleRealByMatch(const lib::Rules::Real &rFirst, - const lib::Rules::Real &rSecond, - InvertibleVertexMap &match) { - using Result = BaseResult; - auto visitor = Visitor::MatchConstraints(); - auto res = composeLabelled( - rFirst.getDPORule(), rSecond.getDPORule(), match, visitor); - if(res) res->rResult.initComponents(); // TODO: move to the visitor finalizer - return res; +std::optional composeRuleRealByMatch(const lib::Rules::Real &rFirst, + const lib::Rules::Real &rSecond, + InvertibleVertexMap &match) { + auto visitor = Visitor::MatchConstraints(rFirst.getDPORule(), rSecond.getDPORule()); + LabelledResult result(rFirst.getDPORule().getRule(), rSecond.getDPORule().getRule()); + const bool success = composeLabelled( + result, rFirst.getDPORule(), rSecond.getDPORule(), match, visitor); + if(success) { + assert(result.pString || result.pTerm); + auto rule = result.pString + ? lib::Rules::LabelledRule(std::move(result.rDPO), std::move(result.pString), + std::move(result.pStereo)) + : lib::Rules::LabelledRule(std::move(result.rDPO), std::move(result.pTerm), + std::move(result.pStereo)); + rule.leftData.matchConstraints = std::move(result.matchConstraints); + return rule; + } else return {}; } template @@ -47,7 +55,7 @@ std::unique_ptr composeRuleRealByMatch(const lib::Rules::Real if(verbose) logger.indent() << "Composition failed" << std::endl; return nullptr; } - auto rResult = std::make_unique(std::move(resultOpt->rResult), labelType); + auto rResult = std::make_unique(std::move(*resultOpt), labelType); if(verbose) logger.indent() << "Composition done, rNew is '" << rResult->getName() << "'" << std::endl; return rResult; @@ -74,7 +82,7 @@ struct MatchMakerCallback { << "\t= " << rFirst.getName() << "\t. " << rSecond.getName() << std::endl; if(getConfig().rc.printMatches.get()) { - IO::RC::Write::test(rFirst, rSecond, m, *rResult); + RC::Write::test(rFirst, rSecond, m, *rResult); } const bool cont = rr(std::move(rResult)); if(!cont) return false; diff --git a/libs/libmod/src/mod/lib/RC/ComposeRuleRealParallel.cpp b/libs/libmod/src/mod/lib/RC/ComposeRuleRealParallel.cpp new file mode 100644 index 0000000..50c6427 --- /dev/null +++ b/libs/libmod/src/mod/lib/RC/ComposeRuleRealParallel.cpp @@ -0,0 +1,10 @@ +#include "ComposeRuleReal.hpp" + +#include +#include + +namespace mod::lib::RC { + +MOD_RC_COMPOSE_BY_MATCH_MAKER_IMPL(Parallel) + +} // namespace mod::lib::RC \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/ComposeRuleRealSub.cpp b/libs/libmod/src/mod/lib/RC/ComposeRuleRealSub.cpp new file mode 100644 index 0000000..cfaa995 --- /dev/null +++ b/libs/libmod/src/mod/lib/RC/ComposeRuleRealSub.cpp @@ -0,0 +1,10 @@ +#include "ComposeRuleReal.hpp" + +#include +#include + +namespace mod::lib::RC { + +MOD_RC_COMPOSE_BY_MATCH_MAKER_IMPL(Sub) + +} // namespace mod::lib::RC \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/ComposeRuleRealSuper.cpp b/libs/libmod/src/mod/lib/RC/ComposeRuleRealSuper.cpp new file mode 100644 index 0000000..0ffbb72 --- /dev/null +++ b/libs/libmod/src/mod/lib/RC/ComposeRuleRealSuper.cpp @@ -0,0 +1,10 @@ +#include "ComposeRuleReal.hpp" + +#include +#include + +namespace mod::lib::RC { + +MOD_RC_COMPOSE_BY_MATCH_MAKER_IMPL(Super) + +} // namespace mod::lib::RC \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/Evaluator.cpp b/libs/libmod/src/mod/lib/RC/Evaluator.cpp index 4b82972..cb84b89 100644 --- a/libs/libmod/src/mod/lib/RC/Evaluator.cpp +++ b/libs/libmod/src/mod/lib/RC/Evaluator.cpp @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -226,7 +226,7 @@ std::vector> Evaluator::eval(const rule::RCExp::Expr } void Evaluator::print() const { - std::string fileNoExt = IO::RC::Write::pdf(*this); + std::string fileNoExt = RC::Write::pdf(*this); IO::post() << "summaryRC \"" << fileNoExt << "\"" << std::endl; } diff --git a/libs/libmod/src/mod/lib/IO/RC.cpp b/libs/libmod/src/mod/lib/RC/IO/Write.cpp similarity index 71% rename from libs/libmod/src/mod/lib/IO/RC.cpp rename to libs/libmod/src/mod/lib/RC/IO/Write.cpp index fa6321b..392d9f3 100644 --- a/libs/libmod/src/mod/lib/IO/RC.cpp +++ b/libs/libmod/src/mod/lib/RC/IO/Write.cpp @@ -1,49 +1,49 @@ -#include "RC.hpp" +#include "Write.hpp" #include #include #include -#include #include #include -#include +#include +#include #include -namespace mod::lib::IO::RC::Write { +namespace mod::lib::RC::Write { -std::string dot(const lib::RC::Evaluator &rc) { - typedef lib::RC::Evaluator::Vertex Vertex; - typedef lib::RC::Evaluator::Edge Edge; - const lib::RC::Evaluator::GraphType &rcg = rc.getGraph(); - post::FileHandle s(getUniqueFilePrefix() + "rc.dot"); +std::string dot(const Evaluator &rc) { + using Vertex = Evaluator::Vertex; + using Edge = Evaluator::Edge; + const Evaluator::GraphType &rcg = rc.getGraph(); + post::FileHandle s(IO::makeUniqueFilePrefix() + "rc.dot"); std::string fileNoExt = s; fileNoExt.erase(end(fileNoExt) - 4, end(fileNoExt)); s << "digraph g {" << std::endl; - for(Vertex v : asRange(vertices(rcg))) { + for(Vertex v: asRange(vertices(rcg))) { s << "\t" << get(boost::vertex_index_t(), rcg, v) << " ["; switch(rcg[v].kind) { - case lib::RC::Evaluator::VertexKind::Rule: + case Evaluator::VertexKind::Rule: s << " label=\"" << rcg[v].rule->getName() << "\""; break; - case lib::RC::Evaluator::VertexKind::Composition: + case Evaluator::VertexKind::Composition: s << " shape=point"; break; } s << " ];" << std::endl; } - for(Edge e : asRange(edges(rcg))) { + for(Edge e: asRange(edges(rcg))) { s << "\t" << get(boost::vertex_index_t(), rcg, source(e, rcg)) << " -> " << get(boost::vertex_index_t(), rcg, target(e, rcg)) << " ["; switch(rcg[e].kind) { - case lib::RC::Evaluator::EdgeKind::First: + case Evaluator::EdgeKind::First: s << " label=1"; break; - case lib::RC::Evaluator::EdgeKind::Second: + case Evaluator::EdgeKind::Second: s << " label=2"; break; - case lib::RC::Evaluator::EdgeKind::Result: + case Evaluator::EdgeKind::Result: break; } s << " ];" << std::endl; @@ -52,13 +52,13 @@ std::string dot(const lib::RC::Evaluator &rc) { return fileNoExt; } -std::string svg(const lib::RC::Evaluator &rc) { +std::string svg(const Evaluator &rc) { std::string fileNoExt = dot(rc); IO::post() << "gv rc \"" << fileNoExt << "\" svg" << std::endl; return fileNoExt; } -std::string pdf(const lib::RC::Evaluator &rc) { +std::string pdf(const Evaluator &rc) { std::string fileNoExt = svg(rc); IO::post() << "svgToPdf \"" << fileNoExt << "\"" << std::endl; return fileNoExt; @@ -70,13 +70,13 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const const auto &lg = get_labelled_left(rNew.getDPORule()); const auto &g = get_graph(lg); const auto &mol = get_molecule(lg); - for(const auto v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { const auto ad = mol[v]; if(ad.getRadical()) continue; if(ad.getCharge() != 0) continue; if(ad.getIsotope() != Isotope()) continue; int valence = 0; - for(const auto e : asRange(out_edges(v, g))) { + for(const auto e: asRange(out_edges(v, g))) { switch(mol[e]) { case BondType::Single: valence += 1; @@ -107,7 +107,7 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const } using CoreVertex = lib::Rules::Vertex; using CoreEdge = lib::Rules::Edge; - IO::Rules::Write::Options options; + Rules::Write::Options options; options.CollapseHydrogens(true); options.EdgesAsBonds(true); if(getConfig().rc.matchesWithIndex.get()) @@ -125,29 +125,29 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const std::map vFirstToCommon, vSecondToCommon, vNewToCommon; lib::Rules::LabelledRule dpoCommon(rFirst.getDPORule(), false); lib::Rules::GraphType &gComon = get_graph(dpoCommon); - lib::Rules::PropStringCore &pStringCommon = *dpoCommon.pString; - for(const CoreVertex v : asRange(vertices(rFirst.getGraph()))) + lib::Rules::PropString &pStringCommon = *dpoCommon.pString; + for(const CoreVertex v: asRange(vertices(rFirst.getGraph()))) vFirstToCommon[v] = v; // TODO: this will completely break if vertices are deleted in the composed rule - for(const CoreVertex v : asRange(vertices(rNew.getGraph()))) + for(const CoreVertex v: asRange(vertices(rNew.getGraph()))) vNewToCommon[v] = v; // copy rSecond vertices - for(const CoreVertex v : asRange(vertices(rSecond.getGraph()))) { + for(const CoreVertex v: asRange(vertices(rSecond.getGraph()))) { const auto rightIter = match.right.find(v); if(rightIter != match.right.end()) { vSecondToCommon[v] = rightIter->second; } else { const CoreVertex vCommon = add_vertex(gComon); vSecondToCommon[v] = vCommon; - gComon[vCommon].membership = lib::Rules::Membership::Context; - const std::string &label = rSecond.getGraph()[v].membership == lib::Rules::Membership::Left - ? rSecond.getStringState().getLeft()[v] - : rSecond.getStringState().getRight()[v]; + gComon[vCommon].membership = lib::Rules::Membership::K; + const std::string &label = rSecond.getGraph()[v].membership == lib::Rules::Membership::L + ? get_string(rSecond.getDPORule()).getLeft()[v] + : get_string(rSecond.getDPORule()).getRight()[v]; pStringCommon.add(vCommon, label, label); } } // copy rSecond edges - for(const CoreEdge e : asRange(edges(rSecond.getGraph()))) { + for(const CoreEdge e: asRange(edges(rSecond.getGraph()))) { const CoreVertex vSrcSecond = source(e, rSecond.getGraph()); const CoreVertex vTarSecond = target(e, rSecond.getGraph()); const auto iterSrc = vSecondToCommon.find(vSrcSecond); @@ -159,10 +159,10 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const auto pEdge = edge(vSrc, vTar, gComon); if(pEdge.second) continue; pEdge = add_edge(vSrc, vTar, gComon); - gComon[pEdge.first].membership = lib::Rules::Membership::Context; - const std::string &label = rSecond.getGraph()[e].membership == lib::Rules::Membership::Left - ? rSecond.getStringState().getLeft()[e] - : rSecond.getStringState().getRight()[e]; + gComon[pEdge.first].membership = lib::Rules::Membership::K; + const std::string &label = rSecond.getGraph()[e].membership == lib::Rules::Membership::L + ? get_string(rSecond.getDPORule()).getLeft()[e] + : get_string(rSecond.getDPORule()).getRight()[e]; pStringCommon.add(pEdge.first, label, label); } lib::Rules::Real rCommon(std::move(dpoCommon), rFirst.getLabelType()); @@ -175,7 +175,7 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const const auto secondIdOffset = num_vertices(rFirst.getGraph()); std::set matchVerticesInCommon; - for(const CoreVertex v : asRange(vertices(rFirst.getGraph()))) { + for(const CoreVertex v: asRange(vertices(rFirst.getGraph()))) { if(match.left.find(v) == match.left.end()) continue; const auto iter = vFirstToCommon.find(v); assert(iter != end(vFirstToCommon)); @@ -196,23 +196,23 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const assert(iter != end(vNewToCommon)); return matchVerticesInCommon.find(iter->second) != end(matchVerticesInCommon); }; - const auto rawFilesFirst = IO::Rules::Write::tikz(rFirstCopy, 0, options, "L", "K", "R", - IO::Rules::Write::BaseArgs{visible, vColour, eColour}, - disallowCollapseFirst); - const auto rawFilesSecond = IO::Rules::Write::tikz(rSecondCopy, secondIdOffset, options, "L", "K", "R", - IO::Rules::Write::BaseArgs{visible, vColour, eColour}, - disallowCollapseSecond); - const auto rawFilesNew = IO::Rules::Write::tikz(rNewCopy, 0, options, "L", "K", "R", - IO::Rules::Write::BaseArgs{visible, vColour, eColour}, - disallowCollapseNew); - post::FileHandle s(getUniqueFilePrefix() + "rcMatch.tex"); + const auto rawFilesFirst = Rules::Write::tikz(rFirstCopy, 0, options, "L", "K", "R", + Rules::Write::BaseArgs{visible, vColour, eColour}, + disallowCollapseFirst); + const auto rawFilesSecond = Rules::Write::tikz(rSecondCopy, secondIdOffset, options, "L", "K", "R", + Rules::Write::BaseArgs{visible, vColour, eColour}, + disallowCollapseSecond); + const auto rawFilesNew = Rules::Write::tikz(rNewCopy, 0, options, "L", "K", "R", + Rules::Write::BaseArgs{visible, vColour, eColour}, + disallowCollapseNew); + post::FileHandle s(IO::makeUniqueFilePrefix() + "rcMatch.tex"); { s << "\\rcMatchFig"; s << '{' << rawFilesFirst.first << '}' << '{' << rFirst.getId() << '}'; s << '{'; bool first = true; - for(const auto[vFirst, vSecond] : match.left) { + for(const auto[vFirst, vSecond]: match.left) { const auto vIdFirst = get(boost::vertex_index_t(), rFirst.getGraph(), vFirst); auto vIdSecond = get(boost::vertex_index_t(), rSecond.getGraph(), vSecond); vIdSecond += num_vertices(rFirst.getGraph()); @@ -227,7 +227,7 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const << '{' << rNew.getId() << '}'; s << '\n'; } - post::FileHandle sAux(getUniqueFilePrefix() + "rcMatch_aux.tex"); + post::FileHandle sAux(IO::makeUniqueFilePrefix() + "rcMatch_aux.tex"); { sAux << "\\\\\n"; sAux << "Files:\\\\\n \\texttt{" << IO::escapeForLatex(rawFilesFirst.first) @@ -238,7 +238,7 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const << "}\\\\\n"; sAux << "Match: \n"; bool first = true; - for(const auto[vFirst, vSecond] : match.left) { + for(const auto[vFirst, vSecond]: match.left) { if(!first) sAux << ", "; sAux << "$" << vFirst << "\\rightarrow " << vSecond << "$\n"; first = false; @@ -249,4 +249,4 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const IO::post() << std::flush; } -} // namespace mod::lib::IO::RC::Write \ No newline at end of file +} // namespace mod::lib::RC::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/RC.hpp b/libs/libmod/src/mod/lib/RC/IO/Write.hpp similarity index 73% rename from libs/libmod/src/mod/lib/IO/RC.hpp rename to libs/libmod/src/mod/lib/RC/IO/Write.hpp index 31cb488..ebe140b 100644 --- a/libs/libmod/src/mod/lib/IO/RC.hpp +++ b/libs/libmod/src/mod/lib/RC/IO/Write.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_LIB_IO_RC_HPP -#define MOD_LIB_IO_RC_HPP +#ifndef MOD_LIB_RC_IO_WRITE_HPP +#define MOD_LIB_RC_IO_WRITE_HPP #include @@ -10,10 +10,10 @@ namespace mod::lib::RC { struct Evaluator; } // namespace mod::lib::RC -namespace mod::lib::IO::RC::Write { -std::string dot(const lib::RC::Evaluator &rc); -std::string svg(const lib::RC::Evaluator &rc); -std::string pdf(const lib::RC::Evaluator &rc); +namespace mod::lib::RC::Write { +std::string dot(const Evaluator &rc); +std::string svg(const Evaluator &rc); +std::string pdf(const Evaluator &rc); using CoreCoreMap = boost::bimap; @@ -21,7 +21,7 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const template void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const VertexMap &m, const lib::Rules::Real &rNew) { - using GraphDom = lib::Rules::LabelledRule::LeftGraphType; + using GraphDom = lib::Rules::LabelledRule::SideProjectedGraphType; const auto &gDom = get_graph(get_labelled_left(rSecond.getDPORule())); const auto &gCodom = get_graph(get_labelled_right(rFirst.getDPORule())); CoreCoreMap match; @@ -33,6 +33,6 @@ void test(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, const test(rFirst, rSecond, match, rNew); } -} // namespace mod::lib::IO::RC::Write +} // namespace mod::lib::RC::Write -#endif // MOD_LIB_IO_RC_HPP \ No newline at end of file +#endif // MOD_LIB_RC_IO_WRITE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/LabelledComposition.hpp b/libs/libmod/src/mod/lib/RC/LabelledComposition.hpp index 301fd22..bc62161 100644 --- a/libs/libmod/src/mod/lib/RC/LabelledComposition.hpp +++ b/libs/libmod/src/mod/lib/RC/LabelledComposition.hpp @@ -6,18 +6,21 @@ #include #include #include -#include #include #include #include #include +#include namespace mod::lib::RC { namespace detail { -template -std::optional composeLabelledFinallyDoIt(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, VisitorT visitor) { - return compose(rFirst, rSecond, match, std::move(visitor)); +template +bool composeLabelledFinallyDoIt(Result &result, + const lib::Rules::LabelledRule &rFirst, + const lib::Rules::LabelledRule &rSecond, + InvertibleVertexMap &match, VisitorT visitor) { + return compose(result, rFirst.getRule(), rSecond.getRule(), match, std::move(visitor)); } template @@ -25,12 +28,16 @@ struct LabelTypeToVisitor; template<> struct LabelTypeToVisitor { - using type = Visitor::String; + static Visitor::String make(const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond) { + return {rFirst, rSecond}; + } }; template<> struct LabelTypeToVisitor { - using type = Visitor::Term; + static Visitor::Term make(const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond) { + return {rFirst, rSecond}; + } }; template @@ -38,22 +45,31 @@ struct WithStereoVisitor; template<> struct WithStereoVisitor { - using type = Visitor::Stereo; + static Visitor::Stereo make(const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond) { + return {rFirst, rSecond}; + } }; template<> struct WithStereoVisitor { - using type = Visitor::Null; + static Visitor::Null make(const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond) { + return {}; + } }; } // namespace detail -template -std::optional composeLabelled(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, VisitorT visitor = Visitor::Null()) { - return detail::composeLabelledFinallyDoIt(rFirst, rSecond, match, Visitor::makeVisitor( - std::move(visitor), - typename detail::LabelTypeToVisitor::type(), - typename detail::WithStereoVisitor::type() +template +bool composeLabelled(Result &result, const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond, + InvertibleVertexMap &match, + VisitorT visitor = Visitor::Null()) { + return detail::composeLabelledFinallyDoIt( + result, rFirst, rSecond, match, + Visitor::makeVisitor( + std::move(visitor), + detail::LabelTypeToVisitor::make(rFirst, rSecond), + detail::WithStereoVisitor::make(rFirst, rSecond) )); } diff --git a/libs/libmod/src/mod/lib/RC/MatchBuilder.cpp b/libs/libmod/src/mod/lib/RC/MatchBuilder.cpp index 83a1c48..738e00c 100644 --- a/libs/libmod/src/mod/lib/RC/MatchBuilder.cpp +++ b/libs/libmod/src/mod/lib/RC/MatchBuilder.cpp @@ -13,7 +13,7 @@ bool MatchBuilder::VertexPred::operator()(lib::Rules::Vertex vSecond, lib::Rules // ---------------------------------------------------------------------------- -bool MatchBuilder::EdgePred::operator()(lib::Rules::Edge eFirst, lib::Rules::Edge eSecond) { +bool MatchBuilder::EdgePred::operator()(lib::Rules::Edge eSecond, lib::Rules::Edge eFirst) { return lib::GraphMorphism::predicateSelectByLabelSeetings( get_labelled_left(rSecond.getDPORule()), get_labelled_right(rFirst.getDPORule()), eSecond, eFirst, labelSettings); @@ -23,7 +23,7 @@ bool MatchBuilder::EdgePred::operator()(lib::Rules::Edge eFirst, lib::Rules::Edg MatchBuilder::MatchBuilder(const lib::Rules::Real &rFirst, const lib::Rules::Real &rSecond, LabelSettings labelSettings) : rFirst(rFirst), rSecond(rSecond), labelSettings(labelSettings), - match(get_left(rSecond.getDPORule()), get_right(rFirst.getDPORule()), + match(getL(rSecond.getDPORule().getRule()), getR(rFirst.getDPORule().getRule()), EdgePred{rFirst, rSecond, labelSettings}, VertexPred{rFirst, rSecond, labelSettings}) {} lib::Rules::Vertex MatchBuilder::getSecondFromFirst(lib::Rules::Vertex v) const { @@ -71,7 +71,6 @@ std::unique_ptr MatchBuilder::compose(bool verbose) const { [[maybe_unused]] const bool cont = lib::GraphMorphism::matchSelectByLabelSettings( get_labelled_left(rSecond.getDPORule()), get_labelled_right(rFirst.getDPORule()), match.getVertexMap(), ls, mr); - assert(cont == bool(res)); return res; } @@ -89,19 +88,20 @@ std::vector> MatchBuilder::composeAll(bool max return true; })(rFirst, rSecond, std::move(m), verbose, IO::Logger(std::cout)); }; - const auto &gLeft = get_labelled_left(rSecond.getDPORule()); - const auto &gRight = get_labelled_right(rFirst.getDPORule()); + const auto &lgLeft = get_labelled_left(rSecond.getDPORule()); + const auto &lgRight = get_labelled_right(rFirst.getDPORule()); if(maximum) { - auto mr = jla_boost::GraphMorphism::makeMaxmimumSubgraphCallback(get_graph(gLeft), get_graph(gRight), mrCompose); + auto mr = jla_boost::GraphMorphism::makeMaxmimumSubgraphCallback(get_graph(lgLeft), get_graph(lgRight), + mrCompose); // the non-extended match, and importantly send it through mr to make sure maximum is respected - lib::GraphMorphism::matchSelectByLabelSettings(gLeft, gRight, match.getSizedVertexMap(), ls, std::ref(mr)); + lib::GraphMorphism::matchSelectByLabelSettings(lgLeft, lgRight, match.getSizedVertexMap(), ls, std::ref(mr)); // enumerate the rest m(std::ref(mr)); // and now release the maximum ones mr.outputMatches(); } else { // the non-extended match - lib::GraphMorphism::matchSelectByLabelSettings(gLeft, gRight, match.getSizedVertexMap(), ls, mrCompose); + lib::GraphMorphism::matchSelectByLabelSettings(lgLeft, lgRight, match.getSizedVertexMap(), ls, mrCompose); // enumerate the rest m(mrCompose); } diff --git a/libs/libmod/src/mod/lib/RC/MatchBuilder.hpp b/libs/libmod/src/mod/lib/RC/MatchBuilder.hpp index 5e427d8..c411384 100644 --- a/libs/libmod/src/mod/lib/RC/MatchBuilder.hpp +++ b/libs/libmod/src/mod/lib/RC/MatchBuilder.hpp @@ -3,7 +3,6 @@ #include #include -#include namespace mod::lib::RC { @@ -37,8 +36,9 @@ struct MatchBuilder { const LabelSettings labelSettings; private: // note: the match has rSecond as domain and rFirst as codomain, i.e., <-, - // so everything todo with match is somewhat "backwards" - jla_boost::GraphMorphism::CommonSubgraphEnumerator match; }; diff --git a/libs/libmod/src/mod/lib/RC/MatchMaker/Common.hpp b/libs/libmod/src/mod/lib/RC/MatchMaker/Common.hpp index b6ac6d4..9198b96 100644 --- a/libs/libmod/src/mod/lib/RC/MatchMaker/Common.hpp +++ b/libs/libmod/src/mod/lib/RC/MatchMaker/Common.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include namespace mod::lib::RC { @@ -20,7 +20,7 @@ struct Common { const lib::Rules::Real &rSecond, Callback callback, LabelSettings labelSettings) { - using MapImpl = std::vector; + using MapImpl = std::vector; std::vector maps; const auto mr = [&rFirst, &rSecond, &callback, this, &maps] (auto &&m, const auto &gSecond, const auto &gFirst) -> bool { diff --git a/libs/libmod/src/mod/lib/RC/MatchMaker/ComponentWiseUtil.hpp b/libs/libmod/src/mod/lib/RC/MatchMaker/ComponentWiseUtil.hpp index 6824c61..95357a0 100644 --- a/libs/libmod/src/mod/lib/RC/MatchMaker/ComponentWiseUtil.hpp +++ b/libs/libmod/src/mod/lib/RC/MatchMaker/ComponentWiseUtil.hpp @@ -41,7 +41,6 @@ struct WrappedComponentGraph { using PropTermType = typename Rule::PropTermType; using PropStereoType = typename Rule::PropStereoType; public: - WrappedComponentGraph(const ComponentGraph &g, std::size_t i, const Rule &r) : g(jla_boost::makeFilteredWrapper(g)), i(i), r(r) {} @@ -69,7 +68,6 @@ struct WrappedComponentGraph { get_vertex_order(const WrappedComponentGraph &g) { return get_vertex_order_component(g.i, g.r); } - private: GraphType g; std::size_t i; @@ -87,7 +85,6 @@ template struct RuleRuleComponentMonomorphism { using Morphism = GM::VectorVertexMap; public: - RuleRuleComponentMonomorphism(const RuleSideDom &rsDom, const RuleSideCodom &rsCodom, bool enforceConstraints, diff --git a/libs/libmod/src/mod/lib/RC/MatchMaker/LabelledMatch.hpp b/libs/libmod/src/mod/lib/RC/MatchMaker/LabelledMatch.hpp index 4e0d022..0a2b157 100644 --- a/libs/libmod/src/mod/lib/RC/MatchMaker/LabelledMatch.hpp +++ b/libs/libmod/src/mod/lib/RC/MatchMaker/LabelledMatch.hpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include @@ -16,35 +16,38 @@ constexpr int V_MorphismGen = 2; constexpr int V_Composition = 12; namespace detail { -template -void initTermLabel(const RFirst &rFirst, const RSecond &rSecond) { +inline void initTermLabel(const lib::Rules::Real &rFirst, // TODO: make rules generic, when needed + const lib::Rules::Real &rSecond) { const auto &termFirst = get_term(rFirst.getDPORule()); const auto &termSecond = get_term(rSecond.getDPORule()); if(!isValid(termFirst)) { std::string msg = "Term state of rFirst is invalid:\n" + termFirst.getParsingError(); - lib::IO::Rules::Write::summary(rFirst, true); + lib::Rules::Write::summary(rFirst, true); throw mod::FatalError(std::move(msg)); } if(!isValid(termSecond)) { std::string msg = "Term state of rSecond is invalid:\n" + termSecond.getParsingError(); - lib::IO::Rules::Write::summary(rSecond, true); + lib::Rules::Write::summary(rSecond, true); throw mod::FatalError(std::move(msg)); } } -template -void initStereoLabel(const RFirst &rFirst, const RSecond &rSecond) { +inline void initStereoLabel(const lib::Rules::Real &rFirst, // TODO: make rules generic, when needed + const lib::Rules::Real &rSecond) { get_stereo(rFirst.getDPORule()); get_stereo(rSecond.getDPORule()); } } // namespace detail -template -void initByLabelSettings(const RFirst &rFirst, const RSecond &rSecond, LabelSettings labelSettings) { +inline void initByLabelSettings(const lib::Rules::Real &rFirst, // TODO: make rules generic, when needed + const lib::Rules::Real &rSecond, + LabelSettings labelSettings) { switch(labelSettings.type) { - case LabelType::String: break; - case LabelType::Term: detail::initTermLabel(rFirst, rSecond); + case LabelType::String: + break; + case LabelType::Term: + detail::initTermLabel(rFirst, rSecond); break; } if(labelSettings.withStereo) detail::initStereoLabel(rFirst, rSecond); @@ -57,11 +60,12 @@ namespace detail { // return mr(rFirst, rSecond, std::move(m)); //} -template -bool handleMapToStereo(const RFirst &rFirst, const RSecond &rSecond, - VertexMap &&m, MR &&mr, - LabelSettings labelSettings, - const int verbosity, IO::Logger logger) { +template +bool handleMapToStereo(const lib::Rules::Real &rFirst, // TODO: make rules generic, when needed + const lib::Rules::Real &rSecond, + VertexMap &&m, MR &&mr, + LabelSettings labelSettings, + const int verbosity, IO::Logger logger) { if(!labelSettings.withStereo) { return mr(rFirst, rSecond, std::move(m), verbosity >= V_Composition, logger); } @@ -85,11 +89,12 @@ bool handleMapToStereo(const RFirst &rFirst, const RSecond &rSecond, MOD_ABORT; } -template -bool handleMapToTerm(const RFirst &rFirst, const RSecond &rSecond, - VertexMap &&m, MR &&mr, - LabelSettings labelSettings, - const int verbosity, IO::Logger logger) { +template +bool handleMapToTerm(const lib::Rules::Real &rFirst, // TODO: make rules generic, when needed + const lib::Rules::Real &rSecond, + VertexMap &&m, MR &&mr, + LabelSettings labelSettings, + const int verbosity, IO::Logger logger) { namespace GM = jla_boost::GraphMorphism; using GraphDom = typename GM::VertexMapTraits::GraphDom; using GraphCodom = typename GM::VertexMapTraits::GraphCodom; @@ -105,7 +110,7 @@ bool handleMapToTerm(const RFirst &rFirst, const RSecond &rSecond, machine.verify(); lib::Term::MGU mgu(machine.getHeap().size()); // first unify mapped vertices and edges - for(const auto vCodom : asRange(vertices(gCodom))) { + for(const auto vCodom: asRange(vertices(gCodom))) { const auto vDom = get_inverse(m, gDom, gCodom, vCodom); if(vDom == vNullDom) continue; const auto addrCodom = termCodom[vCodom]; @@ -114,7 +119,7 @@ bool handleMapToTerm(const RFirst &rFirst, const RSecond &rSecond, if(mgu.status != lib::Term::MGU::Status::Exists) break; machine.verify(); } - for(const auto eCodom : asRange(edges(gCodom))) { + for(const auto eCodom: asRange(edges(gCodom))) { if(mgu.status != lib::Term::MGU::Status::Exists) break; machine.verify(); const auto vSrcCodom = source(eCodom, gCodom); @@ -146,11 +151,12 @@ bool handleMapToTerm(const RFirst &rFirst, const RSecond &rSecond, return handleMapToStereo(rFirst, rSecond, std::move(mTerm), mr, labelSettings, verbosity, logger); } -template -bool handleMapToStringOrTerm(const RFirst &rFirst, const RSecond &rSecond, - VertexMap &&m, MR &&mr, - LabelSettings labelSettings, - const int verbosity, IO::Logger logger) { +template +bool handleMapToStringOrTerm(const lib::Rules::Real &rFirst, // TODO: make rules generic, when needed + const lib::Rules::Real &rSecond, + VertexMap &&m, MR &&mr, + LabelSettings labelSettings, + const int verbosity, IO::Logger logger) { switch(labelSettings.type) { case LabelType::String: return handleMapToStereo(rFirst, rSecond, std::move(m), std::move(mr), labelSettings, verbosity, logger); @@ -162,13 +168,14 @@ bool handleMapToStringOrTerm(const RFirst &rFirst, const RSecond &rSecond, } // namesapce detail -template -bool handleMapByLabelSettings(const RFirst &rFirst, const RSecond &rSecond, - VertexMap &&m, MR &&mr, - LabelSettings labelSettings, - const int verbosity, IO::Logger logger) { +template +bool handleMapByLabelSettings(const lib::Rules::Real &rFirst, // TODO: make rules generic, when needed + const lib::Rules::Real &rSecond, + VertexMap &&m, MR &&mr, + LabelSettings labelSettings, + const int verbosity, IO::Logger logger) { return detail::handleMapToStringOrTerm(rFirst, rSecond, std::forward(m), std::forward(mr), - labelSettings, verbosity, logger); + labelSettings, verbosity, logger); } } // namespace mod::lib::RC diff --git a/libs/libmod/src/mod/lib/RC/MatchMaker/Parallel.hpp b/libs/libmod/src/mod/lib/RC/MatchMaker/Parallel.hpp index 592e2d0..494d79b 100644 --- a/libs/libmod/src/mod/lib/RC/MatchMaker/Parallel.hpp +++ b/libs/libmod/src/mod/lib/RC/MatchMaker/Parallel.hpp @@ -1,14 +1,12 @@ -#ifndef MOD_LIB_RC_MATCH_MAKER_PARALLEL_H -#define MOD_LIB_RC_MATCH_MAKER_PARALLEL_H +#ifndef MOD_LIB_RC_MATCH_MAKER_PARALLEL_HPP +#define MOD_LIB_RC_MATCH_MAKER_PARALLEL_HPP #include #include -#include +#include -namespace mod { -namespace lib { -namespace RC { +namespace mod::lib::RC { struct Parallel { Parallel(int verbosity, IO::Logger logger) : verbosity(verbosity), logger(logger) {} @@ -18,8 +16,8 @@ struct Parallel { const lib::Rules::Real &rSecond, Callback callback, LabelSettings labelSettings) { - using GraphDom = lib::Rules::LabelledRule::LeftGraphType; - using GraphCodom = lib::Rules::LabelledRule::RightGraphType; + using GraphDom = lib::Rules::LabelledRule::SideGraphType; + using GraphCodom = lib::Rules::LabelledRule::SideGraphType; using Map = jla_boost::GraphMorphism::InvertibleVectorVertexMap; const auto &gDom = get_graph(get_labelled_left(rSecond.getDPORule())); const auto &gCodom = get_graph(get_labelled_right(rFirst.getDPORule())); @@ -31,9 +29,6 @@ struct Parallel { IO::Logger logger; }; -} // namespace RC -} // namespace lib -} // namespace mod +} // namespace mod::lib::RC - -#endif /* MOD_LIB_RC_MATCH_MAKER_PARALLEL_H */ \ No newline at end of file +#endif // MOD_LIB_RC_MATCH_MAKER_PARALLEL_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/MatchMaker/Sub.hpp b/libs/libmod/src/mod/lib/RC/MatchMaker/Sub.hpp index 0657957..3369090 100644 --- a/libs/libmod/src/mod/lib/RC/MatchMaker/Sub.hpp +++ b/libs/libmod/src/mod/lib/RC/MatchMaker/Sub.hpp @@ -3,24 +3,22 @@ #include #include -#include +#include +#include #include #include -#include #include #include #include #include -#include - #include namespace mod::lib::RC { struct Sub { - using GraphDom = lib::Rules::LabelledRule::LeftGraphType; - using GraphCodom = lib::Rules::LabelledRule::RightGraphType; + using GraphDom = lib::Rules::LabelledRule::SideGraphType; + using GraphCodom = lib::Rules::LabelledRule::SideGraphType; using VertexMapType = jla_boost::GraphMorphism::InvertibleVectorVertexMap; public: explicit Sub(int verbosity, IO::Logger logger, bool allowPartial) @@ -85,7 +83,7 @@ Sub::matchFromPosition(const lib::Rules::Real &rFirst, auto map = VertexMapType(gDom, gCodom); for(std::size_t pId = 0; pId < position.size(); pId++) { if(position[pId].disabled) continue; - if(position[pId].host == rSecond.getDPORule().numLeftComponents) { + if(position[pId].host == get_num_connected_components(get_labelled_left(rSecond.getDPORule()))) { if(!allowPartial) MOD_ABORT; // we should have gotten this continue; } diff --git a/libs/libmod/src/mod/lib/RC/MatchMaker/Super.hpp b/libs/libmod/src/mod/lib/RC/MatchMaker/Super.hpp index 4da11a3..616be02 100644 --- a/libs/libmod/src/mod/lib/RC/MatchMaker/Super.hpp +++ b/libs/libmod/src/mod/lib/RC/MatchMaker/Super.hpp @@ -3,25 +3,23 @@ #include #include -#include +#include +#include #include #include -#include #include #include #include #include -#include - #include #include namespace mod::lib::RC { struct Super { - using GraphDom = lib::Rules::LabelledRule::LeftGraphType; - using GraphCodom = lib::Rules::LabelledRule::RightGraphType; + using GraphDom = lib::Rules::LabelledRule::SideGraphType; + using GraphCodom = lib::Rules::LabelledRule::SideGraphType; using VertexMapType = jla_boost::GraphMorphism::InvertibleVectorVertexMap; public: Super(int verbosity, IO::Logger logger, bool allowPartial, bool enforceConstraints) @@ -129,7 +127,7 @@ Super::matchFromPosition(const lib::Rules::Real &rFirst, auto map = VertexMapType(gDom, gCodom); for(std::size_t pId = 0; pId < position.size(); ++pId) { if(position[pId].disabled) continue; - if(position[pId].host == rFirst.getDPORule().numRightComponents) { + if(position[pId].host == get_num_connected_components(get_labelled_right(rFirst.getDPORule()))) { if(!allowPartial) MOD_ABORT; // we should have gotten this continue; } diff --git a/libs/libmod/src/mod/lib/RC/Result.hpp b/libs/libmod/src/mod/lib/RC/Result.hpp index 0491d7c..d6afeac 100644 --- a/libs/libmod/src/mod/lib/RC/Result.hpp +++ b/libs/libmod/src/mod/lib/RC/Result.hpp @@ -1,12 +1,11 @@ -#ifndef MOD_LIB_RC_RESULT_H -#define MOD_LIB_RC_RESULT_H +#ifndef MOD_LIB_RC_RESULT_HPP +#define MOD_LIB_RC_RESULT_HPP -#include -#include +#include -namespace mod { -namespace lib { -namespace RC { +#include + +namespace mod::lib::RC { //------------------------------------------------------------------------------ // ResultConcept @@ -33,34 +32,40 @@ namespace RC { // Result result; //}; -//------------------------------------------------------------------------------ -// BaseResult -//------------------------------------------------------------------------------ +struct Result { + using Rule = lib::DPO::CombinedRule; + using CombinedGraph = Rule::CombinedGraphType; + + struct RuleResult { // TODO: temporary hax + using GraphType = CombinedGraph; + using SideGraphType = Rule::SideGraphType; + }; -template -struct BaseResult { - using RuleResult = RuleResultT; - BOOST_CONCEPT_ASSERT((jla_boost::GraphDPO::PushoutRuleConcept)); - BOOST_CONCEPT_ASSERT((jla_boost::GraphDPO::PushoutRuleConcept)); - BOOST_CONCEPT_ASSERT((jla_boost::GraphDPO::PushoutRuleConcept)); - using GraphResult = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; - using GraphFirst = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; - using GraphSecond = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; + Result(const Rule &rFirst, const Rule &rSecond) : + rDPO(new Rule()), + mFirstToResult(rFirst.getCombinedGraph(), rDPO->getCombinedGraph()), + mSecondToResult(rSecond.getCombinedGraph(), rDPO->getCombinedGraph()) {} +public: + std::unique_ptr rDPO; + jla_boost::GraphMorphism::InvertibleVectorVertexMap mFirstToResult; + jla_boost::GraphMorphism::InvertibleVectorVertexMap mSecondToResult; +}; - template - BaseResult(const RuleFirst &rFirst, const RuleSecond &rSecond, Args &&... args) - : rResult(std::forward(args)...), - mFirstToResult(get_graph(rFirst), get_graph(rResult)), - mSecondToResult(get_graph(rSecond), get_graph(rResult)) {} + +struct LabelledResult : Result { + using MatchConstraint = GraphMorphism::Constraints::Constraint; + using PropStringType = lib::Rules::PropString; + using PropTermType = lib::Rules::PropTerm; +public: + using Result::Result; public: - RuleResult rResult; - jla_boost::GraphMorphism::InvertibleVectorVertexMap mFirstToResult; - jla_boost::GraphMorphism::InvertibleVectorVertexMap mSecondToResult; + std::unique_ptr pString; + std::unique_ptr pTerm; + std::unique_ptr pStereo; + std::vector> matchConstraints; }; -} // namespace RC -} // namespace lib -} // namespace mod +} // namespace mod::lib::RC -#endif /* MOD_LIB_RC_RESULT_H */ \ No newline at end of file +#endif // MOD_LIB_RC_RESULT_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/Visitor/Compound.hpp b/libs/libmod/src/mod/lib/RC/Visitor/Compound.hpp index 9d4511b..877cf17 100644 --- a/libs/libmod/src/mod/lib/RC/Visitor/Compound.hpp +++ b/libs/libmod/src/mod/lib/RC/Visitor/Compound.hpp @@ -1,15 +1,9 @@ -#ifndef MOD_LIB_RC_VISITOR_COMPOUND_H -#define MOD_LIB_RC_VISITOR_COMPOUND_H - -#include -#include +#ifndef MOD_LIB_RC_VISITOR_COMPOUND_HPP +#define MOD_LIB_RC_VISITOR_COMPOUND_HPP #include -namespace mod { -namespace lib { -namespace RC { -namespace Visitor { +namespace mod::lib::RC::Visitor { template struct Compound { @@ -17,7 +11,6 @@ struct Compound { template<> struct Compound<> { - template bool init(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) { return true; @@ -28,77 +21,86 @@ struct Compound<> { return true; } public: - template - void copyVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const VertexFirst &vFirst, const VertexResult &vResult) { } + typename VertexFirst, typename VertexResult> + void copyVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + const VertexFirst &vFirst, const VertexResult &vResult) {} template - void copyVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const VertexSecond &vSecond, const VertexResult &vResult) { } + typename VertexSecond, typename VertexResult> + void copyVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + const VertexSecond &vSecond, const VertexResult &vResult) {} template - void copyEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const EdgeFirst &eFirst, const EdgeResult &eResult) { } + typename EdgeFirst, typename EdgeResult> + void copyEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + const EdgeFirst &eFirst, const EdgeResult &eResult) {} template - void copyEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const EdgeSecond &eSecond, const EdgeResult &eResult) { } + typename EdgeSecond, typename EdgeResult> + void copyEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + const EdgeSecond &eSecond, const EdgeResult &eResult) {} public: - template - void printVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexFirst &vFirst) { } + void printVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexFirst &vFirst) {} template - void printVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexSecond &vSecond) { } + void printVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexSecond &vSecond) {} template - void printVertexResult(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexResult &vResult) { } + void printVertexResult(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexResult &vResult) {} template - void printEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const EdgeFirst &eFirst) { } + void printEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const EdgeFirst &eFirst) {} template - void printEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const EdgeSecond &eSecond) { } + void printEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const EdgeSecond &eSecond) {} public: - template - void composeVertexRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { } + typename VertexResult, typename VertexSecond> + void composeVertexRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) {} template - void composeVertexLRvsL(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { } + typename VertexResult, typename VertexSecond> + void composeVertexLRvsL(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) {} template - void composeVertexLRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { } + typename VertexResult, typename VertexSecond> + void composeVertexLRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) {} public: - template - void setEdgeResultRightFromSecondRight(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - EdgeResult eResult, EdgeSecond eSecond) { } + typename EdgeResult, typename EdgeSecond> + void setEdgeResultRightFromSecondRight(const RuleFirst &rFirst, const RuleSecond &rSecond, + const InvertibleVertexMap &match, const Result &result, + EdgeResult eResult, EdgeSecond eSecond) {} }; template struct Compound : Compound { using Base = Compound; - +public: Compound(Visitor visitor, Visitors ...visitors) - : Base(std::move(visitors)...), visitor(std::move(visitor)) { } + : Base(std::move(visitors)...), visitor(std::move(visitor)) {} template bool init(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) { @@ -112,105 +114,114 @@ struct Compound : Compound { return res && Base::template finalize(rFirst, rSecond, match, result); } public: - template - void copyVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const VertexFirst &vFirst, const VertexResult &vResult) { + typename VertexFirst, typename VertexResult> + void copyVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + const VertexFirst &vFirst, const VertexResult &vResult) { visitor.template copyVertexFirst(rFirst, rSecond, match, result, vFirst, vResult); Base::template copyVertexFirst(rFirst, rSecond, match, result, vFirst, vResult); } template - void copyVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const VertexSecond &vSecond, const VertexResult &vResult) { + typename VertexSecond, typename VertexResult> + void copyVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + const VertexSecond &vSecond, const VertexResult &vResult) { visitor.template copyVertexSecond(rFirst, rSecond, match, result, vSecond, vResult); Base::template copyVertexSecond(rFirst, rSecond, match, result, vSecond, vResult); } template - void copyEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const EdgeFirst &eFirst, const EdgeResult &eResult) { + typename EdgeFirst, typename EdgeResult> + void copyEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + const EdgeFirst &eFirst, const EdgeResult &eResult) { visitor.template copyEdgeFirst(rFirst, rSecond, match, result, eFirst, eResult); Base::template copyEdgeFirst(rFirst, rSecond, match, result, eFirst, eResult); } template - void copyEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const EdgeSecond &eSecond, const EdgeResult &eResult) { + typename EdgeSecond, typename EdgeResult> + void copyEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + const EdgeSecond &eSecond, const EdgeResult &eResult) { visitor.template copyEdgeSecond(rFirst, rSecond, match, result, eSecond, eResult); Base::template copyEdgeSecond(rFirst, rSecond, match, result, eSecond, eResult); } public: - template - void printVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexFirst &vFirst) { + void printVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexFirst &vFirst) { visitor.printVertexFirst(rFirst, rSecond, match, result, s, vFirst); Base::template printVertexFirst(rFirst, rSecond, match, result, s, vFirst); } template - void printVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexSecond &vSecond) { + void printVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexSecond &vSecond) { visitor.printVertexSecond(rFirst, rSecond, match, result, s, vSecond); Base::template printVertexSecond(rFirst, rSecond, match, result, s, vSecond); } template - void printVertexResult(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexResult &vResult) { + void printVertexResult(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexResult &vResult) { visitor.printVertexResult(rFirst, rSecond, match, result, s, vResult); Base::template printVertexResult(rFirst, rSecond, match, result, s, vResult); } template - void printEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const EdgeFirst &eFirst) { + void printEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const EdgeFirst &eFirst) { visitor.printEdgeFirst(rFirst, rSecond, match, result, s, eFirst); Base::template printEdgeFirst(rFirst, rSecond, match, result, s, eFirst); } template - void printEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const EdgeSecond &eSecond) { + void printEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const EdgeSecond &eSecond) { visitor.printEdgeSecond(rFirst, rSecond, match, result, s, eSecond); Base::template printEdgeSecond(rFirst, rSecond, match, result, s, eSecond); } public: - template - void composeVertexRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { + typename VertexResult, typename VertexSecond> + void composeVertexRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) { visitor.template composeVertexRvsLR(rFirst, rSecond, match, result, vResult, vSecond); Base::template composeVertexRvsLR(rFirst, rSecond, match, result, vResult, vSecond); } template - void composeVertexLRvsL(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { + typename VertexResult, typename VertexSecond> + void composeVertexLRvsL(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) { visitor.template composeVertexLRvsL(rFirst, rSecond, match, result, vResult, vSecond); Base::template composeVertexLRvsL(rFirst, rSecond, match, result, vResult, vSecond); } template - void composeVertexLRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { + typename VertexResult, typename VertexSecond> + void composeVertexLRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) { visitor.template composeVertexLRvsLR(rFirst, rSecond, match, result, vResult, vSecond); Base::template composeVertexLRvsLR(rFirst, rSecond, match, result, vResult, vSecond); } public: - template - void setEdgeResultRightFromSecondRight(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - EdgeResult eResult, EdgeSecond eSecond) { + typename EdgeResult, typename EdgeSecond> + void setEdgeResultRightFromSecondRight(const RuleFirst &rFirst, const RuleSecond &rSecond, + const InvertibleVertexMap &match, const Result &result, + EdgeResult eResult, EdgeSecond eSecond) { visitor.template setEdgeResultRightFromSecondRight(rFirst, rSecond, match, result, eResult, eSecond); Base::template setEdgeResultRightFromSecondRight(rFirst, rSecond, match, result, eResult, eSecond); } @@ -223,12 +234,9 @@ using Null = Compound<>; template Compound makeVisitor(Visitors... visitors) { - return Compound < Visitors...>(std::move(visitors)...); + return Compound(std::move(visitors)...); } -} // namespace Visitor -} // namespace RC -} // namespace lib -} // namespace mod +} // namespace mod::lib::RC::Visitor -#endif /* MOD_LIB_RC_VISITOR_COMPOUND_H */ \ No newline at end of file +#endif // MOD_LIB_RC_VISITOR_COMPOUND_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/Visitor/MatchConstraints.hpp b/libs/libmod/src/mod/lib/RC/Visitor/MatchConstraints.hpp index 1f85ee1..b99b182 100644 --- a/libs/libmod/src/mod/lib/RC/Visitor/MatchConstraints.hpp +++ b/libs/libmod/src/mod/lib/RC/Visitor/MatchConstraints.hpp @@ -1,13 +1,11 @@ -#ifndef MOD_LIB_RC_VISITOR_MATCH_CONSTRAINTS_H -#define MOD_LIB_RC_VISITOR_MATCH_CONSTRAINTS_H +#ifndef MOD_LIB_RC_VISITOR_MATCH_CONSTRAINTS_HPP +#define MOD_LIB_RC_VISITOR_MATCH_CONSTRAINTS_HPP #include #include +#include -namespace mod { -namespace lib { -namespace RC { -namespace Visitor { +namespace mod::lib::RC::Visitor { namespace detail { template using ConstraintVisitor = lib::GraphMorphism::Constraints::AllVisitor; @@ -20,17 +18,17 @@ using ConstraintSP = lib::GraphMorphism::Constraints::ShortestPath; template struct ConvertFirst : public ConstraintVisitor::LeftGraphType +/* */ typename RuleFirst::SideGraphType /* */ > { - using GraphResult = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; - using GraphFirst = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; - using GraphSecond = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; + using GraphResult = typename Result::RuleResult::GraphType; + using GraphFirst = typename RuleFirst::GraphType; + using GraphSecond = typename RuleSecond::GraphType; - using GraphResultLeft = typename jla_boost::GraphDPO::PushoutRuleTraits::LeftGraphType; - using GraphFirstLeft = typename jla_boost::GraphDPO::PushoutRuleTraits::LeftGraphType; - using GraphSecondLeft = typename jla_boost::GraphDPO::PushoutRuleTraits::LeftGraphType; + using GraphResultLeft = typename Result::RuleResult::SideGraphType; + using GraphFirstLeft = typename RuleFirst::SideGraphType; + using GraphSecondLeft = typename RuleSecond::SideGraphType; - ConvertFirst(const RuleFirst &rFirst, Result &result) : rFirst(rFirst), result(result) { } + ConvertFirst(const RuleFirst &rFirst, Result &result) : rFirst(rFirst), result(result) {} virtual void operator()(const ConstraintAdj &c) override { auto cResult = std::make_unique >(c); @@ -45,15 +43,13 @@ struct ConvertFirst : public ConstraintVisitorvConstrained; - auto vResult = get(result.mFirstToResult, get_graph(rFirst), get_graph(result.rResult), vFirst); + auto vResult = get(result.mFirstToResult, get_graph(rFirst), result.rDPO->getCombinedGraph(), vFirst); assert(vResult != boost::graph_traits::null_vertex()); cResult->vConstrained = vResult; // std::cout << "WARNING: check transfered constraint for " << rFirst.getName() << " -> " << rSecond.getName() << std::endl; this->cResult = std::move(cResult); } - public: - virtual void operator()(const ConstraintSP &c) override { auto cResult = std::make_unique >(c); MOD_ABORT; @@ -66,35 +62,36 @@ struct ConvertFirst : public ConstraintVisitor struct ConvertSecond : public ConstraintVisitor::LeftGraphType +/* */ typename RuleSecond::SideGraphType /* */ > { - using GraphResult = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; - using GraphFirst = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; - using GraphSecond = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; + using GraphResult = typename Result::RuleResult::GraphType; + using GraphFirst = typename RuleFirst::GraphType; + using GraphSecond = typename RuleSecond::GraphType; - using GraphResultLeft = typename jla_boost::GraphDPO::PushoutRuleTraits::LeftGraphType; - using GraphFirstLeft = typename jla_boost::GraphDPO::PushoutRuleTraits::LeftGraphType; - using GraphSecondLeft = typename jla_boost::GraphDPO::PushoutRuleTraits::LeftGraphType; + using GraphResultLeft = typename Result::RuleResult::SideGraphType; + using GraphFirstLeft = typename RuleFirst::SideGraphType; + using GraphSecondLeft = typename RuleSecond::SideGraphType; ConvertSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) - : rFirst(rFirst), rSecond(rSecond), match(match), result(result) { } + : rFirst(rFirst), rSecond(rSecond), match(match), result(result) {} virtual void operator()(const ConstraintAdj &c) override { const auto vSecond = c.vConstrained; - const auto vResult = get(result.mSecondToResult, get_graph(rSecond), get_graph(result.rResult), vSecond); + const auto vResult = get(result.mSecondToResult, get_graph(rSecond), result.rDPO->getCombinedGraph(), vSecond); // vResult may be null_vertex if(vResult == boost::graph_traits::null_vertex()) { // std::cout << "WARNING: constrained vertex " << vSecondId << " deleted in " << rFirst.getName() << " -> " << rSecond.getName() << std::endl; return; } - auto mResult = membership(result.rResult, vResult); - if(mResult == jla_boost::GraphDPO::Membership::Right) { + auto mResult = result.rDPO->getCombinedGraph()[vResult].membership; + if(mResult == lib::DPO::Membership::R) { // std::cout << "WARNING: constrained vertex " << vSecondId << " changed to right side in " << rFirst.getName() << " -> " << rSecond.getName() << std::endl; return; } - if(get(match, get_graph(get_labelled_left(rSecond)), get_graph(get_labelled_right(rFirst)), vSecond) != boost::graph_traits::null_vertex()) { + if(get(match, get_graph(get_labelled_left(rSecond)), get_graph(get_labelled_right(rFirst)), vSecond) != + boost::graph_traits::null_vertex()) { // std::cout << "WARNING: maybe missing constraint on " << vNew << " for " << rFirst.getName() << " -> " << rSecond.getName() << std::endl; return; } @@ -106,8 +103,7 @@ struct ConvertSecond : public ConstraintVisitorvertexTerms.clear(); cResult->edgeTerms.clear(); break; - case LabelType::Term: - { + case LabelType::Term: { cResult->vertexLabels.clear(); cResult->edgeLabels.clear(); // we need to make sure the terms referred to are actually in the result machine, @@ -116,7 +112,7 @@ struct ConvertSecond : public ConstraintVisitoredgeTerms); cResult->vertexTerms.clear(); cResult->edgeTerms.clear(); - auto &m = getMachine(*result.rResult.pTerm); + auto &m = getMachine(*result.pTerm); const auto handleTerm = [&m](const auto tSecond) { m.verify(); // lib::IO::Term::Write::wam(m, lib::Term::getStrings(), std::cout << "Copy " << addr << "\n"); @@ -125,8 +121,8 @@ struct ConvertSecond : public ConstraintVisitorvertexTerms.insert(handleTerm(tSecond)); - for(const auto tSecond : eTerms) cResult->edgeTerms.insert(handleTerm(tSecond)); + for(const auto tSecond: vTerms) cResult->vertexTerms.insert(handleTerm(tSecond)); + for(const auto tSecond: eTerms) cResult->edgeTerms.insert(handleTerm(tSecond)); break; } } @@ -134,10 +130,10 @@ struct ConvertSecond : public ConstraintVisitor " << rSecond.getName() << std::endl; } public: - virtual void operator()(const ConstraintSP &c) override { - auto vResultSrc = get(result.mSecondToResult, get_graph(rSecond), get_graph(result.rResult), c.vSrc); - auto vResultTar = get(result.mSecondToResult, get_graph(rSecond), get_graph(result.rResult), c.vTar); + const auto &gResult = result.rDPO->getCombinedGraph(); + auto vResultSrc = get(result.mSecondToResult, get_graph(rSecond), gResult, c.vSrc); + auto vResultTar = get(result.mSecondToResult, get_graph(rSecond), gResult, c.vTar); auto isSrcDeleted = vResultSrc == boost::graph_traits::null_vertex(); auto isTarDeleted = vResultTar == boost::graph_traits::null_vertex(); if(isSrcDeleted && isTarDeleted) { @@ -147,8 +143,8 @@ struct ConvertSecond : public ConstraintVisitor >(c); @@ -168,31 +164,40 @@ struct ConvertSecond : public ConstraintVisitor struct MatchConstraints : Null { + MatchConstraints(const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond) + : rFirst(rFirst), rSecond(rSecond) {} + + template + bool finalize(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + InvertibleVertexMap &match, Result &result) { + using RuleFirst = lib::Rules::LabelledRule; + using RuleSecond = lib::Rules::LabelledRule; + + assert(&dpoFirst == &rFirst.getRule()); + assert(&dpoSecond == &rSecond.getRule()); - template - bool finalize(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) { if(!getConfig().rc.composeConstraints.get()) return true; - for(const auto &cFirst : rFirst.leftMatchConstraints) { + for(const auto &cFirst: get_match_constraints(get_labelled_left(rFirst))) { detail::ConvertFirst visitor(rFirst, result); cFirst->accept(visitor); assert(visitor.cResult); - result.rResult.leftMatchConstraints.push_back(std::move(visitor.cResult)); + result.matchConstraints.push_back(std::move(visitor.cResult)); } - for(const auto &cSecond : rSecond.leftMatchConstraints) { - detail::ConvertSecond visitor(rFirst, rSecond, match, result); + for(const auto &cSecond: get_match_constraints(get_labelled_left(rSecond))) { + detail::ConvertSecond visitor( + rFirst, rSecond, match, result); cSecond->accept(visitor); if(visitor.cResult) - result.rResult.leftMatchConstraints.push_back(std::move(visitor.cResult)); + result.matchConstraints.push_back(std::move(visitor.cResult)); } return true; } +private: + const lib::Rules::LabelledRule &rFirst; + const lib::Rules::LabelledRule &rSecond; }; -} // namespace Visitor -} // namespace RC -} // namespace lib -} // namespace mod - +} // namespace mod::lib::RC::Visitor -#endif /* MOD_LIB_RC_VISITOR_MATCH_CONSTRAINTS_H */ \ No newline at end of file +#endif // MOD_LIB_RC_VISITOR_MATCH_CONSTRAINTS_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/Visitor/Stereo.hpp b/libs/libmod/src/mod/lib/RC/Visitor/Stereo.hpp index d3d7c8c..1775828 100644 --- a/libs/libmod/src/mod/lib/RC/Visitor/Stereo.hpp +++ b/libs/libmod/src/mod/lib/RC/Visitor/Stereo.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_LIB_RC_VISITOR_STEREO_H -#define MOD_LIB_RC_VISITOR_STEREO_H +#ifndef MOD_LIB_RC_VISITOR_STEREO_HPP +#define MOD_LIB_RC_VISITOR_STEREO_HPP #include #include @@ -10,10 +10,7 @@ // TODO: the edge categories are probably not instantiated correctly if the Any category is used -namespace mod { -namespace lib { -namespace RC { -namespace Visitor { +namespace mod::lib::RC::Visitor { // During the algorithm we: // - allocate space in the data arrays @@ -24,7 +21,7 @@ namespace Visitor { // - instantiate and compose embeddings struct Stereo { - using Membership = jla_boost::GraphDPO::Membership; + using Membership = lib::DPO::Membership; template static auto NullVertex() { @@ -36,43 +33,48 @@ struct Stereo { return boost::graph_traits::null_vertex(); } public: - std::vector vDataLeft, vDataRight; + std::vector vDataLeft, vDataRight; std::vector eDataLeft, eDataRight; std::vector vertexInContext, edgeInContext; private: - - template - auto getVertexFirstChecked(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const VertexSecond &vSecond) const { + template + auto getVertexFirstChecked(const InvertibleVertexMap &match, const VertexSecond &vSecond) const { const auto &gCodom = get_graph(get_labelled_right(rFirst)); const auto &gDom = get_graph(get_labelled_left(rSecond)); const auto m = membership(rSecond, vSecond); - if(m == Membership::Right) return NullVertex(gDom); + if(m == Membership::R) return NullVertex(gDom); else return get(match, gDom, gCodom, vSecond); } - template - auto getVertexSecondChecked(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const VertexFirst &vFirst) const { + template + auto getVertexSecondChecked(const InvertibleVertexMap &match, const VertexFirst &vFirst) const { const auto &gCodom = get_graph(get_labelled_right(rFirst)); const auto &gDom = get_graph(get_labelled_left(rSecond)); const auto m = membership(rFirst, vFirst); - if(m == Membership::Left) return NullVertex(gCodom); + if(m == Membership::L) return NullVertex(gCodom); else return get_inverse(match, gDom, gCodom, vFirst); } public: - - template - bool init(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) { + Stereo(const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond) + : rFirst(rFirst), rSecond(rSecond) {} + + template + bool init(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + InvertibleVertexMap &match, Result &result) { + assert(&dpoFirst == &rFirst.getRule()); + assert(&dpoSecond == &rSecond.getRule()); return true; } - template - bool finalize(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) { + template + bool finalize(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + InvertibleVertexMap &match, Result &result) { const auto &gFirst = get_graph(rFirst); const auto &gSecond = get_graph(rSecond); - const auto &gResult = get_graph(result.rResult); - using GraphFirst = typename std::decay < decltype(gFirst)>::type; - using GraphSecond = typename std::decay < decltype(gSecond)>::type; - const auto getGeoName = [](const auto &vGeo) -> const std::string& { + const auto &gResult = result.rDPO->getCombinedGraph(); + using GraphFirst = typename std::decay::type; + using GraphSecond = typename std::decay::type; + const auto getGeoName = [](const auto &vGeo) -> const std::string & { return lib::Stereo::getGeometryGraph().getGraph()[vGeo].name; }; const auto copyAllFromSide = [&]( @@ -86,7 +88,7 @@ struct Stereo { const auto &conf = *get_stereo(glSide)[vInput]; data.vGeometry = conf.getGeometryVertex(); if(Verbose) std::cout << "\tGeometry: " << getGeoName(data.vGeometry) << "\n"; - for(const auto &emb : conf) { + for(const auto &emb: conf) { switch(emb.type) { case lib::Stereo::EmbeddingEdge::Type::LonePair: // offsets should be corrected somewhere else @@ -102,8 +104,8 @@ struct Stereo { const auto vAdjInput = target(eInput, gInput); if(Verbose) { std::cout << "\tmapping edge: (" - << get(boost::vertex_index_t(), gInput, vInput) << ", " - << get(boost::vertex_index_t(), gInput, vAdjInput) << ")\n"; + << get(boost::vertex_index_t(), gInput, vInput) << ", " + << get(boost::vertex_index_t(), gInput, vAdjInput) << ")\n"; } const auto vAdjResult = get(mInputToResult, gInput, gResult, vAdjInput); if(safe) assert(vAdjResult != NullVertex(gResult)); @@ -124,9 +126,9 @@ struct Stereo { }(); if(Verbose) { std::cout << "\tto edge: (" - << get(boost::vertex_index_t(), gResult, vResult) << ", " - << get(boost::vertex_index_t(), gResult, vAdjResult) << "), offset = " - << eResultOffset << " (of " << out_degree(vResult, gResultSide) << ")\n"; + << get(boost::vertex_index_t(), gResult, vResult) << ", " + << get(boost::vertex_index_t(), gResult, vAdjResult) << "), offset = " + << eResultOffset << " (of " << out_degree(vResult, gResultSide) << ")\n"; } const auto eResult = out_edges(vResult, gResult).first[eResultOffset]; const auto eIdResult = get(boost::edge_index_t(), gResult, eResult); @@ -149,29 +151,33 @@ struct Stereo { const auto handleOnly = [&](auto vResult, auto vInput, const auto &rInput, const auto &mInputToResult) { const auto &gInput = get_graph(rInput); // yay, just copy the embedding, both in the right and left side - const auto m = membership(result.rResult, vResult); - if(m != Membership::Right) { + const auto m = gResult[vResult].membership; + if(m != Membership::R) { // copy left if(Verbose) std::cout << "\tLeft:\n"; - const bool partial = copyAllFromSide(std::true_type(), get_labelled_left(rInput), vInput, gInput, mInputToResult, vResult, get_left(result.rResult), vDataLeft, eDataLeft); + const bool partial = copyAllFromSide(std::true_type(), get_labelled_left(rInput), vInput, gInput, + mInputToResult, vResult, result.rDPO->getLProjected(), vDataLeft, + eDataLeft); (void) partial; assert(!partial); } - if(m != Membership::Left) { + if(m != Membership::L) { // copy right if(Verbose) std::cout << "\tRight:\n"; - const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rInput), vInput, gInput, mInputToResult, vResult, get_right(result.rResult), vDataRight, eDataRight); - (void) partial; + const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rInput), vInput, gInput, + mInputToResult, vResult, result.rDPO->getRProjected(), vDataRight, + eDataRight); + (void) partial; assert(!partial); } }; const auto handleBoth = [&](auto vResult, auto vFirst, auto vSecond) { using EmbEdge = lib::Stereo::EmbeddingEdge; // const auto &geo = lib::Stereo::getGeometryGraph(); - const auto m = membership(result.rResult, vResult); + const auto m = gResult[vResult].membership; const auto vResultId = get(boost::vertex_index_t(), gResult, vResult); - const auto &gR1 = get_right(rFirst); - const auto &gL2 = get_left(rSecond); + const auto &gR1 = getR(rFirst.getRule()); + const auto &gL2 = getL(rSecond.getRule()); const auto &confR1 = *get_stereo(get_labelled_right(rFirst))[vFirst]; const auto &confL2 = *get_stereo(get_labelled_left(rSecond))[vSecond]; // Let's firs tmake a map of the offsets to each other. @@ -182,13 +188,15 @@ struct Stereo { offsetSecondToFirst(confL2.degree(), -1); { // heuristically we 'usually' have gSecondL as a subgraph of gFirstR std::size_t offsetFirst = 0; - for(auto oeFirst = out_edges(vFirst, gR1); oeFirst.first != oeFirst.second; ++oeFirst.first, (void) ++offsetFirst) { + for(auto oeFirst = out_edges(vFirst, gR1); + oeFirst.first != oeFirst.second; ++oeFirst.first, (void) ++offsetFirst) { const auto eFirst = *oeFirst.first; const auto vAdjFirst = target(eFirst, gR1); const auto vAdjSecond = get_inverse(match, gL2, gR1, vAdjFirst); if(vAdjSecond == NullVertex(gSecond)) continue; std::size_t offsetSecond = 0; - for(auto oeSecond = out_edges(vSecond, gL2); oeSecond.first != oeSecond.second; ++oeSecond.first, (void) ++offsetSecond) { + for(auto oeSecond = out_edges(vSecond, gL2); + oeSecond.first != oeSecond.second; ++oeSecond.first, (void) ++offsetSecond) { const auto eSecondCand = *oeSecond.first; if(target(eSecondCand, gL2) == vAdjSecond) { offsetFirstToSecond[offsetFirst] = offsetSecond; @@ -206,17 +214,19 @@ struct Stereo { // TODO: how do we merge virtuals? MOD_ABORT; } - const bool firstToSecondSubgraph = std::none_of(offsetFirstToSecond.begin(), offsetFirstToSecond.end(), [&](auto o) { - return o == -1; - }); - const bool secondToFirstSubgraph = std::none_of(offsetSecondToFirst.begin(), offsetSecondToFirst.end(), [&](auto o) { - return o == -1; - }); + const bool firstToSecondSubgraph = std::none_of(offsetFirstToSecond.begin(), offsetFirstToSecond.end(), + [&](auto o) { + return o == -1; + }); + const bool secondToFirstSubgraph = std::none_of(offsetSecondToFirst.begin(), offsetSecondToFirst.end(), + [&](auto o) { + return o == -1; + }); const auto geoR1 = confR1.getGeometryVertex(); const auto geoL2 = confL2.getGeometryVertex(); - if(m != Membership::Right) { - assert(membership(rFirst, vFirst) == Membership::Context); + if(m != Membership::R) { + assert(membership(rFirst, vFirst) == Membership::K); // auto &data = vDataLeft[vResultId]; // const auto &eData = eDataLeft; const auto firstInContext = get_stereo(rFirst).inContext(vFirst); @@ -224,14 +234,16 @@ struct Stereo { const auto geoL1 = confL1.getGeometryVertex(); if(Verbose) std::cout << "\tHandling L\n" - << "\t\tGeo L1: " << getGeoName(geoL1) << "\n" - << "\t\tGeo R1: " << getGeoName(geoR1) << "\n" - << "\t\tGeo L2: " << getGeoName(geoL2) << "\n"; + << "\t\tGeo L1: " << getGeoName(geoL1) << "\n" + << "\t\tGeo R1: " << getGeoName(geoR1) << "\n" + << "\t\tGeo L2: " << getGeoName(geoL2) << "\n"; if(firstInContext) { if(Verbose) std::cout << "\t\tFirst stereo in context\n"; if(secondToFirstSubgraph) { if(Verbose) std::cout << "\t\tSecond-to-first subgraph: copy and map L1/R1 to L\n"; - const bool partial = copyAllFromSide(std::true_type(), get_labelled_left(rFirst), vFirst, gFirst, result.mFirstToResult, vResult, get_left(result.rResult), vDataLeft, eDataLeft); + const bool partial = copyAllFromSide(std::true_type(), get_labelled_left(rFirst), vFirst, gFirst, + result.mFirstToResult, vResult, result.rDPO->getLProjected(), + vDataLeft, eDataLeft); (void) partial; assert(!partial); } else if(firstToSecondSubgraph) { @@ -245,7 +257,9 @@ struct Stereo { if(Verbose) std::cout << "\t\tFirst stereo changes\n"; if(secondToFirstSubgraph) { if(Verbose) std::cout << "\t\tSecond-to-first subgraph: copy and map L1 to L\n"; - const bool partial = copyAllFromSide(std::true_type(), get_labelled_left(rFirst), vFirst, gFirst, result.mFirstToResult, vResult, get_left(result.rResult), vDataLeft, eDataLeft); + const bool partial = copyAllFromSide(std::true_type(), get_labelled_left(rFirst), vFirst, gFirst, + result.mFirstToResult, vResult, result.rDPO->getLProjected(), + vDataLeft, eDataLeft); (void) partial; assert(!partial); } else if(firstToSecondSubgraph) { @@ -257,8 +271,8 @@ struct Stereo { } } } - if(m != Membership::Left) { - assert(membership(rSecond, vSecond) == Membership::Context); + if(m != Membership::L) { + assert(membership(rSecond, vSecond) == Membership::K); auto &data = vDataRight[vResultId]; const auto &eData = eDataRight; const auto secondInContext = get_stereo(rSecond).inContext(vSecond); @@ -266,20 +280,24 @@ struct Stereo { const auto geoR2 = confR2.getGeometryVertex(); if(Verbose) std::cout << "\tHandling R\n" - << "\t\tGeo R1: " << getGeoName(geoR1) << "\n" - << "\t\tGeo L2: " << getGeoName(geoL2) << "\n" - << "\t\tGeo R2: " << getGeoName(geoR2) << "\n"; + << "\t\tGeo R1: " << getGeoName(geoR1) << "\n" + << "\t\tGeo L2: " << getGeoName(geoL2) << "\n" + << "\t\tGeo R2: " << getGeoName(geoR2) << "\n"; if(secondInContext) { if(Verbose) std::cout << "\t\tSecond stereo in context\n"; if(firstToSecondSubgraph) { if(Verbose) std::cout << "\t\tFirst-to-second subgraph: copy and map L2/R2 to R\n"; - const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rSecond), vSecond, gSecond, result.mSecondToResult, vResult, get_right(result.rResult), vDataRight, eDataRight); + const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rSecond), vSecond, gSecond, + result.mSecondToResult, vResult, result.rDPO->getRProjected(), + vDataRight, eDataRight); (void) partial; assert(!partial); } else if(secondToFirstSubgraph) { if(Verbose) std::cout << "\t\tSecond-to-first subgraph: copy and map R1 to R\n"; - const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rFirst), vFirst, gFirst, result.mFirstToResult, vResult, get_right(result.rResult), vDataRight, eDataRight); - (void) partial; + const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rFirst), vFirst, gFirst, + result.mFirstToResult, vResult, result.rDPO->getRProjected(), + vDataRight, eDataRight); + (void) partial; assert(!partial); } else { if(Verbose) std::cout << "\t\tNon-subgraph: do a merge\n"; @@ -289,19 +307,22 @@ struct Stereo { if(Verbose) std::cout << "\t\tSecond stereo changes\n"; if(firstToSecondSubgraph) { if(Verbose) std::cout << "\t\tFirst-to-second subgraph: copy and map R2 to R\n"; - const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rSecond), vSecond, gSecond, result.mSecondToResult, vResult, get_right(result.rResult), vDataRight, eDataRight); - (void) partial; + const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rSecond), vSecond, gSecond, + result.mSecondToResult, vResult, result.rDPO->getRProjected(), + vDataRight, eDataRight); + (void) partial; assert(!partial); } else if(secondToFirstSubgraph) { if(Verbose) { std::cout << "\t\tSecond-to-first subgraph:\n" - << "\t\t\t- Copy all from R2.\n" - << "\t\t\t- Copy unmatched from R1.\n" - << "\t\t\t- Match R2 stereo onto the result and check if the pushout is valid.\n" - ; + << "\t\t\t- Copy all from R2.\n" + << "\t\t\t- Copy unmatched from R1.\n" + << "\t\t\t- Match R2 stereo onto the result and check if the pushout is valid.\n"; std::cout << "\tCopying all from R2\n"; } - const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rSecond), vSecond, gSecond, result.mSecondToResult, vResult, get_right(result.rResult), vDataRight, eDataRight); + const bool partial = copyAllFromSide(std::true_type(), get_labelled_right(rSecond), vSecond, gSecond, + result.mSecondToResult, vResult, result.rDPO->getRProjected(), + vDataRight, eDataRight); (void) partial; assert(!partial); const auto sizeAfterR2 = data.edges.size(); @@ -312,15 +333,15 @@ struct Stereo { const auto &gBaseInput = get_graph(rFirst); const auto &glSide = get_labelled_right(rFirst); const auto &glSideOther = get_labelled_left(rSecond); - const auto &gResultSide = get_right(result.rResult); + const auto &gResultSide = result.rDPO->getRProjected(); const auto &mInputToResult = result.mFirstToResult; const auto mapToOtherInput = [&](const auto &vFirst) { // the "this->" part is needed to get GCC to not seg. fault - return this->getVertexSecondChecked(rFirst, rSecond, match, vFirst); + return this->getVertexSecondChecked(match, vFirst); }; // the rest should be side invariant const auto &conf = *get_stereo(glSide)[vInput]; - for(const auto &emb : conf) { + for(const auto &emb: conf) { switch(emb.type) { case lib::Stereo::EmbeddingEdge::Type::LonePair: case lib::Stereo::EmbeddingEdge::Type::Radical: @@ -333,8 +354,8 @@ struct Stereo { const auto vAdjInput = target(eInput, gInput); if(Verbose) { std::cout << "\tmapping edge: (" - << get(boost::vertex_index_t(), gInput, vInput) << ", " - << get(boost::vertex_index_t(), gInput, vAdjInput) << ")\n"; + << get(boost::vertex_index_t(), gInput, vInput) << ", " + << get(boost::vertex_index_t(), gInput, vAdjInput) << ")\n"; } const auto vAdjResult = get(mInputToResult, gBaseInput, gResult, vAdjInput); if(vAdjResult == NullVertex(gResult)) { @@ -354,19 +375,21 @@ struct Stereo { if(Verbose) std::cout << "\tnot matched, due to vAdjInputOther = null\n"; return false; } - for(auto eInputOther : asRange(out_edges(vInputOther, gInputOther))) { + for(auto eInputOther: asRange(out_edges(vInputOther, gInputOther))) { if(target(eInputOther, gInputOther) != vAdjInputOther) { if(Verbose) { std::cout << "\tcand = (" - << get(boost::vertex_index_t(), gInputOther, vInputOther) << ", " - << get(boost::vertex_index_t(), gInputOther, vAdjInputOther) << ") not it\n"; + << get(boost::vertex_index_t(), gInputOther, vInputOther) << ", " + << get(boost::vertex_index_t(), gInputOther, vAdjInputOther) + << ") not it\n"; } continue; } if(Verbose) { std::cout << "\tcand = (" - << get(boost::vertex_index_t(), gInputOther, vInputOther) << ", " - << get(boost::vertex_index_t(), gInputOther, vAdjInputOther) << ") is it\n"; + << get(boost::vertex_index_t(), gInputOther, vInputOther) << ", " + << get(boost::vertex_index_t(), gInputOther, vAdjInputOther) + << ") is it\n"; } // TODO: shouldn't we check the membership as well? return true; @@ -382,17 +405,18 @@ struct Stereo { // now find the corresponding edge in result const auto eResultOffset = [&]() { const auto oeResult = out_edges(vResult, gResultSide); - const auto oeIter = std::find_if(oeResult.first, oeResult.second, [&](const auto &eResultCand) { - return target(eResultCand, gResult) == vAdjResult; - }); + const auto oeIter = std::find_if(oeResult.first, oeResult.second, + [&](const auto &eResultCand) { + return target(eResultCand, gResult) == vAdjResult; + }); assert(oeIter != oeResult.second); return std::distance(oeResult.first, oeIter); }(); if(Verbose) { std::cout << "\tto edge: (" - << get(boost::vertex_index_t(), gResult, vResult) << ", " - << get(boost::vertex_index_t(), gResult, vAdjResult) << "), offset = " - << eResultOffset << " (of " << out_degree(vResult, gResultSide) << ")\n"; + << get(boost::vertex_index_t(), gResult, vResult) << ", " + << get(boost::vertex_index_t(), gResult, vAdjResult) << "), offset = " + << eResultOffset << " (of " << out_degree(vResult, gResultSide) << ")\n"; } const auto eResult = out_edges(vResult, gResult).first[eResultOffset]; const auto eIdResult = get(boost::edge_index_t(), gResult, eResult); @@ -413,12 +437,15 @@ struct Stereo { } } if(false) { - copyAllFromSide(std::false_type(), get_labelled_right(rSecond), vSecond, gSecond, result.mSecondToResult, vResult, get_right(result.rResult), vDataRight, eDataRight); + copyAllFromSide(std::false_type(), get_labelled_right(rSecond), vSecond, gSecond, result.mSecondToResult, + vResult, result.rDPO->getRProjected(), vDataRight, eDataRight); const auto prevEmbSize = data.edges.size(); if(data.edges.size() > prevEmbSize) { // we the fixation must be free - if(Verbose) std::cout << "\tfix: data.edges.size() = " << data.edges.size() << " > " << prevEmbSize << " = prevEmbSize, so set free (was " << data.fix << ")\n"; + if(Verbose) + std::cout << "\tfix: data.edges.size() = " << data.edges.size() << " > " << prevEmbSize + << " = prevEmbSize, so set free (was " << data.fix << ")\n"; data.fix = lib::Stereo::Fixation::free(); } else { if(Verbose) std::cout << "\tfix: not changing it (" << data.fix << ")\n"; @@ -428,9 +455,9 @@ struct Stereo { }; // handleBoth() if(Verbose) std::cout << "Stereo Finalization\n" << std::string(80, '-') << '\n'; const auto &gGeometry = lib::Stereo::getGeometryGraph().getGraph(); - for(auto vResult : asRange(vertices(gResult))) { + for(auto vResult: asRange(vertices(gResult))) { if(Verbose) std::cout << "Result vertex: " << get(boost::vertex_index_t(), gResult, vResult) << "\n"; - const auto m = membership(result.rResult, vResult); + const auto m = result.rDPO->getCombinedGraph()[vResult].membership; const auto vResultId = get(boost::vertex_index_t(), gResult, vResult); // If vResult is in only first or only second, we should be able to just copy the embedding. const auto vFirst = get_inverse(result.mFirstToResult, gFirst, gResult, vResult); @@ -440,7 +467,7 @@ struct Stereo { const auto instantiateConfs = [&]() { if(Verbose) std::cout << "\tinstantiating configurations\n"; // TODO: we should probably correct LonePair and Radical offsets here - if(m != Membership::Right) { + if(m != Membership::R) { auto &data = vDataLeft[vResultId]; data.configuration = gGeometry[data.vGeometry].constructor( data.edges.data(), data.edges.data() + data.edges.size(), data.fix, ssErr); @@ -450,7 +477,7 @@ struct Stereo { MOD_ABORT; } } - if(m != Membership::Left) { + if(m != Membership::L) { auto &data = vDataRight[vResultId]; data.configuration = gGeometry[data.vGeometry].constructor( data.edges.data(), data.edges.data() + data.edges.size(), data.fix, ssErr); @@ -476,16 +503,15 @@ struct Stereo { } } - using GraphResult = typename std::decay < decltype(gResult)>::type; + using GraphResult = typename std::decay::type; using ResultVertex = typename boost::graph_traits::vertex_descriptor; using ResultEdge = typename boost::graph_traits::edge_descriptor; struct Inf { const GraphResult &g; - std::vector vData; + std::vector vData; std::vector eData; public: - std::unique_ptr extractConfiguration(const ResultVertex &v) { auto vId = get(boost::vertex_index_t(), g, v); assert(vData[vId].configuration); @@ -504,18 +530,19 @@ struct Stereo { const auto edgeInContext = [this, &gResult](const auto &e) -> bool { return this->edgeInContext[get(boost::edge_index_t(), gResult, e)]; }; - result.rResult.pStereo.reset(new lib::Rules::PropStereoCore(gResult, - Inf{gResult, std::move(vDataLeft), std::move(eDataLeft)}, Inf{gResult, std::move(vDataRight), std::move(eDataRight)}, - vertexInContext, edgeInContext)); + result.pStereo.reset(new lib::Rules::PropStereo( + *result.rDPO, + Inf{gResult, std::move(vDataLeft), std::move(eDataLeft)}, + Inf{gResult, std::move(vDataRight), std::move(eDataRight)}, + vertexInContext, edgeInContext)); return true; } public: - - template - void copyVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const VertexFirst &vFirst, const VertexResult &vResult) { - const auto &gResult = get_graph(result.rResult); + template + void copyVertexFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + const VertexFirst &vFirst, const VertexResult &vResult) { + const auto &gResult = result.rDPO->getCombinedGraph(); const auto vResultId = get(boost::vertex_index_t(), gResult, vResult); (void) vResultId; assert(vDataLeft.size() + 1 == num_vertices(gResult)); @@ -527,11 +554,12 @@ struct Stereo { vertexInContext.push_back(get_stereo(rFirst).inContext(vFirst)); } - template - void copyVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const VertexSecond &vSecond, const VertexResult &vResult) { - const auto &gResult = get_graph(result.rResult); + template + void copyVertexSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + const VertexSecond &vSecond, const VertexResult &vResult) { + const auto &gResult = result.rDPO->getCombinedGraph(); const auto vResultId = get(boost::vertex_index_t(), gResult, vResult); (void) vResultId; assert(vDataLeft.size() + 1 == num_vertices(gResult)); @@ -543,15 +571,16 @@ struct Stereo { vertexInContext.push_back(get_stereo(rSecond).inContext(vSecond)); } - template - void copyEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const EdgeFirst &eFirst, const EdgeResult &eResult) { - using GraphSecond = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; + template + void copyEdgeFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + const EdgeFirst &eFirst, const EdgeResult &eResult) { + using GraphSecond = lib::Rules::LabelledRule::GraphType; const auto m = membership(rFirst, eFirst); const auto &gFirst = get_graph(rFirst); const auto &gSecond = get_graph(rSecond); - const auto &gResult = get_graph(result.rResult); + const auto &gResult = result.rDPO->getCombinedGraph(); const auto eResultId = get(boost::edge_index_t(), gResult, eResult); assert(eDataLeft.size() + 1 == num_edges(gResult)); assert(eDataLeft.size() == eDataRight.size()); @@ -560,20 +589,20 @@ struct Stereo { eDataLeft.emplace_back(); eDataRight.emplace_back(); edgeInContext.push_back(get_stereo(rFirst).inContext(eFirst)); - if(m != Membership::Right) { + if(m != Membership::R) { // copy from rFirst left eDataLeft[eResultId] = get_stereo(get_labelled_left(rFirst))[eFirst]; } - if(m != Membership::Left) { + if(m != Membership::L) { // copy from the epimorphic overlap // if the edge is matched, get data from the unifier // otherwise from rFirst - const auto vSecondSrc = getVertexSecondChecked(rFirst, rSecond, match, source(eFirst, gFirst)); - const auto vSecondTar = getVertexSecondChecked(rFirst, rSecond, match, target(eFirst, gFirst)); + const auto vSecondSrc = getVertexSecondChecked(match, source(eFirst, gFirst)); + const auto vSecondTar = getVertexSecondChecked(match, target(eFirst, gFirst)); const bool isMatched = [&]() { if(vSecondSrc == NullVertex()) return false; if(vSecondTar == NullVertex()) return false; - for(auto eSecond : asRange(out_edges(vSecondSrc, gSecond))) { + for(auto eSecond: asRange(out_edges(vSecondSrc, gSecond))) { if(target(eSecond, gSecond) != vSecondTar) continue; // TODO: shouldn't we check the membership as well? return true; @@ -584,20 +613,22 @@ struct Stereo { eDataRight[eResultId] = get_stereo(get_labelled_right(rFirst))[eFirst]; } else { auto data = get_prop(lib::GraphMorphism::StereoDataT(), match); - eDataRight[eResultId] = get_edge_category(data, get_labelled_left(rSecond), get_labelled_right(rFirst), eFirst); + eDataRight[eResultId] = get_edge_category(data, get_labelled_left(rSecond), get_labelled_right(rFirst), + eFirst); } } } - template - void copyEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const EdgeSecond &eSecond, const EdgeResult &eResult) { - using GraphFirst = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; - const auto m = membership(result.rResult, eResult); + template + void copyEdgeSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + const EdgeSecond &eSecond, const EdgeResult &eResult) { + using GraphFirst = lib::Rules::LabelledRule::GraphType; const auto &gFirst = get_graph(rFirst); const auto &gSecond = get_graph(rSecond); - const auto &gResult = get_graph(result.rResult); + const auto &gResult = result.rDPO->getCombinedGraph(); + const auto m = gResult[eResult].membership; const auto eResultId = get(boost::edge_index_t(), gResult, eResult); assert(eDataLeft.size() + 1 == num_edges(gResult)); assert(eDataLeft.size() == eDataRight.size()); @@ -606,16 +637,16 @@ struct Stereo { eDataLeft.emplace_back(); eDataRight.emplace_back(); edgeInContext.push_back(get_stereo(rSecond).inContext(eSecond)); - if(m != Membership::Right) { + if(m != Membership::R) { // copy from the epimorphic overlap // if the edge is matched, get data from the unifier // otherwise from rSecond - const auto vFirstSrc = getVertexFirstChecked(rFirst, rSecond, match, source(eSecond, gSecond)); - const auto vFirstTar = getVertexFirstChecked(rFirst, rSecond, match, target(eSecond, gSecond)); + const auto vFirstSrc = getVertexFirstChecked(match, source(eSecond, gSecond)); + const auto vFirstTar = getVertexFirstChecked(match, target(eSecond, gSecond)); const bool isMatched = [&]() { if(vFirstSrc == NullVertex()) return false; if(vFirstTar == NullVertex()) return false; - for(auto eFirst : asRange(out_edges(vFirstSrc, gFirst))) { + for(auto eFirst: asRange(out_edges(vFirstSrc, gFirst))) { if(target(eFirst, gFirst) != vFirstTar) continue; // TODO: shouldn't we check the membership as well? return true; @@ -629,13 +660,12 @@ struct Stereo { eDataLeft[eResultId] = get_stereo(get_labelled_left(rSecond))[eSecond]; } } - if(m != Membership::Left) { + if(m != Membership::L) { // copy from rSecond right eDataRight[eResultId] = get_stereo(get_labelled_right(rSecond))[eSecond]; } } private: - template void printData(std::ostream &s, GeometryVertex geometryVertex, IterEmb firstEmb, IterEmb lastEmb) { const auto &gGeometry = lib::Stereo::getGeometryGraph().getGraph(); @@ -663,156 +693,163 @@ struct Stereo { } s << "]"; } - public: - - template - void printVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexFirst &vFirst) { + template + void printVertexFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const VertexFirst &vFirst) { const auto m = membership(rFirst, vFirst); s << ", Stereo("; - if(m != Membership::Right) { + if(m != Membership::R) { const auto &conf = *get_stereo(get_labelled_left(rFirst))[vFirst]; printData(s, conf.getGeometryVertex(), conf.begin(), conf.end()); } else s << "<>"; s << " ->"; if(!get_stereo(rFirst).inContext(vFirst)) s << "!"; s << " "; - if(m != Membership::Left) { + if(m != Membership::L) { const auto &conf = *get_stereo(get_labelled_right(rFirst))[vFirst]; printData(s, conf.getGeometryVertex(), conf.begin(), conf.end()); } else s << "<>"; s << ")"; } - template - void printVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexSecond &vSecond) { + template + void printVertexSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const VertexSecond &vSecond) { const auto m = membership(rSecond, vSecond); s << ", Stereo("; - if(m != Membership::Right) { + if(m != Membership::R) { const auto &conf = *get_stereo(get_labelled_left(rSecond))[vSecond]; printData(s, conf.getGeometryVertex(), conf.begin(), conf.end()); } else s << "<>"; s << " ->"; if(!get_stereo(rSecond).inContext(vSecond)) s << "!"; s << " "; - if(m != Membership::Left) { + if(m != Membership::L) { const auto &conf = *get_stereo(get_labelled_right(rSecond))[vSecond]; printData(s, conf.getGeometryVertex(), conf.begin(), conf.end()); } else s << "<>"; s << ")"; } - template - void printVertexResult(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexResult &vResult) { - const auto m = membership(result.rResult, vResult); - const auto &gResult = get_graph(result.rResult); + template + void printVertexResult(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const VertexResult &vResult) { + const auto &gResult = result.rDPO->getCombinedGraph(); + const auto m = gResult[vResult].membership; const auto vResultId = get(boost::vertex_index_t(), gResult, vResult); const auto &dLeft = vDataLeft[vResultId]; const auto &dRight = vDataRight[vResultId]; s << ", Stereo("; - if(m != Membership::Right) + if(m != Membership::R) printData(s, dLeft.vGeometry, dLeft.edges.begin(), dLeft.edges.end()); else s << "<>"; s << " ->"; if(!vertexInContext[vResultId]) s << "!"; s << " "; - if(m != Membership::Left) + if(m != Membership::L) printData(s, dRight.vGeometry, dRight.edges.begin(), dRight.edges.end()); else s << "<>"; s << ")"; } - template - void printEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const EdgeFirst &eFirst) { + template + void printEdgeFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const EdgeFirst &eFirst) { const auto m = membership(rFirst, eFirst); s << ", Stereo("; - if(m != Membership::Right) { + if(m != Membership::R) { s << get_stereo(get_labelled_left(rFirst))[eFirst]; } else s << "<>"; s << " ->"; if(!get_stereo(rFirst).inContext(eFirst)) s << "!"; s << " "; - if(m != Membership::Left) { + if(m != Membership::L) { s << get_stereo(get_labelled_right(rFirst))[eFirst]; } else s << "<>"; s << ")"; } - template - void printEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const EdgeSecond &eSecond) { + template + void printEdgeSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const EdgeSecond &eSecond) { const auto m = membership(rSecond, eSecond); s << ", Stereo("; - if(m != Membership::Right) { + if(m != Membership::R) { s << get_stereo(get_labelled_left(rSecond))[eSecond]; } else s << "<>"; s << " ->"; if(!get_stereo(rSecond).inContext(eSecond)) s << "!"; s << " "; - if(m != Membership::Left) { + if(m != Membership::L) { s << get_stereo(get_labelled_right(rSecond))[eSecond]; } else s << "<>"; s << ")"; } -public: - template - void composeVertexRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { +public: + template + void composeVertexRvsLR(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + VertexResult vResult, VertexSecond vSecond) { // -> a | a -> b, maybe a == b // to // -> b - assert(!vertexInContext[get(boost::vertex_index_t(), get_graph(result.rResult), vResult)]); + assert(!vertexInContext[get(boost::vertex_index_t(), result.rDPO->getCombinedGraph(), vResult)]); } - template - void composeVertexLRvsL(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { + template + void composeVertexLRvsL(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + VertexResult vResult, VertexSecond vSecond) { // a -> a | a -> // b -> a | a -> // to // a -> // b -> - const auto vIdResult = get(boost::vertex_index_t(), get_graph(result.rResult), vResult); + const auto vIdResult = get(boost::vertex_index_t(), result.rDPO->getCombinedGraph(), vResult); vertexInContext[vIdResult] = false; } - template - void composeVertexLRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { + template + void composeVertexLRvsLR(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + VertexResult vResult, VertexSecond vSecond) { // a != b, a != c, b =? c // a -> a | a -> a // a -> a | a -> c // b -> a | a -> a // b -> a | a -> c // if both vertices are in context, so should the result be - const auto vIdResult = get(boost::vertex_index_t(), get_graph(result.rResult), vResult); + const auto vIdResult = get(boost::vertex_index_t(), result.rDPO->getCombinedGraph(), vResult); vertexInContext[vIdResult] = vertexInContext[vIdResult] && get_stereo(rSecond).inContext(vSecond); } public: - - template - void setEdgeResultRightFromSecondRight(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - EdgeResult eResult, EdgeSecond eSecond) { + template + void + setEdgeResultRightFromSecondRight(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + EdgeResult eResult, EdgeSecond eSecond) { // | -> vs. -> |, simply copy the R2 to R // | -> | vs. | -> |, do the copy, but also set inContext if both are in context - const auto eIdResult = get(boost::edge_index_t(), get_graph(result.rResult), eResult); + const auto eIdResult = get(boost::edge_index_t(), result.rDPO->getCombinedGraph(), eResult); eDataRight[eIdResult] = get_stereo(get_labelled_right(rSecond))[eSecond]; edgeInContext[eIdResult] = edgeInContext[eIdResult] && get_stereo(rSecond).inContext(eSecond); } +private: + const lib::Rules::LabelledRule &rFirst; + const lib::Rules::LabelledRule &rSecond; }; -} // namespace Composer -} // namespace RC -} // namespace lib -} // namespace mod +} // namespace mod::lib::RC::Visitor -#endif /* MOD_LIB_RC_VISITOR_STEREO_H */ +#endif // MOD_LIB_RC_VISITOR_STEREO_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/Visitor/String.hpp b/libs/libmod/src/mod/lib/RC/Visitor/String.hpp index cb50260..511eb70 100644 --- a/libs/libmod/src/mod/lib/RC/Visitor/String.hpp +++ b/libs/libmod/src/mod/lib/RC/Visitor/String.hpp @@ -1,105 +1,112 @@ -#ifndef MOD_LIB_RC_VISITOR_STRING_H -#define MOD_LIB_RC_VISITOR_STRING_H +#ifndef MOD_LIB_RC_VISITOR_STRING_HPP +#define MOD_LIB_RC_VISITOR_STRING_HPP #include #include -namespace mod { -namespace lib { -namespace RC { -namespace Visitor { +namespace mod::lib::RC::Visitor { struct String { - using Membership = jla_boost::GraphDPO::Membership; + using Membership = lib::DPO::Membership; public: + String(const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond) + : rFirst(rFirst), rSecond(rSecond) {} - template - bool init(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, Result &result) { - result.rResult.pString = std::make_unique (get_graph(result.rResult)); + template + bool init(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, Result &result) { + assert(&dpoFirst == &rFirst.getRule()); + assert(&dpoSecond == &rSecond.getRule()); + + result.pString = std::make_unique(*result.rDPO); return true; } - template - bool finalize(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) { - result.rResult.pString->verify(&get_graph(result.rResult)); + template + bool finalize(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + InvertibleVertexMap &match, Result &result) { + result.pString->verify(); return true; } public: - - template - void copyVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const VertexFirst &vFirst, const VertexResult &vResult) { - copyVertex(rFirst, result.rResult, vFirst, vResult, *rFirst.pString); + template + void copyVertexFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + const VertexFirst &vFirst, const VertexResult &vResult) { + copyVertex(rFirst, result, vFirst, vResult, *rFirst.pString); } - template - void copyVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const VertexSecond &vSecond, const VertexResult &vResult) { - copyVertex(rSecond, result.rResult, vSecond, vResult, *rSecond.pString); + template + void copyVertexSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + const VertexSecond &vSecond, const VertexResult &vResult) { + copyVertex(rSecond, result, vSecond, vResult, *rSecond.pString); } - template - void copyEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const EdgeFirst &eFirst, const EdgeResult &eResult) { - copyEdge(rFirst, result.rResult, eFirst, eResult, *rFirst.pString); + template + void copyEdgeFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + const EdgeFirst &eFirst, const EdgeResult &eResult) { + copyEdge(rFirst, result, eFirst, eResult, *rFirst.pString); } - template - void copyEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - const EdgeSecond &eSecond, const EdgeResult &eResult) { - copyEdge(rSecond, result.rResult, eSecond, eResult, *rSecond.pString); + template + void copyEdgeSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + const EdgeSecond &eSecond, const EdgeResult &eResult) { + copyEdge(rSecond, result, eSecond, eResult, *rSecond.pString); } public: - - template - void printVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexFirst &vFirst) { + template + void printVertexFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const VertexFirst &vFirst) { rFirst.pString->print(s, vFirst); } - template - void printVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexSecond &vSecond) { + template + void printVertexSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const VertexSecond &vSecond) { rSecond.pString->print(s, vSecond); } - template - void printVertexResult(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const VertexResult &vResult) { - result.rResult.pString->print(s, vResult); + template + void printVertexResult(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const VertexResult &vResult) { + result.pString->print(s, vResult); } - template - void printEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const EdgeFirst &eFirst) { + template + void printEdgeFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const EdgeFirst &eFirst) { rFirst.pString->print(s, eFirst); } - template - void printEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - std::ostream &s, const EdgeSecond &eSecond) { + template + void printEdgeSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + std::ostream &s, const EdgeSecond &eSecond) { rSecond.pString->print(s, eSecond); } -public: - template - void composeVertexRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { +public: + template + void composeVertexRvsLR(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + VertexResult vResult, VertexSecond vSecond) { // -> a | a -> b, maybe a == b - result.rResult.pString->setRight(vResult, rSecond.pString->getRight()[vSecond]); + result.pString->setRight(vResult, rSecond.pString->getRight()[vSecond]); // -> b } - template - void composeVertexLRvsL(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { + template + void composeVertexLRvsL(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + VertexResult vResult, VertexSecond vSecond) { // vFirst is CONTEXT, so do nothing // a -> a | a -> // b -> a | a -> @@ -108,70 +115,70 @@ struct String { // b -> } - template - void composeVertexLRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - VertexResult vResult, VertexSecond vSecond) { + template + void composeVertexLRvsLR(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + VertexResult vResult, VertexSecond vSecond) { // the left label of vResult is ok, but the right label might have to change // a != b, a != c, b =? c // a -> a | a -> a // a -> a | a -> c // b -> a | a -> a // b -> a | a -> c - result.rResult.pString->setRight(vResult, rSecond.pString->getRight()[vSecond]); + result.pString->setRight(vResult, rSecond.pString->getRight()[vSecond]); } public: - - template - void setEdgeResultRightFromSecondRight(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, const Result &result, - EdgeResult eResult, EdgeSecond eSecond) { - result.rResult.pString->setRight(eResult, rSecond.pString->getRight()[eSecond]); + template + void + setEdgeResultRightFromSecondRight(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + EdgeResult eResult, EdgeSecond eSecond) { + result.pString->setRight(eResult, rSecond.pString->getRight()[eSecond]); } private: - - template - void copyVertex(const RuleFrom &rFrom, const RuleResult &rResult, VertexFrom vFrom, VertexResult vResult, const Prop &pFrom) { - assert(rResult.pString); - auto &pString = *rResult.pString; + template + void copyVertex(const RuleFrom &rFrom, const Result &result, VertexFrom vFrom, VertexResult vResult, + const Prop &pFrom) { + assert(result.pString); + auto &pString = *result.pString; auto m = membership(rFrom, vFrom); - assert(m == membership(rResult, vResult)); + assert(m == result.rDPO->getCombinedGraph()[vResult].membership); switch(m) { - case Membership::Left: + case Membership::L: pString.add(vResult, pFrom.getLeft()[vFrom], ""); break; - case Membership::Right: + case Membership::R: pString.add(vResult, "", pFrom.getRight()[vFrom]); break; - case Membership::Context: + case Membership::K: pString.add(vResult, pFrom.getLeft()[vFrom], pFrom.getRight()[vFrom]); break; } } - template - void copyEdge(const RuleFrom &rFrom, const RuleResult &rResult, EdgeFrom eFrom, EdgeResult eResult, const Prop &pFrom) { + template + void copyEdge(const RuleFrom &rFrom, const Result &result, EdgeFrom eFrom, EdgeResult eResult, const Prop &pFrom) { // the membership of eFrom may be different from eResult - assert(rResult.pString); - auto &pString = *rResult.pString; - auto m = membership(rResult, eResult); + assert(result.pString); + auto &pString = *result.pString; + auto m = result.rDPO->getCombinedGraph()[eResult].membership; switch(m) { - case Membership::Left: + case Membership::L: pString.add(eResult, pFrom.getLeft()[eFrom], ""); break; - case Membership::Right: + case Membership::R: pString.add(eResult, "", pFrom.getRight()[eFrom]); break; - case Membership::Context: + case Membership::K: pString.add(eResult, pFrom.getLeft()[eFrom], pFrom.getRight()[eFrom]); break; } } +private: + const lib::Rules::LabelledRule &rFirst; + const lib::Rules::LabelledRule &rSecond; }; -} // namespace Composer -} // namespace RC -} // namespace lib -} // namespace mod +} // namespace mod::lib::RC::Visitor -#endif /* MOD_LIB_RC_VISITOR_STRING_H */ +#endif // MOD_LIB_RC_VISITOR_STRING_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/RC/Visitor/Term.hpp b/libs/libmod/src/mod/lib/RC/Visitor/Term.hpp index f68ca92..9e5a372 100644 --- a/libs/libmod/src/mod/lib/RC/Visitor/Term.hpp +++ b/libs/libmod/src/mod/lib/RC/Visitor/Term.hpp @@ -2,206 +2,217 @@ #define MOD_LIB_RC_VISITOR_TERM_HPP #include -#include #include #include #include - -#include +#include namespace mod::lib::RC::Visitor { + static constexpr std::size_t TERM_MAX = std::numeric_limits::max(); struct Term { - using Membership = jla_boost::GraphDPO::Membership; + using Membership = lib::DPO::Membership; using AddressType = lib::Term::AddressType; using Cell = lib::Term::Cell; using CellTag = lib::Term::Cell::Tag; public: - template - bool init(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) { - auto &data = get_prop(lib::GraphMorphism::TermDataT(), match); + Term(const lib::Rules::LabelledRule &rFirst, const lib::Rules::LabelledRule &rSecond) + : rFirst(rFirst), rSecond(rSecond) {} + + template + bool init(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + InvertibleVertexMap &match, Result &result) { + assert(&dpoFirst == &rFirst.getRule()); + assert(&dpoSecond == &rSecond.getRule()); + + auto &data = get_prop(GraphMorphism::TermDataT(), match); auto &machine = data.machine; machine.verify(); - result.rResult.pTerm = std::make_unique(get_graph(result.rResult), - std::move(machine)); + result.pTerm = std::make_unique(*result.rDPO, std::move(machine)); if(Verbose) { std::cout << "New machine:\n"; - lib::IO::Term::Write::wam(getMachine(*result.rResult.pTerm), lib::Term::getStrings(), std::cout); + lib::Term::Write::wam(getMachine(*result.pTerm), lib::Term::getStrings(), std::cout); } return true; } - template - bool finalize(const RuleFirst &rFirst, const RuleSecond &rSecond, InvertibleVertexMap &match, Result &result) { - result.rResult.pTerm->verify(&get_graph(result.rResult)); + template + bool finalize(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + InvertibleVertexMap &match, Result &result) { + result.pTerm->verify(); return true; } public: - template - void copyVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - const VertexFirst &vFirst, const VertexResult &vResult) { - assert(result.rResult.pTerm); + template + void copyVertexFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + const VertexFirst &vFirst, const VertexResult &vResult) { + assert(result.pTerm); const auto &pFirst = *rFirst.pTerm; - auto &pResult = *result.rResult.pTerm; + auto &pResult = *result.pTerm; auto m = membership(rFirst, vFirst); - assert(m == membership(result.rResult, vResult)); + assert(m == result.rDPO->getCombinedGraph()[vResult].membership); switch(m) { - case Membership::Left: + case Membership::L: pResult.add(vResult, pFirst.getLeft()[vFirst], TERM_MAX); break; - case Membership::Right: + case Membership::R: pResult.add(vResult, TERM_MAX, pFirst.getRight()[vFirst]); break; - case Membership::Context: + case Membership::K: pResult.add(vResult, pFirst.getLeft()[vFirst], pFirst.getRight()[vFirst]); break; } if(Verbose) { std::cout << "Cur machine:\n"; - lib::IO::Term::Write::wam(getMachine(*result.rResult.pTerm), lib::Term::getStrings(), std::cout); + lib::Term::Write::wam(getMachine(*result.pTerm), lib::Term::getStrings(), std::cout); } } - template - void copyVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - const VertexSecond &vSecond, const VertexResult &vResult) { - assert(result.rResult.pTerm); + template + void copyVertexSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + const VertexSecond &vSecond, const VertexResult &vResult) { + assert(result.pTerm); const auto &pSecond = *rSecond.pTerm; - auto &pResult = *result.rResult.pTerm; + auto &pResult = *result.pTerm; auto m = membership(rSecond, vSecond); - assert(m == membership(result.rResult, vResult)); - if(m != Membership::Left) fixSecondTerm(pSecond.getRight()[vSecond], result); - if(m != Membership::Right) fixSecondTerm(pSecond.getLeft()[vSecond], result); + assert(m == result.rDPO->getCombinedGraph()[vResult].membership); + if(m != Membership::L) fixSecondTerm(pSecond.getRight()[vSecond], result); + if(m != Membership::R) fixSecondTerm(pSecond.getLeft()[vSecond], result); switch(m) { - case Membership::Left: + case Membership::L: pResult.add(vResult, deref(pSecond.getLeft()[vSecond], result), TERM_MAX); break; - case Membership::Right: + case Membership::R: pResult.add(vResult, TERM_MAX, deref(pSecond.getRight()[vSecond], result)); break; - case Membership::Context: + case Membership::K: pResult.add(vResult, deref(pSecond.getLeft()[vSecond], result), deref(pSecond.getRight()[vSecond], result)); break; } if(Verbose) { std::cout << "Cur machine:\n"; - lib::IO::Term::Write::wam(getMachine(*result.rResult.pTerm), lib::Term::getStrings(), std::cout); + lib::Term::Write::wam(getMachine(*result.pTerm), lib::Term::getStrings(), std::cout); } } - template - void copyEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - const EdgeFirst &eFirst, const EdgeResult &eResult) { + template + void copyEdgeFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + const EdgeFirst &eFirst, const EdgeResult &eResult) { // the membership of e may be different from eResult - assert(result.rResult.pTerm); + assert(result.pTerm); const auto &pFirst = *rFirst.pTerm; - auto &pResult = *result.rResult.pTerm; - auto m = membership(result.rResult, eResult); + auto &pResult = *result.pTerm; + auto m = result.rDPO->getCombinedGraph()[eResult].membership; switch(m) { - case Membership::Left: + case Membership::L: pResult.add(eResult, pFirst.getLeft()[eFirst], TERM_MAX); break; - case Membership::Right: + case Membership::R: pResult.add(eResult, TERM_MAX, pFirst.getRight()[eFirst]); break; - case Membership::Context: + case Membership::K: pResult.add(eResult, pFirst.getLeft()[eFirst], pFirst.getRight()[eFirst]); break; } if(Verbose) { std::cout << "Cur machine:\n"; - lib::IO::Term::Write::wam(getMachine(*result.rResult.pTerm), lib::Term::getStrings(), std::cout); + lib::Term::Write::wam(getMachine(*result.pTerm), lib::Term::getStrings(), std::cout); } } - template - void copyEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - const EdgeSecond &eSecond, const EdgeResult &eResult) { + template + void copyEdgeSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + const EdgeSecond &eSecond, const EdgeResult &eResult) { // the membership of e may be different from eResult - assert(result.rResult.pTerm); + assert(result.pTerm); const auto &pSecond = *rSecond.pTerm; - auto &pResult = *result.rResult.pTerm; - auto m = membership(result.rResult, eResult); - if(m != Membership::Left) fixSecondTerm(pSecond.getRight()[eSecond], result); - if(m != Membership::Right) fixSecondTerm(pSecond.getLeft()[eSecond], result); + auto &pResult = *result.pTerm; + auto m = result.rDPO->getCombinedGraph()[eResult].membership; + if(m != Membership::L) fixSecondTerm(pSecond.getRight()[eSecond], result); + if(m != Membership::R) fixSecondTerm(pSecond.getLeft()[eSecond], result); switch(m) { - case Membership::Left: + case Membership::L: pResult.add(eResult, deref(pSecond.getLeft()[eSecond], result), TERM_MAX); break; - case Membership::Right: + case Membership::R: pResult.add(eResult, TERM_MAX, deref(pSecond.getRight()[eSecond], result)); break; - case Membership::Context: + case Membership::K: pResult.add(eResult, deref(pSecond.getLeft()[eSecond], result), deref(pSecond.getRight()[eSecond], result)); break; } if(Verbose) { std::cout << "Cur machine:\n"; - lib::IO::Term::Write::wam(getMachine(*result.rResult.pTerm), lib::Term::getStrings(), std::cout); + lib::Term::Write::wam(getMachine(*result.pTerm), lib::Term::getStrings(), std::cout); } } public: - template - void printVertexFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - std::ostream &s, const VertexFirst &vFirst) { + template + void printVertexFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexFirst &vFirst) { rFirst.pTerm->print(s, vFirst); } - template - void printVertexSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - std::ostream &s, const VertexSecond &vSecond) { + template + void printVertexSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexSecond &vSecond) { rSecond.pTerm->print(s, vSecond); } - template - void printVertexResult(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - std::ostream &s, const VertexResult &vResult) { - result.rResult.pTerm->print(s, vResult); + template + void printVertexResult(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const VertexResult &vResult) { + result.pTerm->print(s, vResult); } - template - void printEdgeFirst(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - std::ostream &s, const EdgeFirst &eFirst) { + template + void printEdgeFirst(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const EdgeFirst &eFirst) { rFirst.pTerm->print(s, eFirst); } - template - void printEdgeSecond(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - std::ostream &s, const EdgeSecond &eSecond) { + template + void printEdgeSecond(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + std::ostream &s, const EdgeSecond &eSecond) { rSecond.pTerm->print(s, eSecond); } public: - template - void composeVertexRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - VertexResult vResult, VertexSecond vSecond) { + template + void composeVertexRvsLR(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) { // -> a | a -> b, maybe a == b auto addr = rSecond.pTerm->getRight()[vSecond]; fixSecondTerm(addr, result); - result.rResult.pTerm->setRight(vResult, deref(addr, result)); + result.pTerm->setRight(vResult, deref(addr, result)); // -> b } - template - void composeVertexLRvsL(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - VertexResult vResult, VertexSecond vSecond) { + template + void composeVertexLRvsL(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) { // vFirst is CONTEXT, so do nothing // a -> a | a -> // b -> a | a -> @@ -210,11 +221,11 @@ struct Term { // b -> } - template - void composeVertexLRvsLR(const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, - const Result &result, - VertexResult vResult, VertexSecond vSecond) { + template + void composeVertexLRvsLR(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, + const Result &result, + VertexResult vResult, VertexSecond vSecond) { // the left label of vResult is ok, but the right label might have to change // a != b, a != c, b =? c // a -> a | a -> a @@ -223,38 +234,41 @@ struct Term { // b -> a | a -> c auto addr = rSecond.pTerm->getRight()[vSecond]; fixSecondTerm(addr, result); - result.rResult.pTerm->setRight(vResult, deref(addr, result)); + result.pTerm->setRight(vResult, deref(addr, result)); } public: - template - void setEdgeResultRightFromSecondRight(const RuleFirst &rFirst, const RuleSecond &rSecond, - const InvertibleVertexMap &match, const Result &result, - EdgeResult eResult, EdgeSecond eSecond) { + template + void + setEdgeResultRightFromSecondRight(const lib::DPO::CombinedRule &dpoFirst, const lib::DPO::CombinedRule &dpoSecond, + const InvertibleVertexMap &match, const Result &result, + EdgeResult eResult, EdgeSecond eSecond) { auto addr = rSecond.pTerm->getRight()[eSecond]; fixSecondTerm(addr, result); - result.rResult.pTerm->setRight(eResult, deref(addr, result)); + result.pTerm->setRight(eResult, deref(addr, result)); } private: template void fixSecondTerm(std::size_t addr, Result &result) { - auto &m = getMachine(*result.rResult.pTerm); + auto &m = getMachine(*result.pTerm); m.verify(); if(Verbose) - lib::IO::Term::Write::wam(m, lib::Term::getStrings(), std::cout << "Copy " << addr << "\n"); + lib::Term::Write::wam(m, lib::Term::getStrings(), std::cout << "Copy " << addr << "\n"); m.copyFromTemp(addr); if(Verbose) - lib::IO::Term::Write::wam(m, lib::Term::getStrings(), std::cout << "After copy " << addr << "\n"); + lib::Term::Write::wam(m, lib::Term::getStrings(), std::cout << "After copy " << addr << "\n"); m.verify(); } template std::size_t deref(std::size_t addrTemp, Result &result) { - getMachine(*result.rResult.pTerm).verify(); - auto addr = getMachine(*result.rResult.pTerm).deref({AddressType::Temp, addrTemp}); + getMachine(*result.pTerm).verify(); + auto addr = getMachine(*result.pTerm).deref({AddressType::Temp, addrTemp}); assert(addr.type == AddressType::Heap); return addr.addr; } +private: + const lib::Rules::LabelledRule &rFirst; + const lib::Rules::LabelledRule &rSecond; }; } // namespace mod::lib::RC::Visitor diff --git a/libs/libmod/src/mod/lib/RC/detail/CompositionHelper.hpp b/libs/libmod/src/mod/lib/RC/detail/CompositionHelper.hpp index 22414b7..f3bb799 100644 --- a/libs/libmod/src/mod/lib/RC/detail/CompositionHelper.hpp +++ b/libs/libmod/src/mod/lib/RC/detail/CompositionHelper.hpp @@ -1,58 +1,56 @@ #ifndef MOD_LIB_RC_DETAIL_COMPOSITIONHELPER_HPP #define MOD_LIB_RC_DETAIL_COMPOSITIONHELPER_HPP +#include +#include #include -#include - #include namespace mod::lib::RC::detail { -using jla_boost::GraphDPO::Membership; +using lib::DPO::Membership; template struct CompositionHelper { - using GraphResult = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; + using GraphResult = typename Result::RuleResult::GraphType; using VertexResult = typename boost::graph_traits::vertex_descriptor; using EdgeResult = typename boost::graph_traits::edge_descriptor; - using GraphFirst = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; + using GraphFirst = typename RuleFirst::CombinedGraphType; using VertexFirst = typename boost::graph_traits::vertex_descriptor; - using GraphSecond = typename jla_boost::GraphDPO::PushoutRuleTraits::GraphType; + using GraphSecond = typename RuleSecond::CombinedGraphType; using VertexSecond = typename boost::graph_traits::vertex_descriptor; using EdgeSecond = typename boost::graph_traits::edge_descriptor; public: - CompositionHelper(const RuleFirst &rFirst, - const RuleSecond &rSecond, - const InvertibleVertexMap &match, + CompositionHelper(Result &result, + const RuleFirst &rFirst, const RuleSecond &rSecond, const InvertibleVertexMap &match, Visitor visitor) - : rFirst(rFirst), rSecond(rSecond), match(match), visitor(std::move(visitor)) {} + : result(result), rFirst(rFirst), rSecond(rSecond), match(match), visitor(std::move(visitor)) {} - std::optional operator()() &&{ + bool operator()() &&{ if(Verbose) std::cout << std::string(80, '=') << std::endl; - Result result(rFirst, rSecond); bool resInit = visitor.template init(rFirst, rSecond, match, result); if(!resInit) { if(Verbose) std::cout << std::string(80, '=') << std::endl; - return {}; + return false; } // Vertices //-------------------------------------------------------------------------- - copyVerticesFirst(result); - composeVerticesSecond(result); + copyVerticesFirst(); + composeVerticesSecond(); // Edges //-------------------------------------------------------------------------- - bool resFirst = copyEdgesFirstUnmatched(result); + bool resFirst = copyEdgesFirstUnmatched(); if(!resFirst) { if(Verbose) std::cout << std::string(80, '=') << std::endl; - return {}; + return false; } - bool resSecond = composeEdgesSecond(result); + bool resSecond = composeEdgesSecond(); if(!resSecond) { if(Verbose) std::cout << std::string(80, '=') << std::endl; - return {}; + return false; } // Finish it @@ -60,10 +58,10 @@ struct CompositionHelper { bool resFinal = visitor.template finalize(rFirst, rSecond, match, result); if(!resFinal) { if(Verbose) std::cout << std::string(80, '=') << std::endl; - return {}; + return false; } if(Verbose) std::cout << std::string(80, '=') << std::endl; - return result; + return true; } private: VertexFirst getNullFirst() const { @@ -79,58 +77,57 @@ struct CompositionHelper { } VertexFirst getVertexFirstChecked(VertexSecond vSecond) const { - const auto &gCodom = get_graph(get_labelled_right(rFirst)); - const auto &gDom = get_graph(get_labelled_left(rSecond)); - const auto m = membership(rSecond, vSecond); - if(m == Membership::Right) return getNullFirst(); + const auto &gCodom = getR(rFirst); + const auto &gDom = getL(rSecond); + const auto m = rSecond.getCombinedGraph()[vSecond].membership; + if(m == Membership::R) return getNullFirst(); else return get(match, gDom, gCodom, vSecond); } VertexSecond getVertexSecond(VertexFirst vFirst) const { - const auto &gCodom = get_graph(get_labelled_right(rFirst)); - const auto &gDom = get_graph(get_labelled_left(rSecond)); - assert(membership(rFirst, vFirst) != Membership::Left); + const auto &gCodom = getR(rFirst); + const auto &gDom = getL(rSecond); + assert(rFirst.getCombinedGraph()[vFirst].membership != Membership::L); return get_inverse(match, gDom, gCodom, vFirst); } private: - void copyVerticesFirst(Result &result) { + void copyVerticesFirst() { if(Verbose) std::cout << "copyVerticesFirst\n" << std::string(80, '-') << std::endl; - auto &rResult = result.rResult; - auto &gResult = get_graph(rResult); - const auto &gFirst = get_graph(rFirst); - const auto &gSecond = get_graph(rSecond); - for(const auto vFirst : asRange(vertices(gFirst))) { + auto &gResult = result.rDPO->getCombinedGraph(); + const auto &gFirst = rFirst.getCombinedGraph(); + const auto &gSecond = rSecond.getCombinedGraph(); + for(const auto vFirst: asRange(vertices(gFirst))) { if(Verbose) { std::cout << "rFirst node:\t" << get(boost::vertex_index_t(), gFirst, vFirst) - << "(" << membership(rFirst, vFirst) << ")" + << "(" << rFirst.getCombinedGraph()[vFirst].membership << ")" << "("; visitor.template printVertexFirst(rFirst, rSecond, match, result, std::cout, vFirst); std::cout << ")" << std::endl; } const bool getsDeleted = [&]() { // must be only in R to be deleted - if(membership(rFirst, vFirst) != Membership::Right) return false; + if(rFirst.getCombinedGraph()[vFirst].membership != Membership::R) return false; // must be matched to be deleted const auto vSecond = getVertexSecond(vFirst); if(vSecond == getNullSecond()) return false; // and the matched must only be in L - return membership(rSecond, vSecond) == Membership::Left; + return rSecond.getCombinedGraph()[vSecond].membership == Membership::L; }(); if(getsDeleted) { put(result.mFirstToResult, gFirst, gResult, vFirst, getNullResult()); if(Verbose) std::cout << "gets deleted" << std::endl; } else { const auto vResult = add_vertex(gResult); - result.mFirstToResult.resizeRight(gFirst, gResult); - result.mSecondToResult.resizeRight(gSecond, gResult); + syncSize(result.mFirstToResult, gFirst, gResult); + syncSize(result.mSecondToResult, gSecond, gResult); put(result.mFirstToResult, gFirst, gResult, vFirst, vResult); - put_membership(rResult, vResult, membership(rFirst, vFirst)); + gResult[vResult].membership = rFirst.getCombinedGraph()[vFirst].membership; visitor.template copyVertexFirst(rFirst, rSecond, match, result, vFirst, vResult); if(Verbose) { std::cout << "new node:\t" << get(boost::vertex_index_t(), gResult, vResult) - << "(" << membership(rResult, vResult) << ")("; + << "(" << gResult[vResult].membership << ")("; visitor.template printVertexResult(rFirst, rSecond, match, result, std::cout, vResult); std::cout << ")" << std::endl; } @@ -138,18 +135,17 @@ struct CompositionHelper { } } - void composeVerticesSecond(Result &result) { + void composeVerticesSecond() { if(Verbose) std::cout << "composeVerticesSecond\n" << std::string(80, '-') << std::endl; - const auto &gFirst = get_graph(rFirst); - const auto &gSecond = get_graph(rSecond); - auto &rResult = result.rResult; - auto &gResult = get_graph(rResult); + const auto &gFirst = rFirst.getCombinedGraph(); + const auto &gSecond = rSecond.getCombinedGraph(); + auto &gResult = result.rDPO->getCombinedGraph(); // copy nodes from second, but compose the matched ones which has already been created - for(const auto vSecond : asRange(vertices(gSecond))) { + for(const auto vSecond: asRange(vertices(gSecond))) { if(Verbose) { std::cout << "rSecond node:\t" << get(boost::vertex_index_t(), gSecond, vSecond) - << "(" << membership(rSecond, vSecond) << ")" + << "(" << rSecond.getCombinedGraph()[vSecond].membership << ")" << "("; visitor.template printVertexSecond(rFirst, rSecond, match, result, std::cout, vSecond); std::cout << ")" << std::endl; @@ -159,14 +155,14 @@ struct CompositionHelper { if(vFirst == getNullFirst()) { // unmatched vertex, create it const auto vResult = add_vertex(gResult); - result.mFirstToResult.resizeRight(gFirst, gResult); - result.mSecondToResult.resizeRight(gSecond, gResult); + syncSize(result.mFirstToResult, gFirst, gResult); + syncSize(result.mSecondToResult, gSecond, gResult); put(result.mSecondToResult, gSecond, gResult, vSecond, vResult); - put_membership(rResult, vResult, membership(rSecond, vSecond)); + gResult[vResult].membership = rSecond.getCombinedGraph()[vSecond].membership; visitor.template copyVertexSecond(rFirst, rSecond, match, result, vSecond, vResult); if(Verbose) { std::cout << "new node:\t" << get(boost::vertex_index_t(), gResult, vResult) - << "(" << membership(rResult, vResult) << ")" + << "(" << gResult[vResult].membership << ")" << "("; visitor.template printVertexResult(rFirst, rSecond, match, result, std::cout, vResult); std::cout << ")" << std::endl; @@ -179,17 +175,17 @@ struct CompositionHelper { } else { if(Verbose) { std::cout << "match to:\t" << get(boost::vertex_index_t(), gResult, vResult) - << "(" << membership(rResult, vResult) << ")" + << "(" << gResult[vResult].membership << ")" << "("; visitor.template printVertexResult(rFirst, rSecond, match, result, std::cout, vResult); std::cout << ")" << std::endl; } // now we calculate the new membership for the node - const auto mFirst = membership(rResult, vResult); // should be a copy of the one from rFirst - const auto mSecond = membership(rSecond, vSecond); - if(mFirst == Membership::Right) { + const auto mFirst = gResult[vResult].membership; // should be a copy of the one from rFirst + const auto mSecond = rSecond.getCombinedGraph()[vSecond].membership; + if(mFirst == Membership::R) { // vFirst appears - if(mSecond == Membership::Left) { + if(mSecond == Membership::L) { // and vSecond disappears, so vResult should not exist at all // We should have caught that already. assert(false); @@ -199,16 +195,16 @@ struct CompositionHelper { visitor.template composeVertexRvsLR(rFirst, rSecond, match, result, vResult, vSecond); } } else { // vFirst is either LEFT or CONTEXT - assert(mFirst != Membership::Left); // we can't match a LEFT node in rRight + assert(mFirst != Membership::L); // we can't match a LEFT node in rRight // vFirst is CONTEXT - if(mSecond == Membership::Left) { + if(mSecond == Membership::L) { // vSecond is disappearing, so vResult is disappearing as well visitor.template composeVertexLRvsL(rFirst, rSecond, match, result, vResult, vSecond); - put_membership(rResult, vResult, Membership::Left); + gResult[vResult].membership = Membership::L; } else { // both vFirst and vSecond is in both of their sides // let the visitor do its thing visitor.template composeVertexLRvsLR(rFirst, rSecond, match, result, vResult, vSecond); - assert(membership(rResult, vResult) == Membership::Context); + assert(gResult[vResult].membership == Membership::K); } // end if vSecond is LEFT } // end if vFirst is RIGHT } // end if vResult is null_vertex @@ -216,17 +212,16 @@ struct CompositionHelper { } // end foreach vSecond } - bool copyEdgesFirstUnmatched(Result &result) { + bool copyEdgesFirstUnmatched() { if(Verbose) std::cout << "copyEdgesFirstUnmatched\n" << std::string(80, '-') << std::endl; - const auto &gFirst = get_graph(rFirst); - const auto &gSecond = get_graph(rSecond); - auto &rResult = result.rResult; - auto &gResult = get_graph(rResult); + const auto &gFirst = rFirst.getCombinedGraph(); + const auto &gSecond = rSecond.getCombinedGraph(); + auto &gResult = result.rDPO->getCombinedGraph(); const auto processEdge = [&](const auto eFirst) { // map source and target to core vertices const auto vSrcFirst = source(eFirst, gFirst); const auto vTarFirst = target(eFirst, gFirst); - const auto meFirst = membership(rFirst, eFirst); + const auto meFirst = gFirst[eFirst].membership; if(Verbose) { const auto vSrcResult = get(result.mFirstToResult, gFirst, gResult, vSrcFirst); const auto vTarResult = get(result.mFirstToResult, gFirst, gResult, vTarFirst); @@ -249,15 +244,13 @@ struct CompositionHelper { // adding shouldn't fail assert(peResult.second); const auto eResult = peResult.first; - put_membership(rResult, eResult, membership(rFirst, eFirst)); + gResult[eResult].membership = gFirst[eFirst].membership; visitor.template copyEdgeFirst(rFirst, rSecond, match, result, eFirst, eResult); - assert( - membership(rResult, vSrcResult) == Membership::Context || membership(rResult, vSrcResult) == meFirst); - assert( - membership(rResult, vTarResult) == Membership::Context || membership(rResult, vTarResult) == meFirst); + assert(gResult[vSrcResult].membership == Membership::K || gResult[vSrcResult].membership == meFirst); + assert(gResult[vTarResult].membership == Membership::K || gResult[vTarResult].membership == meFirst); }; // and now the actual case analysis - if(meFirst == Membership::Left) { + if(meFirst == Membership::L) { if(Verbose) std::cout << "\teFirst in LEFT, clean copy" << std::endl; makeCopy(); return true; @@ -271,23 +264,23 @@ struct CompositionHelper { if(Verbose) std::cout << "\tBoth ends matched" << std::endl; const auto oeSecond = out_edges(vSrcSecond, gSecond); const auto eSecondIter = std::find_if(oeSecond.first, oeSecond.second, - [&gSecond, vTarSecond, this](const auto &eSecond) { + [&gSecond, vTarSecond](const auto &eSecond) { return target(eSecond, gSecond) == vTarSecond && - membership(rSecond, eSecond) != Membership::Right; + gSecond[eSecond].membership != Membership::R; }); // TODO: why do we check the membership in the find_if? // There should be only one edge, so we can bail out here, right? const bool isEdgeMatched = eSecondIter != oeSecond.second; if(isEdgeMatched) { const auto eSecond = *eSecondIter; - if(meFirst == Membership::Right) { + if(meFirst == Membership::R) { if(Verbose) std::cout << "\teFirst matched and in RIGHT, skipping" << std::endl; return true; } if(Verbose) std::cout << "\teFirst matched and in CONTEXT, copying to LEFT or CONTEXT (depending on eSecond (" - << membership(rSecond, eSecond) << "))" << std::endl; + << gSecond[eSecond].membership << "))" << std::endl; const auto vSrcResult = get(result.mFirstToResult, gFirst, gResult, vSrcFirst); const auto vTarResult = get(result.mFirstToResult, gFirst, gResult, vTarFirst); // vResultSrc/vResultTar can not be null_vertex @@ -298,10 +291,10 @@ struct CompositionHelper { assert(peResult.second); const auto eResult = peResult.first; // we use the second membership, so do not use the common copy mechanism in the bottom of the function - put_membership(rResult, eResult, membership(rSecond, eSecond)); + gResult[eResult].membership = gSecond[eSecond].membership; visitor.template copyEdgeFirst(rFirst, rSecond, match, result, eFirst, eResult); - assert(membership(rResult, vSrcResult) != Membership::Right); - assert(membership(rResult, vTarResult) != Membership::Right); + assert(gResult[vSrcResult].membership != Membership::R); + assert(gResult[vTarResult].membership != Membership::R); return true; } @@ -316,9 +309,9 @@ struct CompositionHelper { } // check consistency - const auto mvSrcResult = membership(rResult, vSrcResult); - const auto mvTarResult = membership(rResult, vTarResult); - if(mvSrcResult == Membership::Left || mvTarResult == Membership::Left) { + const auto mvSrcResult = gResult[vSrcResult].membership; + const auto mvTarResult = gResult[vTarResult].membership; + if(mvSrcResult == Membership::L || mvTarResult == Membership::L) { if(Verbose) std::cout << "\tComposition failure: at least one matched vertex has inconsistent context (" << mvSrcResult << " and " << mvTarResult << "), eFirst is (" << meFirst << ")" << std::endl; @@ -343,8 +336,8 @@ struct CompositionHelper { return false; } // matched, but is it consistent? - const auto mvMatchedResult = membership(rResult, vResultMatched); - if(mvMatchedResult == Membership::Left) { + const auto mvMatchedResult = gResult[vResultMatched].membership; + if(mvMatchedResult == Membership::L) { if(Verbose) std::cout << "\tComposition failure: matched vertex has inconsistent context (" << mvMatchedResult << "), eFirst is (" << meFirst << ")" << std::endl; @@ -360,20 +353,19 @@ struct CompositionHelper { makeCopy(); return true; }; - for(const auto eFirst : asRange(edges(gFirst))) { + for(const auto eFirst: asRange(edges(gFirst))) { const bool ok = processEdge(eFirst); if(!ok) return false; } return true; } - bool composeEdgesSecond(Result &result) { + bool composeEdgesSecond() { if(Verbose) std::cout << "composeEdgesSecond\n" << std::string(80, '-') << std::endl; - // const auto &gFirst = get_graph(rFirst); - const auto &gSecond = get_graph(rSecond); - auto &rResult = result.rResult; - auto &gResult = get_graph(rResult); - for(auto eSecond : asRange(edges(gSecond))) { + // const auto &gFirst = rFirst.getCombinedGraph(); + const auto &gSecond = rSecond.getCombinedGraph(); + auto &gResult = result.rDPO->getCombinedGraph(); + for(auto eSecond: asRange(edges(gSecond))) { auto vSecondSrc = source(eSecond, gSecond); auto vSecondTar = target(eSecond, gSecond); auto vResultSrc = get(result.mSecondToResult, gSecond, gResult, vSecondSrc); @@ -382,7 +374,7 @@ struct CompositionHelper { // vSrcNew/vTarNew may be null_vertex std::cout << "Edge second:\t(" << vSecondSrc << ", " << vSecondTar << ")\tmapped to (" << vResultSrc << ", " << vResultTar << ")" - << ", (" << membership(rSecond, eSecond) << ")" + << ", (" << gSecond[eSecond].membership << ")" << "("; visitor.template printEdgeSecond(rFirst, rSecond, match, result, std::cout, eSecond); std::cout << ")" << std::endl; @@ -403,32 +395,28 @@ struct CompositionHelper { // adding shouldn't fail assert(peResult.second); auto eResult = peResult.first; - put_membership(rResult, eResult, membership(rSecond, eSecond)); + gResult[eResult].membership = gSecond[eSecond].membership; visitor.template copyEdgeSecond(rFirst, rSecond, match, result, eSecond, eResult); } else { // one end matched assert(vResultSrc != boost::graph_traits::null_vertex() || vResultTar != boost::graph_traits::null_vertex()); - bool isOk = composeEdgeSecond_oneEndMatched(result, eSecond, vResultSrc, vResultTar, isSrcMatched, - isTarMatched); + bool isOk = composeEdgeSecond_oneEndMatched(eSecond, vResultSrc, vResultTar, isSrcMatched, isTarMatched); if(!isOk) return false; } // if 0 or 1 end matched } else { // maybe a matched edge - bool isOk = composeEdgeSecond_bothEndsMatched(result, eSecond, vFirstSrc, vFirstTar, vResultSrc, - vResultTar); + bool isOk = composeEdgeSecond_bothEndsMatched(eSecond, vFirstSrc, vFirstTar, vResultSrc, vResultTar); if(!isOk) return false; } // end if 0 or 1 matched endNodes } // end for each eSecond return true; } private: - bool composeEdgeSecond_oneEndMatched(Result &result, - EdgeSecond eSecond, + bool composeEdgeSecond_oneEndMatched(EdgeSecond eSecond, VertexResult vResultSrc, VertexResult vResultTar, bool isSrcMatched, bool isTarMatched) { - auto &rResult = result.rResult; - auto &gResult = get_graph(rResult); + auto &gResult = result.rDPO->getCombinedGraph(); if(Verbose) std::cout << "One end matched, eSecond should be copied" << std::endl; assert(isSrcMatched ^ isTarMatched); // at most one of vResultSrc and vResultTar may be null_vertex @@ -438,18 +426,18 @@ struct CompositionHelper { auto vResultOther = isSrcMatched ? vResultTar : vResultSrc; // vResultMatched may be null_vertex, if it's a RIGHT vs. LEFT vertex assert(vResultOther != boost::graph_traits::null_vertex()); - const auto meSecond = membership(rSecond, eSecond); - const auto mvResultOther = membership(rResult, vResultOther); + const auto meSecond = rSecond.getCombinedGraph()[eSecond].membership; + const auto mvResultOther = gResult[vResultOther].membership; (void) mvResultOther; - assert(mvResultOther == Membership::Context || mvResultOther == meSecond); + assert(mvResultOther == Membership::K || mvResultOther == meSecond); if(vResultMatched == boost::graph_traits::null_vertex()) { if(Verbose) std::cout << "\tComposition failure: matched vertex deleted" << std::endl; return false; } - auto mvResultMatched = membership(rResult, vResultMatched); - if(mvResultMatched != Membership::Context && mvResultMatched != meSecond) { + auto mvResultMatched = gResult[vResultMatched].membership; + if(mvResultMatched != Membership::K && mvResultMatched != meSecond) { if(Verbose) std::cout << "\tComposition failure: matched vertex has inconsistent context (" << mvResultMatched << "), eSecond is (" << meSecond << ")" << std::endl; @@ -461,36 +449,34 @@ struct CompositionHelper { // adding shouldn't fail assert(peResult.second); auto eResult = peResult.first; - put_membership(rResult, eResult, membership(rSecond, eSecond)); + gResult[eResult].membership = rSecond.getCombinedGraph()[eSecond].membership; visitor.template copyEdgeSecond(rFirst, rSecond, match, result, eSecond, eResult); return true; } - bool composeEdgeSecond_bothEndsMatched(Result &result, - EdgeSecond eSecond, + bool composeEdgeSecond_bothEndsMatched(EdgeSecond eSecond, VertexFirst vFirstSrc, VertexFirst vFirstTar, VertexResult vResultSrc, VertexResult vResultTar) { - const auto &gFirst = get_graph(rFirst); - auto &rResult = result.rResult; - auto &gResult = get_graph(rResult); + const auto &gFirst = rFirst.getCombinedGraph(); + auto &gResult = result.rDPO->getCombinedGraph(); if(Verbose) std::cout << "Both ends matched" << std::endl; // vResultSrc/vResultTar may be null_vertex std::optional omeFirst; // we search in coreFirst, because it has the matched edges - for(const auto eFirst : asRange(out_edges(vFirstSrc, gFirst))) { + for(const auto eFirst: asRange(out_edges(vFirstSrc, gFirst))) { if(target(eFirst, gFirst) != vFirstTar) continue; - omeFirst = membership(rFirst, eFirst); + omeFirst = gFirst[eFirst].membership; break; } - auto meSecond = membership(rSecond, eSecond); + auto meSecond = rSecond.getCombinedGraph()[eSecond].membership; // possibilities: - // -> vs. | -> + // -> vs. | -> // -> vs. -> | // -> vs. | -> | - // | -> vs. | -> + // | -> vs. | -> // | -> vs. -> | // | -> vs. | -> | // -> | vs. | -> first not in result @@ -501,11 +487,11 @@ struct CompositionHelper { // | -> | vs. | -> | first in result as CONTEXT // can we do a simple copy? if(!omeFirst) { - // -> vs. | -> + // -> vs. | -> // -> vs. -> | // -> vs. | -> | if(Verbose) std::cout << "\tSimple copy of eSecond" << std::endl; - if(meSecond == Membership::Left) { + if(meSecond == Membership::L) { if(Verbose) std::cout << "\t\teSecond in LEFT, check ends" << std::endl; // vResultSrc/vResultTar may be null_vertex if(vResultSrc == boost::graph_traits::null_vertex()) { @@ -516,11 +502,11 @@ struct CompositionHelper { if(Verbose) std::cout << "\tComposition failure: vResultTar deleted" << std::endl; return false; } - if(membership(rResult, vResultSrc) == Membership::Right) { + if(gResult[vResultSrc].membership == Membership::R) { if(Verbose) std::cout << "\tComposition failure: vResultSrc not in LEFT or CONTEXT" << std::endl; return false; } - if(membership(rResult, vResultTar) == Membership::Right) { + if(gResult[vResultTar].membership == Membership::R) { if(Verbose) std::cout << "\tComposition failure: vResultTar not in LEFT or CONTEXT" << std::endl; return false; } @@ -532,7 +518,7 @@ struct CompositionHelper { // adding shouldn't fail assert(peResult.second); auto eResult = peResult.first; - put_membership(rResult, eResult, membership(rSecond, eSecond)); + gResult[eResult].membership = rSecond.getCombinedGraph()[eSecond].membership; visitor.template copyEdgeSecond(rFirst, rSecond, match, result, eSecond, eResult); return true; } @@ -540,14 +526,14 @@ struct CompositionHelper { // check for parallel errors assert(omeFirst); auto meFirst = *omeFirst; - if(meFirst == Membership::Left && meSecond != Membership::Right) { + if(meFirst == Membership::L && meSecond != Membership::R) { // creating parallel in left // | -> vs. | -> // | -> vs. | -> | if(Verbose) std::cout << "\tComposition failure: duplicate edge in L" << std::endl; return false; } - if(meFirst != Membership::Left && meSecond == Membership::Right) { + if(meFirst != Membership::L && meSecond == Membership::R) { // creating parallel in right // -> | vs. -> | // | -> | vs. -> | @@ -563,7 +549,7 @@ struct CompositionHelper { // | -> | vs. | -> first in result as LEFT // | -> | vs. | -> | first in result as CONTEXT // check if we should just ignore the edge (matched or handled - if(meSecond == Membership::Left) { + if(meSecond == Membership::L) { // -> | vs. | -> first not in result // | -> | vs. | -> first in result as LEFT if(Verbose) @@ -578,16 +564,16 @@ struct CompositionHelper { // vResultSrc/vResultTar can not be null_vertex assert(vResultSrc != boost::graph_traits::null_vertex()); assert(vResultTar != boost::graph_traits::null_vertex()); - if(meFirst == Membership::Right) { + if(meFirst == Membership::R) { // copy eSecond as RIGHT to result - assert(meSecond == Membership::Context); + assert(meSecond == Membership::K); // -> | vs. | -> | if(Verbose) std::cout << "\t -> | vs. | -> |, copy to RIGHT" << std::endl; auto peResult = add_edge(vResultSrc, vResultTar, gResult); // adding shouldn't fail assert(peResult.second); auto eResult = peResult.first; - put_membership(rResult, eResult, Membership::Right); + gResult[eResult].membership = Membership::R; visitor.template copyEdgeSecond(rFirst, rSecond, match, result, eSecond, eResult); return true; } @@ -606,11 +592,12 @@ struct CompositionHelper { std::cout << "\t'| -> vs. -> |' or '| -> | vs. | -> |', promote eNew to CONTEXT and set right from second right" << std::endl; - put_membership(rResult, eResult, Membership::Context); + gResult[eResult].membership = Membership::K; visitor.template setEdgeResultRightFromSecondRight(rFirst, rSecond, match, result, eResult, eSecond); return true; } private: + Result &result; const RuleFirst &rFirst; const RuleSecond &rSecond; const InvertibleVertexMap &match; diff --git a/libs/libmod/src/mod/lib/Rules/ConnectedComponent.hpp b/libs/libmod/src/mod/lib/Rules/ConnectedComponent.hpp index 6a020aa..856e61f 100644 --- a/libs/libmod/src/mod/lib/Rules/ConnectedComponent.hpp +++ b/libs/libmod/src/mod/lib/Rules/ConnectedComponent.hpp @@ -1,11 +1,9 @@ -#ifndef MOD_LIB_RULES_CONNECTEDCOMPONENT_H -#define MOD_LIB_RULES_CONNECTEDCOMPONENT_H +#ifndef MOD_LIB_RULES_CONNECTEDCOMPONENT_HPP +#define MOD_LIB_RULES_CONNECTEDCOMPONENT_HPP #include -namespace mod { -namespace lib { -namespace Rules { +namespace mod::lib::Rules { template struct ConnectedComponentFilter { @@ -13,10 +11,10 @@ struct ConnectedComponentFilter { using Edge = typename boost::graph_traits::edge_descriptor; // because filters in boost::filetered_graph must be default constructible - ConnectedComponentFilter() : g(nullptr), componentMap(nullptr) { } + ConnectedComponentFilter() : g(nullptr), componentMap(nullptr) {} ConnectedComponentFilter(const Graph *g, const ComponentMap *componentMap, std::size_t component) - : g(g), componentMap(componentMap), component(component) { } + : g(g), componentMap(componentMap), component(component) {} bool operator()(Vertex v) const { auto vComponent = (*componentMap)[get(boost::vertex_index_t(), *g, v)]; @@ -33,8 +31,6 @@ struct ConnectedComponentFilter { std::size_t component; }; -} // namespace Rules -} // namespace lib -} // namespace mod +} // namespace mod::lib::Rules -#endif /* MOD_LIB_RULES_CONNECTEDCOMPONENT_H */ \ No newline at end of file +#endif // MOD_LIB_RULES_CONNECTEDCOMPONENT_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/GraphAsRuleCache.cpp b/libs/libmod/src/mod/lib/Rules/GraphAsRuleCache.cpp index 9b3edda..85af19a 100644 --- a/libs/libmod/src/mod/lib/Rules/GraphAsRuleCache.cpp +++ b/libs/libmod/src/mod/lib/Rules/GraphAsRuleCache.cpp @@ -9,18 +9,18 @@ namespace mod::lib::Rules { std::shared_ptr GraphAsRuleCache::getBindRule(const lib::Graph::Single *g) { - return getRule(g, Membership::Right); + return getRule(g, Membership::R); } std::shared_ptr GraphAsRuleCache::getIdRule(const lib::Graph::Single *g) { - return getRule(g, Membership::Context); + return getRule(g, Membership::K); } std::shared_ptr GraphAsRuleCache::getUnbindRule(const lib::Graph::Single *g) { - return getRule(g, Membership::Left); + return getRule(g, Membership::L); } -std::shared_ptr GraphAsRuleCache::getRule(const lib::Graph::Single *g, jla_boost::GraphDPO::Membership m) { +std::shared_ptr GraphAsRuleCache::getRule(const lib::Graph::Single *g, lib::DPO::Membership m) { const auto iter = storage.find({g, m}); if(iter != end(storage)) return iter->second; auto r = rule::Rule::makeRule(graphToRule(g->getLabelledGraph(), m, g->getName())); diff --git a/libs/libmod/src/mod/lib/Rules/GraphAsRuleCache.hpp b/libs/libmod/src/mod/lib/Rules/GraphAsRuleCache.hpp index f80c00e..a5e81fc 100644 --- a/libs/libmod/src/mod/lib/Rules/GraphAsRuleCache.hpp +++ b/libs/libmod/src/mod/lib/Rules/GraphAsRuleCache.hpp @@ -2,8 +2,7 @@ #define MOD_LIB_RULES_GRAPHASRULECACHE_HPP #include - -#include +#include #include #include @@ -18,9 +17,9 @@ struct GraphAsRuleCache { std::shared_ptr getIdRule(const lib::Graph::Single *g); std::shared_ptr getUnbindRule(const lib::Graph::Single *g); private: - std::shared_ptr getRule(const lib::Graph::Single *g, jla_boost::GraphDPO::Membership m); + std::shared_ptr getRule(const lib::Graph::Single *g, lib::DPO::Membership m); private: - std::map, std::shared_ptr> storage; + std::map, std::shared_ptr> storage; }; } // namespace mod::lib::Rules diff --git a/libs/libmod/src/mod/lib/Rules/GraphDecl.hpp b/libs/libmod/src/mod/lib/Rules/GraphDecl.hpp index d62b9c2..b6732aa 100644 --- a/libs/libmod/src/mod/lib/Rules/GraphDecl.hpp +++ b/libs/libmod/src/mod/lib/Rules/GraphDecl.hpp @@ -1,50 +1,16 @@ -#ifndef MOD_LIB_RULES_GRAPHDECL_H -#define MOD_LIB_RULES_GRAPHDECL_H +#ifndef MOD_LIB_RULES_GRAPHDECL_HPP +#define MOD_LIB_RULES_GRAPHDECL_HPP -#include -#include -#include +#include +#include -namespace mod { -namespace lib { -namespace Rules { +namespace mod::lib::Rules { +using lib::DPO::Membership; -using jla_boost::GraphDPO::Membership; - -struct VProp { - Membership membership; -}; - -struct EProp { - Membership membership; -}; - -using GraphType = jla_boost::EdgeIndexedAdjacencyList; +using GraphType = lib::DPO::CombinedRule::CombinedGraphType; using Vertex = boost::graph_traits::vertex_descriptor; using Edge = boost::graph_traits::edge_descriptor; -using SideGraphType = jla_boost::GraphDPO::FilteredGraphProjection; - -struct MembershipPropertyMap { - - MembershipPropertyMap(const GraphType &g) : g(g) { } -public: - const GraphType &g; -}; - -inline MembershipPropertyMap makeMembershipPropertyMap(const GraphType &g) { - return MembershipPropertyMap(g); -} - -inline Membership get(MembershipPropertyMap m, Vertex v) { - return get(&VProp::membership, m.g, v); -} - -inline Membership get(MembershipPropertyMap m, Edge e) { - return get(&EProp::membership, m.g, e); -} -} // namespace Rules -} // namespace lib -} // namespace mod +} // namespace mod::lib::Rules -#endif /* MOD_LIB_RULES_GRAPHDECL_H */ +#endif // MOD_LIB_RULES_GRAPHDECL_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/GraphToRule.hpp b/libs/libmod/src/mod/lib/Rules/GraphToRule.hpp index 20e797e..9184649 100644 --- a/libs/libmod/src/mod/lib/Rules/GraphToRule.hpp +++ b/libs/libmod/src/mod/lib/Rules/GraphToRule.hpp @@ -13,31 +13,31 @@ std::unique_ptr graphToRule(const LGraph &lg, Membership membership, const using EdgeCat = lib::Stereo::EdgeCategory; const auto &g = get_graph(lg); const auto &pStringGraph = get_string(lg); - assert(membership == Membership::Left || membership == Membership::Context || membership == Membership::Right); - lib::Rules::LabelledRule rule; - auto &gCore = get_graph(rule); - rule.pString = std::make_unique(gCore); - auto &pString = *rule.pString; + assert(membership == Membership::L || membership == Membership::K || membership == Membership::R); + auto cRule = std::make_unique(); + auto &gCore = cRule->getCombinedGraph(); + auto pStringPtr = std::make_unique(*cRule); + auto &pString = *pStringPtr; - for(const auto v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { const auto vCore = add_vertex(gCore); assert(get(boost::vertex_index_t(), g, v) == get(boost::vertex_index_t(), gCore, vCore)); gCore[vCore].membership = membership; const auto &label = pStringGraph[v]; switch(membership) { - case Membership::Left: + case Membership::L: pString.add(vCore, label, ""); break; - case Membership::Right: + case Membership::R: pString.add(vCore, "", label); break; - case Membership::Context: + case Membership::K: pString.add(vCore, label, label); break; } } - for(const auto e : asRange(edges(g))) { + for(const auto e: asRange(edges(g))) { const auto vSrcCore = vertex(get(boost::vertex_index_t(), g, source(e, g)), gCore); const auto vTarCore = vertex(get(boost::vertex_index_t(), g, target(e, g)), gCore); const auto eCore = add_edge(vSrcCore, vTarCore, gCore).first; @@ -45,25 +45,26 @@ std::unique_ptr graphToRule(const LGraph &lg, Membership membership, const gCore[eCore].membership = membership; const auto &label = pStringGraph[e]; switch(membership) { - case Membership::Left: + case Membership::L: pString.add(eCore, label, ""); break; - case Membership::Right: + case Membership::R: pString.add(eCore, "", label); break; - case Membership::Context: + case Membership::K: pString.add(eCore, label, label); break; } } + std::unique_ptr pStereoPtr; if(has_stereo(lg)) { const auto &pStereoGraph = get_stereo(lg); std::vector eStereo(num_edges(g)); - for(const auto e : asRange(edges(g))) { + for(const auto e: asRange(edges(g))) { const auto eId = get(boost::edge_index_t(), g, e); - const auto eCore = *(edges(gCore).first + eId); + const auto eCore = *std::next(edges(gCore).first, eId); // TODO: get rid of O(n) lookup const auto eIdCore = get(boost::edge_index_t(), gCore, eCore); assert(eId == eIdCore); eStereo[eIdCore] = pStereoGraph[e]; @@ -82,20 +83,19 @@ std::unique_ptr graphToRule(const LGraph &lg, Membership membership, const return e; }; const auto inf = Stereo::makeCloner(lg, gCore, vertexMap, edgeMap); - rule.pStereo = std::make_unique(gCore, inf, inf, jla_boost::AlwaysTrue(), - jla_boost::AlwaysTrue()); + pStereoPtr = std::make_unique(*cRule, inf, inf, jla_boost::AlwaysTrue(), + jla_boost::AlwaysTrue()); } - rule.initComponents(); std::string completeName; switch(membership) { - case Membership::Left: + case Membership::L: completeName += "unbind"; break; - case Membership::Context: + case Membership::K: completeName += "id"; break; - case Membership::Right: + case Membership::R: completeName += "bind"; break; default: @@ -105,7 +105,8 @@ std::unique_ptr graphToRule(const LGraph &lg, Membership membership, const completeName += "<"; completeName += name; completeName += ">"; - auto res = std::make_unique(std::move(rule), std::nullopt); + LabelledRule lRule(std::move(cRule), std::move(pStringPtr), std::move(pStereoPtr)); + auto res = std::make_unique(std::move(lRule), std::nullopt); res->setName(completeName); return res; } diff --git a/libs/libmod/src/mod/lib/Rules/IO/DepictionData.cpp b/libs/libmod/src/mod/lib/Rules/IO/DepictionData.cpp new file mode 100644 index 0000000..1311d98 --- /dev/null +++ b/libs/libmod/src/mod/lib/Rules/IO/DepictionData.cpp @@ -0,0 +1,614 @@ +#include "DepictionData.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace mod::lib::Rules::Write { +namespace { +constexpr bool VERBOSE = false; +} // namespace + +DepictionData::Side::Side(const DepictionData &depict, const SideData &data, + const Chem::OBMolHandle CoordData::*obSide, + const lib::DPO::CombinedRule::SideGraphType &g, + SideToCG mToCG, + PropMolecule::Side pMol, + std::function fStereo) + : depict(depict), data(data), obSide(obSide), g(g), mToCG(mToCG), pMol(pMol), fStereo(fStereo) {} + +AtomData DepictionData::Side::getAtomData(SideVertex vS) const { + return pMol[vS]; +} + +AtomId DepictionData::Side::getAtomId(SideVertex vS) const { + return pMol[vS].getAtomId(); +} + +Isotope DepictionData::Side::getIsotope(SideVertex vS) const { + return pMol[vS].getIsotope(); +} + +Charge DepictionData::Side::getCharge(SideVertex vS) const { + return pMol[vS].getCharge(); +} + +bool DepictionData::Side::getRadical(SideVertex vS) const { + return pMol[vS].getRadical(); +} + +std::string DepictionData::Side::getVertexLabelNoIsotopeChargeRadical(SideVertex vS) const { + const auto atomId = getAtomId(vS); + if(atomId != AtomIds::Invalid) + return Chem::symbolFromAtomId(atomId); + const auto &nonAtomToPhonyAtom = data.nonAtomToPhonyAtom; + const auto nonAtomIter = nonAtomToPhonyAtom.find(vS); + assert(nonAtomIter != end(nonAtomToPhonyAtom)); + const auto labelIter = depict.phonyAtomToString.find(nonAtomIter->second.getAtomId()); + assert(labelIter != end(depict.phonyAtomToString)); + return labelIter->second; +} + +BondType DepictionData::Side::getBondData(SideEdge eS) const { + return pMol[eS]; +} + +std::string DepictionData::Side::getEdgeLabel(SideEdge eS) const { + const auto bt = getBondData(eS); + if(bt != BondType::Invalid) + return std::string(1, Chem::bondToChar(bt)); + const auto &nonBondEdges = data.nonBondEdges; + const auto iter = nonBondEdges.find(eS); + assert(iter != end(nonBondEdges)); + return iter->second; +} + +bool DepictionData::Side::hasImportantStereo(SideVertex vS) const { + if(!has_stereo(depict.lr)) return false; + return !fStereo()[vS]->morphismDynamicOk(); +} + +lib::IO::Graph::Write::EdgeFake3DType +DepictionData::Side::getEdgeFake3DType(SideEdge eS, bool withHydrogen) const { + const auto vSrc = source(eS, g); + const auto vTar = target(eS, g); + if(!hasImportantStereo(vSrc) && !hasImportantStereo(vTar)) + return lib::IO::Graph::Write::EdgeFake3DType::None; +#ifndef MOD_HAVE_OPENBABEL + throw FatalError(MOD_NO_OPENBABEL_ERROR_STR); +#else + assert(depict.hasMoleculeEncoding); + const auto idSrc = get(boost::vertex_index_t(), g, vSrc); + const auto idTar = get(boost::vertex_index_t(), g, vTar); + const CoordData &cData = withHydrogen ? depict.cDataAll : depict.cDataNoHydrogen; + return (cData.*obSide).getBondFake3D(idSrc, idTar); +#endif +} + +bool DepictionData::Side::getHasCoordinates() const { + return depict.getHasCoordinates(); +} + +double DepictionData::Side::getX(SideVertex vS, bool withHydrogen) const { + const auto v = get(mToCG, g, depict.lr.getRule().getCombinedGraph(), vS); + return depict.getX(v, withHydrogen); +} + +double DepictionData::Side::getY(SideVertex vS, bool withHydrogen) const { + const auto v = get(mToCG, g, depict.lr.getRule().getCombinedGraph(), vS); + return depict.getY(v, withHydrogen); +} + +const AtomData &DepictionData::Side::operator()(SideVertex vS) const { + if(getAtomId(vS) != AtomIds::Invalid) + return pMol[vS]; + const auto iter = data.nonAtomToPhonyAtom.find(vS); + assert(iter != end(data.nonAtomToPhonyAtom)); + return iter->second; +} + +BondType DepictionData::Side::operator()(SideEdge eS) const { + const auto bt = getBondData(eS); + return bt == BondType::Invalid ? BondType::Single : bt; +} + +//bool DepictionDataCore::Side::isAtomIdInvalidContext(Vertex v) const { +// switch(membership) { +// case Membership::Left: +// case Membership::Right +// return true; +// } +// const auto &molState = depict.moleculeState; +// if(membership == Membership::Left) return true; +// else if(membership == Membership::Right) return true; +// else { +// auto m = depict.g[v].membership; +// // if(m != Membership::StateChange) return true; +// auto leftId = molState.getNormal(v).getAtomId(); +// auto rightId = molState.getChange(v).getAtomId(); +// if(leftId == rightId) return true; +// else return false; +// } +//} + +//------------------------------------------------------------------------------ +// K +//------------------------------------------------------------------------------ + +DepictionData::K::K(const DepictionData &depict) : depict(depict) {} + +AtomId DepictionData::K::getAtomId(KVertex v) const { + const auto &rDPO = depict.lr.getRule(); + const auto vL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), v); + const auto vR = get(getMorR(rDPO), getK(rDPO), getR(rDPO), v); + const auto &pMol = get_molecule(depict.lr); + const auto l = pMol.getLeft()[vL].getAtomId(); + const auto r = pMol.getRight()[vR].getAtomId(); + if(l == r) return l; + else return AtomIds::Invalid; +} + +Isotope DepictionData::K::getIsotope(KVertex v) const { + const auto &rDPO = depict.lr.getRule(); + const auto vL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), v); + const auto vR = get(getMorR(rDPO), getK(rDPO), getR(rDPO), v); + const auto &pMol = get_molecule(depict.lr); + const auto l = pMol.getLeft()[vL].getIsotope(); + const auto r = pMol.getRight()[vR].getIsotope(); + if(l == r) return l; + else return Isotope(); +} + +Charge DepictionData::K::getCharge(KVertex v) const { + const auto &rDPO = depict.lr.getRule(); + const auto vL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), v); + const auto vR = get(getMorR(rDPO), getK(rDPO), getR(rDPO), v); + const auto &pMol = get_molecule(depict.lr); + const auto l = pMol.getLeft()[vL].getCharge(); + const auto r = pMol.getRight()[vR].getCharge(); + if(l == r) return l; + else return Charge(0); +} + +bool DepictionData::K::getRadical(KVertex v) const { + const auto &rDPO = depict.lr.getRule(); + const auto vL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), v); + const auto vR = get(getMorR(rDPO), getK(rDPO), getR(rDPO), v); + const auto &pMol = get_molecule(depict.lr); + const auto l = pMol.getLeft()[vL].getRadical(); + const auto r = pMol.getRight()[vR].getRadical(); + if(l == r) return l; + else return false; +} + +std::string DepictionData::K::getVertexLabelNoIsotopeChargeRadical(KVertex v) const { + const auto &rDPO = depict.lr.getRule(); + const auto vL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), v); + const auto vR = get(getMorR(rDPO), getK(rDPO), getR(rDPO), v); + const auto atomId = getAtomId(v); + if(atomId != AtomIds::Invalid) + return Chem::symbolFromAtomId(atomId); + const auto &pMol = get_molecule(depict.lr); + const auto nonAtomIterLeft = depict.leftData.nonAtomToPhonyAtom.find(vL); + const auto nonAtomIterRight = depict.rightData.nonAtomToPhonyAtom.find(vR); + std::string left, right; + if(nonAtomIterLeft == end(depict.leftData.nonAtomToPhonyAtom)) { + const auto a = pMol.getLeft()[vL].getAtomId(); + left = Chem::symbolFromAtomId(a); + } else { + auto labelIterLeft = depict.phonyAtomToString.find(nonAtomIterLeft->second.getAtomId()); + assert(labelIterLeft != end(depict.phonyAtomToString)); + left = labelIterLeft->second; + } + if(nonAtomIterRight == end(depict.rightData.nonAtomToPhonyAtom)) { + const auto a = pMol.getRight()[vR].getAtomId(); + right = Chem::symbolFromAtomId(a); + } else { + auto labelIterRight = depict.phonyAtomToString.find(nonAtomIterRight->second.getAtomId()); + assert(labelIterRight != end(depict.phonyAtomToString)); + right = labelIterRight->second; + } + if(left == right) return left; + else return R"X($\langle$)X" + left + ", " + right + R"X($\rangle$)X"; +} + +BondType DepictionData::K::getBondData(KEdge e) const { + const auto &rDPO = depict.lr.getRule(); + const auto &pMol = get_molecule(depict.lr); + const auto eL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), e); + const auto eR = get(getMorR(rDPO), getK(rDPO), getR(rDPO), e); + const auto l = pMol.getLeft()[eL]; + const auto r = pMol.getRight()[eR]; + if(l == r) return l; + else return BondType::Invalid; +} + +std::string DepictionData::K::getEdgeLabel(KEdge e) const { + const auto &rDPO = depict.lr.getRule(); + const auto eL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), e); + const auto eR = get(getMorR(rDPO), getK(rDPO), getR(rDPO), e); + const auto bt = getBondData(e); + if(bt != BondType::Invalid) + return std::string(1, Chem::bondToChar(bt)); + std::string left, right; + const auto iterLeft = depict.leftData.nonBondEdges.find(eL); + const auto iterRight = depict.rightData.nonBondEdges.find(eR); + const auto &pMol = get_molecule(depict.lr); + if(iterLeft != end(depict.leftData.nonBondEdges)) left = iterLeft->second; + else left = std::string(1, Chem::bondToChar(pMol.getLeft()[eL])); + if(iterRight != end(depict.rightData.nonBondEdges)) right = iterRight->second; + else right = std::string(1, Chem::bondToChar(pMol.getRight()[eR])); + if(left == right) return left; + else return "$\\langle$" + left + ", " + right + "$\\rangle$"; +} + +bool DepictionData::K::hasImportantStereo(KVertex v) const { + if(!has_stereo(depict.lr)) return false; + return get_stereo(depict.lr).inContext(v) && depict.hasImportantStereo(v); +} + +lib::IO::Graph::Write::EdgeFake3DType DepictionData::K::getEdgeFake3DType(KEdge e, bool withHydrogen) const { + // TODO: translate v to vL and vR + const auto &g = depict.lr.getRule().getCombinedGraph(); + const auto vSrc = source(e, g); + const auto vTar = target(e, g); + if(!hasImportantStereo(vSrc) && !hasImportantStereo(vTar)) + return lib::IO::Graph::Write::EdgeFake3DType::None; +#ifndef MOD_HAVE_OPENBABEL + throw FatalError(MOD_NO_OPENBABEL_ERROR_STR); +#else + assert(depict.hasMoleculeEncoding); + const auto idSrc = get(boost::vertex_index_t(), g, vSrc); + const auto idTar = get(boost::vertex_index_t(), g, vTar); + const CoordData &cData = withHydrogen ? depict.cDataAll : depict.cDataNoHydrogen; + if(has_stereo(depict.lr) && get_stereo(depict.lr).inContext(vSrc) && get_stereo(depict.lr).inContext(vTar)) + return cData.obMolLeft.getBondFake3D(idSrc, idTar); + else + return lib::IO::Graph::Write::EdgeFake3DType::None; +#endif +} + +bool DepictionData::K::getHasCoordinates() const { + return depict.getHasCoordinates(); +} + +double DepictionData::K::getX(KVertex v, bool withHydrogen) const { + return depict.getX(v, withHydrogen); +} + +double DepictionData::K::getY(KVertex v, bool withHydrogen) const { + return depict.getY(v, withHydrogen); +} + +const AtomData &DepictionData::K::operator()(KVertex v) const { + const auto &rDPO = depict.lr.getRule(); + const auto vL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), v); + // for now, we just return whatever is in left, it's fake data anyway + return depict.getLeft()(vL); +} + +BondType DepictionData::K::operator()(KEdge e) const { + const auto bt = getBondData(e); + return bt == BondType::Invalid ? BondType::Single : bt; +} + +//------------------------------------------------------------------------------ +// Combined +//------------------------------------------------------------------------------ + +DepictionData::Combined::Combined(const DepictionData &depict) : depict(depict) {} + +const AtomData &DepictionData::Combined::operator()(CombinedVertex v) const { + if(VERBOSE) std::cout << "DepictionData::Combined(" << &depict << "): v=" << v << std::endl; + // fake data, for creating OBMol + const auto &rDPO = depict.lr.getRule(); + const auto &pMol = get_molecule(depict.lr); + // return whatever we find + const auto m = rDPO.getCombinedGraph()[v].membership; + if(m != Membership::R) { + const auto vL = get_inverse(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), v); + if(VERBOSE) std::cout << " vL=" << vL << std::endl; + const auto &atomData = pMol.getLeft()[vL]; + const auto atomId = atomData.getAtomId(); + if(atomId != AtomIds::Invalid) return atomData; + else { + const auto iter = depict.leftData.nonAtomToPhonyAtom.find(vL); + assert(iter != end(depict.leftData.nonAtomToPhonyAtom)); + return iter->second; + } + } else { + const auto vR = get_inverse(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), v); + if(VERBOSE) std::cout << " vR=" << vR << std::endl; + const auto &atomData = pMol.getRight()[vR]; + const auto atomId = atomData.getAtomId(); + if(atomId != AtomIds::Invalid) return atomData; + else { + const auto iter = depict.rightData.nonAtomToPhonyAtom.find(vR); + assert(iter != end(depict.rightData.nonAtomToPhonyAtom)); + return iter->second; + } + } +} + +BondType DepictionData::Combined::operator()(CombinedEdge e) const { + // fake data, for creating OBMol + const auto &rDPO = depict.lr.getRule(); + const auto &g = rDPO.getCombinedGraph(); + const auto &pMol = get_molecule(depict.lr); + // if there is agreement, return that, otherwise prefer invalid bonds + // this should give a bit more freedom in bond angles + const auto m = g[e].membership; + BondType l = BondType::Single, r = BondType::Single; + if(m != Membership::R) { + const auto eL = get_inverse(rDPO.getLtoCG(), getL(rDPO), g, e); + l = pMol.getLeft()[eL]; + } + if(m != Membership::L) { + const auto eR = get_inverse(rDPO.getRtoCG(), getR(rDPO), g, e); + r = pMol.getRight()[eR]; + } + if(l == r) return l; + else return BondType::Invalid; +} + +//------------------------------------------------------------------------------ +// DepictionData +//------------------------------------------------------------------------------ + +DepictionData::DepictionData(const LabelledRule &lr) + : lr(lr), hasMoleculeEncoding(true) { + if(VERBOSE) std::cout << "DepictionData(" << this << "):" << std::endl; + + const auto &rDPO = lr.getRule(); + const auto &g = get_graph(lr); + const auto &pString = get_string(lr); + const auto &pMol = get_molecule(lr); + { // vertexData + std::vector atomUsed(AtomIds::Max + 1, false); + Chem::markSpecialAtomsUsed(atomUsed); + const auto findNonChemicalVertices = [&atomUsed](const lib::DPO::CombinedRule::SideGraphType &g, + const PropMolecule::Side &pMol) { + std::vector res; + if(VERBOSE) std::cout << " |V|=" << num_vertices(g) << std::endl; + for(const auto v: asRange(vertices(g))) { + const auto atomId = pMol[v].getAtomId(); + if(atomId != AtomIds::Invalid) atomUsed[atomId] = true; + else { + if(VERBOSE) std::cout << " v=" << v << " invalid" << std::endl; + res.emplace_back(v); + } + } + return res; + }; + if(VERBOSE) std::cout << " findNonChemicalVertices(L):" << std::endl; + const auto vsToProcessL = findNonChemicalVertices(getL(rDPO), pMol.getLeft()); + if(VERBOSE) std::cout << " findNonChemicalVertices(R):" << std::endl; + const auto vsToProcessR = findNonChemicalVertices(getR(rDPO), pMol.getRight()); + + // map non-atom labels to atoms + std::map atomIdFromLabel; + auto atomIdIter = atomUsed.begin() + 1; // skip invalid + const auto processVertices = [this, &atomIdFromLabel, &atomUsed, &atomIdIter]( + const std::vector &vs, const PropString::Side &pString, const PropMolecule::Side &pMol, + SideData &data) { + if(VERBOSE) std::cout << " |vs|=" << vs.size() << std::endl; + for(const auto v: vs) { + std::string label = std::get<0>(Chem::extractIsotopeChargeRadical(pString[v])); + if(VERBOSE) std::cout << " v=" << v << ", label=" << label << std::endl; + const auto atomId = [this, &atomIdFromLabel, &label, &atomUsed, &atomIdIter]() { + const auto iter = atomIdFromLabel.find(label); + if(iter != end(atomIdFromLabel)) { + if(VERBOSE) std::cout << " already handled" << std::endl; + return iter->second; + } + atomIdIter = std::find(atomIdIter, atomUsed.end(), false); + if(atomIdIter == atomUsed.end()) { + if(VERBOSE) std::cout << " no more atomIds" << std::endl; + hasMoleculeEncoding = false; + return AtomIds::Invalid; + } + unsigned char atomId = atomIdIter - atomUsed.begin(); + if(VERBOSE) std::cout << " atomId=" << int(atomId) << std::endl; + atomUsed[atomId] = true; + phonyAtomToString[AtomId(atomId)] = label; + atomIdFromLabel.emplace(label, AtomId(atomId)); + return AtomId(atomId); + }(); + if(atomId == AtomIds::Invalid) break; + const auto charge = pMol[v].getCharge(); + const bool radical = pMol[v].getRadical(); + if(VERBOSE) std::cout << " nonAtomToPhonyAtom[" << v << "]" << std::endl; + data.nonAtomToPhonyAtom[v] = AtomData(atomId, charge, radical); + } + }; + if(VERBOSE) std::cout << " processVertices(L):" << std::endl; + processVertices(vsToProcessL, pString.getLeft(), pMol.getLeft(), leftData); + if(VERBOSE) std::cout << " processVertices(R):" << std::endl; + processVertices(vsToProcessR, pString.getRight(), pMol.getRight(), rightData); + } + { // edgeData + const auto handleEdges = [](const lib::DPO::CombinedRule::SideGraphType &g, + const PropString::Side &pString, const PropMolecule::Side &pMol, + SideData &data) { + for(const auto e: asRange(edges(g))) { + const auto bt = pMol[e]; + if(bt == BondType::Invalid) { + if(VERBOSE) + std::cout << " e=" << e << "(" << e.get_property() << "), label=" << pString[e] << std::endl; + data.nonBondEdges[e] = pString[e]; + } + } + }; + if(VERBOSE) std::cout << " handleEdges(L):" << std::endl; + handleEdges(getL(rDPO), pString.getLeft(), pMol.getLeft(), leftData); + if(VERBOSE) std::cout << " handleEdges(R):" << std::endl; + handleEdges(getR(rDPO), pString.getRight(), pMol.getRight(), rightData); + } + + if(hasMoleculeEncoding) { +#ifdef MOD_HAVE_OPENBABEL + const auto doIt = [&](CoordData &cData, const bool withHydrogen) { + std::tie(cData.obMol, cData.obMolLeft, cData.obMolRight) + = Chem::makeOBMol(lr, getCombined(), getCombined(), + getLeft(), getLeft(), + getRight(), getRight(), + [this](const lib::DPO::CombinedRule::CombinedVertex v) { + return mayCollapse(v); + }, withHydrogen); + cData.x.resize(num_vertices(g)); + cData.y.resize(num_vertices(g)); + for(const auto v: asRange(vertices(g))) { + const auto vId = get(boost::vertex_index_t(), g, v); + if(cData.obMol.hasAtom(vId)) { + cData.x[vId] = cData.obMol.getAtomX(vId); + cData.y[vId] = cData.obMol.getAtomY(vId); + } else { + assert(!withHydrogen); + cData.x[vId] = std::numeric_limits::quiet_NaN(); + cData.y[vId] = std::numeric_limits::quiet_NaN(); + } + } + }; + doIt(cDataAll, true); + doIt(cDataNoHydrogen, false); +#endif + } +} + +bool DepictionData::hasImportantStereo(CombinedVertex v) const { + if(!has_stereo(lr)) return false; + const auto &g = get_graph(lr); + const auto m = g[v].membership; + // TODO: map v to side vertices + //assert(false); + if(m != Membership::R && !get_stereo(get_labelled_left(lr))[v]->morphismDynamicOk()) return true; + if(m != Membership::L && !get_stereo(get_labelled_right(lr))[v]->morphismDynamicOk()) return true; + return false; +} + +bool DepictionData::mayCollapse(CombinedVertex v) const { + const auto &rDPO = lr.getRule(); + const auto &g = rDPO.getCombinedGraph(); + if(g[v].membership != lib::Rules::Membership::K) + return false; + for(const auto e: asRange(out_edges(v, g))) + if(g[e].membership != lib::Rules::Membership::K) + return false; + // Do not use cgAtom and cgBond, as they provide fake data. + // Instead, create some differently fake data specifically for hydrogen collapsing. + const auto atomData = [&](const CombinedVertex v) { + const auto vL = get_inverse(rDPO.getLtoCG(), getL(rDPO), g, v); + const auto vR = get_inverse(rDPO.getRtoCG(), getR(rDPO), g, v); + const auto l = getLeft().getAtomData(vL); + const auto r = getRight().getAtomData(vR); + if(l == r) return l; + else return AtomData(AtomIds::Max); + }; + const auto bondData = [&](const CombinedEdge e) { + const auto eL = get_inverse(rDPO.getLtoCG(), getL(rDPO), g, e); + const auto eR = get_inverse(rDPO.getRtoCG(), getR(rDPO), g, e); + const auto l = getLeft().getBondData(eL); + const auto r = getRight().getBondData(eR); + if(l == r) return l; + else return BondType::Invalid; + }; + return lib::Chem::isCollapsibleHydrogen(v, g, atomData, bondData, [this](const CombinedVertex v) { + return hasImportantStereo(v); + }); +} + +bool DepictionData::getHasCoordinates() const { +#ifdef MOD_HAVE_OPENBABEL + return hasMoleculeEncoding; +#else + return false; +#endif +} + +double DepictionData::getX(CombinedVertex v, bool withHydrogen) const { + if(!getHasCoordinates()) MOD_ABORT; + const auto &g = get_graph(lr); + const auto vId = get(boost::vertex_index_t(), g, v); + const CoordData &cData = withHydrogen ? cDataAll : cDataNoHydrogen; + assert(vId < cData.x.size()); + return cData.x[vId]; +} + +double DepictionData::getY(CombinedVertex v, bool withHydrogen) const { + if(!getHasCoordinates()) MOD_ABORT; + const auto &g = get_graph(lr); + const auto vId = get(boost::vertex_index_t(), g, v); + const CoordData &cData = withHydrogen ? cDataAll : cDataNoHydrogen; + assert(vId < cData.y.size()); + return cData.y[vId]; +} + +void DepictionData::copyCoords(const DepictionData &other, const std::map &vMap) { + if(!other.getHasCoordinates()) return; + const auto &g = get_graph(lr); + const auto doIt = [&](CoordData &cData, const bool withHydrogen) { + cData.x.resize(num_vertices(g)); + cData.y.resize(num_vertices(g)); + for(const auto v: asRange(vertices(g))) { + const auto iter = vMap.find(v); + if(iter == end(vMap)) { + std::cout << "Vertex " << v << " (id=" << get(boost::vertex_index_t(), g, v) << ") not mapped." + << std::endl; + std::cout << "Map:" << std::endl; + for(auto p: vMap) std::cout << "\t" << p.first << " => " << p.second << std::endl; + std::cout << "num_vertices: " << num_vertices(g) << std::endl; + std::cout << "other.num_vertices: " << num_vertices(get_graph(other.lr)) << std::endl; + MOD_ABORT; + } + const auto vOther = iter->second; + const auto vId = get(boost::vertex_index_t(), g, v); + cData.x[vId] = other.getX(vOther, withHydrogen); + cData.y[vId] = other.getY(vOther, withHydrogen); + } + if(hasMoleculeEncoding) { +#ifdef MOD_HAVE_OPENBABEL + cData.obMol.setCoordinates(cData.x, cData.y); + cData.obMolLeft.setCoordinates(cData.x, cData.y); + cData.obMolRight.setCoordinates(cData.x, cData.y); +#endif + } + }; + doIt(cDataAll, true); + doIt(cDataNoHydrogen, false); +} + +DepictionData::Side DepictionData::getLeft() const { + if(!hasMoleculeEncoding) MOD_ABORT; + return {*this, leftData, &CoordData::obMolLeft, getL(lr.getRule()), + lr.getRule().getLtoCG(), + get_molecule(lr).getLeft(), + [this]() { return get_stereo(lr).getLeft(); }}; +} + +DepictionData::K DepictionData::getContext() const { + if(!hasMoleculeEncoding) MOD_ABORT; + return {*this}; +} + +DepictionData::Side DepictionData::getRight() const { + if(!hasMoleculeEncoding) MOD_ABORT; + return {*this, rightData, &CoordData::obMolRight, getR(lr.getRule()), + lr.getRule().getRtoCG(), + get_molecule(lr).getRight(), + [this]() { return get_stereo(lr).getRight(); }}; +} + +DepictionData::Combined DepictionData::getCombined() const { + return {*this}; +} + +} // namespace mod::lib::Rules::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/IO/DepictionData.hpp b/libs/libmod/src/mod/lib/Rules/IO/DepictionData.hpp new file mode 100644 index 0000000..d30f787 --- /dev/null +++ b/libs/libmod/src/mod/lib/Rules/IO/DepictionData.hpp @@ -0,0 +1,138 @@ +#ifndef MOD_LIB_RULES_IO_DEPICTIONDATA_HPP +#define MOD_LIB_RULES_IO_DEPICTIONDATA_HPP + +#include +#include +#include +#include + +#include +#include + +namespace mod { +struct AtomId; +struct Charge; +struct AtomData; +enum class BondType; +} // namespace mod +namespace mod::lib::Rules { +struct PropString; +struct PropMolecule; +} // namespace mod::lib::Rules +namespace mod::lib::Rules::Write { + +struct DepictionData { + using CombinedVertex = lib::DPO::CombinedRule::CombinedVertex; + using CombinedEdge = lib::DPO::CombinedRule::CombinedEdge; + using SideVertex = lib::DPO::CombinedRule::SideVertex; + using SideEdge = lib::DPO::CombinedRule::SideEdge; + using KVertex = lib::DPO::CombinedRule::KVertex; + using KEdge = lib::DPO::CombinedRule::KEdge; + using SideToCG = lib::DPO::CombinedRule::ToCombinedMorphismSide; +private: + struct SideData; + struct CoordData; +public: + struct Side { + Side(const DepictionData &depict, const SideData &data, + const Chem::OBMolHandle CoordData::*obSide, + const lib::DPO::CombinedRule::SideGraphType &g, + SideToCG mToCG, + PropMolecule::Side pMol, // we need definitely need mol data + // we don't want to instantiate stereo data unless needed + std::function fStereo); + public: // used in GraphWriteGeneric + AtomData getAtomData(SideVertex vS) const; // shortcut to moleculeState + AtomId getAtomId(SideVertex vS) const; // shortcut to moleculeState + Isotope getIsotope(SideVertex vS) const; // shortcut to moleculeState + Charge getCharge(SideVertex vS) const; // shortcut to moleculeState + bool getRadical(SideVertex vS) const; // shortcut to moleculeState + std::string getVertexLabelNoIsotopeChargeRadical(SideVertex vS) const; + BondType getBondData(SideEdge eS) const; // shortcut to moleculeState + std::string getEdgeLabel(SideEdge eS) const; + bool hasImportantStereo(SideVertex vS) const; + lib::IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(SideEdge eS, bool withHydrogen) const; + bool getHasCoordinates() const; + double getX(SideVertex vS, bool withHydrogen) const; + double getY(SideVertex vS, bool withHydrogen) const; + public: // for coordinates + const AtomData &operator()(SideVertex vS) const; // fake data + BondType operator()(SideEdge eS) const; // fake data + private: + const DepictionData &depict; + const SideData &data; + const Chem::OBMolHandle CoordData::*obSide; + const lib::DPO::CombinedRule::SideGraphType &g; + const SideToCG mToCG; + PropMolecule::Side pMol; + std::function fStereo; + }; + struct K { + K(const DepictionData &depict); + public: // used in GraphWriteGeneric + AtomId getAtomId(KVertex v) const; // shortcut to moleculeState + Isotope getIsotope(KVertex v) const; // shortcut to moleculeState + Charge getCharge(KVertex v) const; // shortcut to moleculeState + bool getRadical(KVertex v) const; // shortcut to moleculeState + std::string getVertexLabelNoIsotopeChargeRadical(KVertex v) const; + BondType getBondData(KEdge e) const; // shortcut to moleculeState + std::string getEdgeLabel(KEdge e) const; + bool hasImportantStereo(KVertex v) const; + lib::IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(KEdge e, bool withHydrogen) const; + bool getHasCoordinates() const; + double getX(KVertex v, bool withHydrogen) const; + double getY(KVertex v, bool withHydrogen) const; + public: // for coordinates + const AtomData &operator()(KVertex v) const; // fake data + BondType operator()(KEdge e) const; // fake data + private: + const DepictionData &depict; + }; + struct Combined { + Combined(const DepictionData &depict); + public: // for coordinates + const AtomData &operator()(CombinedVertex v) const; // fake data + BondType operator()(CombinedEdge e) const; // fake data + private: + const DepictionData &depict; + }; +public: + DepictionData(const DepictionData &) = delete; + DepictionData &operator=(const DepictionData &) = delete; +public: + DepictionData(const LabelledRule &lr); + bool hasImportantStereo(CombinedVertex v) const; + bool mayCollapse(CombinedVertex v) const; + bool getHasCoordinates() const; + double getX(CombinedVertex v, bool withHydrogen) const; + double getY(CombinedVertex v, bool withHydrogen) const; + // vMap: this -> other + void copyCoords(const DepictionData &other, const std::map &vMap); +public: // projections + Side getLeft() const; + K getContext() const; + Side getRight() const; + Combined getCombined() const; +private: + const LabelledRule &lr; + bool hasMoleculeEncoding; + std::map phonyAtomToString; + struct SideData { + std::map nonAtomToPhonyAtom; + std::map nonBondEdges; + } leftData, rightData; + + struct CoordData { + std::vector x, y; +#ifdef MOD_HAVE_OPENBABEL + // the pushout, for generating coords + lib::Chem::OBMolHandle obMol; + // each side, for stereo, with coords copied from the pushout + lib::Chem::OBMolHandle obMolLeft, obMolRight; +#endif + } cDataAll, cDataNoHydrogen; +}; + +} // namespace mod::lib::Rules + +#endif // MOD_LIB_RULES_IO_DEPICTIONDATA_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/IO/Read.cpp b/libs/libmod/src/mod/lib/Rules/IO/Read.cpp new file mode 100644 index 0000000..e4be344 --- /dev/null +++ b/libs/libmod/src/mod/lib/Rules/IO/Read.cpp @@ -0,0 +1,984 @@ +#include "Read.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace mod::lib::Rules::Read { +using lib::IO::Result; +namespace GML = lib::IO::GML; +namespace { + +template +struct Label { + std::optional left, context, right; +}; + +struct VertexLabels { + bool inLeft = false, inContext = false, inRight = false; + Label string, stereo; + bool stereoInContext = false; +public: + lib::DPO::CombinedRule::CombinedVertex cgVertex; + std::optional parsedEmbeddingLeft, parsedEmbeddingRight; +}; + +struct EdgeLabels { + bool inLeft = false, inContext = false, inRight = false; + Label string, stereo; + bool stereoInContext = false; +public: + lib::DPO::CombinedRule::CombinedEdge cgEdge; +}; + +Result parseGML(std::string_view input) { + GML::Rule rule; + gml::ast::KeyValue ast; + try { + ast = gml::parser::parse(input); + } catch(const gml::parser::error &e) { + return Result<>::Error(e.what()); + } + using namespace gml::converter::edsl; + auto cVertex = GML::makeVertexConverter(0); + auto cEdge = GML::makeEdgeConverter(0); + auto nodeLabels = list("nodeLabels") + (string("label", &GML::AdjacencyConstraint::nodeLabels)); + auto edgeLabels = list("edgeLabels") + (string("label", &GML::AdjacencyConstraint::edgeLabels)); + auto constrainAdj = list("constrainAdj", &GML::Rule::matchConstraints) + (int_("id", &GML::AdjacencyConstraint::id), 1, 1) + (string("op", &GML::AdjacencyConstraint::op), 1, 1) + (int_("count", &GML::AdjacencyConstraint::count), 1, 1) + (nodeLabels)(edgeLabels); + auto constrainShortestPath = list("constrainShortestPath", + &GML::Rule::matchConstraints) + (int_("source", &GML::ShortestPathConstraint::source), 1, 1) + (int_("target", &GML::ShortestPathConstraint::target), 1, 1) + (string("op", &GML::ShortestPathConstraint::op), 1, 1) + (int_("length", &GML::ShortestPathConstraint::length), 1, 1); + auto makeSide = [&](std::string name, GML::Graph GML::Rule::*side) { + return list(name, side)(cVertex)(cEdge); + }; + auto cRule = list("rule") + (string("ruleID", &GML::Rule::id), 0, 1) + (string("labelType", &GML::Rule::labelType), 0, 1) + (makeSide("left", &GML::Rule::left), 0, 1) + (makeSide("context", &GML::Rule::context), 0, 1) + (makeSide("right", &GML::Rule::right), 0, 1) + (constrainAdj)(constrainShortestPath); + auto iterBegin = * + auto iterEnd = iterBegin + 1; + try { + gml::converter::convert(iterBegin, iterEnd, cRule, rule); + } catch(const gml::converter::error &e) { + return Result<>::Error(e.what()); + } + return rule; +} + +Result<> checkGraphDuplicatesAndLoops(const GML::Graph &side, std::string_view name) { + std::unordered_set vertexIds; + for(const GML::Vertex &v: side.vertices) { + if(vertexIds.find(v.id) != end(vertexIds)) + return Result<>::Error("Duplicate vertex " + std::to_string(v.id) + " in " + std::string(name) + " graph."); + vertexIds.insert(v.id); + } + std::set> edgeIds; + for(const GML::Edge &e: side.edges) { + if(e.source == e.target) + return Result<>::Error( + "Loop edge (on " + std::to_string(e.source) + ", in " + std::string(name) + ") is not allowed."); + auto eSorted = std::minmax(e.source, e.target); + if(edgeIds.find(eSorted) != end(edgeIds)) + return Result<>::Error( + "Duplicate edge (" + std::to_string(e.source) + ", " + std::to_string(e.target) + ") in " + + std::string(name) + " graph."); + edgeIds.insert(eSorted); + } + return Result<>(); +} + +auto extractDataFromGML(const GML::Rule &rule) { + std::map vLabelsFromId; + std::map, EdgeLabels> eLabelsFromIds; + + for(const GML::Vertex &vGML: rule.left.vertices) { + auto &v = vLabelsFromId[vGML.id]; + v.inLeft = true; + v.string.left = vGML.label; + v.stereo.left = vGML.stereo; + } + for(const GML::Vertex &vGML: rule.context.vertices) { + auto &v = vLabelsFromId[vGML.id]; + v.inContext = true; + v.string.context = vGML.label; + v.stereo.context = vGML.stereo; + } + for(const GML::Vertex &vGML: rule.right.vertices) { + auto &v = vLabelsFromId[vGML.id]; + v.inRight = true; + v.string.right = vGML.label; + v.stereo.right = vGML.stereo; + } + for(const GML::Edge &eGML: rule.left.edges) { + auto eSorted = std::minmax(eGML.source, eGML.target); + auto &e = eLabelsFromIds[eSorted]; + e.inLeft = true; + e.string.left = eGML.label; + e.stereo.left = eGML.stereo; + } + for(const GML::Edge &eGML: rule.context.edges) { + auto eSorted = std::minmax(eGML.source, eGML.target); + auto &e = eLabelsFromIds[eSorted]; + e.inContext = true; + e.string.context = eGML.label; + e.stereo.context = eGML.stereo; + } + for(const GML::Edge &eGML: rule.right.edges) { + auto eSorted = std::minmax(eGML.source, eGML.target); + auto &e = eLabelsFromIds[eSorted]; + e.inRight = true; + e.string.right = eGML.label; + e.stereo.right = eGML.stereo; + } + + return std::pair(std::move(vLabelsFromId), std::move(eLabelsFromIds)); +} + +struct MatchConstraintConverter { + MatchConstraintConverter(LabelledRule &dpoResult, const std::map &vLabelsFromId) + : dpoResult(dpoResult), vLabelsFromId(vLabelsFromId) {} + + Result<> operator()(const GML::AdjacencyConstraint &cGML) { + const auto iter = vLabelsFromId.find(cGML.id); + if(iter == end(vLabelsFromId)) + return Result<>::Error("Error in rule GML. Vertex " + std::to_string(cGML.id) + + " in adjacency constraint does not exist."); + const auto vConstrained = iter->second.cgVertex; + lib::GraphMorphism::Constraints::Operator op; + { + const auto &s = cGML.op; + using namespace lib::GraphMorphism::Constraints; + if(s == "<") op = Operator::LT; + else if(s == "<=") op = Operator::LEQ; + else if(s == "=") op = Operator::EQ; + else if(s == ">=") op = Operator::GEQ; + else if(s == ">") op = Operator::GT; + else return Result<>::Error("Error in rule GML. Unknown operator '" + s + "' in adjacency constraint."); + } + auto c = std::make_unique< + lib::GraphMorphism::Constraints::VertexAdjacency + >(vConstrained, op, cGML.count); + c->vertexLabels.insert(cGML.nodeLabels.begin(), cGML.nodeLabels.end()); + c->edgeLabels.insert(cGML.edgeLabels.begin(), cGML.edgeLabels.end()); + dpoResult.leftData.matchConstraints.push_back(std::move(c)); + return Result<>(); + } + + Result<> operator()(const GML::ShortestPathConstraint &cGML) { + const auto iterSrc = vLabelsFromId.find(cGML.source); + const auto iterTar = vLabelsFromId.find(cGML.target); + if(iterSrc == end(vLabelsFromId)) + return Result<>::Error("Error in rule GML. Vertex " + std::to_string(cGML.source) + + " in shortest path constraint does not exist."); + if(iterTar == end(vLabelsFromId)) + return Result<>::Error("Error in rule GML. Vertex " + std::to_string(cGML.target) + + " in shortest path constraint does not exist."); + const auto vSrc = iterSrc->second.cgVertex; + const auto vTar = iterTar->second.cgVertex; + lib::GraphMorphism::Constraints::Operator op; + { + const auto &s = cGML.op; + using namespace lib::GraphMorphism::Constraints; + if(s == "<") op = Operator::LT; + else if(s == "<=") op = Operator::LEQ; + else if(s == "=") op = Operator::EQ; + else if(s == ">=") op = Operator::GEQ; + else if(s == ">") op = Operator::GT; + else + return Result<>::Error( + "Error in rule GML. Unknown operator '" + s + "' in shortest path constraint."); + } + const auto compSrc = get_component(get_labelled_left(dpoResult))[ + get(boost::vertex_index_t(), get_graph(dpoResult), vSrc)]; + const auto compTar = get_component(get_labelled_left(dpoResult))[ + get(boost::vertex_index_t(), get_graph(dpoResult), vTar)]; + if(compSrc != compTar) + return Result<>::Error( + "Error in rule GML. Vertex " + std::to_string(cGML.source) + " and " + std::to_string(cGML.target) + + " are in different connected components of the left graph. " + + "This is currently not supported for the shortest path constraint."); + auto c = std::make_unique< + lib::GraphMorphism::Constraints::ShortestPath + >(vSrc, vTar, op, cGML.length); + dpoResult.leftData.matchConstraints.push_back(std::move(c)); + return Result<>(); + } +public: + LabelledRule &dpoResult; + const std::map &vLabelsFromId; +}; + +} // namespace + +Result gml(lib::IO::Warnings &warnings, std::string_view input) { + auto resRule = parseGML(input); + if(!resRule) return std::move(resRule); // TODO: remove std::move when C++20/P1825R0 is available + auto &rule = *resRule; + + if(auto res = checkGraphDuplicatesAndLoops(rule.left, "left"); !res) return res; + if(auto res = checkGraphDuplicatesAndLoops(rule.context, "context"); !res) return res; + if(auto res = checkGraphDuplicatesAndLoops(rule.right, "right"); !res) return res; + auto labelsFromIdPair = extractDataFromGML(rule); // TODO: (C++20) use structured binding directly + auto &[vLabelsFromId, eLabelsFromIds] = labelsFromIdPair; + + Data data; + data.rule = LabelledRule(); + data.name = rule.id; + if(rule.labelType) { + const std::string <String = *rule.labelType; + if(ltString == "string") data.labelType = LabelType::String; + else if(ltString == "term") data.labelType = LabelType::Term; + else return Result<>::Error("Error in rule GML. Unknown labelType '" + ltString + "'."); + } + + auto &dpoResult = *data.rule; + auto &rDPO = dpoResult.getRule(); + dpoResult.pString = std::make_unique(dpoResult.getRule()); + auto &pString = *dpoResult.pString; + + using CombinedVertex = DPO::CombinedRule::CombinedVertex; + std::map vIdFromCG; + for(auto &[id, vData]: vLabelsFromId) { + // First find the right membership: + // inContext <=> inLeft && inRight + if(vData.inContext) vData.inLeft = vData.inRight = true; + else if(vData.inLeft && vData.inRight) vData.inContext = true; + + // Check labels and make (left, right) the correct labels + if(vData.string.context) { + if(vData.string.left) + return Result<>::Error( + "Error in rule GML. Vertex " + std::to_string(id) + " has a label both in 'context' and 'left'."); + if(vData.string.right) + return Result<>::Error( + "Error in rule GML. Vertex " + std::to_string(id) + " has a label both in 'context' and 'right'."); + // Note: terms follow the same semantics as string, i.e., the same string in L and R becomes the exact same terms. + vData.string.left = vData.string.right = vData.string.context; + } + + // Check that there is a string in left/right when inLeft/inRight + if(vData.inLeft && !vData.string.left) + return Result<>::Error("Error in rule GML. Vertex " + std::to_string(id) + " is in L, but has no label."); + if(vData.inRight && !vData.string.right) + return Result<>::Error("Error in rule GML. Vertex " + std::to_string(id) + " is in R, but has no label."); + + if(vData.inContext) { + const auto vK = addVertexK(rDPO); + pString.addK(vK, *vData.string.left, *vData.string.right); + vData.cgVertex = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), vK); + } else if(vData.inLeft) { + assert(!vData.inRight); + const auto vL = addVertexL(rDPO); + pString.addL(vL, *vData.string.left); + vData.cgVertex = get(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), vL); + } else { + assert(vData.inRight); + const auto vR = addVertexR(rDPO); + pString.addR(vR, *vData.string.right); + vData.cgVertex = get(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), vR); + } + vIdFromCG[vData.cgVertex] = id; + data.externalToInternalIds[id] = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), vData.cgVertex); + } // for each vertex + + for(auto &[eIds, eData]: eLabelsFromIds) { + const auto[src, tar] = eIds; + if(vLabelsFromId.find(src) == end(vLabelsFromId)) + return Result<>::Error( + "Error in rule GML. Edge endpoint '" + std::to_string(src) + "' does not exist for edge (" + + std::to_string(src) + ", " + std::to_string(tar) + ")."); + if(vLabelsFromId.find(tar) == end(vLabelsFromId)) + return Result<>::Error( + "Error in rule GML. Edge endpoint '" + std::to_string(tar) + "' does not exist for edge (" + + std::to_string(src) + ", " + std::to_string(tar) + ")."); + const CombinedVertex vcSrc = vLabelsFromId[src].cgVertex, vcTar = vLabelsFromId[tar].cgVertex; + // First find the right membership: + // inContext <=> inLeft && inRight + if(eData.inContext) eData.inLeft = eData.inRight = true; + else if(eData.inLeft && eData.inRight) eData.inContext = true; + + // checking dangling in left/right + if(eData.inLeft) { + const auto &g = rDPO.getCombinedGraph(); + if(g[vcSrc].membership == Membership::R) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") dangling: edge is present in L but endpoint " + std::to_string(src) + + " only present in R."); + if(g[vcTar].membership == Membership::R) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") dangling: edge is present in L but endpoint " + std::to_string(tar) + + " only present in R."); + } + if(eData.inRight) { + const auto &g = rDPO.getCombinedGraph(); + if(g[vcSrc].membership == Membership::L) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") dangling: edge is present in R but endpoint " + std::to_string(src) + + " only present in L."); + if(g[vcTar].membership == Membership::L) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") dangling: edge is present in R but endpoint " + std::to_string(tar) + + " only present in L."); + } + + // Check labels and make (left, right) the correct labels + if(eData.string.context) { + if(eData.string.left) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") has a label both in 'context' and 'left'."); + if(eData.string.right) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") has a label both in 'context' and 'right'."); + // TODO: for term it matters if it's L+R or it's K + eData.string.left = eData.string.right = eData.string.context; + } + + // Check that there is a string in left/right when inLeft/inRight + if(eData.inLeft && !eData.string.left) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") is in L, but has no label."); + if(eData.inRight && !eData.string.right) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") is in R, but has no label."); + + if(eData.inContext) { + const auto eK = addEdgeK(rDPO, vcSrc, vcTar); + pString.addK(eK, *eData.string.left, *eData.string.right); + eData.cgEdge = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), eK); + } else if(eData.inLeft) { + assert(!eData.inRight); + const auto vLSrc = get_inverse(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), vcSrc); + const auto vLTar = get_inverse(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), vcTar); + const auto eL = addEdgeL(rDPO, vLSrc, vLTar); + pString.addL(eL, *eData.string.left); + eData.cgEdge = get(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), eL); + } else { + assert(eData.inRight); + const auto vRSrc = get_inverse(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), vcSrc); + const auto vRTar = get_inverse(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), vcTar); + const auto eR = addEdgeR(rDPO, vRSrc, vRTar); + pString.addR(eR, *eData.string.right); + eData.cgEdge = get(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), eR); + } + } // for each edge + // the graph is set, so initialise the component storage + dpoResult.initComponents(); + + // constraints + for(const GML::MatchConstraint &cGML: rule.matchConstraints) { + MatchConstraintConverter visitor(dpoResult, vLabelsFromId); + if(auto res = std::visit(visitor, cGML); !res) return res; + } // for each constraint + + { // perhaps we can stop now, if there is not stereo annotation + bool doStereo = false; + for(const auto &v: rule.left.vertices) doStereo = doStereo || v.stereo; + for(const auto &v: rule.context.vertices) doStereo = doStereo || v.stereo; + for(const auto &v: rule.right.vertices) doStereo = doStereo || v.stereo; + for(const auto &e: rule.left.edges) doStereo = doStereo || e.stereo; + for(const auto &e: rule.context.edges) doStereo = doStereo || e.stereo; + for(const auto &e: rule.right.edges) doStereo = doStereo || e.stereo; + if(!doStereo) return std::move(data); // TODO: remove std::move when C++20/P1825R0 is available + } + + // ========================================================================================= + // Stereo + // ========================================================================================= + + for(auto &[id, vData]: vLabelsFromId) { + // Check labels and make (left, right) the correct labels + if(vData.stereo.context) { + if(vData.stereo.left) + return Result<>::Error( + "Error in rule GML. Vertex " + std::to_string(id) + " has stereo both in 'context' and 'left'."); + if(vData.stereo.right) + return Result<>::Error( + "Error in rule GML. Vertex " + std::to_string(id) + " has stereo both in 'context' and 'right'."); + vData.stereo.left = vData.stereo.right = vData.stereo.context; + } + } + + for(auto &[eIds, eData]: eLabelsFromIds) { + const auto[src, tar] = eIds; + if(eData.stereo.context) { + if(eData.stereo.left) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") has stereo both in 'context' and 'left'."); + if(eData.stereo.right) + return Result<>::Error("Error in rule GML. Edge (" + std::to_string(src) + ", " + std::to_string(tar) + + ") has stereo both in 'context' and 'right'."); + eData.stereo.left = eData.stereo.right = eData.stereo.context; + } + } + + PropMolecule mol(dpoResult.getRule(), pString); // temporary for doing the inference + auto molLeft = mol.getLeft(); + auto molRight = mol.getRight(); + auto leftInference = lib::Stereo::Inference(getL(rDPO), molLeft, true); + auto rightInference = lib::Stereo::Inference(getR(rDPO), molRight, true); + // Set the explicitly defined edge categories. + //---------------------------------------------------------------------------- + for(const auto &eIdLabelPair: eLabelsFromIds) { + const auto &[eIds, labels] = eIdLabelPair; + const auto handleSide = [&eIdLabelPair, &rDPO]( + const std::optional &os, const std::string &side, + auto &inference, const auto &mSideToCG, const auto &gSide) -> Result<> { + // TODO: (C++20) use structured binding in loop and capture them + const auto &[eIds, labels] = eIdLabelPair; + if(!os) return Result<>(); + const std::string &s = *os; + if(s.size() != 1) + return Result<>::Error("Error in stereo data for edge (" + std::to_string(eIds.first) + + ", " + std::to_string(eIds.second) + ") in " + side + + ". Parsing error in stereo data '" + s + "'."); + lib::Stereo::EdgeCategory cat; + switch(s.front()) { + case '*': + cat = lib::Stereo::EdgeCategory::Any; + break; + default: + return Result<>::Error("Error in stereo data for edge (" + std::to_string(eIds.first) + ", " + + std::to_string(eIds.second) + ") in " + side + + ". Parsing error in stereo data '" + s + "'."); + } + const auto eSide = get_inverse(mSideToCG, gSide, rDPO.getCombinedGraph(), labels.cgEdge); + auto res = inference.assignEdgeCategory(eSide, cat); + if(!res) { + res.setError("Error in stereo data for edge (" + std::to_string(eIds.first) + ", " + + std::to_string(eIds.second) + ") in " + side + ". " + res.extractError()); + return res; + } + return res; + }; + if(auto res = handleSide(labels.stereo.left, "L", leftInference, rDPO.getLtoCG(), getL(rDPO)); !res) return res; + if(auto res = handleSide(labels.stereo.right, "R", rightInference, rDPO.getRtoCG(), getR(rDPO)); !res) return res; + } // for each edge + // Set the explicitly defined vertex stereo data. + //---------------------------------------------------------------------------- + const auto &gGeometry = lib::Stereo::getGeometryGraph(); + for(auto &eVIdLabelsPair: vLabelsFromId) { + auto &[vId, vLabels] = eVIdLabelsPair; // TODO: (C++20) remove, structured binding capture + // TODO: (C++20) use structured binding in loop + const auto handleSide = [&](const std::optional &os, const std::string &side, auto &inference, + auto &parsedEmbedding, const auto &mSideToCG, const auto &gSide) { + auto &[vLabelsFromId, eLabelsFromIds] = labelsFromIdPair; // TODO: (C++20) remove, structured binding capture + const auto &[vId, vLabels] = eVIdLabelsPair; + if(!os) return Result<>(); + if(auto parsedEmbeddingRes = lib::Stereo::Read::parseEmbedding(*os)) { + parsedEmbedding = std::move(*parsedEmbeddingRes); + } else { + return Result<>::Error( + "Error in stereo data for vertex " + std::to_string(vId) + " in " + side + ". " + + parsedEmbeddingRes.extractError()); + } + const auto &vcg = vLabels.cgVertex; + const auto vSide = get_inverse(mSideToCG, gSide, rDPO.getCombinedGraph(), vcg); + // Geometry + //.......................................................................... + const auto &embGML = *parsedEmbedding; + if(embGML.geometry) { + const auto vGeo = gGeometry.findGeometry(*embGML.geometry); + if(vGeo == gGeometry.nullGeometry()) + return Result<>::Error("Error in stereo data for vertex " + std::to_string(vId) + " in " + side + + ". Invalid geometry '" + *embGML.geometry + "'."); + if(auto res = inference.assignGeometry(vSide, vGeo); !res) { + return Result<>::Error( + "Error in stereo data for vertex " + std::to_string(vId) + " in " + side + ". " + + res.extractError()); + } + } + // Edges + //.......................................................................... + if(embGML.edges) { + inference.initEmbedding(vSide); + for(const auto &e: *embGML.edges) { + if(const int *idPtr = std::get_if(&e)) { + const int idNeighbour = *idPtr; + if(vLabelsFromId.find(idNeighbour) == end(vLabelsFromId)) + return Result<>::Error("Error in graph GML. Neighbour vertex " + std::to_string(idNeighbour) + + " in stereo embedding for vertex " + + std::to_string(vId) + " in " + side + " does not exist."); + const auto vFromVertexId = [&labelsFromIdPair](int id) { + auto &[vLabelsFromId, eLabelsFromIds] = labelsFromIdPair; // TODO: (C++20) remove, structured binding capture + const auto iter = vLabelsFromId.find(id); + assert(iter != end(vLabelsFromId)); + return iter->second.cgVertex; + }; + auto epCG = edge(vcg, vFromVertexId(idNeighbour), rDPO.getCombinedGraph()); + if(!epCG.second) + return Result<>::Error("Error in graph GML. Vertex " + std::to_string(idNeighbour) + + " in stereo embedding for vertex " + + std::to_string(vId) + " in " + side + " is not a neighbour."); + const auto eSide = get_inverse(mSideToCG, gSide, rDPO.getCombinedGraph(), epCG.first); + inference.addEdge(vSide, eSide); + } else if(const char *virtPtr = std::get_if(&e)) { + switch(*virtPtr) { + case 'e': + inference.addLonePair(vSide); + break; + case 'r': + inference.addRadical(vSide); + break; + default: + return Result<>::Error( + "Error in graph GML. Virtual neighbour in stereo embedding for vertex " + + std::to_string(vId) + " in " + side + " has unknown type '" + *virtPtr + "'."); + } + } else { + MOD_ABORT; // the parser should know what is allowed + } + } + } + // Fixation + //.......................................................................... + if(embGML.fixation) { + // TODO: expand this when more complicated geometries are implemented + const bool isFixed = *embGML.fixation; + if(isFixed) inference.fixSimpleGeometry(vSide); + } + return Result<>(); + }; + if(auto res = handleSide(vLabels.stereo.left, "L", leftInference, vLabels.parsedEmbeddingLeft, + rDPO.getLtoCG(), getL(rDPO)); !res) + return res; + if(auto res = handleSide(vLabels.stereo.right, "R", rightInference, vLabels.parsedEmbeddingRight, + rDPO.getRtoCG(), getR(rDPO)); !res) + return res; + } // for each vertex + + const auto finalize = [&warnings, &rDPO, &vIdFromCG](auto &inference, const std::string &side, + const auto &gSide, const auto &mSideToCG) { + return inference.finalize(warnings, + [&rDPO, &vIdFromCG, &side, &gSide, &mSideToCG](lib::DPO::CombinedRule::SideVertex vS) { + const auto v = get(mSideToCG, gSide, rDPO.getCombinedGraph(), vS); + const auto iter = vIdFromCG.find(v); + assert(iter != vIdFromCG.end()); + return std::to_string(iter->second) + " in " + side; + }); + }; + if(auto resLeft = finalize(leftInference, "L", getL(rDPO), rDPO.getLtoCG()); !resLeft) return resLeft; + if(auto resRight = finalize(rightInference, "R", getR(rDPO), rDPO.getRtoCG()); !resRight) return resRight; + + const auto vertexInContext = [&](lib::DPO::CombinedRule::CombinedVertex v) -> bool { + auto &[vLabelsFromId, eLabelsFromIds] = labelsFromIdPair; // TODO: (C++20) remove, structured binding capture + const auto idIter = vIdFromCG.find(v); + assert(idIter != end(vIdFromCG)); + const auto lIter = vLabelsFromId.find(idIter->second); + assert(lIter != end(vLabelsFromId)); + assert(lIter->second.inContext); + const auto &stereo = lIter->second.stereo; + // TODO: this should be looked at at some point + // if there is any stereo data, maybe we are in context + if(stereo.left.has_value() || stereo.context.has_value() || stereo.right.has_value()) + return stereo.context.has_value(); + else // otherwise, default to be in context + return true; + }; + + const auto edgeInContext = [&](lib::DPO::CombinedRule::CombinedEdge e) -> bool { + auto &[vLabelsFromId, eLabelsFromIds] = labelsFromIdPair; // TODO: (C++20) remove, structured binding capture + const auto vSrc = source(e, rDPO.getCombinedGraph()); + const auto vTar = target(e, rDPO.getCombinedGraph()); + const auto idSrcIter = vIdFromCG.find(vSrc); + const auto idTarIter = vIdFromCG.find(vTar); + assert(idSrcIter != end(vIdFromCG)); + assert(idTarIter != end(vIdFromCG)); + assert(idSrcIter->second < idTarIter->second); + const auto lIter = eLabelsFromIds.find(std::pair(idSrcIter->second, idTarIter->second)); + assert(lIter != end(eLabelsFromIds)); + assert(lIter->second.inContext); + const auto &stereo = lIter->second.stereo; + // TODO: this should be looked at at some point + // if there is any stereo data, maybe we are in context + if(stereo.left.has_value() || stereo.context.has_value() || stereo.right.has_value()) + return stereo.context.has_value(); + else // otherwise, default to be in context + return true; + }; + dpoResult.pStereo = std::make_unique(dpoResult.getRule(), + std::move(leftInference), std::move(rightInference), + vertexInContext, edgeInContext); + return std::move(data); // TODO: remove std::move when C++20/P1825R0 is available +} + +namespace { +//#define MOD_RULEDFS_DEBUG + +using CombinedVertex = DPO::CombinedRule::CombinedVertex; + +namespace dfsDetail { +using namespace IO::DFS; +using Vertex = IO::DFS::Vertex; +using Edge = IO::DFS::Edge; + +using LeftEdgeMapType = std::map, std::string>; + +struct ConvertLeft { + struct ConvertRes { + CombinedVertex next; + bool isRingClosure; + }; + + ConvertLeft(const IO::DFS::Read::RuleResult &res, DPO::CombinedRule &rDPO, PropString &pString) + : res(res), rDPO(rDPO), pString(pString) {} + + LeftEdgeMapType operator()(Chain &chain) &&{ + const auto sub = (*this)(chain.head, lib::Graph::GraphType::null_vertex()); + CombinedVertex vPrev = sub.next; + assert(!sub.isRingClosure); + assert(vPrev != lib::Graph::GraphType::null_vertex()); + for(EVPair &ev: chain.tail) { + vPrev = (*this)(ev, vPrev); + assert(vPrev != lib::Graph::GraphType::null_vertex()); + } + return std::move(edges); + } + + ConvertRes operator()(Vertex &vertex, CombinedVertex prev) { + const auto sub = boost::apply_visitor(*this, vertex.vertex); + assert(!(sub.isRingClosure && prev == lib::Graph::GraphType::null_vertex())); + const CombinedVertex branchRoot = sub.isRingClosure ? prev : sub.next; + for(Branch &branch: vertex.branches) { + CombinedVertex branchPrev = branchRoot; + for(EVPair &ev: branch.tail) { + branchPrev = (*this)(ev, branchPrev); + assert(branchPrev != lib::Graph::GraphType::null_vertex()); + } + } + return sub; + } + + CombinedVertex operator()(EVPair &ev, CombinedVertex prev) { + const auto res = (*this)(ev.second, prev); + makeEdge(prev, res.next, ev.first.label); + if(res.isRingClosure) return prev; + else return res.next; + } + + ConvertRes operator()(LabelVertex &vDFS) { + const auto vL = addVertexL(rDPO); + pString.addL(vL, vDFS.label); + const auto v = get(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), vL); + +#ifdef MOD_RULEDFS_DEBUG + std::cout << "RuleDFS: ConvertLeft, addVertexL("; + pString.print(std::cout, v); + std::cout << ")\n"; +#endif + + const auto vId = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), v); + vDFS.gVertexId = vId; + + std::optional rightLabel; + if(vDFS.id && !vDFS.ringClosure) { + const auto iterRight = res.right.vertexFromId.find(*vDFS.id); + if(iterRight != res.right.vertexFromId.end()) { + rightLabel = iterRight->second->label; + assert(iterRight->second->gVertexId == -1); + iterRight->second->gVertexId = vId; + } + } + if(rightLabel) { + const auto vR = promoteVertexL(rDPO, vL); + pString.promoteL(vL, vR, *rightLabel); + } + if(vDFS.ringClosure) { + const auto vRing = vertex(vDFS.ringClosure->gVertexId, rDPO.getCombinedGraph()); + makeEdge(v, vRing, "-"); + } + return {v, false}; + } + + ConvertRes operator()(RingClosure &rc) { + return {vertex(rc.other->gVertexId, rDPO.getCombinedGraph()), true}; + } +private: + void makeEdge(CombinedVertex vSrc, CombinedVertex vTar, const std::string &label) { + if(label.empty()) return; // a dot edge for no-edge + std::pair eSorted = std::minmax(vSrc, vTar); + std::tie(vSrc, vTar) = eSorted; + assert(edges.find({vSrc, vTar}) == edges.end()); + edges[{vSrc, vTar}] = label; + } +public: + const IO::DFS::Read::RuleResult &res; + DPO::CombinedRule &rDPO; + PropString &pString; + LeftEdgeMapType edges; +}; + +struct ConvertRight { + struct ConvertRes { + CombinedVertex next; + bool isRingClosure; + }; + + ConvertRight(const IO::DFS::Read::RuleResult &res, DPO::CombinedRule &rDPO, PropString &pString, + LeftEdgeMapType &leftEdges) + : res(res), rDPO(rDPO), pString(pString), leftEdges(leftEdges) {} + + void operator()(Chain &chain) &&{ + const auto sub = (*this)(chain.head, lib::Graph::GraphType::null_vertex()); + CombinedVertex vPrev = sub.next; + assert(!sub.isRingClosure); + assert(vPrev != lib::Graph::GraphType::null_vertex()); + for(EVPair &ev: chain.tail) { + vPrev = (*this)(ev, vPrev); + assert(vPrev != lib::Graph::GraphType::null_vertex()); + } + } + + ConvertRes operator()(Vertex &vertex, CombinedVertex prev) { + const auto sub = boost::apply_visitor(*this, vertex.vertex); + assert(!(sub.isRingClosure && prev == lib::Graph::GraphType::null_vertex())); + const CombinedVertex branchRoot = sub.isRingClosure ? prev : sub.next; + for(Branch &branch: vertex.branches) { + CombinedVertex branchPrev = branchRoot; + for(EVPair &ev: branch.tail) { + branchPrev = (*this)(ev, branchPrev); + assert(branchPrev != lib::Graph::GraphType::null_vertex()); + } + } + return sub; + } + + CombinedVertex operator()(EVPair &ev, CombinedVertex prev) { + const auto res = (*this)(ev.second, prev); + makeEdge(prev, res.next, ev.first.label); + if(res.isRingClosure) return prev; + else return res.next; + } + + ConvertRes operator()(LabelVertex &vDFS) { + // ConvertLeft will set our gVertexId if it is a context vertex + CombinedVertex v; + if(vDFS.gVertexId == -1) { + assert(!vDFS.id || vDFS.ringClosure || res.left.vertexFromId.find(*vDFS.id) == res.left.vertexFromId.end()); + const auto vR = addVertexR(rDPO); + v = get(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), vR); + const auto vId = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), v); + vDFS.gVertexId = vId; + pString.addR(vR, vDFS.label); +#ifdef MOD_RULEDFS_DEBUG + std::cout << "RuleDFS: ConvertRight, add_vertex("; + pString.print(std::cout, v); + std::cout << ")\n"; +#endif + } else { + assert(vDFS.id); + assert(!vDFS.ringClosure); + assert(res.left.vertexFromId.find(*vDFS.id) != res.left.vertexFromId.end()); + v = vertex(vDFS.gVertexId, rDPO.getCombinedGraph()); + } + if(vDFS.ringClosure) { + const auto vRing = vertex(vDFS.ringClosure->gVertexId, rDPO.getCombinedGraph()); + makeEdge(v, vRing, "-"); + } + return {v, false}; + } + + ConvertRes operator()(RingClosure &rc) { + return {vertex(rc.other->gVertexId, rDPO.getCombinedGraph()), true}; + } +private: + void makeEdge(CombinedVertex vSrc, CombinedVertex vTar, const std::string &label) { + if(label.empty()) return; // a dot edge for no-edge + std::pair eSorted = std::minmax(vSrc, vTar); + std::tie(vSrc, vTar) = eSorted; + const auto iterLeft = leftEdges.find({vSrc, vTar}); + if(iterLeft != leftEdges.end()) { + const auto eK = addEdgeK(rDPO, vSrc, vTar); + pString.addK(eK, iterLeft->second, label); + leftEdges.erase(iterLeft); +#ifdef MOD_RULEDFS_DEBUG + std::cout << "RuleDFS: ConvertRight, addEdgeK("; + pString.print(std::cout, eK); + std::cout << ")\n"; +#endif + } else { + const auto vRSrc = get_inverse(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), vSrc); + const auto vRTar = get_inverse(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), vTar); + const auto eR = addEdgeR(rDPO, vRSrc, vRTar); + pString.addR(eR, label); +#ifdef MOD_RULEDFS_DEBUG + std::cout << "RuleDFS: ConvertRight, addEdgeR("; + pString.print(std::cout, get(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), eR)); + std::cout << ")\n"; +#endif + } +#ifdef MOD_RULEDFS_DEBUG + pString.print(std::cout << "\tsrc=", vSrc); + std::cout << '\n'; + pString.print(std::cout << "\ttar=", vTar); + std::cout << '\n'; + std::cout << std::flush; +#endif + } +public: + const IO::DFS::Read::RuleResult &res; + DPO::CombinedRule &rDPO; + PropString &pString; + LeftEdgeMapType &leftEdges; +}; + +struct ImplicitLeft { + ImplicitLeft(const IO::DFS::Read::RuleResult &res) : res(res) {} + + Result<> operator()(const Chain &chain) &&{ + auto sub = (*this)(chain.head); + if(!sub) return sub; + for(const EVPair &ev: chain.tail) { + auto sub = (*this)(ev.second); + if(!sub) return sub; + } + return {}; + } + + Result<> operator()(const Vertex &vertex) { + auto sub = boost::apply_visitor(*this, vertex.vertex); + if(!sub) return sub; + for(const Branch &branch: vertex.branches) { + for(const EVPair &ev: branch.tail) { + auto sub = (*this)(ev.second); + if(!sub) return sub; + } + } + return {}; + } + + Result<> operator()(const LabelVertex &vDFS) { + if(!vDFS.implicit) return {}; + return Result<>::Error("Vertices with implicit hydrogen atoms currently not supported."); + } + + Result<> operator()(const RingClosure &rc) { + return {}; + } +public: + const IO::DFS::Read::RuleResult &res; +}; + +struct ImplicitRight { + ImplicitRight(const IO::DFS::Read::RuleResult &res) : res(res) {} + + Result<> operator()(const Chain &chain) &&{ + auto sub = (*this)(chain.head); + if(!sub) return sub; + for(const EVPair &ev: chain.tail) { + auto sub = (*this)(ev.second); + if(!sub) return sub; + } + return {}; + } + + Result<> operator()(const Vertex &vertex) { + auto sub = boost::apply_visitor(*this, vertex.vertex); + if(!sub) return sub; + for(const Branch &branch: vertex.branches) { + for(const EVPair &ev: branch.tail) { + auto sub = (*this)(ev.second); + if(!sub) return sub; + } + } + return {}; + } + + Result<> operator()(const LabelVertex &vDFS) { + if(!vDFS.implicit) return {}; + return Result<>::Error("Vertices with implicit hydrogen atoms currently not supported."); + } + + Result<> operator()(const RingClosure &rc) { + return {}; + } +public: + const IO::DFS::Read::RuleResult &res; +}; + +} // namespace dfsDetail +} // namespace + +Result dfs(lib::IO::Warnings &warnings, std::string_view input) { + auto astRes = lib::IO::DFS::Read::rule(input); + if(!astRes) return lib::IO::Result<>::Error(astRes.extractError()); + + Data data; + auto rDPO = std::make_unique(); + auto pString = std::make_unique(*rDPO); + + std::map, std::string> leftEdges; + if(astRes->left.ast) + leftEdges = dfsDetail::ConvertLeft(*astRes, *rDPO, *pString)(*astRes->left.ast); + if(astRes->right.ast) + dfsDetail::ConvertRight(*astRes, *rDPO, *pString, leftEdges)(*astRes->right.ast); + // add remaining leftEdges + for(const auto &[ep, label]: leftEdges) { + const auto vL1 = get_inverse(rDPO->getLtoCG(), getL(*rDPO), rDPO->getCombinedGraph(), ep.first); + const auto vL2 = get_inverse(rDPO->getLtoCG(), getL(*rDPO), rDPO->getCombinedGraph(), ep.second); + const auto eL = addEdgeL(*rDPO, vL1, vL2); + pString->addL(eL, label); +#ifdef MOD_RULEDFS_DEBUG + std::cout << "RuleDFS: remaining leftEdges, add_edge("; + pString->print(std::cout, get(rDPO->getLtoCG(), getL(*rDPO), rDPO->getCombinedGraph(), eL)); + std::cout << ")\n"; + pString->print(std::cout << "\tsrc=", ep.first); + std::cout << '\n'; + pString->print(std::cout << "\ttar=", ep.second); + std::cout << '\n'; + std::cout << std::flush; +#endif + } + + if(astRes->left.ast) + if(auto res = dfsDetail::ImplicitLeft(*astRes)(*astRes->left.ast); !res) + return res; + if(astRes->right.ast) + if(auto res = dfsDetail::ImplicitRight(*astRes)(*astRes->right.ast); !res) + return res; + + if(astRes->left.ast) { + for(const auto[id, vDFS]: astRes->left.vertexFromId) { + assert(vDFS->gVertexId != -1); + data.externalToInternalIds[id] = vDFS->gVertexId; + } + } + if(astRes->right.ast) { + for(const auto[id, vDFS]: astRes->right.vertexFromId) { + assert(vDFS->gVertexId != -1); + if(auto iter = data.externalToInternalIds.find(id); iter == data.externalToInternalIds.end()) { + data.externalToInternalIds[id] = vDFS->gVertexId; + } else { + assert(iter->second == vDFS->gVertexId); + } + } + } + + data.rule.emplace(std::move(rDPO), std::move(pString), nullptr); + return std::move(data); // TODO: remove std::move when C++20/P1825R0 is available +} + +} // namespace mod::lib::Rules::Read \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/IO/Read.hpp b/libs/libmod/src/mod/lib/Rules/IO/Read.hpp new file mode 100644 index 0000000..8ea42f5 --- /dev/null +++ b/libs/libmod/src/mod/lib/Rules/IO/Read.hpp @@ -0,0 +1,31 @@ +#ifndef MOD_LIB_RULE_IO_READ_HPP +#define MOD_LIB_RULE_IO_READ_HPP + +#include +#include +#include + +#include +#include +#include +#include + +namespace mod::lib::Rules { +struct Real; +} // namespace mod::lib::Rules +namespace mod::lib::Rules::Read { + +struct Data { + std::optional rule; + std::optional name; + std::optional labelType; + // maps external IDs to vertex IDs in the combined graph + std::map externalToInternalIds; +}; + +lib::IO::Result gml(lib::IO::Warnings &warnings, std::string_view input); +lib::IO::Result dfs(lib::IO::Warnings &warnings, std::string_view input); + +} // namespace mod::lib::Rules::Read + +#endif // MOD_LIB_RULE_IO_READ_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/IO/Write.cpp b/libs/libmod/src/mod/lib/Rules/IO/Write.cpp new file mode 100644 index 0000000..75b4844 --- /dev/null +++ b/libs/libmod/src/mod/lib/Rules/IO/Write.cpp @@ -0,0 +1,984 @@ +#include "Write.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace mod::lib::Rules::Write { +namespace { + +// returns the filename _without_ extension + +std::string getFilePrefix(const Real &r) { + return IO::makeUniqueFilePrefix() + "r_" + boost::lexical_cast(r.getId()); +} + +void gmlSide(const Real &r, std::ostream &s, Membership printMembership, bool withCoords) { + if(withCoords) { + const auto &depict = r.getDepictionData(); + if(!depict.getHasCoordinates()) MOD_ABORT; + } + const auto &lr = r.getDPORule(); + const auto &rDPO = lr.getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto &pString = get_string(lr); + + for(const auto vCG: asRange(vertices(gCombined))) { + const auto vMembership = gCombined[vCG].membership; + if(printMembership == Membership::K) { + if(vMembership != Membership::K) continue; + if(pString.isChanged(vCG)) continue; + } else { + if(vMembership == Membership::K) { + if(!pString.isChanged(vCG)) continue; + } else { + if(printMembership != vMembership) continue; + } + } + s << "\t\tnode [ id " << get(boost::vertex_index_t(), gCombined, vCG) << " label \""; + switch(printMembership) { + case Membership::L: + case Membership::K: + s << pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG)]; + break; + case Membership::R: + s << pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG)]; + break; + } + s << "\""; + if(withCoords) { + const auto &depict = r.getDepictionData(); + s << " vis2d [ x " << depict.getX(vCG, true) << " y " << depict.getY(vCG, true) << " ]"; + } + s << " ]\n"; + } + + // we want the GML to be sorted, so iterate based on the vertices + for(const auto vCGSrc: asRange(vertices(gCombined))) { + for(const auto eCG: asRange(out_edges(vCGSrc, gCombined))) { + const auto vCGTar = target(eCG, gCombined); + const auto vSrcId = get(boost::vertex_index_t(), gCombined, vCGSrc); + const auto vTarId = get(boost::vertex_index_t(), gCombined, vCGTar); + if(vSrcId > vTarId) continue; + const auto eMembership = gCombined[eCG].membership; + if(printMembership == Membership::K) { + if(eMembership != Membership::K) continue; + if(pString.isChanged(eCG)) continue; + } else { + if(eMembership == Membership::K) { + if(!pString.isChanged(eCG)) continue; + } else { + if(printMembership != eMembership) continue; + } + } + s << "\t\tedge [ source " << vSrcId << " target " << vTarId << " label \""; + switch(printMembership) { + case Membership::L: + case Membership::K: + s << pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, eCG)]; + break; + case Membership::R: + s << pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, eCG)]; + break; + } + s << "\" ]\n"; + } + } +} + +void printEdgeStyle(std::ostream &s, Membership eSide, int src, int tar) { + s << "\t" << src << " -- " << tar << " [ "; + switch(eSide) { + case Membership::L: + s << "style=dashed "; + break; + case Membership::R: + s << "style=dotted "; + break; + default: + break; + } +} + +} // namespace + +void gml(const Real &r, bool withCoords, std::ostream &s) { + s << "rule [\n"; + s << "\truleID \"" << r.getName() << "\"\n"; + if(r.getLabelType()) { + s << "\tlabelType \""; + switch(*r.getLabelType()) { + case LabelType::String: + s << "string"; + break; + case LabelType::Term: + s << "term"; + break; + } + s << "\"\n"; + } + { + std::stringstream str; + gmlSide(r, str, Membership::L, withCoords); + if(str.str().size() > 0) s << "\tleft [\n" << str.str() << "\t]\n"; + } + { + std::stringstream str; + gmlSide(r, str, Membership::K, withCoords); + if(str.str().size() > 0) s << "\tcontext [\n" << str.str() << "\t]\n"; + } + { + std::stringstream str; + gmlSide(r, str, Membership::R, withCoords); + if(str.str().size() > 0) s << "\tright [\n" << str.str() << "\t]\n"; + } + for(const auto &c: get_match_constraints(get_labelled_left(r.getDPORule()))) + lib::GraphMorphism::Write::gmlConstraint(s, getL(r.getDPORule().getRule()), "\t", *c); + s << "]"; +} + +std::string gml(const Real &r, bool withCoords) { + post::FileHandle s(getFilePrefix(r) + ".gml"); + gml(r, withCoords, s); + return s; +} + +std::string dotCombined(const Real &r) { + std::stringstream fileName; + fileName << "r_" << r.getId() << "_combined.dot"; + post::FileHandle s(IO::makeUniqueFilePrefix() + fileName.str()); + std::string fileNoExt = s; + fileNoExt.erase(fileNoExt.end() - 4, fileNoExt.end()); + + const auto &rDPO = r.getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto &pString = get_string(r.getDPORule()); + s << "graph G {\n"; + for(const auto vCG: asRange(vertices(gCombined))) { + const auto membership = gCombined[vCG].membership; + s << "\t" << get(boost::vertex_index_t(), gCombined, vCG) << " [ label=\""; + switch(membership) { + case Membership::L: + s << pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG)]; + break; + case Membership::K: + s << pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG)] << " | " + << pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG)]; + break; + case Membership::R: + s << pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG)]; + break; + } + s << "\""; + switch(membership) { + case Membership::L: + s << " style=dashed"; + break; + case Membership::R: + s << " style=dotted"; + break; + default: + break; + } + s << " ]\n"; + } + + // we want the output to be sorted, so iterate based on the vertices + for(const auto vCGSrc: asRange(vertices(gCombined))) { + for(const auto eCG: asRange(out_edges(vCGSrc, gCombined))) { + const auto vCGTar = target(eCG, gCombined); + const auto vSrcId = get(boost::vertex_index_t(), gCombined, vCGSrc); + const auto vTarId = get(boost::vertex_index_t(), gCombined, vCGTar); + if(vSrcId > vTarId) continue; + const auto membership = gCombined[eCG].membership; + std::string label; + switch(membership) { + case Membership::L: + label = pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, eCG)]; + break; + case Membership::K: + label = pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, eCG)] + " | " + + pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, eCG)]; + break; + case Membership::R: + label = pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, eCG)]; + break; + } + switch(label[0]) { + // case '=': // fall through to make two edges + // // assert(false); + // printEdgeStyle(s, membership, vSrcId, vTarId); + // s << "]\n"; + // case '-': // print the rest of the label + // printEdgeStyle(s, membership, vSrcId, vTarId); + // s << "label=\"" << (label.c_str() + 1) << "\" ]\n"; + // break; + default: + printEdgeStyle(s, membership, vSrcId, vTarId); + s << "label=\"" << label << "\" ]\n"; + break; + } + } + } + s << "}\n"; + return fileNoExt; +} + +std::string svgCombined(const Real &r) { + std::string fileNoExt = dotCombined(r); + IO::post() << "gv ruleCombined \"" << fileNoExt << "\" svg\n"; + return fileNoExt; +} + +std::string pdfCombined(const Real &r) { + std::string fileNoExt = svgCombined(r); + IO::post() << "svgToPdf \"" << fileNoExt << "\"\n"; + return fileNoExt; +} + +namespace { + +struct DotCacheEntry { + std::size_t id; + std::string prefix; +public: + friend bool operator<(const DotCacheEntry &a, const DotCacheEntry &b) { + return std::tie(a.id, a.prefix) < std::tie(b.id, b.prefix); + } +}; + +} // namespace + +std::string dot(const Real &r, const Options &options) { + static std::map cache; + const auto iter = cache.find({r.getId(), options.graphvizPrefix}); + if(iter != end(cache)) return iter->second; + + std::string fileNoExt = getFilePrefix(r); + post::FileHandle s(fileNoExt + ".dot"); + + const auto &rDPO = r.getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto &pString = get_string(r.getDPORule()); + + s << "graph G {\n"; + if(!options.graphvizPrefix.empty()) + s << "\t" << options.graphvizPrefix << '\n'; + + for(const auto vCG: asRange(vertices(gCombined))) { + const auto vId = get(boost::vertex_index_t(), gCombined, vCG); + s << vId << " [ label=\""; + const auto membership = gCombined[vCG].membership; + switch(membership) { + case Membership::L: + s << pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG)]; + break; + case Membership::K: + s << pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG)] << " | " + << pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG)]; + break; + case Membership::R: + s << pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG)]; + break; + } + s << "\" ];\n"; + } + + // we want the output to be sorted, so iterate based on the vertices + for(const auto vCGSrc: asRange(vertices(gCombined))) { + for(const auto eCG: asRange(out_edges(vCGSrc, gCombined))) { + const auto vCGTar = target(eCG, gCombined); + const auto vSrcId = get(boost::vertex_index_t(), gCombined, vCGSrc); + const auto vTarId = get(boost::vertex_index_t(), gCombined, vCGTar); + if(vSrcId > vTarId) continue; + s << vSrcId << " -- " << vTarId << " [ label=\""; + const auto membership = gCombined[eCG].membership; + switch(membership) { + case Membership::L: + s << pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, eCG)]; + break; + case Membership::K: + s << pString.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, eCG)] << " | " + << pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, eCG)]; + break; + case Membership::R: + s << pString.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, eCG)]; + break; + } + s << "\" ];\n"; + } + } + s << "}\n"; + + cache[{r.getId(), options.graphvizPrefix}] = fileNoExt; + return fileNoExt; +} + +namespace { + +struct OpenBabelCoordsCacheEntry { + std::size_t id; + bool collapseHydrogens; + int rotation; + bool mirror; + int idOffset; +public: + friend bool operator<(const OpenBabelCoordsCacheEntry &a, const OpenBabelCoordsCacheEntry &b) { + return std::tie(a.id, a.collapseHydrogens, a.rotation, a.mirror, a.idOffset) + < std::tie(b.id, b.collapseHydrogens, b.rotation, b.mirror, b.idOffset); + } +}; + +bool disallowHydrogenCollapseByChangeThenCallback(const Real &r, const CombinedVertex vCG, + std::function disallowCollapse) { + if(getConfig().rule.collapseChangedHydrogens.get()) + return disallowCollapse(vCG); + const auto &gCombined = r.getDPORule().getRule().getCombinedGraph(); + // if we are changed, then disallow + if(gCombined[vCG].membership != Membership::K) + return true; + // and if any incident edges are changed, then disallow + for(const auto eCG: asRange(out_edges(vCG, gCombined))) { + if(gCombined[eCG].membership != Membership::K) + return true; + } + return disallowCollapse(vCG); +} + +} // namespace + +std::string coords(const Real &r, int idOffset, const Options &options, + std::function disallowCollapse_) { + assert(idOffset >= 0); + const auto &depict = r.getDepictionData(); + if(options.withGraphvizCoords || !depict.getHasCoordinates()) { + if(idOffset != 0) + throw FatalError("Blame the lazy programmer. Offset other than 0 not yet supported in dot coords."); + + // we map 1-to-1 a dot file to a coord file, so cache by the dot filename + static std::map cache; + const auto fileNoExt = dot(r, options); + const auto iter = cache.find(fileNoExt); + if(iter != end(cache)) return iter->second; + + IO::post() << "coordsFromGV rule \"" << fileNoExt << "\" noOverlay\n"; + // the coord file is still for the tex coord file which is just then created in post + std::string file = fileNoExt + "_coord"; + cache[fileNoExt] = file; + return file; + } else { + static std::map cache; + const auto iter = cache.find({r.getId(), options.collapseHydrogens, options.rotation, options.mirror, idOffset}); + if(iter != end(cache)) return iter->second; + + const auto &gCombined = r.getDPORule().getRule().getCombinedGraph(); + const std::string fileNoExt = [&]() { + auto f = getFilePrefix(r); + if(options.collapseHydrogens) f += "_mol"; + if(options.rotation != 0) f += "_r" + std::to_string(options.rotation); + if(options.mirror) f += "_m" + std::to_string(options.mirror); + if(idOffset != 0) f += "_id" + std::to_string(idOffset); + f += "_coord"; + return f; + }(); + post::FileHandle s(fileNoExt + ".tex"); + s << "% dummy\n"; + const bool useCollapsedCoords = [&]() { + // if the options didn't request collapsing, don't use it + if(!options.collapseHydrogens) return false; + // if there is _any_ user-defined collapsing, then make all coordinates available + for(const auto vCG: asRange(vertices(gCombined))) + if(disallowCollapse_(vCG)) + return false; + return true; + }(); + + for(const auto vCG: asRange(vertices(gCombined))) { + const auto vId = get(boost::vertex_index_t(), gCombined, vCG); + // if we are in the collapsed case and the depictor collapsed it, don't print + if(useCollapsedCoords && depict.mayCollapse(vCG)) continue; + const auto[x, y] = pointTransform( + depict.getX(vCG, !useCollapsedCoords), + depict.getY(vCG, !useCollapsedCoords), + options.rotation, options.mirror); + s << "\\coordinate[overlay] (\\modIdPrefix v-coord-" << (vId + idOffset) << ") at (" + << std::fixed << x << ", " << y << ") {};\n"; + } + if(options.collapseHydrogens && !useCollapsedCoords) { + // don't cache these as the user predicate influences it + } else { + cache[{r.getId(), options.collapseHydrogens, options.rotation, options.mirror, idOffset}] = fileNoExt; + } + return fileNoExt; + } +} + +namespace { + +using SideGraph = lib::DPO::CombinedRule::SideGraphType; +using SideVertex = lib::DPO::CombinedRule::SideVertex; +using SideEdge = lib::DPO::CombinedRule::SideEdge; +using MorphismType = lib::DPO::CombinedRule::MorphismType; +using ToCombinedMorphismSide = lib::DPO::CombinedRule::ToCombinedMorphismSide; +using KVertex = lib::DPO::CombinedRule::KVertex; +using KEdge = lib::DPO::CombinedRule::KEdge; +using DepictSide = DepictionData::Side; +using LabelledSide = LabelledRule::Side; + +struct AdvOptionsSide { + AdvOptionsSide(const Real &r, int idOffset, const BaseArgs &args, + std::function disallowHydrogenCollapse, + std::string changeColour, + const SideGraph &g, const MorphismType &mToSide, const ToCombinedMorphismSide &mToCG, + DepictSide depict, LabelledSide lg) + : idOffset(idOffset), changeColour(std::move(changeColour)), r(r), rDPO(r.getDPORule().getRule()), + args(args), disallowHydrogenCollapse_(disallowHydrogenCollapse), + g(g), mToCG(mToCG), depict(depict), lg(lg) {} +public: + std::string getColour(SideVertex vS) const { + const auto vCG = get(mToCG, g, rDPO.getCombinedGraph(), vS); + const bool isChanged = r.getDPORule().getRule().getCombinedGraph()[vCG].membership != Membership::K + || get_string(r.getDPORule()).isChanged(vCG); + if(isChanged) return changeColour; + else return args.vColour(vCG); + } + + std::string getColour(SideEdge eS) const { + const auto eCG = get(mToCG, g, rDPO.getCombinedGraph(), eS); + const bool isChanged = r.getDPORule().getRule().getCombinedGraph()[eCG].membership != Membership::K + || get_string(r.getDPORule()).isChanged(eCG); + if(isChanged) return changeColour; + else return args.eColour(eCG); + } + + bool isVisible(SideVertex vS) const { + const auto v = get(mToCG, g, rDPO.getCombinedGraph(), vS); + return args.visible(v); + } + + std::string getShownId(SideVertex vS) const { + const auto v = get(mToCG, g, rDPO.getCombinedGraph(), vS); + return std::to_string(get(boost::vertex_index_t(), rDPO.getCombinedGraph(), v)); + } + + bool overwriteWithIndex(SideVertex) const { + return false; + } + + lib::IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(SideEdge eS, bool withHydrogen) const { + return depict.getEdgeFake3DType(eS, withHydrogen); + } + + std::string getOpts(SideVertex vS) const { + return std::string(); + } +private: + template + std::string getStereoStringVertex(SideVertex vS, const F f) const { +// assert(false); // TODO: map vS + const auto &conf = *get_stereo(lg)[vS]; + const auto getNeighbourId = [&](const lib::Stereo::EmbeddingEdge &emb) { + return get(boost::vertex_index_t(), g, target(emb.getEdge(vS, g), g)); + }; + std::string res = f(conf, getNeighbourId); + const auto v = get(mToCG, g, rDPO.getCombinedGraph(), vS); + if(!get_stereo(r.getDPORule()).inContext(v)) { + res = "{\\color{" + changeColour + "}" + res + "}"; + } + return res; + } +public: + std::string getRawStereoString(SideVertex vS) const { +// assert(false); // TODO: anything to map? + return getStereoStringVertex(vS, [&](const auto &conf, auto getNId) { + return conf.asRawString(getNId); + }); + } + + std::string getPrettyStereoString(SideVertex vS) const { +// assert(false); // TODO: anything to map? + return getStereoStringVertex(vS, [&](const auto &conf, auto getNId) { + return conf.asPrettyString(getNId); + }); + } + + std::string getStereoString(SideEdge eS) const { +// assert(false); // TODO: map eS + const auto cat = get_stereo(lg)[eS]; + std::string res = boost::lexical_cast(cat); + const auto e = get(mToCG, g, rDPO.getCombinedGraph(), eS); + if(!get_stereo(r.getDPORule()).inContext(e)) { + res = "{\\color{" + changeColour + "}" + res + "}"; + } + return res; + } + + std::string getEdgeAnnotation(SideEdge) const { + return {}; + } + + bool disallowHydrogenCollapse(SideVertex vS) const { + const auto v = get(mToCG, g, rDPO.getCombinedGraph(), vS); + return disallowHydrogenCollapseByChangeThenCallback(r, v, disallowHydrogenCollapse_); + } +public: + auto getOutputId(SideVertex vS) const { + const auto v = get(mToCG, g, rDPO.getCombinedGraph(), vS); + return idOffset + get(boost::vertex_index_t(), rDPO.getCombinedGraph(), v); + } +private: + const int idOffset; + const std::string changeColour; + const Real &r; + const lib::DPO::CombinedRule &rDPO; + const BaseArgs &args; + std::function disallowHydrogenCollapse_; + const SideGraph &g; + const ToCombinedMorphismSide &mToCG; + const DepictSide depict; + const LabelledSide lg; +}; + +struct AdvOptionsK { + AdvOptionsK(const Real &r, int idOffset, const BaseArgs &args, + std::function disallowHydrogenCollapse, + std::string changeColour) + : idOffset(idOffset), changeColour(std::move(changeColour)), r(r), rDPO(r.getDPORule().getRule()), + args(args), disallowHydrogenCollapse_(disallowHydrogenCollapse) {} +public: + std::string getColour(KVertex vK) const { + const auto v = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), vK); + const bool isChanged = get_string(r.getDPORule()).isChanged(v); + if(isChanged) return changeColour; + else return args.vColour(v); + } + + std::string getColour(KEdge eK) const { + const auto eCG = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), eK); + assert(r.getDPORule().getRule().getCombinedGraph()[eCG].membership == Membership::K); + const bool isChanged = get_string(r.getDPORule()).isChanged(eCG); + if(isChanged) return changeColour; + else return args.eColour(eCG); + } + + bool isVisible(KVertex vK) const { + const auto v = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), vK); + return args.visible(v); + } + + std::string getShownId(KVertex vK) const { + const auto v = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), vK); + return std::to_string(get(boost::vertex_index_t(), rDPO.getCombinedGraph(), v)); + } + + bool overwriteWithIndex(KVertex) const { + return false; + } + + lib::IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(KEdge eK, bool withHydrogen) const { + const auto e = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), eK); + return r.getDepictionData().getContext().getEdgeFake3DType(e, withHydrogen); + } + + std::string getOpts(KVertex) const { + return {}; + } +private: + template + std::string getStereoStringVertex(const KVertex vK, const F f) const { + const auto v = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), vK); + const auto &lr = r.getDPORule(); + if(get_stereo(lr).inContext(v)) { + const auto &lgLeft = get_labelled_left(r.getDPORule()); + const auto vL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), vK); + const auto &conf = *get_stereo(lgLeft)[vL]; + const auto getNeighbourId = [&](const lib::Stereo::EmbeddingEdge &emb) { + const auto &gLeft = get_graph(lgLeft); + return get(boost::vertex_index_t(), gLeft, target(emb.getEdge(vL, gLeft), gLeft)); + }; + return f(conf, getNeighbourId); + } else return {}; + } +public: + std::string getRawStereoString(KVertex vK) const { + return getStereoStringVertex(vK, [&](const auto &conf, auto getNId) { + return conf.asRawString(getNId); + }); + } + + std::string getPrettyStereoString(KVertex vK) const { + return getStereoStringVertex(vK, [&](const auto &conf, auto getNId) { + return conf.asPrettyString(getNId); + }); + } + + std::string getStereoString(KEdge eK) const { + const auto e = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), eK); + const auto &lr = r.getDPORule(); + if(get_stereo(lr).inContext(e)) { + const auto &lgLeft = get_labelled_left(r.getDPORule()); + const auto eL = get(getMorL(rDPO), getK(rDPO), getL(rDPO), eK); + const auto cat = get_stereo(lgLeft)[eL]; + return boost::lexical_cast(cat); + } else return {}; + } + + std::string getEdgeAnnotation(KEdge) const { + return {}; + } + + bool disallowHydrogenCollapse(KVertex vK) const { + const auto v = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), vK); + return disallowHydrogenCollapseByChangeThenCallback(r, v, disallowHydrogenCollapse_); + } +public: + int getOutputId(KVertex vK) const { + const auto v = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), vK); + return idOffset + get(boost::vertex_index_t(), rDPO.getCombinedGraph(), v); + } +private: + const int idOffset; + const std::string changeColour; + const Real &r; + const lib::DPO::CombinedRule &rDPO; + const BaseArgs &args; + std::function disallowHydrogenCollapse_; +}; + +} // namespace + +std::pair +tikz(const std::string &fileCoordsNoExt, const Real &r, unsigned int idOffset, const Options &options, + const std::string &suffixL, const std::string &suffixK, const std::string &suffixR, const BaseArgs &args, + std::function disallowCollapse) { + std::string strOptions = options.getStringEncoding(); + std::string fileNoExt = IO::makeUniqueFilePrefix() + "r_" + boost::lexical_cast(r.getId()); + fileNoExt += "_" + strOptions; + + const auto &rDPO = r.getDPORule().getRule(); + + std::string fileCoords = fileCoordsNoExt + ".tex"; + { // left + post::FileHandle s(fileNoExt + "_" + suffixL + ".tex"); + const auto &g = getL(rDPO); + const auto &depict = r.getDepictionData().getLeft(); + const auto adv = AdvOptionsSide(r, idOffset, args, disallowCollapse, + getConfig().rule.changeColourL.get(), + g, getMorL(rDPO), rDPO.getLtoCG(), + r.getDepictionData().getLeft(), + get_labelled_left(r.getDPORule())); + IO::Graph::Write::tikz(s, options, g, depict, fileCoords, adv, jla_boost::Nop<>(), ""); + } + { // context + post::FileHandle s(fileNoExt + "_" + suffixK + ".tex"); + const auto &g = getK(rDPO); + const auto &depict = r.getDepictionData().getContext(); + + struct EdgeVisible { + EdgeVisible() = default; + + EdgeVisible(const Real &r) : r(&r) {} + + bool operator()(const CombinedEdge e) const { + if(getConfig().rule.printChangedEdgesInContext.get()) return true; + return !get_string(r->getDPORule()).isChanged(e); + } + private: + const Real *r = nullptr; + }; + boost::filtered_graph gFiltered(g, EdgeVisible(r)); + const auto adv = AdvOptionsK(r, idOffset, args, disallowCollapse, + getConfig().rule.changeColourK.get()); + IO::Graph::Write::tikz(s, options, gFiltered, depict, fileCoords, adv, jla_boost::Nop<>(), ""); + } + { // right + post::FileHandle s(fileNoExt + "_" + suffixR + ".tex"); + const auto &g = getR(rDPO); + const auto &depict = r.getDepictionData().getRight(); + const auto adv = AdvOptionsSide(r, idOffset, args, disallowCollapse, + getConfig().rule.changeColourR.get(), + g, getMorR(rDPO), rDPO.getRtoCG(), + r.getDepictionData().getRight(), + get_labelled_right(r.getDPORule())); + IO::Graph::Write::tikz(s, options, g, depict, fileCoords, adv, jla_boost::Nop<>(), ""); + } + return std::make_pair(fileNoExt, fileCoordsNoExt); +} + +std::pair tikz(const Real &r, unsigned int idOffset, const Options &options, + const std::string &suffixL, const std::string &suffixK, + const std::string &suffixR, const BaseArgs &args, + std::function disallowCollapse) { + std::string fileCoordsNoExt = coords(r, idOffset, options, disallowCollapse); + return tikz(fileCoordsNoExt, r, idOffset, options, suffixL, suffixK, suffixR, args, disallowCollapse); +} + +std::string pdf(const Real &r, const Options &options, + const std::string &suffixL, const std::string &suffixK, const std::string &suffixR, + const BaseArgs &args) { + std::string fileNoExt, fileCoordsNoExt; + const unsigned int idOffset = 0; + std::tie(fileNoExt, fileCoordsNoExt) = tikz(r, idOffset, options, suffixL, suffixK, suffixR, args, + jla_boost::AlwaysFalse()); + IO::post() << "compileTikz \"" << fileNoExt << "_" << suffixL << "\" \"" << fileCoordsNoExt << "\"\n"; + IO::post() << "compileTikz \"" << fileNoExt << "_" << suffixK << "\" \"" << fileCoordsNoExt << "\"\n"; + IO::post() << "compileTikz \"" << fileNoExt << "_" << suffixR << "\" \"" << fileCoordsNoExt << "\"\n"; + IO::post().flush(); + return fileNoExt; +} + +std::pair +tikzTransitionState(const std::string &fileCoordsNoExt, const Real &r, unsigned int idOffset, + const Options &options, + const std::string &suffix, const BaseArgs &args) { + MOD_ABORT; +} + +std::pair +tikzTransitionState(const Real &r, unsigned int idOffset, const Options &options, + const std::string &suffix, const BaseArgs &args) { + MOD_ABORT; +} + +std::string pdfTransitionState(const Real &r, const Options &options, + const std::string &suffix, const BaseArgs &args) { + std::string fileNoExt, fileCoordsNoExt; + const unsigned int idOffset = 0; + std::tie(fileNoExt, fileCoordsNoExt) = tikzTransitionState(r, idOffset, options, suffix, args); + IO::post() << "compileTikz \"" << fileNoExt << "_" << suffix << "\" \"" << fileCoordsNoExt << "\"\n"; + return fileNoExt; +} + +std::string pdfCombined(const Real &r, const Options &options) { + MOD_ABORT; +} + +std::pair summary(const Real &r, bool printCombined) { + graph::Printer first; + graph::Printer second; + second.setReactionDefault(); + return summary(r, first.getOptions(), second.getOptions(), printCombined); +} + +std::pair +summary(const Real &r, const Options &first, const Options &second, bool printCombined) { + auto visible = jla_boost::AlwaysTrue(); + auto vColour = jla_boost::Nop(); + auto eColour = jla_boost::Nop(); + const BaseArgs args{visible, vColour, eColour}; + std::string graphLike = pdf(r, first, "L", "K", "R", args); + std::string molLike = first == second ? "" : pdf(r, second, "L", "K", "R", args); + std::string combined = printCombined + ? pdfCombined(r /*, Options().EdgesAsBonds().RaiseCharges()*/) + : ""; + std::string constraints = + IO::makeUniqueFilePrefix() + "r_" + boost::lexical_cast(r.getId()) + "_constraints.tex"; + { + post::FileHandle s(constraints); + for(const auto &c: get_match_constraints(get_labelled_left(r.getDPORule()))) { + lib::GraphMorphism::Write::texConstraint(s, getL(r.getDPORule().getRule()), *c); + s << "\n"; + } + } + IO::post() << "summaryRule \"" << r.getName() << "\" \"" << graphLike << "\" \"" << molLike << "\" \"" << combined + << "\" \"" << constraints << "\"\n"; + if(molLike.empty()) + return std::pair(graphLike, graphLike); + else + return std::pair(graphLike, molLike); +} + +void termState(const Real &r) { + using namespace lib::Term; + using CombinedVertex = lib::DPO::CombinedRule::CombinedVertex; + using CombinedEdge = lib::DPO::CombinedRule::CombinedEdge; + IO::post() << "summarySubsection \"Term State for " << r.getName() << "\"\n"; + post::FileHandle s(IO::makeUniqueFilePrefix() + "termState.tex"); + + const auto &rDPO = r.getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto &pTerm = get_term(r.getDPORule()); + + s << "\\begin{verbatim}\n"; + if(isValid(pTerm)) { + std::unordered_map>> addrToVertex; + std::unordered_map>> addrToEdge; + std::unordered_map> addrToConstraintInfo; + const auto insertVertex = [&addrToVertex](std::size_t addr, CombinedVertex v, Membership membership) { + Address a{AddressType::Heap, addr}; + addrToVertex[a].insert({v, membership}); + }; + const auto insertEdge = [&addrToEdge](std::size_t addr, CombinedEdge e, Membership membership) { + Address a{AddressType::Heap, addr}; + addrToEdge[a].insert({e, membership}); + }; + for(const auto vCG: asRange(vertices(gCombined))) { + switch(gCombined[vCG].membership) { + case Membership::L: + insertVertex(pTerm.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG)], vCG, Membership::L); + break; + case Membership::K: + insertVertex(pTerm.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG)], vCG, Membership::L); + insertVertex(pTerm.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG)], vCG, + Membership::R); + break; + case Membership::R: + insertVertex(pTerm.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG)], vCG, + Membership::R); + break; + } + } + for(const auto eCG: asRange(edges(r.getDPORule().getRule().getCombinedGraph()))) { + switch(gCombined[eCG].membership) { + case Membership::L: + insertEdge(pTerm.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, eCG)], eCG, Membership::L); + break; + case Membership::K: + insertEdge(pTerm.getLeft()[get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, eCG)], eCG, Membership::L); + insertEdge(pTerm.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, eCG)], eCG, Membership::R); + break; + case Membership::R: + insertEdge(pTerm.getRight()[get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, eCG)], eCG, Membership::R); + break; + } + } + + using SideGraphType = lib::DPO::CombinedRule::SideGraphType; + + struct Visitor : lib::GraphMorphism::Constraints::AllVisitor { + Visitor(std::unordered_map> &addrMap, + const lib::DPO::CombinedRule::CombinedGraphType &gCombined) + : addrMap(addrMap), gCombined(gCombined) {} + + virtual void operator()(const lib::GraphMorphism::Constraints::VertexAdjacency &c) override { + const auto vStr = boost::lexical_cast(get(boost::vertex_index_t(), gCombined, c.vConstrained)); + for(const auto a: c.vertexTerms) { + Address addr{AddressType::Heap, a}; + std::string msg = "VertexAdj(" + vStr + ", " + side + ", V)"; + addrMap[addr].insert(std::move(msg)); + } + for(const auto a: c.edgeTerms) { + Address addr{AddressType::Heap, a}; + std::string msg = "VertexAdj(" + vStr + ", " + side + ", E)"; + addrMap[addr].insert(std::move(msg)); + } + } + + virtual void operator()(const lib::GraphMorphism::Constraints::ShortestPath &c) override {} + public: + std::unordered_map> &addrMap; + const lib::DPO::CombinedRule::CombinedGraphType &gCombined; + std::string side; + }; + Visitor vis(addrToConstraintInfo, gCombined); + vis.side = "L"; + for(const auto &c: get_match_constraints(get_labelled_left(r.getDPORule()))) + c->accept(vis); + vis.side = "R"; + for(const auto &c: get_match_constraints(get_labelled_right(r.getDPORule()))) + c->accept(vis); + + Term::Write::wam(getMachine(pTerm), lib::Term::getStrings(), s, [&](Address addr, std::ostream &s) { + s << " "; + bool first = true; + for(auto vm: addrToVertex[addr]) { + if(!first) s << ", "; + first = false; + s << "v(" << get(boost::vertex_index_t(), gCombined, vm.first) << ", "; + switch(vm.second) { + case Membership::L: + s << "L"; + break; + case Membership::R: + s << "R"; + break; + case Membership::K: + s << "K"; + break; + } + s << ")"; + } + for(auto em: addrToEdge[addr]) { + if(!first) s << ", "; + first = false; + s << "e(" + << get(boost::vertex_index_t(), gCombined, source(em.first, gCombined)) + << ", " + << get(boost::vertex_index_t(), gCombined, target(em.first, gCombined)) + << ", "; + switch(em.second) { + case Membership::L: + s << "L"; + break; + case Membership::R: + s << "R"; + break; + case Membership::K: + s << "K"; + break; + } + s << ")"; + } + for(auto &msg: addrToConstraintInfo[addr]) { + if(!first) s << ", "; + first = false; + s << msg; + } + }); + } else { + std::string msg = "Parsing failed for rule '" + r.getName() + "'. " + pTerm.getParsingError(); + throw TermParsingError(std::move(msg)); + } + s << "\\end{verbatim}\n"; + IO::post() << "summaryInput \"" << std::string(s) << "\"\n"; +} + +std::string stereoSummary(const Real &r, lib::DPO::CombinedRule::CombinedVertex vcg, Membership m, + const IO::Graph::Write::Options &options) { + assert(m != Membership::K); + const auto &lr = r.getDPORule(); + const auto &rDPO = lr.getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + if(m == Membership::L) assert(gCombined[vcg].membership != Membership::R); + if(m == Membership::R) assert(gCombined[vcg].membership != Membership::L); + const std::string side = m == Membership::L ? "L" : "R"; + std::string name = "r_" + std::to_string(r.getId()) + "_" + side + "_stereo_" + + std::to_string(get(boost::vertex_index_t(), gCombined, vcg)); + IO::post() << "summarySubsection \"Stereo, r " << r.getId() << ", v " << get(boost::vertex_index_t(), gCombined, vcg) + << " " << side << "\"\n"; + const auto handler = [&](const auto &lgSide, const auto &mSideToCG, const auto &depict) { + const auto &gSide = get_graph(lgSide); + const auto vSide = get_inverse(mSideToCG, gSide, gCombined, vcg); + return lib::Stereo::Write::pdf(gSide, vSide, *get_stereo(lgSide)[vSide], name, depict, options, + [](const auto &gSide, const auto vSide) { + return get(boost::vertex_index_t(), gSide, vSide); + }); + }; + std::string f = m == Membership::L + ? handler(get_labelled_left(lr), rDPO.getLtoCG(), r.getDepictionData().getLeft()) + : handler(get_labelled_right(lr), rDPO.getRtoCG(), r.getDepictionData().getRight()); + post::FileHandle s(IO::makeUniqueFilePrefix() + "stereo.tex"); + s << "\\begin{center}\n"; + s << "\\includegraphics{" << f << "}\\\\\n"; + s << "File: \\texttt{" << IO::escapeForLatex(f) << "}\n"; + s << "\\end{center}\n"; + IO::post() << "summaryInput \"" << std::string(s) << "\"\n"; + return f; +} + +} // namespace mod::lib::Rules::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/IO/Write.hpp b/libs/libmod/src/mod/lib/Rules/IO/Write.hpp new file mode 100644 index 0000000..b077ba3 --- /dev/null +++ b/libs/libmod/src/mod/lib/Rules/IO/Write.hpp @@ -0,0 +1,65 @@ +#ifndef MOD_LIB_RULE_IO_WRITE_HPP +#define MOD_LIB_RULE_IO_WRITE_HPP + +#include +#include + +namespace mod::lib::Rules { +struct Real; +} // namespace mod::lib::Rules +namespace mod::lib::Rules::Write { + +using Options = IO::Graph::Write::Options; +using CombinedVertex = lib::DPO::CombinedRule::CombinedVertex; +using CombinedEdge = lib::DPO::CombinedRule::CombinedEdge; + +struct BaseArgs { + std::function visible; + std::function vColour; + std::function eColour; +}; + +// returns the filename _with_ extension +void gml(const Real &r, bool withCoords, std::ostream &s); +std::string gml(const Real &r, bool withCoords); +// returns the filename without extension +std::string dotCombined(const Real &r); +std::string svgCombined(const Real &r); +std::string pdfCombined(const Real &r); +// returns the filename _without_ extension +std::string +dot(const Real &r, const Options &options); // does not handle labels correctly, is for coordinate generation +std::string coords(const Real &r, int idOffset, const Options &options, + std::function disallowCollapse_); +std::pair +tikz(const std::string &fileCoordsNoExt, const Real &r, unsigned int idOffset, const Options &options, + const std::string &suffixL, const std::string &suffixK, const std::string &suffixR, const BaseArgs &args, + std::function disallowCollapse); +std::pair tikz(const Real &r, unsigned int idOffset, const Options &options, + const std::string &suffixL, const std::string &suffixK, + const std::string &suffixR, const BaseArgs &args, + std::function disallowCollapse); +std::string pdf(const Real &r, const Options &options, + const std::string &suffixL, const std::string &suffixK, const std::string &suffixR, + const BaseArgs &args); +std::pair +tikzTransitionState(const std::string &fileCoordsNoExt, const Real &r, unsigned int idOffset, + const Options &options, + const std::string &suffix, const BaseArgs &args); +std::pair +tikzTransitionState(const Real &r, unsigned int idOffset, const Options &options, + const std::string &suffix, const BaseArgs &args); +std::string pdfTransitionState(const Real &r, const Options &options, + const std::string &suffix, const BaseArgs &args); +//std::string pdfCombined(const Real &r, const Options &options); // TODO +std::pair summary(const Real &r, bool printCombined); +std::pair +summary(const Real &r, const Options &first, const Options &second, bool printCombined); +void termState(const Real &r); + +std::string stereoSummary(const Real &r, lib::DPO::CombinedRule::CombinedVertex v, Membership m, + const IO::Graph::Write::Options &options); + +} // namespace mod::lib::Rules::Write + +#endif // MOD_LIB_RULE_IO_WRITE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/LabelledRule.cpp b/libs/libmod/src/mod/lib/Rules/LabelledRule.cpp index 7e1e94d..e0a00cd 100644 --- a/libs/libmod/src/mod/lib/Rules/LabelledRule.cpp +++ b/libs/libmod/src/mod/lib/Rules/LabelledRule.cpp @@ -11,83 +11,133 @@ namespace mod::lib::Rules { +LabelledRule::LabelledRule(std::unique_ptr rule, + std::unique_ptr pString, + std::unique_ptr pStereo) + : rule(std::move(rule)), pString(std::move(pString)), pStereo(std::move(pStereo)) { + initComponents(); +} + +LabelledRule::LabelledRule(std::unique_ptr rule, + std::unique_ptr pTerm, + std::unique_ptr pStereo) + : rule(std::move(rule)), pTerm(std::move(pTerm)), pStereo(std::move(pStereo)) { + initComponents(); +} + // LabelledRule //------------------------------------------------------------------------------ -LabelledRule::LabelledRule() : g(new GraphType()) {} +LabelledRule::LabelledRule() : rule(new lib::DPO::CombinedRule()) {} LabelledRule::LabelledRule(const LabelledRule &other, bool withConstraints) : LabelledRule() { - auto &g = *this->g; - const auto &gOther = get_graph(other); - this->pString = std::make_unique(g); + const auto &cg = rule->getCombinedGraph(); + this->pString = std::make_unique(getRule()); auto &pString = *this->pString; - auto &pStringOther = get_string(other); - for(const auto vOther : asRange(vertices(gOther))) { - const auto v = add_vertex(g); - g[v].membership = membership(other, vOther); - switch(g[v].membership) { - case Membership::Left: - pString.add(v, pStringOther.getLeft()[vOther], ""); + const auto &ruleOther = other.getRule(); + const auto &cgOther = ruleOther.getCombinedGraph(); + const auto &pStringOther = get_string(other); + std::vector vNewFromOther(num_vertices(cgOther)); + for(const auto vcgOther: asRange(vertices(cgOther))) { + switch(cgOther[vcgOther].membership) { + case Membership::L: { + const auto vL = addVertexL(*rule); + pString.addL(vL, pStringOther.getLeft()[get_inverse(ruleOther.getLtoCG(), getL(ruleOther), + cgOther, vcgOther)]); + vNewFromOther[get(boost::vertex_index_t(), cgOther, vcgOther)] + = get(rule->getLtoCG(), getL(*rule), cg, vL); break; - case Membership::Right: - pString.add(v, "", pStringOther.getRight()[vOther]); + } + case Membership::R: { + const auto vR = addVertexR(*rule); + pString.addR(vR, pStringOther.getRight()[get_inverse(ruleOther.getRtoCG(), getR(ruleOther), + cgOther, vcgOther)]); + vNewFromOther[get(boost::vertex_index_t(), cgOther, vcgOther)] + = get(rule->getRtoCG(), getL(*rule), cg, vR); break; - case Membership::Context: - pString.add(v, pStringOther.getLeft()[vOther], pStringOther.getRight()[vOther]); + } + case Membership::K: { + const auto vK = addVertexK(*rule); + pString.addK(vK, + pStringOther.getLeft()[get_inverse(ruleOther.getLtoCG(), getL(ruleOther), + cgOther, vcgOther)], + pStringOther.getRight()[get_inverse(ruleOther.getRtoCG(), getR(ruleOther), + cgOther, vcgOther)]); + vNewFromOther[get(boost::vertex_index_t(), cgOther, vcgOther)] = vK; break; } + } } - for(const auto eOther : asRange(edges(gOther))) { - const auto e = add_edge(source(eOther, gOther), target(eOther, gOther), g).first; - g[e].membership = membership(other, eOther); - switch(g[e].membership) { - case Membership::Left: - pString.add(e, pStringOther.getLeft()[eOther], ""); + for(const auto ecgOther: asRange(edges(cgOther))) { + const auto vcgNewSrc = vNewFromOther[get(boost::vertex_index_t(), cgOther, source(ecgOther, cgOther))]; + const auto vcgNewTar = vNewFromOther[get(boost::vertex_index_t(), cgOther, target(ecgOther, cgOther))]; + switch(cgOther[ecgOther].membership) { + case Membership::L: { + const auto vLSrc = get_inverse(rule->getLtoCG(), getL(*rule), cg, vcgNewSrc); + const auto vLTar = get_inverse(rule->getLtoCG(), getL(*rule), cg, vcgNewTar); + const auto eL = addEdgeL(*rule, vLSrc, vLTar); + pString.addL(eL, pStringOther.getLeft()[get_inverse(ruleOther.getLtoCG(), getL(ruleOther), + cgOther, ecgOther)]); break; - case Membership::Right: - pString.add(e, "", pStringOther.getRight()[eOther]); + } + case Membership::R: { + const auto vRSrc = get_inverse(rule->getRtoCG(), getR(*rule), cg, vcgNewSrc); + const auto vRTar = get_inverse(rule->getRtoCG(), getR(*rule), cg, vcgNewTar); + const auto eR = addEdgeR(*rule, vRSrc, vRTar); + pString.addR(eR, pStringOther.getRight()[get_inverse(ruleOther.getRtoCG(), getR(ruleOther), + cgOther, ecgOther)]); break; - case Membership::Context: - pString.add(e, pStringOther.getLeft()[eOther], pStringOther.getRight()[eOther]); + } + case Membership::K: { + const auto eK = addEdgeK(*rule, vcgNewSrc, vcgNewTar); + pString.addK(eK, + pStringOther.getLeft()[get_inverse(ruleOther.getLtoCG(), getL(ruleOther), + cgOther, ecgOther)], + pStringOther.getRight()[get_inverse(ruleOther.getRtoCG(), getR(ruleOther), + cgOther, ecgOther)]); break; } + } } if(other.pStereo) { const auto &lgLeft = get_labelled_left(other); const auto &lgRight = get_labelled_right(other); - const auto infLeft = Stereo::makeCloner(lgLeft, get_left(*this), jla_boost::Identity(), jla_boost::Identity()); - const auto infRight = Stereo::makeCloner(lgRight, get_right(*this), jla_boost::Identity(), jla_boost::Identity()); + const auto infLeft = Stereo::makeCloner(lgLeft, get_L_projected(*this), jla_boost::Identity(), + jla_boost::Identity()); + const auto infRight = Stereo::makeCloner(lgRight, get_R_projected(*this), jla_boost::Identity(), + jla_boost::Identity()); const auto inContext = [&](const auto &ve) { return get_stereo(other).inContext(ve); }; - this->pStereo.reset(new PropStereoCore(g, infLeft, infRight, inContext, inContext)); + this->pStereo.reset(new PropStereo(getRule(), infLeft, infRight, inContext, inContext)); } if(withConstraints) { - leftMatchConstraints.reserve(other.leftMatchConstraints.size()); - rightMatchConstraints.reserve(other.rightMatchConstraints.size()); - for(auto &&c : other.leftMatchConstraints) - leftMatchConstraints.push_back(c->clone()); - for(auto &&c : other.rightMatchConstraints) - rightMatchConstraints.push_back(c->clone()); + leftData.matchConstraints.reserve(other.leftData.matchConstraints.size()); + rightData.matchConstraints.reserve(other.rightData.matchConstraints.size()); + for(auto &&c: other.leftData.matchConstraints) + leftData.matchConstraints.push_back(c->clone()); + for(auto &&c: other.rightData.matchConstraints) + rightData.matchConstraints.push_back(c->clone()); } } +lib::DPO::CombinedRule &LabelledRule::getRule() { return *rule; } +const lib::DPO::CombinedRule &LabelledRule::getRule() const { return *rule; } + void LabelledRule::initComponents() { // TODO: structure this better - if(numLeftComponents != std::numeric_limits::max()) MOD_ABORT; - leftComponents.resize(num_vertices(get_graph(*this)), -1); - rightComponents.resize(num_vertices(get_graph(*this)), -1); - numLeftComponents = boost::connected_components(get_graph(get_labelled_left(*this)), leftComponents.data()); - numRightComponents = boost::connected_components(get_graph(get_labelled_right(*this)), rightComponents.data()); + if(leftData.numComponents != -1) MOD_ABORT; + leftData.component.resize(num_vertices(get_graph(*this)), -1); + rightData.component.resize(num_vertices(get_graph(*this)), -1); + leftData.numComponents = boost::connected_components(get_graph(get_labelled_left(*this)), + leftData.component.data()); + rightData.numComponents = boost::connected_components(get_graph(get_labelled_right(*this)), + rightData.component.data()); } void LabelledRule::invert() { - // invert the underlying graph - // and not the actual inversion - auto &g = *this->g; - for(const auto v : asRange(vertices(g))) - g[v].membership = jla_boost::GraphDPO::invert(g[v].membership); - for(const auto e : asRange(edges(g))) - g[e].membership = jla_boost::GraphDPO::invert(g[e].membership); + // invert the underlying rule + using lib::DPO::invert; + invert(*rule); // invert props if(pString) pString->invert(); if(pTerm) pTerm->invert(); @@ -98,23 +148,25 @@ void LabelledRule::invert() { } // also invert the component stuff using std::swap; - swap(this->numLeftComponents, this->numRightComponents); - swap(this->leftComponents, this->rightComponents); - swap(this->leftMatchConstraints, this->rightMatchConstraints); - // clear cached stuff - this->projs.reset(); + swap(leftData, rightData); } GraphType &get_graph(LabelledRule &r) { - return *r.g; + return r.rule->getCombinedGraph(); } const GraphType &get_graph(const LabelledRule &r) { - return *r.g; + return r.rule->getCombinedGraph(); } const LabelledRule::PropStringType &get_string(const LabelledRule &r) { assert(r.pString || r.pTerm); + if(!r.pString) { + r.pString.reset(new LabelledRule::PropStringType( + r.getRule(), r.leftData.matchConstraints, r.rightData.matchConstraints, + get_term(r), lib::Term::getStrings() + )); + } return *r.pString; } @@ -122,7 +174,7 @@ const LabelledRule::PropTermType &get_term(const LabelledRule &r) { assert(r.pString || r.pTerm); if(!r.pTerm) { r.pTerm.reset(new LabelledRule::PropTermType( - get_graph(r), r.leftMatchConstraints, r.rightMatchConstraints, + r.getRule(), r.leftData.matchConstraints, r.rightData.matchConstraints, get_string(r), lib::Term::getStrings() )); } @@ -139,8 +191,8 @@ const LabelledRule::PropStereoType &get_stereo(const LabelledRule &r) { auto gRight = get_labelled_right(r); auto pMoleculeLeft = get_molecule(gLeft); auto pMoleculeRight = get_molecule(gRight); - auto leftInference = lib::Stereo::makeInference(get_graph(gLeft), pMoleculeLeft, true); - auto rightInference = lib::Stereo::makeInference(get_graph(gRight), pMoleculeRight, true); + auto leftInference = lib::Stereo::Inference(get_graph(gLeft), pMoleculeLeft, true); + auto rightInference = lib::Stereo::Inference(get_graph(gRight), pMoleculeRight, true); { lib::IO::Warnings warnings; @@ -161,187 +213,123 @@ const LabelledRule::PropStereoType &get_stereo(const LabelledRule &r) { res.throwIfError(); } - r.pStereo.reset(new PropStereoCore(get_graph(r), - std::move(leftInference), std::move(rightInference), jla_boost::AlwaysTrue(), - jla_boost::AlwaysTrue())); + r.pStereo.reset(new PropStereo(r.getRule(), + std::move(leftInference), std::move(rightInference), jla_boost::AlwaysTrue(), + jla_boost::AlwaysTrue())); } return *r.pStereo; } const LabelledRule::PropMoleculeType &get_molecule(const LabelledRule &r) { if(!r.pMolecule) { - r.pMolecule.reset(new LabelledRule::PropMoleculeType(get_graph(r), get_string(r))); + r.pMolecule.reset(new LabelledRule::PropMoleculeType(r.getRule(), get_string(r))); } return *r.pMolecule; } -const LabelledRule::LeftGraphType &get_left(const LabelledRule &r) { - if(!r.projs) r.projs.reset(new LabelledRule::Projections(r)); - return r.projs->left; +const LabelledRule::SideProjectedGraphType &get_L_projected(const LabelledRule &r) { + return r.rule->getLProjected(); } -const LabelledRule::ContextGraphType &get_context(const LabelledRule &r) { - if(!r.projs) r.projs.reset(new LabelledRule::Projections(r)); - return r.projs->context; +const LabelledRule::SideProjectedGraphType &get_R_projected(const LabelledRule &r) { + return r.rule->getRProjected(); } -const LabelledRule::RightGraphType &get_right(const LabelledRule &r) { - if(!r.projs) r.projs.reset(new LabelledRule::Projections(r)); - return r.projs->right; -} - -jla_boost::GraphDPO::Membership membership(const LabelledRule &r, const Vertex &v) { +lib::DPO::Membership membership(const LabelledRule &r, const Vertex &v) { return get_graph(r)[v].membership; } -jla_boost::GraphDPO::Membership membership(const LabelledRule &r, const Edge &e) { +lib::DPO::Membership membership(const LabelledRule &r, const Edge &e) { return get_graph(r)[e].membership; } -void put_membership(LabelledRule &r, const Vertex &v, jla_boost::GraphDPO::Membership m) { +void put_membership(LabelledRule &r, const Vertex &v, lib::DPO::Membership m) { get_graph(r)[v].membership = m; } -void put_membership(LabelledRule &r, const Edge &e, jla_boost::GraphDPO::Membership m) { +void put_membership(LabelledRule &r, const Edge &e, lib::DPO::Membership m) { get_graph(r)[e].membership = m; } -LabelledRule::LabelledLeftType get_labelled_left(const LabelledRule &r) { - return LabelledRule::LabelledLeftType(r); +LabelledRule::Side get_labelled_left(const LabelledRule &r) { + return LabelledRule::Side(r, getL(r.getRule()), + &PropString::getLeft, &PropTerm::getLeft, + &PropStereo::getLeft, &PropMolecule::getLeft, + r.leftData); } -LabelledRule::LabelledRightType get_labelled_right(const LabelledRule &r) { - return LabelledRule::LabelledRightType(r); +LabelledRule::Side get_labelled_right(const LabelledRule &r) { + return LabelledRule::Side(r, getR(r.getRule()), + &PropString::getRight, &PropTerm::getRight, + &PropStereo::getRight, &PropMolecule::getRight, + r.rightData); } -LabelledRule::Projections::Projections(const LabelledRule &r) - : left(get_graph(r), Membership::Left), - context(get_graph(r), Membership::Context), - right(get_graph(r), Membership::Right) {} - - -// LabelledSideGraph -//------------------------------------------------------------------------------ - -namespace detail { - -LabelledSideGraph::LabelledSideGraph(const LabelledRule &r, jla_boost::GraphDPO::Membership m) - : r(r), m(m) {} - -} // namespace detail // LabelledLeftGraph //------------------------------------------------------------------------------ -LabelledLeftGraph::LabelledLeftGraph(const LabelledRule &r) - : Base(r, jla_boost::GraphDPO::Membership::Left) {} +LabelledRule::Side::Side(const LabelledRule &r, const GraphType &g, + PropStringType (LabelledRule::PropStringType::*fString)() const, + PropTermType (LabelledRule::PropTermType::*fTerm)() const, + PropStereoType (LabelledRule::PropStereoType::*fStereo)() const, + PropMoleculeType (LabelledRule::PropMoleculeType::*fMol)() const, + const SideData &data) + : r(r), g(g), fString(fString), fTerm(fTerm), fStereo(fStereo), fMol(fMol), data(data) {} -const LabelledLeftGraph::GraphType &get_graph(const LabelledLeftGraph &g) { - return get_left(g.r); +const LabelledRule::Side::GraphType &get_graph(const LabelledRule::Side &g) { + return g.g; } -LabelledLeftGraph::PropStringType get_string(const LabelledLeftGraph &g) { - return get_string(g.r).getLeft(); +LabelledRule::Side::PropStringType get_string(const LabelledRule::Side &g) { + return (get_string(g.r).*g.fString)(); } -LabelledLeftGraph::PropTermType get_term(const LabelledLeftGraph &g) { - return get_term(g.r).getLeft(); +LabelledRule::Side::PropTermType get_term(const LabelledRule::Side &g) { + return (get_term(g.r).*g.fTerm)(); } -bool has_stereo(const LabelledLeftGraph &g) { +bool has_stereo(const LabelledRule::Side &g) { return has_stereo(g.r); } -LabelledLeftGraph::PropStereoType get_stereo(const LabelledLeftGraph &g) { - return get_stereo(g.r).getLeft(); +LabelledRule::Side::PropStereoType get_stereo(const LabelledRule::Side &g) { + return (get_stereo(g.r).*g.fStereo)(); } -const std::vector > & -get_match_constraints(const LabelledLeftGraph &g) { - return g.r.leftMatchConstraints; +LabelledRule::Side::PropMoleculeType get_molecule(const LabelledRule::Side &g) { + return (get_molecule(g.r).*g.fMol)(); } -std::size_t get_num_connected_components(const LabelledLeftGraph &g) { - return g.r.numLeftComponents; +const std::vector> & +get_match_constraints(const LabelledRule::Side &g) { + return g.data.matchConstraints; } -LabelledLeftGraph::PropMoleculeType get_molecule(const LabelledLeftGraph &g) { - return get_molecule(g.r).getLeft(); +std::size_t get_num_connected_components(const LabelledRule::Side &g) { + return g.data.numComponents; } -LabelledLeftGraph::Base::ComponentGraph -get_component_graph(std::size_t i, const LabelledLeftGraph &g) { - assert(i < get_num_connected_components(g)); - LabelledLeftGraph::Base::ComponentFilter filter(&get_graph(g), &g.r.leftComponents, i); - return LabelledLeftGraph::Base::ComponentGraph(get_graph(g), filter, filter); +const std::vector get_component(const LabelledRule::Side &g) { + return g.data.component; } -const std::vector::vertex_descriptor> & -get_vertex_order_component(std::size_t i, const LabelledLeftGraph &g) { +LabelledRule::Side::ComponentGraph +get_component_graph(std::size_t i, const LabelledRule::Side &g) { assert(i < get_num_connected_components(g)); - // the number of connected components is initialized externally after construction, so we have this annoying hax - if(g.vertex_orders.empty()) g.vertex_orders.resize(get_num_connected_components(g)); - if(g.vertex_orders[i].empty()) { - g.vertex_orders[i] = get_vertex_order(lib::GraphMorphism::DefaultFinderArgsProvider(), get_component_graph(i, g)); - } - return g.vertex_orders[i]; -} - -// LabelledRightGraph -//------------------------------------------------------------------------------ - -LabelledRightGraph::LabelledRightGraph(const LabelledRule &r) - : Base(r, jla_boost::GraphDPO::Membership::Right) {} - -const LabelledRightGraph::GraphType &get_graph(const LabelledRightGraph &g) { - return get_right(g.r); -} - -LabelledRightGraph::PropStringType get_string(const LabelledRightGraph &g) { - return get_string(g.r).getRight(); -} - -LabelledRightGraph::PropTermType get_term(const LabelledRightGraph &g) { - return get_term(g.r).getRight(); -} - -bool has_stereo(const LabelledRightGraph &g) { - return has_stereo(g.r); -} - -LabelledRightGraph::PropStereoType get_stereo(const LabelledRightGraph &g) { - return get_stereo(g.r).getRight(); -} - -const std::vector > & -get_match_constraints(const LabelledRightGraph &g) { - return g.r.rightMatchConstraints; -} - -std::size_t get_num_connected_components(const LabelledRightGraph &g) { - return g.r.numRightComponents; -} - -LabelledRightGraph::Base::ComponentGraph -get_component_graph(std::size_t i, const LabelledRightGraph &g) { - assert(i < get_num_connected_components(g)); - LabelledRightGraph::Base::ComponentFilter filter(&get_graph(g), &g.r.rightComponents, i); - return LabelledRightGraph::Base::ComponentGraph(get_graph(g), filter, filter); -} - -LabelledRightGraph::PropMoleculeType get_molecule(const LabelledRightGraph &g) { - return get_molecule(g.r).getRight(); + LabelledRule::Side::ComponentFilter filter(&get_graph(g), &g.data.component, i); + return LabelledRule::Side::ComponentGraph(get_graph(g), filter, filter); } const std::vector::vertex_descriptor> & -get_vertex_order_component(std::size_t i, const LabelledRightGraph &g) { +get_vertex_order_component(std::size_t i, const LabelledRule::Side &g) { assert(i < get_num_connected_components(g)); + auto &vertex_orders = g.data.vertex_orders; // the number of connected components is initialized externally after construction, so we have this annoying hax - if(g.vertex_orders.empty()) g.vertex_orders.resize(get_num_connected_components(g)); - if(g.vertex_orders[i].empty()) { - g.vertex_orders[i] = get_vertex_order(lib::GraphMorphism::DefaultFinderArgsProvider(), get_component_graph(i, g)); - } - return g.vertex_orders[i]; + if(vertex_orders.empty()) vertex_orders.resize(get_num_connected_components(g)); + if(vertex_orders[i].empty()) + vertex_orders[i] = get_vertex_order(lib::GraphMorphism::DefaultFinderArgsProvider(), get_component_graph(i, g)); + return vertex_orders[i]; } } // namespace mod::lib::Rules \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/LabelledRule.hpp b/libs/libmod/src/mod/lib/Rules/LabelledRule.hpp index a7461c3..872a0eb 100644 --- a/libs/libmod/src/mod/lib/Rules/LabelledRule.hpp +++ b/libs/libmod/src/mod/lib/Rules/LabelledRule.hpp @@ -1,6 +1,7 @@ #ifndef MOD_LIB_RULES_LABELLED_RULE_HPP #define MOD_LIB_RULES_LABELLED_RULE_HPP +#include #include #include #include @@ -12,147 +13,121 @@ #include namespace mod::lib::Rules { -struct LabelledLeftGraph; -struct LabelledRightGraph; -class LabelledRule { -public: // LabelledGraphConcept, PushoutRuleConcept - using GraphType = lib::Rules::GraphType; -public: // PushoutRuleConcept - using LeftGraphType = lib::Rules::SideGraphType; - using ContextGraphType = lib::Rules::SideGraphType; - using RightGraphType = lib::Rules::SideGraphType; +struct LabelledRule { + using RuleType = lib::DPO::CombinedRule; public: // LabelledGraphConcept - using PropStringType = PropStringCore; - using PropTermType = PropTermCore; - using PropStereoType = PropStereoCore; - using PropMoleculeType = PropMoleculeCore; + using GraphType = RuleType::CombinedGraphType; + using PropStringType = PropString; + using PropTermType = PropTerm; + using PropStereoType = PropStereo; +public: + using PropMoleculeType = PropMolecule; +public: // RuleConcept + using SideGraphType = RuleType::SideGraphType; + using KGraphType = RuleType::KGraphType; +public: // Projected view + using SideProjectedGraphType = RuleType::SideProjectedGraphType; public: // Other using Vertex = boost::graph_traits::vertex_descriptor; using Edge = boost::graph_traits::edge_descriptor; - using LeftMatchConstraint = GraphMorphism::Constraints::Constraint; - using RightMatchConstraint = GraphMorphism::Constraints::Constraint; - using LabelledLeftType = LabelledLeftGraph; - using LabelledRightType = LabelledRightGraph; + using MatchConstraint = GraphMorphism::Constraints::Constraint; +public: + struct SideData; +public: + struct Side { + using GraphType = SideGraphType; + using ComponentFilter = ConnectedComponentFilter>; + using ComponentGraph = boost::filtered_graph; + using PropStringType = LabelledRule::PropStringType::Side; + using PropTermType = LabelledRule::PropTermType::Side; + using PropStereoType = LabelledRule::PropStereoType::Side; + public: + using PropMoleculeType = LabelledRule::PropMoleculeType::Side; + public: + // We don't want to convert data unless needed, so instead of storing the + // side prop proxy objects, we store member function pointers to retrieve them. + explicit Side(const LabelledRule &r, const GraphType &g, + PropStringType (LabelledRule::PropStringType::*fString)() const, + PropTermType (LabelledRule::PropTermType::*fTerm)() const, + PropStereoType (LabelledRule::PropStereoType::*fStereo)() const, + PropMoleculeType (LabelledRule::PropMoleculeType::*fMol)() const, + const SideData &data); + public: + friend const GraphType &get_graph(const Side &g); + friend PropStringType get_string(const Side &g); + friend PropTermType get_term(const Side &g); + friend bool has_stereo(const Side &g); + friend PropStereoType get_stereo(const Side &g); + public: + friend PropMoleculeType get_molecule(const Side &g); + public: + friend const std::vector> & + get_match_constraints(const Side &g); + public: + friend std::size_t get_num_connected_components(const Side &g); + friend const std::vector get_component(const Side &g); + friend ComponentGraph get_component_graph(std::size_t i, const Side &g); + public: + friend const std::vector::vertex_descriptor> & + get_vertex_order_component(std::size_t i, const Side &g); + public: + const LabelledRule &r; + const GraphType &g; + PropStringType (LabelledRule::PropStringType::*fString)() const; + PropTermType (LabelledRule::PropTermType::*fTerm)() const; + PropStereoType (LabelledRule::PropStereoType::*fStereo)() const; + PropMoleculeType (LabelledRule::PropMoleculeType::*fMol)() const; + const SideData &data; + }; +public: + explicit LabelledRule(std::unique_ptr rule, + std::unique_ptr pString, + std::unique_ptr pStereo); + explicit LabelledRule(std::unique_ptr rule, + std::unique_ptr pTerm, + std::unique_ptr pStereo); + LabelledRule(); // TODO: remove + LabelledRule(const LabelledRule &other, bool withConstraints); // TODO: hmm + lib::DPO::CombinedRule &getRule(); // TODO: remove non-const version? + const lib::DPO::CombinedRule &getRule() const; public: - LabelledRule(); - LabelledRule(const LabelledRule &other, bool withConstraints); void initComponents(); // TODO: this is a huge hax void invert(); -public: // LabelledGraphConcept, PushoutRuleConcept +public: // LabelledGraphConcept friend GraphType &get_graph(LabelledRule &r); friend const GraphType &get_graph(const LabelledRule &r); -public: // LabelledGraphConcept friend const PropStringType &get_string(const LabelledRule &r); friend const PropTermType &get_term(const LabelledRule &r); friend bool has_stereo(const LabelledRule &r); friend const PropStereoType &get_stereo(const LabelledRule &r); public: friend const PropMoleculeType &get_molecule(const LabelledRule &r); -public: // PushoutRuleConcept - friend const LeftGraphType &get_left(const LabelledRule &r); - friend const ContextGraphType &get_context(const LabelledRule &r); - friend const RightGraphType &get_right(const LabelledRule &r); - friend jla_boost::GraphDPO::Membership membership(const LabelledRule &r, const Vertex &v); - friend jla_boost::GraphDPO::Membership membership(const LabelledRule &r, const Edge &e); - friend void put_membership(LabelledRule &r, const Vertex &v, jla_boost::GraphDPO::Membership m); - friend void put_membership(LabelledRule &r, const Edge &e, jla_boost::GraphDPO::Membership m); public: - friend LabelledLeftType get_labelled_left(const LabelledRule &r); - friend LabelledRightType get_labelled_right(const LabelledRule &r); + friend const SideProjectedGraphType &get_L_projected(const LabelledRule &r); + friend const SideProjectedGraphType &get_R_projected(const LabelledRule &r); + friend lib::DPO::Membership membership(const LabelledRule &r, const Vertex &v); + friend lib::DPO::Membership membership(const LabelledRule &r, const Edge &e); + friend void put_membership(LabelledRule &r, const Vertex &v, lib::DPO::Membership m); + friend void put_membership(LabelledRule &r, const Edge &e, lib::DPO::Membership m); +public: + friend Side get_labelled_left(const LabelledRule &r); + friend Side get_labelled_right(const LabelledRule &r); private: - struct Projections { - Projections(const LabelledRule &r); - public: - LeftGraphType left; - ContextGraphType context; - RightGraphType right; - }; - std::unique_ptr g; - mutable std::unique_ptr projs; + std::unique_ptr rule; public: mutable std::unique_ptr pString; mutable std::unique_ptr pTerm; mutable std::unique_ptr pStereo; - std::vector > leftMatchConstraints; - std::vector > rightMatchConstraints; private: mutable std::unique_ptr pMolecule; public: - std::size_t numLeftComponents = -1, numRightComponents = -1; - std::vector leftComponents, rightComponents; -}; - -namespace detail { - -struct LabelledSideGraph { - using LabelledRule = lib::Rules::LabelledRule; - using GraphType = lib::Rules::SideGraphType; - using ComponentFilter = ConnectedComponentFilter >; - using ComponentGraph = boost::filtered_graph; -public: - LabelledSideGraph(const LabelledRule &r, jla_boost::GraphDPO::Membership m); -public: - const LabelledRule &r; - const jla_boost::GraphDPO::Membership m; -protected: - mutable std::vector::vertex_descriptor> > vertex_orders; -}; - -} // namespace detail - -struct LabelledLeftGraph : detail::LabelledSideGraph { - using Base = detail::LabelledSideGraph; - using PropStringType = LabelledRule::PropStringType::LeftType; - using PropTermType = LabelledRule::PropTermType::LeftType; - using PropStereoType = LabelledRule::PropStereoType::LeftType; -public: - using PropMoleculeType = typename LabelledRule::PropMoleculeType::LeftType; -public: - explicit LabelledLeftGraph(const LabelledRule &r); - friend const Base::GraphType &get_graph(const LabelledLeftGraph &g); - friend PropStringType get_string(const LabelledLeftGraph &g); - friend PropTermType get_term(const LabelledLeftGraph &g); - friend bool has_stereo(const LabelledLeftGraph &g); - friend PropStereoType get_stereo(const LabelledLeftGraph &g); -public: - friend const std::vector > & - get_match_constraints(const LabelledLeftGraph &g); -public: - friend std::size_t get_num_connected_components(const LabelledLeftGraph &g); - friend Base::ComponentGraph get_component_graph(std::size_t i, const LabelledLeftGraph &g); -public: - friend PropMoleculeType get_molecule(const LabelledLeftGraph &g); -public: - friend const std::vector::vertex_descriptor> & - get_vertex_order_component(std::size_t i, const LabelledLeftGraph &g); -}; - -struct LabelledRightGraph : detail::LabelledSideGraph { - using Base = detail::LabelledSideGraph; - using PropStringType = LabelledRule::PropStringType::RightType; - using PropTermType = LabelledRule::PropTermType::RightType; - using PropStereoType = LabelledRule::PropStereoType::RightType; -public: - using PropMoleculeType = typename LabelledRule::PropMoleculeType::RightType; -public: - explicit LabelledRightGraph(const LabelledRule &r); - friend const Base::GraphType &get_graph(const LabelledRightGraph &g); - friend PropStringType get_string(const LabelledRightGraph &g); - friend PropTermType get_term(const LabelledRightGraph &g); - friend bool has_stereo(const LabelledRightGraph &g); - friend PropStereoType get_stereo(const LabelledRightGraph &g); -public: - friend const std::vector > & - get_match_constraints(const LabelledRightGraph &g); -public: - friend std::size_t get_num_connected_components(const LabelledRightGraph &g); - friend Base::ComponentGraph get_component_graph(std::size_t i, const LabelledRightGraph &g); -public: - friend PropMoleculeType get_molecule(const LabelledRightGraph &g); -public: - friend const std::vector::vertex_descriptor> & - get_vertex_order_component(std::size_t i, const LabelledRightGraph &g); + struct SideData { + std::size_t numComponents = -1; + std::vector component; + std::vector> matchConstraints; + mutable std::vector> vertex_orders; + } leftData, rightData; }; } // namespace mod::lib::Rules diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Depiction.cpp b/libs/libmod/src/mod/lib/Rules/Properties/Depiction.cpp deleted file mode 100644 index d9e61a2..0000000 --- a/libs/libmod/src/mod/lib/Rules/Properties/Depiction.cpp +++ /dev/null @@ -1,527 +0,0 @@ -#include "Depiction.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace mod::lib::Rules { - -//------------------------------------------------------------------------------ -// template DepictionData -//------------------------------------------------------------------------------ - -template -DepictionDataCore::DepictionData::DepictionData(const DepictionDataCore &depict) : depict(depict) {} - -template -AtomId DepictionDataCore::DepictionData::getAtomId(Vertex v) const { - const auto &pMol = get_molecule(depict.lr); - switch(membership) { - case Membership::Left: - return pMol.getLeft()[v].getAtomId(); - case Membership::Right: - return pMol.getRight()[v].getAtomId(); - case Membership::Context: - auto leftId = pMol.getLeft()[v].getAtomId(); - auto rightId = pMol.getRight()[v].getAtomId(); - if(leftId == rightId) return leftId; - else return AtomIds::Invalid; - } -} - -template -Isotope DepictionDataCore::DepictionData::getIsotope(Vertex v) const { - const auto &pMol = get_molecule(depict.lr); - switch(membership) { - case Membership::Left: - return pMol.getLeft()[v].getIsotope(); - case Membership::Right: - return pMol.getRight()[v].getIsotope(); - case Membership::Context: - auto leftId = pMol.getLeft()[v].getIsotope(); - auto rightId = pMol.getRight()[v].getIsotope(); - if(leftId == rightId) return leftId; - else return Isotope(); - } -} - -template -Charge DepictionDataCore::DepictionData::getCharge(Vertex v) const { - const auto &pMol = get_molecule(depict.lr); - switch(membership) { - case Membership::Left: - return pMol.getLeft()[v].getCharge(); - case Membership::Right: - return pMol.getRight()[v].getCharge(); - case Membership::Context: - auto leftCharge = pMol.getLeft()[v].getCharge(); - auto rightCharge = pMol.getRight()[v].getCharge(); - if(leftCharge == rightCharge) return leftCharge; - else return Charge(0); - } -} - -template -bool DepictionDataCore::DepictionData::getRadical(Vertex v) const { - const auto &pMol = get_molecule(depict.lr); - switch(membership) { - case Membership::Left: - return pMol.getLeft()[v].getRadical(); - case Membership::Right: - return pMol.getRight()[v].getRadical(); - case Membership::Context: - auto leftRadical = pMol.getLeft()[v].getRadical(); - auto rightRadical = pMol.getRight()[v].getRadical(); - if(leftRadical == rightRadical) return leftRadical; - else return false; - } -} - -template -BondType DepictionDataCore::DepictionData::getBondData(Edge e) const { - const auto &pMol = get_molecule(depict.lr); - switch(membership) { - case Membership::Left: - return pMol.getLeft()[e]; - case Membership::Right: - return pMol.getRight()[e]; - case Membership::Context: - auto btLeft = pMol.getLeft()[e]; - auto btRight = pMol.getRight()[e]; - if(btLeft == btRight) return btLeft; - else return BondType::Invalid; - } -} - -template -std::string DepictionDataCore::DepictionData::getVertexLabelNoIsotopeChargeRadical(Vertex v) const { - auto atomId = getAtomId(v); - if(atomId != AtomIds::Invalid) - return Chem::symbolFromAtomId(atomId); - switch(membership) { - case Membership::Left: - case Membership::Right: { - const auto &nonAtomToPhonyAtom = - membership == Membership::Left ? depict.nonAtomToPhonyAtomLeft : depict.nonAtomToPhonyAtomRight; - auto nonAtomIter = nonAtomToPhonyAtom.find(v); - assert(nonAtomIter != end(nonAtomToPhonyAtom)); - auto labelIter = depict.phonyAtomToString.find(nonAtomIter->second.getAtomId()); - assert(labelIter != end(depict.phonyAtomToString)); - return labelIter->second; - } - case Membership::Context: { - const auto &pMol = get_molecule(depict.lr); - auto nonAtomIterLeft = depict.nonAtomToPhonyAtomLeft.find(v); - auto nonAtomIterRight = depict.nonAtomToPhonyAtomRight.find(v); - std::string left, right; - if(nonAtomIterLeft == end(depict.nonAtomToPhonyAtomLeft)) { - auto atomId = pMol.getLeft()[v].getAtomId(); - left = Chem::symbolFromAtomId(atomId); - } else { - auto labelIterLeft = depict.phonyAtomToString.find(nonAtomIterLeft->second.getAtomId()); - assert(labelIterLeft != end(depict.phonyAtomToString)); - left = labelIterLeft->second; - } - if(nonAtomIterRight == end(depict.nonAtomToPhonyAtomRight)) { - auto atomId = pMol.getRight()[v].getAtomId(); - right = Chem::symbolFromAtomId(atomId); - } else { - auto labelIterRight = depict.phonyAtomToString.find(nonAtomIterRight->second.getAtomId()); - assert(labelIterRight != end(depict.phonyAtomToString)); - right = labelIterRight->second; - } - if(left == right) return left; - else return R"X($\langle$)X" + left + ", " + right + R"X($\rangle$)X"; - } - } -} - -template -std::string DepictionDataCore::DepictionData::getEdgeLabel(Edge e) const { - auto bt = getBondData(e); - if(bt != BondType::Invalid) - return std::string(1, Chem::bondToChar(bt)); - switch(membership) { - case Membership::Left: - case Membership::Right: { - const auto &nonBondEdges = membership == Membership::Left ? depict.nonBondEdgesLeft : depict.nonBondEdgesRight; - auto iter = nonBondEdges.find(e); - if(iter == end(nonBondEdges)) std::cout << "WTF: " << e << std::endl; - assert(iter != end(nonBondEdges)); - return iter->second; - } - case Membership::Context: { - std::string left, right; - auto iterLeft = depict.nonBondEdgesLeft.find(e); - auto iterRight = depict.nonBondEdgesRight.find(e); - const auto &pMol = get_molecule(depict.lr); - if(iterLeft != end(depict.nonBondEdgesLeft)) left = iterLeft->second; - else left = std::string(1, Chem::bondToChar(pMol.getLeft()[e])); - if(iterRight != end(depict.nonBondEdgesRight)) right = iterRight->second; - else right = std::string(1, Chem::bondToChar(pMol.getRight()[e])); - if(left == right) return left; - else return "$\\langle$" + left + ", " + right + "$\\rangle$"; - } - } -} - -template -const AtomData &DepictionDataCore::DepictionData::operator()(Vertex v) const { - switch(membership) { - case Membership::Left: - if(getAtomId(v) != AtomIds::Invalid) return get_molecule(depict.lr).getLeft()[v]; - else { - auto iter = depict.nonAtomToPhonyAtomLeft.find(v); - assert(iter != end(depict.nonAtomToPhonyAtomLeft)); - return iter->second; - } - case Membership::Right: - if(getAtomId(v) != AtomIds::Invalid) return get_molecule(depict.lr).getRight()[v]; - else { - auto iter = depict.nonAtomToPhonyAtomRight.find(v); - assert(iter != end(depict.nonAtomToPhonyAtomLeft)); - return iter->second; - } - case Membership::Context: - // for now we just return whatever is in left, it's fake data anyway - return depict.getLeft()(v); - } -} - -template -BondType DepictionDataCore::DepictionData::operator()(Edge e) const { - auto bt = getBondData(e); - return bt == BondType::Invalid ? BondType::Single : bt; -} - -template -bool DepictionDataCore::DepictionData::hasImportantStereo(Vertex v) const { - const auto &lr = depict.lr; - if(!has_stereo(lr)) return false; - switch(membership) { - case Membership::Left: { - const auto &lg = get_labelled_left(lr); - return !get_stereo(lg)[v]->morphismDynamicOk(); - } - case Membership::Right: { - const auto &lg = get_labelled_right(lr); - return !get_stereo(lg)[v]->morphismDynamicOk(); - } - case Membership::Context: - return get_stereo(lr).inContext(v) && depict.hasImportantStereo(v); - } - __builtin_unreachable(); -} - -template -bool DepictionDataCore::DepictionData::getHasCoordinates() const { - return depict.getHasCoordinates(); -} - -template -double DepictionDataCore::DepictionData::getX(Vertex v, bool withHydrogen) const { - return depict.getX(v, withHydrogen); -} - -template -double DepictionDataCore::DepictionData::getY(Vertex v, bool withHydrogen) const { - return depict.getY(v, withHydrogen); -} - -template -lib::IO::Graph::Write::EdgeFake3DType -DepictionDataCore::DepictionData::getEdgeFake3DType(Edge e, bool withHydrogen) const { -#ifndef MOD_HAVE_OPENBABEL - MOD_NO_OPENBABEL_ERROR -#else - assert(depict.hasMoleculeEncoding); - const auto &g = get_graph(depict.lr); - const auto vSrc = source(e, g); - const auto vTar = target(e, g); - const auto idSrc = get(boost::vertex_index_t(), g, vSrc); - const auto idTar = get(boost::vertex_index_t(), g, vTar); - const CoordData &cData = withHydrogen ? depict.cDataAll : depict.cDataNoHydrogen; - switch(membership) { - case Membership::Left: - return cData.obMolLeft.getBondFake3D(idSrc, idTar); - case Membership::Right: - return cData.obMolRight.getBondFake3D(idSrc, idTar); - case Membership::Context: - if(has_stereo(depict.lr) && get_stereo(depict.lr).inContext(vSrc) && get_stereo(depict.lr).inContext(vTar)) - return cData.obMolLeft.getBondFake3D(idSrc, idTar); - else - return lib::IO::Graph::Write::EdgeFake3DType::None; - } -#endif - MOD_ABORT; -} - -//template -//bool DepictionDataCore::DepictionData::isAtomIdInvalidContext(Vertex v) const { -// switch(membership) { -// case Membership::Left: -// case Membership::Right -// return true; -// } -// const auto &molState = depict.moleculeState; -// if(membership == Membership::Left) return true; -// else if(membership == Membership::Right) return true; -// else { -// auto m = depict.g[v].membership; -// // if(m != Membership::StateChange) return true; -// auto leftId = molState.getNormal(v).getAtomId(); -// auto rightId = molState.getChange(v).getAtomId(); -// if(leftId == rightId) return true; -// else return false; -// } -//} - -//------------------------------------------------------------------------------ -// DepictionDataCore -//------------------------------------------------------------------------------ - -DepictionDataCore::DepictionDataCore(const LabelledRule &lr) - : lr(lr), hasMoleculeEncoding(true), hasCoordinates(false) { - const auto &g = get_graph(lr); - const auto &pString = get_string(lr); - const auto &pMol = get_molecule(lr); - { // vertexData - std::vector atomUsed(AtomIds::Max + 1, false); - Chem::markSpecialAtomsUsed(atomUsed); - std::vector> verticesToProcess; // vertex x {Left, Right} - for(Vertex v : asRange(vertices(g))) { - auto m = g[v].membership; - if(m != Membership::Right) { - unsigned char atomId = pMol.getLeft()[v].getAtomId(); - if(atomId != AtomIds::Invalid) atomUsed[atomId] = true; - else verticesToProcess.emplace_back(v, Membership::Left); - } - if(m != Membership::Left) { - unsigned char atomId = pMol.getRight()[v].getAtomId(); - if(atomId != AtomIds::Invalid) atomUsed[atomId] = true; - else verticesToProcess.emplace_back(v, Membership::Right); - } - } - // map non-atom labels to atoms - std::map labelToAtomId; - for(auto p : verticesToProcess) { - Vertex v = p.first; - Membership m = p.second; - assert(m != Membership::Context); - std::string label = std::get<0>(Chem::extractIsotopeChargeRadical( - m == Membership::Left ? pString.getLeft()[v] : pString.getRight()[v] - )); - auto iter = labelToAtomId.find(label); - if(iter == end(labelToAtomId)) { - unsigned char atomId = 1; - for(; atomId <= AtomIds::Max; atomId++) { - if(atomUsed[atomId]) continue; - atomUsed[atomId] = true; - iter = labelToAtomId.emplace(label, AtomId(atomId)).first; - phonyAtomToString[AtomId(atomId)] = label; - break; - } - if(atomId > AtomIds::Max) { - hasMoleculeEncoding = false; - break; - } - } - auto atomId = iter->second; - if(m == Membership::Left) { - auto charge = pMol.getLeft()[v].getCharge(); - bool radical = pMol.getLeft()[v].getRadical(); - nonAtomToPhonyAtomLeft[v] = AtomData(atomId, charge, radical); - } else { - auto charge = pMol.getRight()[v].getCharge(); - bool radical = pMol.getRight()[v].getRadical(); - nonAtomToPhonyAtomRight[v] = AtomData(atomId, charge, radical); - } - } - } - { // edgeData - for(Edge e : asRange(edges(g))) { - auto m = g[e].membership; - if(m != Membership::Right) { - auto bt = pMol.getLeft()[e]; - if(bt == BondType::Invalid) nonBondEdgesLeft[e] = pString.getLeft()[e]; - } - if(m != Membership::Left) { - auto bt = pMol.getRight()[e]; - if(bt == BondType::Invalid) nonBondEdgesRight[e] = pString.getRight()[e]; - } - } - } - - if(hasMoleculeEncoding) { -#ifdef MOD_HAVE_OPENBABEL - const auto doIt = [&](CoordData &cData, const bool withHydrogen) { - std::tie(cData.obMol, cData.obMolLeft, cData.obMolRight) - = Chem::makeOBMol(lr, std::cref(*this), std::cref(*this), - getLeft(), getLeft(), - getRight(), getRight(), - withHydrogen); - cData.x.resize(num_vertices(g)); - cData.y.resize(num_vertices(g)); - for(const auto v : asRange(vertices(g))) { - const auto vId = get(boost::vertex_index_t(), g, v); - if(cData.obMol.hasAtom(vId)) { - cData.x[vId] = cData.obMol.getAtomX(vId); - cData.y[vId] = cData.obMol.getAtomY(vId); - } else { - assert(!withHydrogen); - cData.x[vId] = std::numeric_limits::quiet_NaN(); - cData.y[vId] = std::numeric_limits::quiet_NaN(); - } - } - }; - doIt(cDataAll, true); - doIt(cDataNoHydrogen, false); - hasCoordinates = true; -#endif - } -} - -const AtomData &DepictionDataCore::operator()(Vertex v) const { - const auto &g = get_graph(lr); - const auto &pMol = get_molecule(lr); - // return whatever we find - auto m = g[v].membership; - if(m != Membership::Right) { - auto atomId = pMol.getLeft()[v].getAtomId(); - if(atomId != AtomIds::Invalid) return pMol.getLeft()[v]; - else { - auto iter = nonAtomToPhonyAtomLeft.find(v); - assert(iter != end(nonAtomToPhonyAtomLeft)); - return iter->second; - } - } else { - auto atomId = pMol.getRight()[v].getAtomId(); - if(atomId != AtomIds::Invalid) return pMol.getRight()[v]; - else { - auto iter = nonAtomToPhonyAtomRight.find(v); - assert(iter != end(nonAtomToPhonyAtomRight)); - return iter->second; - } - } -} - -BondType DepictionDataCore::operator()(Edge e) const { - const auto &g = get_graph(lr); - const auto &pMol = get_molecule(lr); - // if there is agreement, return that, otherwise prefer invalid bonds - // this should give a bit more freedom in bond angles - const auto m = g[e].membership; - BondType l = BondType::Single, r = BondType::Single; - if(m != Membership::Right) - l = pMol.getLeft()[e]; - if(m != Membership::Left) - r = pMol.getRight()[e]; - if(l == r) return l; - else return BondType::Invalid; -} - -bool DepictionDataCore::hasImportantStereo(Vertex v) const { - if(!has_stereo(lr)) return false; - const auto &g = get_graph(lr); - const auto m = g[v].membership; - if(m != Membership::Right && !get_stereo(get_labelled_left(lr))[v]->morphismDynamicOk()) return true; - if(m != Membership::Left && !get_stereo(get_labelled_right(lr))[v]->morphismDynamicOk()) return true; - return false; -} - -bool DepictionDataCore::getHasCoordinates() const { -#ifdef MOD_HAVE_OPENBABEL - if(getConfig().io.useOpenBabelCoords.get()) - return hasMoleculeEncoding; - else return false; -#else - return false; -#endif -} - -double DepictionDataCore::getX(Vertex v, bool withHydrogen) const { - if(!getHasCoordinates()) MOD_ABORT; - const auto &g = get_graph(lr); - unsigned int vId = get(boost::vertex_index_t(), g, v); - const CoordData &cData = withHydrogen ? cDataAll : cDataNoHydrogen; - assert(vId < cData.x.size()); - return cData.x[vId]; -} - -double DepictionDataCore::getY(Vertex v, bool withHydrogen) const { - if(!getHasCoordinates()) MOD_ABORT; - const auto &g = get_graph(lr); - unsigned int vId = get(boost::vertex_index_t(), g, v); - const CoordData &cData = withHydrogen ? cDataAll : cDataNoHydrogen; - assert(vId < cData.y.size()); - return cData.y[vId]; -} - -void DepictionDataCore::copyCoords(const DepictionDataCore &other, const std::map &vMap) { - if(!other.getHasCoordinates()) return; - const auto &g = get_graph(lr); - const auto doIt = [&](CoordData &cData, const bool withHydrogen) { - cData.x.resize(num_vertices(g)); - cData.y.resize(num_vertices(g)); - for(const Vertex v : asRange(vertices(g))) { - const auto iter = vMap.find(v); - if(iter == end(vMap)) { - std::cout << "Vertex " << v << " (id=" << get(boost::vertex_index_t(), g, v) << ") not mapped." - << std::endl; - std::cout << "Map:" << std::endl; - for(auto p : vMap) std::cout << "\t" << p.first << " => " << p.second << std::endl; - std::cout << "num_vertices: " << num_vertices(g) << std::endl; - std::cout << "other.num_vertices: " << num_vertices(get_graph(other.lr)) << std::endl; - MOD_ABORT; - } - const Vertex vOther = iter->second; - const auto vId = get(boost::vertex_index_t(), g, v); - cData.x[vId] = other.getX(vOther, withHydrogen); - cData.y[vId] = other.getY(vOther, withHydrogen); - } - if(hasMoleculeEncoding) { -#ifdef MOD_HAVE_OPENBABEL - cData.obMol.setCoordinates(cData.x, cData.y); - cData.obMolLeft.setCoordinates(cData.x, cData.y); - cData.obMolRight.setCoordinates(cData.x, cData.y); -#endif - } - }; - doIt(cDataAll, true); - doIt(cDataNoHydrogen, false); - hasCoordinates = true; -} - -DepictionDataCore::DepictionData DepictionDataCore::getLeft() const { - if(!hasMoleculeEncoding) MOD_ABORT; - return DepictionData(*this); -} - -DepictionDataCore::DepictionData DepictionDataCore::getContext() const { - if(!hasMoleculeEncoding) MOD_ABORT; - return DepictionData(*this); -} - -DepictionDataCore::DepictionData DepictionDataCore::getRight() const { - if(!hasMoleculeEncoding) MOD_ABORT; - return DepictionData(*this); -} - -template -struct DepictionDataCore::DepictionData; -template -struct DepictionDataCore::DepictionData; -template -struct DepictionDataCore::DepictionData; - -} // namespace mod::lib::Rules \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Depiction.hpp b/libs/libmod/src/mod/lib/Rules/Properties/Depiction.hpp deleted file mode 100644 index 0bbfacc..0000000 --- a/libs/libmod/src/mod/lib/Rules/Properties/Depiction.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef MOD_LIB_RULES_STATE_DEPICTION_HPP -#define MOD_LIB_RULES_STATE_DEPICTION_HPP - -#include -#include -#include -#include - -#include -#include - -namespace mod { -struct AtomId; -struct Charge; -struct AtomData; -enum class BondType; -} // namespace mod -namespace mod::lib::Rules { -struct PropStringCore; -struct PropMoleculeCore; - -struct DepictionDataCore { - - template - struct DepictionData { // instantiated in the cpp file - DepictionData(const DepictionDataCore &depict); - AtomId getAtomId(Vertex v) const; // shortcut to moleculeState - Isotope getIsotope(Vertex v) const; // shortcut to moleculeState - Charge getCharge(Vertex v) const; // shortcut to moleculeState - bool getRadical(Vertex v) const; // shortcut to moleculeState - BondType getBondData(Edge e) const; // shortcut to moleculeState - std::string getVertexLabelNoIsotopeChargeRadical(Vertex v) const; - std::string getEdgeLabel(Edge e) const; - const AtomData &operator()(Vertex v) const; // fake data - BondType operator()(Edge e) const; // fake data - bool hasImportantStereo(Vertex v) const; - bool getHasCoordinates() const; - double getX(Vertex v, bool withHydrogen) const; - double getY(Vertex v, bool withHydrogen) const; - lib::IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(Edge e, bool withHydrogen) const; - // private: - // bool isAtomIdInvalidContext(Vertex v) const; - private: - const DepictionDataCore &depict; - }; -public: - DepictionDataCore(const LabelledRule &lr); - DepictionDataCore(const DepictionDataCore&) = delete; - DepictionDataCore &operator=(const DepictionDataCore&) = delete; - const AtomData &operator()(Vertex v) const; // fake data, for creating OBMol - BondType operator()(Edge e) const; // fake data, for creating OBMol - bool hasImportantStereo(Vertex v) const; - bool getHasCoordinates() const; - double getX(Vertex v, bool withHydrogen) const; - double getY(Vertex v, bool withHydrogen) const; - // vMap: this -> other - void copyCoords(const DepictionDataCore &other, const std::map &vMap); -public: // projections - DepictionData getLeft() const; - DepictionData getContext() const; - DepictionData getRight() const; -private: - const LabelledRule &lr; - bool hasMoleculeEncoding, hasCoordinates; - std::map nonAtomToPhonyAtomLeft, nonAtomToPhonyAtomRight; - std::map phonyAtomToString; - std::map nonBondEdgesLeft, nonBondEdgesRight; - - struct CoordData { - std::vector x, y; -#ifdef MOD_HAVE_OPENBABEL - lib::Chem::OBMolHandle obMol; // the pushout, for generating coords - lib::Chem::OBMolHandle obMolLeft, obMolRight; // each side, with copied coords, for stereo -#endif - } cDataAll, cDataNoHydrogen; -}; - -} // namespace mod::lib::Rules - -#endif // MOD_LIB_RULES_STATE_DEPICTION_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Molecule.cpp b/libs/libmod/src/mod/lib/Rules/Properties/Molecule.cpp index bc6c6df..31c786e 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/Molecule.cpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/Molecule.cpp @@ -7,56 +7,42 @@ #include -namespace mod { -namespace lib { -namespace Rules { +namespace mod::lib::Rules { -PropMoleculeCore::PropMoleculeCore(const GraphType &g, const PropStringCore &labelState) : Base(g), isReaction(true) { - vertexState.resize(num_vertices(g)); - for(Vertex v : asRange(vertices(g))) { - auto decodeLabel = [this](const std::string & label) { - auto p = Chem::decodeVertexLabel(label); - if(std::get<0>(p) == AtomIds::Invalid) isReaction = false; - return AtomData(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p)); - }; - auto vId = get(boost::vertex_index_t(), g, v); - switch(g[v].membership) { - case Membership::Left: - vertexState[vId].left = decodeLabel(labelState.getLeft()[v]); - break; - case Membership::Right: - vertexState[vId].right = decodeLabel(labelState.getRight()[v]); - break; - case Membership::Context: - vertexState[vId].left = decodeLabel(labelState.getLeft()[v]); - vertexState[vId].right = decodeLabel(labelState.getRight()[v]); - break; - } +PropMolecule::PropMolecule(const RuleType &rule, const PropString &pString) + : Base(rule), isReaction(true) { + const auto decodeVLabel = [this](const std::string &label) { + const auto p = Chem::decodeVertexLabel(label); + if(std::get<0>(p) == AtomIds::Invalid) isReaction = false; + return AtomData(std::get<0>(p), std::get<1>(p), std::get<2>(p), std::get<3>(p)); + }; + const auto decodeELabel = [this](const std::string &label) { + const auto bondType = Chem::decodeEdgeLabel(label); + if(bondType == BondType::Invalid) isReaction = false; + return bondType; + }; + + vPropL.resize(num_vertices(getL(rule))); + vPropR.resize(num_vertices(getR(rule))); + for(const auto v: asRange(vertices(getL(rule)))) { + const auto vId = get(boost::vertex_index_t(), getL(rule), v); + vPropL[vId] = decodeVLabel(pString.getLeft()[v]); + } + for(const auto v: asRange(vertices(getR(rule)))) { + const auto vId = get(boost::vertex_index_t(), getR(rule), v); + vPropR[vId] = decodeVLabel(pString.getRight()[v]); } - edgeState.resize(num_edges(g)); - for(Edge e : asRange(edges(g))) { - auto decodeLabel = [this](const std::string & label) { - auto bondType = Chem::decodeEdgeLabel(label); - if(bondType == BondType::Invalid) isReaction = false; - return bondType; - }; - auto eId = get(boost::edge_index_t(), g, e); - switch(g[e].membership) { - case Membership::Left: - edgeState[eId].left = decodeLabel(labelState.getLeft()[e]); - break; - case Membership::Right: - edgeState[eId].right = decodeLabel(labelState.getRight()[e]); - break; - case Membership::Context: - edgeState[eId].left = decodeLabel(labelState.getLeft()[e]); - edgeState[eId].right = decodeLabel(labelState.getRight()[e]); - break; - } + ePropL.resize(num_edges(getL(rule))); + ePropR.resize(num_edges(getR(rule))); + for(const auto e: asRange(edges(getL(rule)))) { + const auto eId = get(boost::edge_index_t(), getL(rule), e); + ePropL[eId] = decodeELabel(pString.getLeft()[e]); + } + for(const auto e: asRange(edges(getR(rule)))) { + const auto eId = get(boost::edge_index_t(), getR(rule), e); + ePropR[eId] = decodeELabel(pString.getRight()[e]); } } -} // namespace Rules -} // namespace lib -} // namespace mod \ No newline at end of file +} // namespace mod::lib::Rules \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Molecule.hpp b/libs/libmod/src/mod/lib/Rules/Properties/Molecule.hpp index ae7e2fd..bdf74a2 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/Molecule.hpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/Molecule.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_LIB_RULES_PROP_MOLECULE_H -#define MOD_LIB_RULES_PROP_MOLECULE_H +#ifndef MOD_LIB_RULES_PROP_MOLECULE_HPP +#define MOD_LIB_RULES_PROP_MOLECULE_HPP #include #include @@ -8,27 +8,25 @@ namespace mod { struct AtomData; enum class BondType; -namespace lib { -namespace Rules { -struct PropStringCore; +} // namespace mod +namespace mod::lib::Rules { +struct PropString; -class PropMoleculeCore : private PropCore { - // read-only of data - using Base = PropCore; +struct PropMolecule : private PropBase { + // read-only of data, so do 'using' of things needed + using Base = PropBase; + using Base::Side; public: - using Base::LeftType; - using Base::RightType; + PropMolecule(const RuleType &rule, const PropString &pString); +public: // to be able to form pointers to getLeft and getRight it is not enough to 'using' them + Side getLeft() const { return {*this, vPropL, ePropL, getL(rule)}; } + Side getRight() const { return {*this, vPropR, ePropR, getR(rule)}; } public: - PropMoleculeCore(const GraphType &g, const PropStringCore &labelState); using Base::invert; - using Base::getLeft; - using Base::getRight; private: bool isReaction; }; -} // namespace Rules -} // namespace lib -} // namespace mod +} // namespace mod::lib::Rules -#endif /* MOD_LIB_RULES_STATE_MOLECULE_H */ +#endif // MOD_LIB_RULES_STATE_MOLECULE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Property.cpp b/libs/libmod/src/mod/lib/Rules/Properties/Property.cpp index cb3e3f0..b3ec297 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/Property.cpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/Property.cpp @@ -4,22 +4,41 @@ namespace mod::lib::Rules::detail { -void PropVerify(const void *g, const void *gOther, - std::size_t nGraph, std::size_t nOther, - std::size_t mGraph, std::size_t mOther) { - if(g != gOther) { - std::cout << "Different graphs: g = " << (std::uintptr_t) g - << ", &this->g = " << (std::uintptr_t) gOther << std::endl; +void PropVerify(const lib::DPO::CombinedRule &rule, + std::size_t vLSize, std::size_t vRSize, + std::size_t eLSize, std::size_t eRSize) { + const auto printStuff = [&]() { + std::cout << "|V(CG)| = " << num_vertices(rule.getCombinedGraph()) << '\t' + << "|E(CG)| = " << num_edges(rule.getCombinedGraph()) << std::endl; + std::cout << "|V(L)| = " << num_vertices(getL(rule)) << '\t' + << "|E(L)| = " << num_edges(getL(rule)) << std::endl; + std::cout << "|V(K)| = " << num_vertices(getK(rule)) << '\t' + << "|E(K)| = " << num_edges(getK(rule)) << std::endl; + std::cout << "|V(R)| = " << num_vertices(getR(rule)) << '\t' + << "|E(R)| = " << num_edges(getR(rule)) << std::endl; + std::cout << "|PV(L)| = " << vLSize << '\t' + << "|PE(L)| = " << eLSize << std::endl; + std::cout << "|PV(R)| = " << vRSize << '\t' + << "|PE(R)| = " << eRSize << std::endl; + }; + if(num_vertices(getL(rule)) != vLSize) { + std::cout << "Different |V(L)|:\n"; + printStuff(); MOD_ABORT; } - if(nGraph != nOther) { - std::cout << "Different sizes: num_vertices(this->g) = " << nGraph - << ", vertexLabels.size() = " << nOther << std::endl; + if(num_vertices(getR(rule)) != vRSize) { + std::cout << "Different |V(R)|:\n"; + printStuff(); MOD_ABORT; } - if(mGraph != mOther) { - std::cout << "Different sizes: num_edges(this->g) = " << mGraph - << ", edgeLabels.size() = " << mOther << std::endl; + if(num_edges(getL(rule)) != eLSize) { + std::cout << "Different |E(L)|:\n"; + printStuff(); + MOD_ABORT; + } + if(num_edges(getR(rule)) != eRSize) { + std::cout << "Different |E(R)|:\n"; + printStuff(); MOD_ABORT; } } diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Property.hpp b/libs/libmod/src/mod/lib/Rules/Properties/Property.hpp index 00ff229..8beb6a1 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/Property.hpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/Property.hpp @@ -2,94 +2,100 @@ #define MOD_LIB_RULES_PROP_HPP #include +#include +#include #include #include #include -#include #include +#include #include #include namespace mod::lib::Rules { -using jla_boost::GraphDPO::Membership; +using lib::DPO::Membership; -#define MOD_RULE_STATE_TEMPLATE_PARAMS \ - template -#define MOD_RULE_STATE_TEMPLATE_ARGS \ - Derived, Graph, LeftVertexType, LeftEdgeType, RightVertexType, RightEdgeType +#define MOD_RULE_PROP_TEMPLATE_PARAMS typename Derived, typename VertexProp, typename EdgeProp +#define MOD_RULE_PROP_TEMPLATE_ARGS Derived, VertexProp, EdgeProp -namespace detail { +template +struct PropBase { + using RuleType = lib::DPO::CombinedRule; +public: + using VertexProp = VertexPropT; + using EdgeProp = EdgePropT; +public: + struct Side { + using RuleType = lib::DPO::CombinedRule; + public: + const VertexProp &operator[](RuleType::SideVertex v) const; + const EdgeProp &operator[](RuleType::SideEdge e) const; -MOD_RULE_STATE_TEMPLATE_PARAMS -struct LeftState; + friend auto get(const Side &p, const RuleType::SideVertex &v) -> decltype(p[v]) { return p[v]; } -MOD_RULE_STATE_TEMPLATE_PARAMS -struct RightState; + friend auto get(const Side &p, const RuleType::SideEdge &e) -> decltype(p[e]) { return p[e]; } -} // namespace detail + public: + const PropBase &p; + const std::vector &pV; + const std::vector &pE; + const RuleType::SideGraphType &g; + public: + using Handler = IdentityPropertyHandler; + }; +public: + using ValueTypeVertex = std::pair, boost::optional>; + using ValueTypeEdge = std::pair, boost::optional>; +public: + void verify() const; + + explicit PropBase(const RuleType &rule) : rule(rule) {} + + Side getLeft() const { return {*this, vPropL, ePropL, getL(rule)}; } + + Side getRight() const { return {*this, vPropR, ePropR, getR(rule)}; } -template -struct PropCore { - using LeftVertexType = LeftVertexTypeT; - using LeftEdgeType = LeftEdgeTypeT; - using RightVertexType = RightVertexTypeT; - using RightEdgeType = RightEdgeTypeT; - using Vertex = typename boost::graph_traits::vertex_descriptor; - using Edge = typename boost::graph_traits::edge_descriptor; - using LeftType = detail::LeftState; - using RightType = detail::RightState; - friend class detail::LeftState; - friend class detail::RightState; - using ValueTypeVertex = std::pair, boost::optional >; - using ValueTypeEdge = std::pair, boost::optional >; public: - void verify(const Graph *g) const; - explicit PropCore(const Graph &g); - ValueTypeVertex operator[](Vertex v) const; - ValueTypeEdge operator[](Edge e) const; - LeftType getLeft() const; - RightType getRight() const; void invert(); - void add(Vertex v, const LeftVertexType &valueLeft, const RightVertexType &valueRight); - void add(Edge e, const LeftEdgeType &valueLeft, const RightEdgeType &valueRight); +public: // vertex + void addL(RuleType::SideVertex v, VertexProp p); + void addR(RuleType::SideVertex v, VertexProp p); + void addK(RuleType::KVertex v, VertexProp pL, VertexProp pR); + void promoteL(RuleType::SideVertex vL, RuleType::SideVertex vR, VertexProp pR); +public: // edge + void addL(RuleType::SideEdge e, EdgeProp p); + void addR(RuleType::SideEdge e, EdgeProp p); + void addK(RuleType::KEdge e, EdgeProp pL, EdgeProp pR); +public: + ValueTypeVertex operator[](RuleType::CombinedVertex v) const; + ValueTypeEdge operator[](RuleType::CombinedEdge e) const; +public: + friend auto get(const PropBase &p, RuleType::CombinedVertex v) -> decltype(p[v]) { return p[v]; } + + friend auto get(const PropBase &p, RuleType::CombinedEdge e) -> decltype(p[e]) { return p[e]; } + +public: // old stuff not yet fully evaluated + void add(RuleType::CombinedVertex v, const VertexProp &valueLeft, const VertexProp &valueRight); + void add(RuleType::CombinedEdge e, const EdgeProp &valueLeft, const EdgeProp &valueRight); // does not modify the other side - void setLeft(Vertex v, const LeftVertexType &value); - void setRight(Vertex v, const RightVertexType &value); - void setLeft(Edge e, const LeftEdgeType &value); - void setRight(Edge e, const RightEdgeType &value); - bool isChanged(Vertex v) const; - bool isChanged(Edge e) const; - void print(std::ostream &s, Vertex v) const; - void print(std::ostream &s, Edge e) const; + void setLeft(RuleType::CombinedVertex v, const VertexProp &value); + void setRight(RuleType::CombinedVertex v, const VertexProp &value); + void setLeft(RuleType::CombinedEdge e, const EdgeProp &value); + void setRight(RuleType::CombinedEdge e, const EdgeProp &value); +public: // is updated + bool isChanged(RuleType::CombinedVertex v) const; + bool isChanged(RuleType::CombinedEdge e) const; + void print(std::ostream &s, RuleType::CombinedVertex v) const; + void print(std::ostream &s, RuleType::CombinedEdge e) const; const Derived &getDerived() const; protected: - const Graph &g; + const RuleType &rule; protected: - struct VertexStore { - VertexStore() = default; - VertexStore(const LeftVertexType &left, const RightVertexType &right) : left(left), right(right) {} - VertexStore(LeftVertexType &&left, RightVertexType &&right) : left(std::move(left)), right(std::move(right)) {} - public: - LeftVertexType left; - RightVertexType right; - }; - - struct EdgeStore { - EdgeStore() = default; - EdgeStore(const LeftEdgeType &left, const RightEdgeType &right) : left(left), right(right) {} - public: - LeftEdgeType left; - RightEdgeType right; - }; - - std::vector vertexState; - std::vector edgeState; + std::vector vPropL, vPropR; + std::vector ePropL, ePropR; public: struct Handler { template @@ -119,230 +125,277 @@ struct PropCore { }; }; -template -auto get(const PropCore &p, VertexOrEdge ve) -> decltype(p[ve]) { - return p[ve]; -} +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ -namespace detail { +template +const VertexProp &PropBase::Side::operator[](RuleType::SideVertex v) const { + const auto vId = get(boost::vertex_index_t(), g, v); + assert(vId < pV.size()); + return pV[vId]; +} -MOD_RULE_STATE_TEMPLATE_PARAMS -struct LeftState { - LeftState(const PropCore &state) : state(state) {} +template +const EdgeProp &PropBase::Side::operator[](RuleType::SideEdge e) const { + const auto eId = get(boost::edge_index_t(), g, e); + assert(eId < pE.size()); + return pE[eId]; +} - const LeftVertexType &operator[](typename boost::graph_traits::vertex_descriptor v) const { - auto vId = get(boost::vertex_index_t(), state.g, v); - assert(vId < state.vertexState.size()); - assert(state.g[v].membership != Membership::Right); - return state.vertexState[vId].left; - } +namespace detail { - const LeftEdgeType &operator[](typename boost::graph_traits::edge_descriptor e) const { - auto eId = get(boost::edge_index_t(), state.g, e); - assert(eId < state.edgeState.size()); - assert(state.g[e].membership != Membership::Right); - return state.edgeState[eId].left; - } -public: - const PropCore &state; -public: - using Handler = IdentityPropertyHandler; -}; +void PropVerify(const lib::DPO::CombinedRule &rule, + std::size_t vLSize, std::size_t vRSize, + std::size_t eLSize, std::size_t eRSize); -MOD_RULE_STATE_TEMPLATE_PARAMS -struct RightState { - RightState(const PropCore &state) : state(state) {} +} // namespace detail - const RightVertexType &operator[](typename boost::graph_traits::vertex_descriptor v) const { - auto vId = get(boost::vertex_index_t(), state.g, v); - assert(vId < state.vertexState.size()); - assert(state.g[v].membership != Membership::Left); - return state.vertexState[vId].right; - } +template +void PropBase::verify() const { + // dispatch to a non-templated function so the iostream stuff is not in a header + detail::PropVerify(rule, vPropL.size(), vPropR.size(), ePropL.size(), ePropR.size()); +} - const RightEdgeType &operator[](typename boost::graph_traits::edge_descriptor e) const { - auto eId = get(boost::edge_index_t(), state.g, e); - assert(eId < state.edgeState.size()); - assert(state.g[e].membership != Membership::Left); - return state.edgeState[eId].right; - } -public: - const PropCore &state; -public: - using Handler = IdentityPropertyHandler; -}; +// =============================================================================================== -template -auto get(const LeftState &p, const VertexOrEdge &ve) -> decltype(p[ve]) { - return p[ve]; +template +void PropBase::invert() { + using std::swap; + swap(vPropL, vPropR); + swap(ePropL, ePropR); } -template -auto get(const RightState &p, const VertexOrEdge &ve) -> decltype(p[ve]) { - return p[ve]; +template +void PropBase::addL(RuleType::SideVertex v, VertexProp p) { + assert(num_vertices(getL(rule)) == vPropL.size() + 1); + assert(get(boost::vertex_index_t(), getL(rule), v) == vPropL.size()); + vPropL.push_back(std::move(p)); + vPropR.emplace_back(); + verify(); } -} // namespace detail - -//------------------------------------------------------------------------------ -// Implementation -//------------------------------------------------------------------------------ +template +void PropBase::addR(RuleType::SideVertex v, VertexProp p) { + assert(num_vertices(getR(rule)) == vPropR.size() + 1); + assert(get(boost::vertex_index_t(), getR(rule), v) == vPropR.size()); + vPropL.emplace_back(); + vPropR.push_back(std::move(p)); + verify(); +} -namespace detail { -void PropVerify(const void *g, const void *gOther, - std::size_t nGraph, std::size_t nOther, - std::size_t mGraph, std::size_t mOther); -} // namespace detail +template +void PropBase::addK(RuleType::KVertex vK, VertexProp pL, VertexProp pR) { + assert(num_vertices(getL(rule)) == vPropL.size() + 1); + assert(num_vertices(getR(rule)) == vPropR.size() + 1); + assert(get(boost::vertex_index_t(), getL(rule), + get(getMorL(rule), getK(rule), getL(rule), vK)) + == vPropL.size()); + assert(get(boost::vertex_index_t(), getR(rule), + get(getMorR(rule), getK(rule), getR(rule), vK)) + == vPropR.size()); + vPropL.push_back(std::move(pL)); + vPropR.push_back(std::move(pR)); + verify(); +} -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::verify(const Graph *g) const { - detail::PropVerify(g, &this->g, num_vertices(*g), vertexState.size(), num_edges(*g), edgeState.size()); +template +void PropBase::promoteL(RuleType::SideVertex vL, RuleType::SideVertex vR, VertexProp pR) { + assert(vL == vR); + const auto vRId = get(boost::vertex_index_t(), getR(rule), vR); + vPropR[vRId] = std::move(pR); + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -PropCore::PropCore(const Graph &g) : g(g) {} - -MOD_RULE_STATE_TEMPLATE_PARAMS -std::pair, boost::optional > -PropCore::operator[](typename boost::graph_traits::vertex_descriptor v) const { - assert(v != boost::graph_traits::null_vertex()); - boost::optional l; - boost::optional r; - if(g[v].membership != Membership::Right) l = getLeft()[v]; - if(g[v].membership != Membership::Left) r = getRight()[v]; - return std::make_pair(l, r); +template +void PropBase::addL(RuleType::SideEdge e, EdgeProp p) { + assert(num_edges(getL(rule)) == ePropL.size() + 1); + assert(get(boost::edge_index_t(), getL(rule), e) == ePropL.size()); + ePropL.push_back(std::move(p)); + ePropR.emplace_back(); + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -std::pair, boost::optional > -PropCore::operator[](typename boost::graph_traits::edge_descriptor e) const { - boost::optional l; - boost::optional r; - if(g[e].membership != Membership::Right) l = getLeft()[e]; - if(g[e].membership != Membership::Left) r = getRight()[e]; - return std::make_pair(l, r); +template +void PropBase::addR(RuleType::SideEdge e, EdgeProp p) { + assert(num_edges(getR(rule)) == ePropR.size() + 1); + assert(get(boost::edge_index_t(), getR(rule), e) == ePropR.size()); + ePropL.emplace_back(); + ePropR.push_back(std::move(p)); + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -typename detail::LeftState -PropCore::getLeft() const { - return detail::LeftState(*this); +template +void PropBase::addK(RuleType::KEdge eK, EdgeProp pL, EdgeProp pR) { + assert(num_edges(getL(rule)) == ePropL.size() + 1); + assert(num_edges(getR(rule)) == ePropR.size() + 1); + assert(get(boost::edge_index_t(), getL(rule), + get(getMorL(rule), getK(rule), getL(rule), eK)) + == ePropL.size()); + assert(get(boost::edge_index_t(), getR(rule), + get(getMorR(rule), getK(rule), getR(rule), eK)) + == ePropR.size()); + ePropL.push_back(std::move(pL)); + ePropR.push_back(std::move(pR)); + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -typename detail::RightState -PropCore::getRight() const { - return detail::RightState(*this); +// =============================================================================================== + +template +std::pair, boost::optional> +PropBase::operator[](RuleType::CombinedVertex v) const { + assert(v != boost::graph_traits::null_vertex()); + boost::optional l; + boost::optional r; + if(rule.getCombinedGraph()[v].membership != Membership::R) + l = getLeft()[get_inverse(rule.getLtoCG(), getL(rule), rule.getCombinedGraph(), v)]; + if(rule.getCombinedGraph()[v].membership != Membership::L) + r = getRight()[get_inverse(rule.getRtoCG(), getR(rule), rule.getCombinedGraph(), v)]; + return {l, r}; } -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::invert() { - using std::swap; - for(auto &vs : vertexState) swap(vs.left, vs.right); - for(auto &es : edgeState) swap(es.left, es.right); +template +std::pair, boost::optional> +PropBase::operator[](RuleType::CombinedEdge e) const { + boost::optional l; + boost::optional r; + const auto &cg = rule.getCombinedGraph(); + if(cg[e].membership != Membership::R) + l = getLeft()[get_inverse(rule.getLtoCG(), getL(rule), cg, e)]; + if(cg[e].membership != Membership::L) + r = getRight()[get_inverse(rule.getRtoCG(), getR(rule), cg, e)]; + return {l, r}; } -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::add(Vertex v, - const LeftVertexType &valueLeft, - const RightVertexType &valueRight) { - assert(num_vertices(g) == vertexState.size() + 1); - assert(get(boost::vertex_index_t(), g, v) == vertexState.size()); - vertexState.emplace_back(valueLeft, valueRight); - verify(&g); + +template +void PropBase::add(RuleType::CombinedVertex v, const VertexProp &valueLeft, + const VertexProp &valueRight) { + assert(num_vertices(rule.getCombinedGraph()) == vPropL.size() + 1); + assert(num_vertices(rule.getCombinedGraph()) == vPropR.size() + 1); + assert(get(boost::vertex_index_t(), rule.getCombinedGraph(), v) == vPropL.size()); + assert(get(boost::vertex_index_t(), rule.getCombinedGraph(), v) == vPropR.size()); + vPropL.push_back(valueLeft); + vPropR.push_back(valueRight); + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -void -PropCore::add(Edge e, const LeftEdgeType &valueLeft, const RightEdgeType &valueRight) { - assert(num_edges(g) == edgeState.size() + 1); - assert(get(boost::edge_index_t(), g, e) == edgeState.size()); - edgeState.emplace_back(valueLeft, valueRight); - verify(&g); +template +void PropBase::add(RuleType::CombinedEdge e, const EdgeProp &valueLeft, + const EdgeProp &valueRight) { + assert(num_edges(rule.getCombinedGraph()) == ePropL.size() + 1); + assert(num_edges(rule.getCombinedGraph()) == ePropR.size() + 1); + assert(get(boost::edge_index_t(), rule.getCombinedGraph(), e) == ePropL.size()); + assert(get(boost::edge_index_t(), rule.getCombinedGraph(), e) == ePropR.size()); + ePropL.push_back(valueLeft); + ePropR.push_back(valueRight); + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::setLeft(Vertex v, const LeftVertexType &value) { - auto vId = get(boost::vertex_index_t(), g, v); - assert(vId < vertexState.size()); - assert(g[v].membership != Membership::Right); - vertexState[vId].left = value; - verify(&g); +template +void PropBase::setLeft(RuleType::CombinedVertex v, const VertexProp &value) { + const auto vId = get(boost::vertex_index_t(), rule.getCombinedGraph(), v); + assert(vId < vPropL.size()); + assert(rule.getCombinedGraph()[v].membership != Membership::R); + vPropL[vId] = value; + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::setRight(Vertex v, const RightVertexType &value) { - auto vId = get(boost::vertex_index_t(), g, v); - assert(vId < vertexState.size()); - assert(g[v].membership != Membership::Left); - vertexState[vId].right = value; - verify(&g); +template +void PropBase::setRight(RuleType::CombinedVertex v, const VertexProp &value) { + const auto vId = get(boost::vertex_index_t(), rule.getCombinedGraph(), v); + assert(vId < vPropR.size()); + assert(rule.getCombinedGraph()[v].membership != Membership::L); + vPropR[vId] = value; + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::setLeft(Edge e, const LeftEdgeType &value) { - auto eId = get(boost::edge_index_t(), g, e); - assert(eId < edgeState.size()); - assert(g[e].membership != Membership::Right); - edgeState[eId].left = value; - verify(&g); +template +void PropBase::setLeft(RuleType::CombinedEdge e, const EdgeProp &value) { + const auto eId = get(boost::edge_index_t(), rule.getCombinedGraph(), e); + assert(eId < ePropL.size()); + assert(rule.getCombinedGraph()[e].membership != Membership::R); + ePropL[eId] = value; + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::setRight(Edge e, const RightEdgeType &value) { - auto eId = get(boost::edge_index_t(), g, e); - assert(eId < edgeState.size()); - assert(g[e].membership != Membership::Left); - edgeState[eId].right = value; - verify(&g); +template +void PropBase::setRight(RuleType::CombinedEdge e, const EdgeProp &value) { + const auto eId = get(boost::edge_index_t(), rule.getCombinedGraph(), e); + assert(eId < ePropR.size()); + assert(rule.getCombinedGraph()[e].membership != Membership::L); + ePropR[eId] = value; + verify(); } -MOD_RULE_STATE_TEMPLATE_PARAMS -bool PropCore::isChanged(Vertex v) const { - if(g[v].membership != Membership::Context) return false; - auto vId = get(boost::vertex_index_t(), g, v); - assert(vId < vertexState.size()); - return vertexState[vId].left != vertexState[vId].right; +template +bool PropBase::isChanged(RuleType::CombinedVertex v) const { + if(rule.getCombinedGraph()[v].membership != Membership::K) return false; + const auto vL = get_inverse(rule.getLtoCG(), getL(rule), rule.getCombinedGraph(), v); + const auto vR = get_inverse(rule.getRtoCG(), getR(rule), rule.getCombinedGraph(), v); + const auto vLid = get(boost::vertex_index_t(), getL(rule), vL); + const auto vRid = get(boost::vertex_index_t(), getR(rule), vR); + assert(vLid < vPropL.size()); + assert(vRid < vPropR.size()); + return vPropL[vLid] != vPropR[vRid]; } -MOD_RULE_STATE_TEMPLATE_PARAMS -bool PropCore::isChanged(Edge e) const { - if(g[e].membership != Membership::Context) return false; - auto eId = get(boost::edge_index_t(), g, e); - assert(eId < edgeState.size()); - return edgeState[eId].left != edgeState[eId].right; +template +bool PropBase::isChanged(RuleType::CombinedEdge e) const { + if(rule.getCombinedGraph()[e].membership != Membership::K) return false; + const auto eL = get_inverse(rule.getLtoCG(), getL(rule), rule.getCombinedGraph(), e); + const auto eR = get_inverse(rule.getRtoCG(), getR(rule), rule.getCombinedGraph(), e); + const auto eLid = get(boost::edge_index_t(), getL(rule), eL); + const auto eRid = get(boost::edge_index_t(), getR(rule), eR); + assert(eLid < ePropL.size()); + assert(eRid < ePropR.size()); + return ePropL[eLid] != ePropR[eRid]; } -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::print(std::ostream &s, Vertex v) const { - auto vId = get(boost::vertex_index_t(), g, v); - assert(vId < vertexState.size()); - auto membership = g[v].membership; - if(membership == Membership::Right) s << "<>"; - else s << '\'' << vertexState[vId].left << '\''; +template +void PropBase::print(std::ostream &s, RuleType::CombinedVertex v) const { + const auto membership = rule.getCombinedGraph()[v].membership; + if(membership == Membership::R) s << "<>"; + else { + const auto vL = get_inverse(rule.getLtoCG(), getL(rule), rule.getCombinedGraph(), v); + const auto vLid = get(boost::vertex_index_t(), getL(rule), vL); + assert(vLid < vPropL.size()); + s << '\'' << vPropL[vLid] << '\''; + } s << " -> "; - if(membership == Membership::Left) s << "<>"; - else s << '\'' << vertexState[vId].right << '\''; + if(membership == Membership::L) s << "<>"; + else { + const auto vR = get_inverse(rule.getRtoCG(), getR(rule), rule.getCombinedGraph(), v); + const auto vRid = get(boost::vertex_index_t(), getR(rule), vR); + assert(vRid < vPropR.size()); + s << '\'' << vPropR[vRid] << '\''; + } } -MOD_RULE_STATE_TEMPLATE_PARAMS -void PropCore::print(std::ostream &s, Edge e) const { - auto eId = get(boost::edge_index_t(), g, e); - assert(eId < edgeState.size()); - auto membership = g[e].membership; - if(membership == Membership::Right) s << "<>"; - else s << '\'' << edgeState[eId].left << '\''; +template +void PropBase::print(std::ostream &s, RuleType::CombinedEdge e) const { + const auto membership = rule.getCombinedGraph()[e].membership; + if(membership == Membership::R) s << "<>"; + else { + const auto eL = get_inverse(rule.getLtoCG(), getL(rule), rule.getCombinedGraph(), e); + const auto eLid = get(boost::edge_index_t(), getL(rule), eL); + assert(eLid < ePropL.size()); + s << '\'' << ePropL[eLid] << '\''; + } s << " -> "; - if(membership == Membership::Left) s << "<>"; - else s << '\'' << edgeState[eId].right << '\''; + if(membership == Membership::L) s << "<>"; + else { + const auto eR = get_inverse(rule.getRtoCG(), getR(rule), rule.getCombinedGraph(), e); + const auto eRid = get(boost::edge_index_t(), getR(rule), eR); + assert(eRid < ePropR.size()); + s << '\'' << ePropR[eRid] << '\''; + } } -MOD_RULE_STATE_TEMPLATE_PARAMS -const Derived &PropCore::getDerived() const { - return static_cast (*this); +template +const Derived &PropBase::getDerived() const { + return static_cast(*this); } } // namespace mod::lib::Rules diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Stereo.hpp b/libs/libmod/src/mod/lib/Rules/Properties/Stereo.hpp index 26c816f..9fd684d 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/Stereo.hpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/Stereo.hpp @@ -10,52 +10,43 @@ namespace mod::lib::Rules { -struct PropStereoCore - : private PropCore, lib::Stereo::EdgeCategory> { +struct PropStereo + : private PropBase, lib::Stereo::EdgeCategory> { // read-only of data - using Base = PropCore, lib::Stereo::EdgeCategory>; - using Base::LeftVertexType; - using Base::LeftEdgeType; - using Base::RightVertexType; - using Base::RightEdgeType; - using Base::LeftType; - using Base::RightType; + using Base = PropBase, lib::Stereo::EdgeCategory>; + using Base::VertexProp; + using Base::EdgeProp; + using Base::Side; struct ValueTypeVertex { - boost::optional left; - boost::optional right; + boost::optional left, right; bool inContext; }; struct ValueTypeEdge { - std::optional left; - std::optional right; + std::optional left, right; bool inContext; }; public: template - PropStereoCore(const GraphType &g, InferenceLeft &&leftInference, InferenceRight &&rightInference, - VertexInContext vCallback, EdgeInContext eCallback) : Base(g) { + PropStereo(const RuleType &rule, InferenceLeft &&leftInference, InferenceRight &&rightInference, + VertexInContext vCallback, EdgeInContext eCallback) : Base(rule) { // The eCallback is only responsible for the information about being in context. // This function will ensure that it's valid to put in context as well. // However, the vCallback is also responsible for ensuring that it is valid to put it in context. // This function only checks what is easy, i.e., whether the vertex and its incident edges are in context. - vertexState.reserve(num_vertices(g)); - vertexInContext.reserve(num_vertices(g)); - for(const auto v : asRange(vertices(g))) { - assert(get(boost::vertex_index_t(), g, v) == vertexState.size()); - std::unique_ptr l, r; - if(g[v].membership != Membership::Right) l = leftInference.extractConfiguration(v); - if(g[v].membership != Membership::Left) r = rightInference.extractConfiguration(v); - { // verify + const auto handleVertices = [](const lib::DPO::CombinedRule::SideGraphType &g, + std::vector &vProp, auto &inference) { + for(const auto vS: asRange(vertices(g))) { + assert(get(boost::vertex_index_t(), g, vS) >= vProp.size()); + vProp.resize(get(boost::vertex_index_t(), g, vS)); + assert(get(boost::vertex_index_t(), g, vS) == vProp.size()); + auto p = inference.extractConfiguration(vS); + { // verify #ifndef NDEBUG - const auto verify = [&g, &v](const lib::Stereo::Configuration &conf, const auto m) { - const auto oe = out_edges(v, g); - const auto d = std::count_if(oe.first, oe.second, [&g, m](const auto &e) { - return g[e].membership != m; - }); + const auto d = degree(vS, g); int dConf = 0; - for(const auto &emb : conf) { + for(const auto &emb: *p) { if(emb.type != lib::Stereo::EmbeddingEdge::Type::Edge) { assert(emb.offset >= d); } else { // edge @@ -64,65 +55,94 @@ struct PropStereoCore }; } assert(dConf == d); - }; - if(l) verify(*l, Membership::Right); - if(r) verify(*r, Membership::Left); - assert(bool(l) || bool(r)); #endif - } // end verify - vertexState.emplace_back(std::move(l), std::move(r)); + } // end verify + vProp.push_back(std::move(p)); + } + }; + vPropL.reserve(num_vertices(getL(rule))); + vPropR.reserve(num_vertices(getR(rule))); + vertexInContext.reserve(num_vertices(rule.getCombinedGraph())); + handleVertices(getL(rule), vPropL, leftInference); + handleVertices(getR(rule), vPropR, rightInference); + assert(vPropL.size() <= num_vertices(rule.getCombinedGraph())); + assert(vPropR.size() <= num_vertices(rule.getCombinedGraph())); + vPropL.resize(num_vertices(rule.getCombinedGraph())); + vPropR.resize(num_vertices(rule.getCombinedGraph())); + + const auto &cg = rule.getCombinedGraph(); + for(const auto v: asRange(vertices(cg))) { const bool inContext = [&]() { - if(g[v].membership != Membership::Context) return false; + if(cg[v].membership != Membership::K) return false; if(!vCallback(v)) return false; - for(const auto eOut : asRange(out_edges(v, g))) { - if(g[eOut].membership != Membership::Context) return false; - } - assert(bool(vertexState.back().left)); - assert(bool(vertexState.back().right)); + for(const auto eOut: asRange(out_edges(v, rule.getCombinedGraph()))) + if(cg[eOut].membership != Membership::K) + return false; return true; }(); vertexInContext.push_back(inContext); } - edgeState.reserve(num_edges(g)); - edgeInContext.reserve(num_edges(g)); - for(const auto e : asRange(edges(g))) { - assert(get(boost::edge_index_t(), g, e) == edgeState.size()); - lib::Stereo::EdgeCategory - lCat = static_cast(-1), - rCat = static_cast(-1); - if(g[e].membership != Membership::Right) lCat = leftInference.getEdgeCategory(e); - if(g[e].membership != Membership::Left) rCat = rightInference.getEdgeCategory(e); - edgeState.emplace_back(lCat, rCat); - const auto m = g[e].membership; - const bool inContext = - m == Membership::Context - && lCat == rCat - && eCallback(e); + + const auto handleEdges = [](const lib::DPO::CombinedRule::SideGraphType &g, + std::vector &eProp, auto &inference) { + for(const auto eS: asRange(edges(g))) { + assert(get(boost::edge_index_t(), g, eS) >= eProp.size()); + eProp.resize(get(boost::edge_index_t(), g, eS)); + assert(get(boost::edge_index_t(), g, eS) == eProp.size()); + auto cat = inference.getEdgeCategory(eS); + eProp.push_back(cat); + } + }; + ePropL.reserve(num_edges(cg)); + ePropR.reserve(num_edges(cg)); + edgeInContext.reserve(num_edges(cg)); + handleEdges(getL(rule), ePropL, leftInference); + handleEdges(getR(rule), ePropR, rightInference); + assert(ePropL.size() <= num_edges(rule.getCombinedGraph())); + assert(ePropR.size() <= num_edges(rule.getCombinedGraph())); + ePropL.resize(num_edges(rule.getCombinedGraph())); + ePropR.resize(num_edges(rule.getCombinedGraph())); + + for(const auto e: asRange(edges(cg))) { + const bool inContext = [&]() { + const auto m = cg[e].membership; + if(m != Membership::K) return false; + const auto eL = get(getMorL(rule), getK(rule), getL(rule), e); + const auto eR = get(getMorR(rule), getK(rule), getR(rule), e); + const auto catL = ePropL[get(boost::edge_index_t(), getL(rule), eL)]; + const auto catR = ePropR[get(boost::edge_index_t(), getR(rule), eR)]; + if(catL != catR) return false; + return eCallback(e); + }(); edgeInContext.push_back(inContext); } - verify(&g); + verify(); } - ValueTypeVertex operator[](Vertex v) const { - assert(v != boost::graph_traits::null_vertex()); + ValueTypeVertex operator[](lib::DPO::CombinedRule::CombinedVertex v) const { + assert(v != boost::graph_traits::null_vertex()); ValueTypeVertex res; - if(g[v].membership != Membership::Right) res.left = getLeft()[v]; - if(g[v].membership != Membership::Left) res.right = getRight()[v]; + if(rule.getCombinedGraph()[v].membership != Membership::R) res.left = getLeft()[v]; + if(rule.getCombinedGraph()[v].membership != Membership::L) res.right = getRight()[v]; res.inContext = inContext(v); return res; } - ValueTypeEdge operator[](Edge e) const { + ValueTypeEdge operator[](lib::DPO::CombinedRule::CombinedEdge e) const { ValueTypeEdge res; - if(g[e].membership != Membership::Right) res.left = getLeft()[e]; - if(g[e].membership != Membership::Left) res.right = getRight()[e]; + if(rule.getCombinedGraph()[e].membership != Membership::R) res.left = getLeft()[e]; + if(rule.getCombinedGraph()[e].membership != Membership::L) res.right = getRight()[e]; res.inContext = inContext(e); return res; } +public: // to be able to form pointers to getLeft and getRight it is not enough to 'using' them + Side getLeft() const { return {*this, vPropL, ePropL, getL(rule)}; } + Side getRight() const { return {*this, vPropR, ePropR, getR(rule)}; } +public: // we have redefined operator[], so get should be redefined as well + friend auto get(const PropStereo &p, RuleType::CombinedVertex v) -> decltype(p[v]) { return p[v]; } + friend auto get(const PropStereo &p, RuleType::CombinedEdge e) -> decltype(p[e]) { return p[e]; } public: using Base::verify; - using Base::getLeft; - using Base::getRight; using Base::print; struct Handler { @@ -170,22 +190,17 @@ struct PropStereoCore } }; public: - bool inContext(Vertex v) const { - return vertexInContext[get(boost::vertex_index_t(), g, v)]; + bool inContext(lib::DPO::CombinedRule::CombinedVertex v) const { + return vertexInContext[get(boost::vertex_index_t(), rule.getCombinedGraph(), v)]; } - bool inContext(Edge e) const { - return edgeInContext[get(boost::edge_index_t(), g, e)]; + bool inContext(lib::DPO::CombinedRule::CombinedEdge e) const { + return edgeInContext[get(boost::edge_index_t(), rule.getCombinedGraph(), e)]; } private: std::vector vertexInContext, edgeInContext; }; -template -auto get(const PropStereoCore &p, const VertexOrEdge &ve) -> decltype(p[ve]) { - return p[ve]; -} - } // namespace mod::lib::Rules #endif // MOD_LIB_RULE_PROP_STEREO_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/Properties/String.cpp b/libs/libmod/src/mod/lib/Rules/Properties/String.cpp index 1a36999..0b8c1f8 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/String.cpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/String.cpp @@ -1,65 +1,67 @@ #include "String.hpp" #include -#include #include #include +#include -namespace mod { -namespace lib { -namespace Rules { +namespace mod::lib::Rules { -PropStringCore::PropStringCore(const GraphType &g) : PropCore(g) { - verify(&g); +PropString::PropString(const RuleType &rule) : PropBase(rule) { + verify(); } -PropStringCore::PropStringCore(const GraphType &g, - const std::vector &leftMatchConstraints, - const std::vector &rightMatchConstraints, - const PropTermCore &term, const StringStore &strings) : PropCore(g) { +PropString::PropString(const RuleType &rule, + const std::vector &leftMatchConstraints, + const std::vector &rightMatchConstraints, + const PropTerm &term, const StringStore &strings) + : PropBase(rule) { std::stringstream ss; const auto termToString = [&ss, &term, &strings](std::size_t addr) -> std::string { ss.str(std::string()); - lib::IO::Term::Write::term(getMachine(term), { - lib::Term::AddressType::Heap, addr + Term::Write::term(getMachine(term), { + Term::AddressType::Heap, addr }, strings, ss); return ss.str(); }; - vertexState.resize(num_vertices(g)); - for(auto v : asRange(vertices(g))) { - auto vId = get(boost::vertex_index_t(), g, v); - switch(g[v].membership) { - case Membership::Left: - vertexState[vId].left = termToString(term.getLeft()[v]); + vPropL.resize(num_vertices(rule.getCombinedGraph())); + vPropR.resize(num_vertices(rule.getCombinedGraph())); + for(auto v: asRange(vertices(rule.getCombinedGraph()))) { + auto vId = get(boost::vertex_index_t(), rule.getCombinedGraph(), v); + switch(rule.getCombinedGraph()[v].membership) { + case Membership::L: + vPropL[vId] = termToString(term.getLeft()[v]); break; - case Membership::Right: - vertexState[vId].right = termToString(term.getRight()[v]); + case Membership::R: + vPropR[vId] = termToString(term.getRight()[v]); break; - case Membership::Context: - vertexState[vId].left = termToString(term.getLeft()[v]); - vertexState[vId].right = termToString(term.getRight()[v]); + case Membership::K: + vPropL[vId] = termToString(term.getLeft()[v]); + vPropR[vId] = termToString(term.getRight()[v]); break; } } - edgeState.resize(num_edges(g)); - for(auto e : asRange(edges(g))) { - auto eId = get(boost::edge_index_t(), g, e); - switch(g[e].membership) { - case Membership::Left: - edgeState[eId].left = termToString(term.getLeft()[e]); + ePropL.resize(num_edges(rule.getCombinedGraph())); + ePropR.resize(num_edges(rule.getCombinedGraph())); + for(auto e: asRange(edges(rule.getCombinedGraph()))) { + auto eId = get(boost::edge_index_t(), rule.getCombinedGraph(), e); + switch(rule.getCombinedGraph()[e].membership) { + case Membership::L: + ePropL[eId] = termToString(term.getLeft()[e]); break; - case Membership::Right: - edgeState[eId].right = termToString(term.getRight()[e]); + case Membership::R: + ePropR[eId] = termToString(term.getRight()[e]); break; - case Membership::Context: - edgeState[eId].left = termToString(term.getLeft()[e]); - edgeState[eId].right = termToString(term.getRight()[e]); + case Membership::K: + ePropL[eId] = termToString(term.getLeft()[e]); + ePropR[eId] = termToString(term.getRight()[e]); break; } } using HandlerType = decltype(termToString); + using SideGraphType = lib::DPO::CombinedRule::SideGraphType; struct Visitor : lib::GraphMorphism::Constraints::AllVisitorNonConst { Visitor(HandlerType &termToString) : termToString(termToString) {} @@ -67,9 +69,9 @@ PropStringCore::PropStringCore(const GraphType &g, virtual void operator()(lib::GraphMorphism::Constraints::VertexAdjacency &c) override { assert(c.vertexLabels.size() == 0); assert(c.edgeLabels.size() == 0); - for(const auto &t : c.vertexTerms) + for(const auto &t: c.vertexTerms) c.vertexLabels.insert(termToString(t)); - for(const auto &t : c.edgeTerms) + for(const auto &t: c.edgeTerms) c.edgeLabels.insert(termToString(t)); } @@ -80,12 +82,10 @@ PropStringCore::PropStringCore(const GraphType &g, const auto handleConstraints = [&](const auto &cs) { Visitor vis(termToString); - for(auto &c : cs) c->accept(vis); + for(auto &c: cs) c->accept(vis); }; handleConstraints(leftMatchConstraints); handleConstraints(rightMatchConstraints); } -} // namespace Rules -} // namespace lib -} // namespace mod \ No newline at end of file +} // namespace mod::lib::Rules \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/Properties/String.hpp b/libs/libmod/src/mod/lib/Rules/Properties/String.hpp index b5c5472..4aaa568 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/String.hpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/String.hpp @@ -1,28 +1,26 @@ -#ifndef MOD_LIB_RULES_PROP_STRING_H -#define MOD_LIB_RULES_PROP_STRING_H +#ifndef MOD_LIB_RULES_PROP_STRING_HPP +#define MOD_LIB_RULES_PROP_STRING_HPP #include #include #include -namespace mod { -namespace lib { +namespace mod::lib { struct StringStore; -namespace Rules { -struct PropTermCore; +} // namespace mod::lib +namespace mod::lib::Rules { +struct PropTerm; -struct PropStringCore : PropCore { - using ConstraintPtr = std::unique_ptr >; +struct PropString : PropBase { + using ConstraintPtr = std::unique_ptr>; public: - explicit PropStringCore(const GraphType &g); - PropStringCore(const GraphType &g, - const std::vector &leftMatchConstraints, - const std::vector &rightMatchConstraints, - const PropTermCore &term, const StringStore &strings); + explicit PropString(const RuleType &rule); + PropString(const RuleType &rule, + const std::vector &leftMatchConstraints, + const std::vector &rightMatchConstraints, + const PropTerm &term, const StringStore &strings); }; -} // namespace Rules -} // namespace lib -} // namespace mod +} // namespace mod::lib::Rules -#endif /* MOD_LIB_RULES_PROP_STRING_H */ \ No newline at end of file +#endif // MOD_LIB_RULES_PROP_STRING_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Term.cpp b/libs/libmod/src/mod/lib/Rules/Properties/Term.cpp index 52c062a..563ae69 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/Term.cpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/Term.cpp @@ -4,21 +4,21 @@ #include #include #include -#include #include +#include #include namespace mod::lib::Rules { -PropTermCore::PropTermCore(const GraphType &core, - const std::vector &leftMatchConstraints, - const std::vector &rightMatchConstraints, - const PropStringCore &label, const StringStore &stringStore) : Base(core) { +PropTerm::PropTerm(const RuleType &rule, + const std::vector &leftMatchConstraints, + const std::vector &rightMatchConstraints, + const PropString &pString, const StringStore &stringStore) : Base(rule) { // take every unique string and parse it std::unordered_map labelToAddress; - lib::Term::RawAppendStore varToAddr; - const auto handleLabel = [&stringStore, &labelToAddress, &varToAddr, this](const std::string & label) { + Term::RawAppendStore varToAddr; + const auto handleLabel = [&stringStore, &labelToAddress, &varToAddr, this](const std::string &label) { // if there is a * in the label, then we can not just use label caching if(label.find('*') == std::string::npos) { auto iter = labelToAddress.find(label); @@ -26,107 +26,119 @@ PropTermCore::PropTermCore(const GraphType &core, return iter->second; } } - lib::Term::RawTerm rawTerm; + Term::RawTerm rawTerm; try { - rawTerm = lib::IO::Term::Read::rawTerm(label, stringStore); - } catch(const lib::IO::ParsingError &e) { + rawTerm = Term::Read::rawTerm(label, stringStore); + } catch(const IO::ParsingError &e) { parsingError = e.msg; return std::numeric_limits::max(); } - lib::Term::Address addr = lib::Term::append(machine, rawTerm, varToAddr); + Term::Address addr = Term::append(machine, rawTerm, varToAddr); labelToAddress[label] = addr.addr; return addr.addr; }; - vertexState.resize(num_vertices(core)); - for(Vertex v : asRange(vertices(core))) { - auto vId = get(boost::vertex_index_t(), g, v); - switch(core[v].membership) { - case Membership::Left: - vertexState[vId].left = handleLabel(label.getLeft()[v]); - break; - case Membership::Right: - vertexState[vId].right = handleLabel(label.getRight()[v]); - break; - case Membership::Context: - if(label.getLeft()[v] == label.getRight()[v]) { - // no label-change, so use the same for both (e.g., "*" -> "*" should be the _same_ variable) - vertexState[vId].left = vertexState[vId].right = handleLabel(label.getLeft()[v]); - } else { - vertexState[vId].left = handleLabel(label.getLeft()[v]); - vertexState[vId].right = handleLabel(label.getRight()[v]); + + vPropL.reserve(num_vertices(getL(rule))); + vPropR.reserve(num_vertices(getR(rule))); + for(const auto vL: asRange(vertices(getL(rule)))) { + assert(get(boost::vertex_index_t(), getL(rule), vL) >= vPropL.size()); + vPropL.resize(get(boost::vertex_index_t(), getL(rule), vL)); + assert(get(boost::vertex_index_t(), getL(rule), vL) == vPropL.size()); + vPropL.push_back(handleLabel(pString.getLeft()[vL])); + } + for(const auto vR: asRange(vertices(getR(rule)))) { + assert(get(boost::vertex_index_t(), getR(rule), vR) >= vPropR.size()); + vPropR.resize(get(boost::vertex_index_t(), getR(rule), vR)); + assert(get(boost::vertex_index_t(), getR(rule), vR) == vPropR.size()); + const auto vK = get_inverse(getMorR(rule), getK(rule), getR(rule), vR); + if(vK != lib::DPO::CombinedRule::KGraphType::null_vertex()) { + const auto vL = get(getMorL(rule), getK(rule), getL(rule), vK); + if(pString.getLeft()[vL] == pString.getRight()[vR]) { + // copy the term from left + vPropR.push_back(vPropL[get(boost::vertex_index_t(), getL(rule), vL)]); + continue; } - break; } + vPropR.push_back(handleLabel(pString.getRight()[vR])); } - edgeState.resize(num_edges(core)); - for(Edge e : asRange(edges(core))) { - auto eId = get(boost::edge_index_t(), g, e); - switch(core[e].membership) { - case Membership::Left: - edgeState[eId].left = handleLabel(label.getLeft()[e]); - break; - case Membership::Right: - edgeState[eId].right = handleLabel(label.getRight()[e]); - break; - case Membership::Context: - if(label.getLeft()[e] == label.getRight()[e]) { - // no label-change, so use the same for both (e.g., "*" -> "*" should be the _same_ variable) - auto convertedLabel = handleLabel(label.getLeft()[e]); - edgeState[eId].left = convertedLabel; - edgeState[eId].right = convertedLabel; - } else { - edgeState[eId].left = handleLabel(label.getLeft()[e]); - edgeState[eId].right = handleLabel(label.getRight()[e]); + assert(vPropL.size() <= num_vertices(rule.getCombinedGraph())); + assert(vPropR.size() <= num_vertices(rule.getCombinedGraph())); + vPropL.resize(num_vertices(rule.getCombinedGraph())); + vPropR.resize(num_vertices(rule.getCombinedGraph())); + + ePropL.reserve(num_edges(getL(rule))); + ePropR.reserve(num_edges(getR(rule))); + for(const auto eL: asRange(edges(getL(rule)))) { + assert(get(boost::edge_index_t(), getL(rule), eL) >= ePropL.size()); + ePropL.resize(get(boost::edge_index_t(), getL(rule), eL)); + assert(get(boost::edge_index_t(), getL(rule), eL) == ePropL.size()); + ePropL.push_back(handleLabel(pString.getLeft()[eL])); + } + for(const auto eR: asRange(edges(getR(rule)))) { + assert(get(boost::edge_index_t(), getR(rule), eR) >= ePropR.size()); + ePropR.resize(get(boost::edge_index_t(), getR(rule), eR)); + assert(get(boost::edge_index_t(), getR(rule), eR) == ePropR.size()); + const auto eK = get_inverse(getMorR(rule), getK(rule), getR(rule), eR); + if(eK != lib::DPO::CombinedRule::KGraphType::edge_descriptor()) { + const auto eL = get(getMorL(rule), getK(rule), getL(rule), eK); + if(pString.getLeft()[eL] == pString.getRight()[eR]) { + // copy the term from left + ePropR.push_back(ePropL[get(boost::edge_index_t(), getL(rule), eL)]); + continue; } - break; } + ePropR.push_back(handleLabel(pString.getRight()[eR])); } + assert(ePropL.size() <= num_edges(rule.getCombinedGraph())); + assert(ePropR.size() <= num_edges(rule.getCombinedGraph())); + ePropL.resize(num_edges(rule.getCombinedGraph())); + ePropR.resize(num_edges(rule.getCombinedGraph())); using HandlerType = decltype(handleLabel); + using SideGraphType = lib::DPO::CombinedRule::SideGraphType; - struct Visitor : lib::GraphMorphism::Constraints::AllVisitorNonConst { - - Visitor(HandlerType &handleLabel) : handleLabel(handleLabel) { } + struct Visitor : GraphMorphism::Constraints::AllVisitorNonConst { + Visitor(HandlerType &handleLabel) : handleLabel(handleLabel) {} - virtual void operator()(lib::GraphMorphism::Constraints::VertexAdjacency &c) override { + virtual void operator()(GraphMorphism::Constraints::VertexAdjacency &c) override { assert(c.vertexTerms.size() == 0); assert(c.edgeTerms.size() == 0); - for(const std::string &s : c.vertexLabels) + for(const std::string &s: c.vertexLabels) c.vertexTerms.insert(handleLabel(s)); - for(const std::string &s : c.edgeLabels) + for(const std::string &s: c.edgeLabels) c.edgeTerms.insert(handleLabel(s)); } - virtual void operator()(lib::GraphMorphism::Constraints::ShortestPath &c) override { } + virtual void operator()(GraphMorphism::Constraints::ShortestPath &c) override {} private: HandlerType handleLabel; }; const auto handleConstraints = [&](const auto &cs) { Visitor vis(handleLabel); - for(auto &c : cs) c->accept(vis); + for(auto &c: cs) c->accept(vis); }; handleConstraints(leftMatchConstraints); handleConstraints(rightMatchConstraints); - verify(&this->g); + verify(); } -PropTermCore::PropTermCore(const GraphType &core, lib::Term::Wam machine) : Base(core), machine(std::move(machine)) { } +PropTerm::PropTerm(const RuleType &rule, Term::Wam machine) : Base(rule), machine(std::move(machine)) {} -bool isValid(const PropTermCore &core) { +bool isValid(const PropTerm &core) { return !core.parsingError.has_value(); } -const std::string &PropTermCore::getParsingError() const { +const std::string &PropTerm::getParsingError() const { assert(!isValid(*this)); return *parsingError; } -lib::Term::Wam &getMachine(PropTermCore &core) { +Term::Wam &getMachine(PropTerm &core) { return core.machine; } -const lib::Term::Wam &getMachine(const PropTermCore &core) { +const Term::Wam &getMachine(const PropTerm &core) { return core.machine; } diff --git a/libs/libmod/src/mod/lib/Rules/Properties/Term.hpp b/libs/libmod/src/mod/lib/Rules/Properties/Term.hpp index 6aaafcd..e607f56 100644 --- a/libs/libmod/src/mod/lib/Rules/Properties/Term.hpp +++ b/libs/libmod/src/mod/lib/Rules/Properties/Term.hpp @@ -10,40 +10,32 @@ #include namespace mod::lib::Rules { -struct PropStringCore; +struct PropString; -struct PropTermCore : PropCore { - using Base = PropCore; - using ConstraintPtr = std::unique_ptr >; +struct PropTerm : PropBase { + using Base = PropBase; + using ConstraintPtr = std::unique_ptr>; public: - PropTermCore(const GraphType &core, - const std::vector &leftMatchConstraints, - const std::vector &rightMatchConstraints, - const PropStringCore &label, const StringStore &stringStore); // parse-construct - PropTermCore(const GraphType &core, lib::Term::Wam machine); // import a machine - friend bool isValid(const PropTermCore &core); + PropTerm(const RuleType &rule, + const std::vector &leftMatchConstraints, + const std::vector &rightMatchConstraints, + const PropString &pString, const StringStore &stringStore); // parse-construct + PropTerm(const RuleType &rule, lib::Term::Wam machine); // import a machine + friend bool isValid(const PropTerm &core); const std::string &getParsingError() const; - friend lib::Term::Wam &getMachine(PropTermCore &core); - friend const lib::Term::Wam &getMachine(const PropTermCore &core); + friend lib::Term::Wam &getMachine(PropTerm &core); + friend const lib::Term::Wam &getMachine(const PropTerm &core); private: std::optional parsingError; lib::Term::Wam machine; }; -inline const lib::Term::Wam &getMachine(const PropTermCore::LeftType &p) { - return getMachine(p.state.getDerived()); +inline const lib::Term::Wam &getMachine(const PropTerm::Side &p) { + return getMachine(p.p.getDerived()); } -inline bool isValid(const PropTermCore::LeftType &p) { - return isValid(p.state.getDerived()); -} - -inline const lib::Term::Wam &getMachine(const PropTermCore::RightType &p) { - return getMachine(p.state.getDerived()); -} - -inline bool isValid(const PropTermCore::RightType &p) { - return isValid(p.state.getDerived()); +inline bool isValid(const PropTerm::Side &p) { + return isValid(p.p.getDerived()); } } // namespace mod::lib::Rules diff --git a/libs/libmod/src/mod/lib/Rules/Real.cpp b/libs/libmod/src/mod/lib/Rules/Real.cpp index 10759b9..8323762 100644 --- a/libs/libmod/src/mod/lib/Rules/Real.cpp +++ b/libs/libmod/src/mod/lib/Rules/Real.cpp @@ -9,8 +9,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -23,48 +22,54 @@ namespace mod::lib::Rules { BOOST_CONCEPT_ASSERT((LabelledGraphConcept)); -BOOST_CONCEPT_ASSERT((LabelledGraphConcept)); -BOOST_CONCEPT_ASSERT((LabelledGraphConcept)); +BOOST_CONCEPT_ASSERT((LabelledGraphConcept)); //------------------------------------------------------------------------------ // Main class //------------------------------------------------------------------------------ -void printEdge(Edge e, const GraphType &core, const PropStringCore &labelState, std::ostream &s) { - Vertex vSrc = source(e, core), vTar = target(e, core); - s << "\t"; - labelState.print(s, vSrc); - s << " -- "; - labelState.print(s, e); - s << " -- "; - labelState.print(s, vTar); -} - -bool Real::sanityChecks(const GraphType &core, const PropStringCore &labelState, std::ostream &s) { +void printEdge(lib::DPO::CombinedRule::CombinedEdge eCG, const lib::DPO::CombinedRule::CombinedGraphType &gCombined, + const PropString &pString, std::ostream &s) { + const auto vSrc = source(eCG, gCombined); + const auto vTar = target(eCG, gCombined); + s << " vSrc (" << vSrc << "): "; + pString.print(s, vSrc); + s << "\n e: "; + pString.print(s, eCG); + s << "\n vTar (" << vTar << "): "; + pString.print(s, vTar); + s << std::endl; +} + +bool Real::sanityChecks(const lib::DPO::CombinedRule::CombinedGraphType &gCombined, + const PropString &pString, std::ostream &s) { // hmm, boost::edge_range is not supported for vecS, right? so we count in a rather stupid way - for(Edge e : asRange(edges(core))) { - Vertex vSrc = source(e, core), vTar = target(e, core); - auto eContext = core[e].membership, - srcContext = core[vSrc].membership, - tarContext = core[vTar].membership; + for(const auto eCG: asRange(edges(gCombined))) { + const auto vSrc = source(eCG, gCombined); + const auto vTar = target(eCG, gCombined); + const auto eContext = gCombined[eCG].membership, + srcContext = gCombined[vSrc].membership, + tarContext = gCombined[vTar].membership; // check danglingness - if(eContext != srcContext && srcContext != Membership::Context) { + if(eContext != srcContext && srcContext != Membership::K) { s << "Rule::sanityCheck\tdangling edge at source: " << std::endl; - printEdge(e, core, labelState, s); + printEdge(eCG, gCombined, pString, s); return false; } - if(eContext != tarContext && tarContext != Membership::Context) { + if(eContext != tarContext && tarContext != Membership::K) { s << "Rule::sanityCheck\tdangling edge at target: " << std::endl; - printEdge(e, core, labelState, s); + printEdge(eCG, gCombined, pString, s); return false; } // check parallelness - unsigned int count = 0; - for(Edge eOut : asRange(out_edges(vSrc, core))) if(target(eOut, core) == vTar) count++; + int count = 0; + for(const auto eOut: asRange(out_edges(vSrc, gCombined))) + if(target(eOut, gCombined) == vTar) + ++count; if(count > 1) { s << "Rule::sanityCheck\tcan't handle parallel edges in lib::Rules::GraphType" << std::endl; - printEdge(e, core, labelState, s); + printEdge(eCG, gCombined, pString, s); return false; } } @@ -73,16 +78,17 @@ bool Real::sanityChecks(const GraphType &core, const PropStringCore &labelState, namespace { std::size_t nextRuleNum = 0; -} // namespace +} // namespace Real::Real(LabelledRule &&rule, std::optional labelType) : id(nextRuleNum++), name("r_{" + boost::lexical_cast(id) + "}"), labelType(labelType), dpoRule(std::move(rule)) { - if(dpoRule.numLeftComponents == std::numeric_limits::max()) dpoRule.initComponents(); + if(get_num_connected_components(get_labelled_left(dpoRule)) == -1) + dpoRule.initComponents(); // only one of propString and propTerm should be defined assert(this->dpoRule.pString || this->dpoRule.pTerm); assert(!this->dpoRule.pString || !this->dpoRule.pTerm); - if(!sanityChecks(getGraph(), getStringState(), std::cout)) { + if(!sanityChecks(getDPORule().getRule().getCombinedGraph(), get_string(getDPORule()), std::cout)) { std::cout << "Rule::sanityCheck\tfailed in rule '" << getName() << "'" << std::endl; MOD_ABORT; } @@ -127,13 +133,13 @@ const lib::Rules::GraphType &Real::getGraph() const { return get_graph(dpoRule); } -DepictionDataCore &Real::getDepictionData() { - if(!depictionData) depictionData.reset(new DepictionDataCore(getDPORule())); +Write::DepictionData &Real::getDepictionData() { + if(!depictionData) depictionData.reset(new Write::DepictionData(getDPORule())); return *depictionData; } -const DepictionDataCore &Real::getDepictionData() const { - if(!depictionData) depictionData.reset(new DepictionDataCore(getDPORule())); +const Write::DepictionData &Real::getDepictionData() const { + if(!depictionData) depictionData.reset(new Write::DepictionData(getDPORule())); return *depictionData; } @@ -143,40 +149,16 @@ bool Real::isChemical() const { } bool Real::isOnlySide(Membership membership) const { - const GraphType &core = getGraph(); - for(Vertex v : asRange(vertices(core))) - if(core[v].membership != membership) return false; - for(Edge e : asRange(edges(core))) - if(core[e].membership != membership) return false; + const auto &gCombined = getDPORule().getRule().getCombinedGraph(); + for(const auto v: asRange(vertices(gCombined))) + if(gCombined[v].membership != membership) return false; + for(const auto e: asRange(edges(gCombined))) + if(gCombined[e].membership != membership) return false; return true; } bool Real::isOnlyRightSide() const { - return isOnlySide(Membership::Right); -} - -const PropStringCore &Real::getStringState() const { - assert(dpoRule.pString || dpoRule.pTerm); - if(!dpoRule.pString) { - dpoRule.pString.reset(new PropStringCore(get_graph(dpoRule), - dpoRule.leftMatchConstraints, dpoRule.rightMatchConstraints, - getTermState(), lib::Term::getStrings())); - } - return *dpoRule.pString; -} - -const PropTermCore &Real::getTermState() const { - assert(dpoRule.pString || dpoRule.pTerm); - if(!dpoRule.pTerm) { - dpoRule.pTerm.reset(new PropTermCore(get_graph(dpoRule), - dpoRule.leftMatchConstraints, dpoRule.rightMatchConstraints, - getStringState(), lib::Term::getStrings())); - } - return *dpoRule.pTerm; -} - -const PropMoleculeCore &Real::getMoleculeState() const { - return get_molecule(getDPORule()); + return isOnlySide(Membership::R); } namespace { diff --git a/libs/libmod/src/mod/lib/Rules/Real.hpp b/libs/libmod/src/mod/lib/Rules/Real.hpp index 9652db2..39f3265 100644 --- a/libs/libmod/src/mod/lib/Rules/Real.hpp +++ b/libs/libmod/src/mod/lib/Rules/Real.hpp @@ -17,14 +17,15 @@ struct PropString; struct Single; } // namespace mod::lib::Graph namespace mod::lib::Rules { -struct PropStringCore; -struct PropMoleculeCore; -struct DepictionDataCore; - -using DPOProjection = LabelledRule::LabelledLeftType::GraphType; +struct PropString; +struct PropMolecule; +namespace Write { +struct DepictionData; +} // namespace Write struct Real { - static bool sanityChecks(const GraphType &core, const PropStringCore &labelState, std::ostream &s); + static bool sanityChecks(const lib::DPO::CombinedRule::CombinedGraphType &gCombined, + const PropString &pString, std::ostream &s); public: Real(LabelledRule &&rule, std::optional labelType); ~Real(); @@ -35,18 +36,14 @@ struct Real { void setName(std::string name); std::optional getLabelType() const; const LabelledRule &getDPORule() const; -public: // shorthands +public: // shorthands, deprecated const GraphType &getGraph() const; - DepictionDataCore &getDepictionData(); // TODO: should not be available as non-const + Write::DepictionData &getDepictionData(); // TODO: should not be available as non-const public: - const DepictionDataCore &getDepictionData() const; + const Write::DepictionData &getDepictionData() const; bool isChemical() const; bool isOnlySide(Membership membership) const; bool isOnlyRightSide() const; // shortcut of above -public: // deprecated - const PropStringCore &getStringState() const; - const PropTermCore &getTermState() const; - const PropMoleculeCore &getMoleculeState() const; public: static std::size_t isomorphism(const Real &rDom, const Real &rCodom, @@ -66,7 +63,7 @@ struct Real { const std::optional labelType; private: LabelledRule dpoRule; - mutable std::unique_ptr depictionData; + mutable std::unique_ptr depictionData; }; struct LessById { @@ -85,6 +82,9 @@ struct IsomorphismPredicate { return 1 == Real::isomorphism(*rDom, *rCodom, 1, settings); } + bool operator()(const std::unique_ptr &rDom, const std::unique_ptr &rCodom) const { + return 1 == Real::isomorphism(*rDom, *rCodom, 1, settings); + } private: LabelSettings settings; }; @@ -96,11 +96,11 @@ inline detail::IsomorphismPredicate makeIsomorphismPredicate(LabelType labelType } struct MembershipPredWrapper { - template - auto operator()(const OuterGraph &gDomain, const OuterGraph &gCodomain, Pred pred) const { + template + auto operator()(const LabelledRule &gLabDomain, const LabelledRule &gLabCodomain, Pred pred) const { return jla_boost::GraphMorphism::makePropertyPredicateEq( - makeMembershipPropertyMap(get_graph(gDomain)), - makeMembershipPropertyMap(get_graph(gCodomain)), pred); + gLabDomain.getRule().makeMembershipPropertyMap(), + gLabCodomain.getRule().makeMembershipPropertyMap(), pred); } }; diff --git a/libs/libmod/src/mod/lib/Stereo/Configuration/Any.cpp b/libs/libmod/src/mod/lib/Stereo/Configuration/Any.cpp index 9e3502f..af87422 100644 --- a/libs/libmod/src/mod/lib/Stereo/Configuration/Any.cpp +++ b/libs/libmod/src/mod/lib/Stereo/Configuration/Any.cpp @@ -2,9 +2,7 @@ #include -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { Any::Any(const GeometryGraph &g, const EmbeddingEdge *b, const EmbeddingEdge *e) : DynamicDegree(g.any, b, e) { } @@ -35,6 +33,4 @@ std::pair Any::asPrettyStringImpl(std::function -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { struct Any final : DynamicDegree { Any(const GeometryGraph &g, const EmbeddingEdge *b, const EmbeddingEdge *e); @@ -22,8 +20,6 @@ struct Any final : DynamicDegree { virtual std::pair asPrettyStringImpl(std::function getNeighbourId) const override; }; -} // namespace Stereo -} // namespace lib -} // namespace mod -#endif /* MOD_LIB_STEREO_CONFIGURATION_ANY_H */ +} // namespace mod::lib::Stereo +#endif // MOD_LIB_STEREO_CONFIGURATION_ANY_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/Configuration/Configuration.cpp b/libs/libmod/src/mod/lib/Stereo/Configuration/Configuration.cpp index 4e7c814..97026b9 100644 --- a/libs/libmod/src/mod/lib/Stereo/Configuration/Configuration.cpp +++ b/libs/libmod/src/mod/lib/Stereo/Configuration/Configuration.cpp @@ -2,9 +2,7 @@ #include -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { // Fixation //------------------------------------------------------------------------------ @@ -133,6 +131,4 @@ DynamicDegree::DynamicDegree(GeometryGraph::Vertex vGeometry, const EmbeddingEdg std::copy(b, e, std::back_inserter(edges)); } -} // namespace Stereo -} // namespace lib -} // namespace mod \ No newline at end of file +} // namespace mod::lib::Stereo \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/Configuration/Configuration.hpp b/libs/libmod/src/mod/lib/Stereo/Configuration/Configuration.hpp index 9f18112..0f18c85 100644 --- a/libs/libmod/src/mod/lib/Stereo/Configuration/Configuration.hpp +++ b/libs/libmod/src/mod/lib/Stereo/Configuration/Configuration.hpp @@ -1,21 +1,15 @@ -#ifndef MOD_LIB_STEREO_CONFIGURATION_CONFIGURATION_H -#define MOD_LIB_STEREO_CONFIGURATION_CONFIGURATION_H +#ifndef MOD_LIB_STEREO_CONFIGURATION_CONFIGURATION_HPP +#define MOD_LIB_STEREO_CONFIGURATION_CONFIGURATION_HPP #include #include #include -namespace mod { -namespace lib { -namespace IO { -namespace Graph { -namespace Write { +namespace mod::lib::IO::Graph::Write { enum struct EdgeFake3DType; -} // namespace Write -} // namespace Graph -} // namespace IO -namespace Stereo { +} // namespace mod::lib::IO::Graph::Write +namespace mod::lib::Stereo { struct Fixation { explicit Fixation(bool f); // simple @@ -30,11 +24,9 @@ struct Fixation { static Fixation simpleFixed(); }; -class Configuration { +struct Configuration { Configuration(const Configuration&) = delete; - Configuration(Configuration&&) = delete; Configuration &operator=(const Configuration&) = delete; - Configuration &operator=(Configuration&&) = delete; protected: explicit Configuration(GeometryGraph::Vertex vGeometry, const EmbeddingEdge *first, const EmbeddingEdge *last); public: @@ -56,24 +48,21 @@ class Configuration { } public: // checking // pre: dynamic type of this and other is the same - virtual bool localPredIso(const Configuration &other) const { return true; } - // pre: dynamic type of this and other is the same + // pre: dynamic type of this and other is the same virtual bool localPredSpec(const Configuration &other) const { return true; } // TODO: this should be a kind of vtable constant - virtual bool morphismStaticOk() const { return true; } // E.g., influenced by fixedness. (TODO: only fixedness? then we could maybe pull it into the base class) - virtual bool morphismDynamicOk() const { return true; } @@ -106,7 +95,6 @@ struct DynamicDegree : Configuration { protected: DynamicDegree(GeometryGraph::Vertex vGeometry, const EmbeddingEdge *b, const EmbeddingEdge *e); public: - virtual const EmbeddingEdge *begin() const override final { return edges.data(); } @@ -121,7 +109,6 @@ struct DynamicDegree : Configuration { template struct StaticDegree : Configuration { protected: - StaticDegree(GeometryGraph::Vertex vGeometry, const std::array &edges) : Configuration(vGeometry, edges.begin(), edges.end()), edges(edges) { } public: @@ -137,8 +124,6 @@ struct StaticDegree : Configuration { std::array edges; }; -} // namespace Stereo -} // namespace lib -} // namespace mod +} // namespace mod::lib::Stereo -#endif /* MOD_LIB_STEREO_CONFIGURATION_CONFIGURATION_H */ \ No newline at end of file +#endif // MOD_LIB_STEREO_CONFIGURATION_CONFIGURATION_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/Configuration/Linear.cpp b/libs/libmod/src/mod/lib/Stereo/Configuration/Linear.cpp index 0129d24..b8101ef 100644 --- a/libs/libmod/src/mod/lib/Stereo/Configuration/Linear.cpp +++ b/libs/libmod/src/mod/lib/Stereo/Configuration/Linear.cpp @@ -2,9 +2,7 @@ #include -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { Linear::Linear(const GeometryGraph &g, const std::array &edges) : StaticDegree<2>(g.linear, edges) { } @@ -35,6 +33,4 @@ std::pair Linear::asPrettyStringImpl(std::function -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { struct Linear final : StaticDegree<2> { Linear(const GeometryGraph &g, const std::array &edges); @@ -22,8 +20,6 @@ struct Linear final : StaticDegree<2> { virtual std::pair asPrettyStringImpl(std::function getNeighbourId) const override; }; -} // namespace Stereo -} // namespace lib -} // namespace mod +} // namespace mod::lib::Stereo -#endif /* MOD_LIB_STEREO_CONFIGURATION_LINEAR_H */ \ No newline at end of file +#endif // MOD_LIB_STEREO_CONFIGURATION_LINEAR_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/Configuration/Tetrahedral.cpp b/libs/libmod/src/mod/lib/Stereo/Configuration/Tetrahedral.cpp index 3067b76..fcb5f97 100644 --- a/libs/libmod/src/mod/lib/Stereo/Configuration/Tetrahedral.cpp +++ b/libs/libmod/src/mod/lib/Stereo/Configuration/Tetrahedral.cpp @@ -2,9 +2,7 @@ #include -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { Tetrahedral::Tetrahedral(const GeometryGraph &g, const std::array &edges_, bool fixed) : StaticDegree<4>(g.tetrahedral, edges_), fixed(fixed) { @@ -191,6 +189,4 @@ std::pair Tetrahedral::asPrettyStringImpl(std::function -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { struct Tetrahedral final : StaticDegree<4> { Tetrahedral(const GeometryGraph &g, const std::array &edges, bool fixed); @@ -30,8 +28,6 @@ struct Tetrahedral final : StaticDegree<4> { bool fixed; }; -} // namespace Stereo -} // namespace lib -} // namespace mod +} // namespace mod::lib::Stereo -#endif /* MOD_LIB_STEREO_CONFIGURATION_TETRAHEDRAL_H */ \ No newline at end of file +#endif // MOD_LIB_STEREO_CONFIGURATION_TETRAHEDRAL_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/Configuration/TrigonalPlanar.cpp b/libs/libmod/src/mod/lib/Stereo/Configuration/TrigonalPlanar.cpp index 1b53b99..20a453d 100644 --- a/libs/libmod/src/mod/lib/Stereo/Configuration/TrigonalPlanar.cpp +++ b/libs/libmod/src/mod/lib/Stereo/Configuration/TrigonalPlanar.cpp @@ -2,9 +2,7 @@ #include -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { TrigonalPlanar::TrigonalPlanar(const GeometryGraph &g, const std::array &edges, bool fixed) : StaticDegree<3>(g.trigonalPlanar, edges), fixed(fixed) { } @@ -68,6 +66,4 @@ std::pair TrigonalPlanar::asPrettyStringImpl(std::function -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { struct TrigonalPlanar final : StaticDegree<3> { TrigonalPlanar(const GeometryGraph &g, const std::array &edges, bool fixed); @@ -29,9 +27,6 @@ struct TrigonalPlanar final : StaticDegree<3> { bool fixed; }; -} // namespace Stereo -} // namespace lib -} // namespace mod - -#endif /* MOD_LIB_STEREO_CONFIGURATION_TRIGONAL_PLANAR_H */ +} // namespace mod::lib::Stereo +#endif // MOD_LIB_STEREO_CONFIGURATION_TRIGONAL_PLANAR_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/EmbeddingEdge.cpp b/libs/libmod/src/mod/lib/Stereo/EmbeddingEdge.cpp index d01561a..2a49c96 100644 --- a/libs/libmod/src/mod/lib/Stereo/EmbeddingEdge.cpp +++ b/libs/libmod/src/mod/lib/Stereo/EmbeddingEdge.cpp @@ -1,14 +1,10 @@ #include "EmbeddingEdge.hpp" -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { EmbeddingEdge::EmbeddingEdge(std::size_t offset, Type type, EdgeCategory cat) -: offset(offset), type(type), cat(cat) { + : offset(offset), type(type), cat(cat) { if(type != Type::Edge) assert(cat == EdgeCategory::Single); } -} // namespace Stereo -} // namespace lib -} // namespace mod \ No newline at end of file +} // namespace mod::lib::Stereo \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/EmbeddingEdge.hpp b/libs/libmod/src/mod/lib/Stereo/EmbeddingEdge.hpp index 807bc5e..1b48e6c 100644 --- a/libs/libmod/src/mod/lib/Stereo/EmbeddingEdge.hpp +++ b/libs/libmod/src/mod/lib/Stereo/EmbeddingEdge.hpp @@ -1,16 +1,13 @@ -#ifndef MOD_LIB_STEREO_EMBEDDINGEDGE_H -#define MOD_LIB_STEREO_EMBEDDINGEDGE_H +#ifndef MOD_LIB_STEREO_EMBEDDINGEDGE_HPP +#define MOD_LIB_STEREO_EMBEDDINGEDGE_HPP #include #include -namespace mod { -namespace lib { -namespace Stereo { +namespace mod::lib::Stereo { struct EmbeddingEdge { - enum class Type { Edge, LonePair, Radical }; @@ -32,8 +29,6 @@ struct EmbeddingEdge { EdgeCategory cat; }; -} // namespace Stereo -} // namespace lib -} // namespace mod +} // namespace mod::lib::Stereo -#endif /* MOD_LIB_STEREO_EMBEDDINGEDGE_H */ \ No newline at end of file +#endif // MOD_LIB_STEREO_EMBEDDINGEDGE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/IO/StereoRead.cpp b/libs/libmod/src/mod/lib/Stereo/IO/Read.cpp similarity index 54% rename from libs/libmod/src/mod/lib/IO/StereoRead.cpp rename to libs/libmod/src/mod/lib/Stereo/IO/Read.cpp index b5feae2..aeba5fa 100644 --- a/libs/libmod/src/mod/lib/IO/StereoRead.cpp +++ b/libs/libmod/src/mod/lib/Stereo/IO/Read.cpp @@ -1,8 +1,9 @@ -#include "Stereo.hpp" +#include "Read.hpp" -#include +#include #include + #include #include #include @@ -12,47 +13,46 @@ #include #include #include +#include #include -namespace mod::lib::IO::Stereo::Read { +namespace mod::lib::Stereo::Read { + namespace { namespace parser { const auto fixation = x3::rule{"fixation"} /* */ = x3::lit('!') >> x3::attr(true); const auto virtualEdge = x3::rule{"virtualEdge"} -/* */ = x3::char_("er"); // lone-pair or radical -const auto edge = x3::rule{"edge"} -/* */ = x3::int_ | virtualEdge; -const auto edges = x3::rule >{"edges"} -/* */ = x3::lit('[') > -(edge % ',') >> ']'; +/* */ = x3::char_("a-zA-Z"); +const auto neighbour = x3::rule{"neighbour"} +/* */ = x3::int_ | virtualEdge; +const auto neighboursInner = x3::rule>{ + "comma separated neighbour list"} +/* */ = -(neighbour % ',') >> ']'; +const auto neighbours = x3::lit('[') > neighboursInner; const auto geometry = x3::rule{"geometry"} /* */ = x3::lexeme[x3::char_("a-zA-Z") >> *x3::char_("a-zA-Z0-9")]; const auto start = x3::rule{"stereoEmbedding"} -/* */ = -geometry >> -edges >> -fixation >> x3::eoi; +/* */ = !x3::eoi >> -geometry >> -neighbours >> -fixation >> x3::eoi; } // namespace parser } // namesapce lib::IO::Result parseEmbedding(const std::string &str) { - using Iter = std::string::const_iterator; - Iter start = str.begin(); - ParsedEmbedding embedding; - bool res = x3::phrase_parse(start, str.end(), parser::start, x3::space, embedding); - if(!res || start != str.end()) { - std::string msg = "Error in stereo embedding at column "; - msg += std::to_string(start - str.begin()); - msg += "."; - if(start != str.end()) msg += " Expected end of input."; - return lib::IO::Result::Error(std::move(msg)); + try { + ParsedEmbedding embedding; + lib::IO::parse(str.begin(), str.end(), parser::start, embedding, false, x3::ascii::space); + return embedding; + } catch(const lib::IO::ParsingError &e) { + return lib::IO::Result<>::Error(e.msg); } - return embedding; } -} // namespace mod::lib::IO::Stereo::Read +} // namespace mod::lib::Stereo::Read -BOOST_FUSION_ADAPT_STRUCT(mod::lib::IO::Stereo::Read::ParsedEmbedding, +BOOST_FUSION_ADAPT_STRUCT(mod::lib::Stereo::Read::ParsedEmbedding, (std::optional, geometry) - (std::optional >, edges) + (std::optional>, edges) (std::optional, fixation) ) \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/IO/Read.hpp b/libs/libmod/src/mod/lib/Stereo/IO/Read.hpp new file mode 100644 index 0000000..899dbb4 --- /dev/null +++ b/libs/libmod/src/mod/lib/Stereo/IO/Read.hpp @@ -0,0 +1,25 @@ +#ifndef MOD_LIB_STEREO_READ_HPP +#define MOD_LIB_STEREO_READ_HPP + +#include + +#include +#include +#include +#include + +namespace mod::lib::Stereo::Read { + +using ParsedEmbeddingEdge = std::variant; + +struct ParsedEmbedding { + std::optional geometry; + std::optional> edges; + std::optional fixation; +}; + +IO::Result parseEmbedding(const std::string &str); + +} // namespace mod::lib::Stereo::Read + +#endif // MOD_LIB_STEREO_READ_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/IO/Write.cpp b/libs/libmod/src/mod/lib/Stereo/IO/Write.cpp new file mode 100644 index 0000000..ff62f51 --- /dev/null +++ b/libs/libmod/src/mod/lib/Stereo/IO/Write.cpp @@ -0,0 +1,133 @@ +#include "Write.hpp" + +#include +#include +#include +#include + +#include + +namespace mod::lib::Stereo::Write { + +void summary(const GeometryGraph &graph) { + const auto &g = graph.getGraph(); + const auto n = num_vertices(g); + std::string fileDot = [&]() { + post::FileHandle s(IO::makeUniqueFilePrefix() + "geometryGraph.dot"); + s << "digraph g {\nrankdir=LR;\n"; + for(auto v : asRange(vertices(g))) { + s << get(boost::vertex_index_t(), g, v) << " [ label=\"" << g[v].name << "\" ];\n"; + } + for(auto e : asRange(edges(g))) { + auto vSrc = source(e, g); + auto vTar = target(e, g); + s << get(boost::vertex_index_t(), g, vSrc) << " -> " << get(boost::vertex_index_t(), g, vTar); + s << " [];\n"; + } + for(std::size_t i = 0; i < graph.chemValids.size(); ++i) { + auto &cv = graph.chemValids[i]; + s << (i + n) << " [ label=\"" << lib::Chem::symbolFromAtomId(cv.atomId); + if(cv.charge != 0) { + if(cv.charge > 0) s << "+"; + else s << "-"; + if(std::abs(cv.charge) != 1) s << std::abs(cv.charge); + } + if(cv.catCount.sum() > 0) s << ", " << cv.catCount; + if(cv.lonePair > 0) s << ", e = " << cv.lonePair; + s << "\" ];\n"; + s << get(boost::vertex_index_t(), g, cv.geometry) << " -> " << (i + n) << " [];\n"; + } + s << "}\n"; + std::string f = s; + return std::string(f.begin(), f.end() - 4); + }(); + IO::post() << "coordsFromGV dgHyperDot \"" << fileDot << "\"\n"; + std::string fileCoords = fileDot + "_coord"; + std::string fileFig = [&]() { + post::FileHandle s(IO::makeUniqueFilePrefix() + "geometryGraph.tex"); + s << "\\begin{tikzpicture}[scale=\\modDGHyperScale]\n"; + s << "\\input{" << fileCoords << ".tex}\n"; + for(auto v : asRange(vertices(g))) { + auto vId = get(boost::vertex_index_t(), g, v); + s << "\\node[draw] (v-" << vId << ") at (v-coord-" << vId << "){"; + s << g[v].name; + s << "};\n"; + } + for(auto e : asRange(edges(g))) { + auto vSrc = source(e, g); + auto vTar = target(e, g); + s << "\\path[draw, ->, >=stealth] (v-" << get(boost::vertex_index_t(), g, vSrc) + << ") to (v-" << get(boost::vertex_index_t(), g, vTar) + << ");\n"; + } + for(std::size_t i = 0; i < graph.chemValids.size(); ++i) { + auto &cv = graph.chemValids[i]; + s << "\\node[draw, ellipse] (v-" << (i + n) << ") at (v-coord-" << (i + n) << "){"; + s << lib::Chem::symbolFromAtomId(cv.atomId); + if(cv.charge != 0) { + s << "$^{"; + if(cv.charge > 0) s << "+"; + else s << "-"; + if(std::abs(cv.charge) != 1) s << std::abs(cv.charge); + s << "}$"; + } + bool hasMore = cv.catCount.sum() > 0 || cv.lonePair > 0; + if(hasMore) s << "$"; + if(cv.catCount.sum() > 0) s << ", " << cv.catCount; + if(cv.lonePair > 0) s << ", e = " << cv.lonePair; + if(hasMore) s << "$"; + s << "};\n"; + s << "\\path[draw] (v-" << get(boost::vertex_index_t(), g, cv.geometry) << ") to (v-" << (i + n) << ");\n"; + } + s << "\\end{tikzpicture}"; + std::string f = s; + return std::string(f.begin(), f.end() - 4); + }(); + std::string fileTex = [&]() { + post::FileHandle s(IO::makeUniqueFilePrefix() + "geometryGraphSummary.tex"); + s + << "\\begin{center}\n" + << "\\includegraphics{" << fileFig << "}\n\n" + << "File: \\texttt{" << IO::escapeForLatex(fileFig) << "}\n" + << "\\end{center}\n" + << "\n" + << "\\section{Stereo, Chemically Valid Configurations}\n" + << "\\begin{center}\n" + << "\\begin{longtable}{@{}lllllll@{}}\n" + << "\\toprule\n" + << "Atom & S & D & T & A & LP & Geometry\\\\\n" + << "\\midrule\n"; + for(std::size_t i = 0; i < graph.chemValids.size(); ++i) { + auto &cv = graph.chemValids[i]; + s << lib::Chem::symbolFromAtomId(cv.atomId); + if(cv.charge != 0) { + s << "$^{"; + if(cv.charge > 0) s << "+"; + else s << "-"; + if(std::abs(cv.charge) != 1) s << std::abs(cv.charge); + s << "}$"; + } + for(auto cat :{EdgeCategory::Single, EdgeCategory::Double, + EdgeCategory::Triple, EdgeCategory::Aromatic}) { + s << " & "; + if(auto count = cv.catCount[cat]) { + s << int(count); + } + } + s << " & "; + if(cv.lonePair > 0) s << cv.lonePair; + s << " & " << g[cv.geometry].name << " \\\\\n"; + } + s + << "\\bottomrule\n" + << "\\end{longtable}\n" + << "\\end{center}\n"; + return std::string(s); + }(); + + IO::post() << "compileTikz \"" << fileFig << "\" \"" << fileCoords << "\"" << std::endl; + IO::post() << "summarySection \"Stereo, Geometry Graph\"" << std::endl; + IO::post() << "summaryInput \"" << fileTex << "\"" << std::endl; +} + +} // namespace mod::lib::Stereo::Write \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/IO/Write.hpp b/libs/libmod/src/mod/lib/Stereo/IO/Write.hpp new file mode 100644 index 0000000..87b608e --- /dev/null +++ b/libs/libmod/src/mod/lib/Stereo/IO/Write.hpp @@ -0,0 +1,13 @@ +#ifndef MOD_LIB_STEREO_WRITE_HPP +#define MOD_LIB_STEREO_WRITE_HPP + +namespace mod::lib::Stereo { +struct GeometryGraph; +} // namespace mod::lib::Stereo +namespace mod::lib::Stereo::Write { + +void summary(const GeometryGraph &g); + +} // namespace mod::lib::Stereo::Write + +#endif // MOD_LIB_STEREO_WRITE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/IO/WriteConfiguration.cpp b/libs/libmod/src/mod/lib/Stereo/IO/WriteConfiguration.cpp new file mode 100644 index 0000000..42d8f77 --- /dev/null +++ b/libs/libmod/src/mod/lib/Stereo/IO/WriteConfiguration.cpp @@ -0,0 +1,109 @@ +#include "WriteConfiguration.hpp" + +#include +#include +#include +#include + +#include + +namespace mod::lib::Stereo::Write { + +} // namespace mod::lib::Stereo::Write +namespace mod::lib::Stereo { +namespace { + +#define MOD_anyPrintCoords() \ +Configuration::printCoords(s, vIds); \ +if(vIds.size() == 1) return; \ +auto centerId = vIds.back(); \ +double angle = 360.0 / (vIds.size() - 1); \ +for(std::size_t i = 0; i < vIds.size() - 1; i++) \ +printSateliteCoord(s, centerId, angle * i + 90, vIds[i]); + +void printSateliteCoord(std::ostream &s, std::size_t centerId, double angle, std::size_t id) { + s << "\\coordinate[overlay, at=($(\\modIdPrefix v-coord-" << centerId << ")+(" << angle << ":1)"; + s << "$)] (\\modIdPrefix v-coord-" << id << ") {};\n"; +} + +} // namespace + +// Configuration +//------------------------------------------------------------------------------ + +IO::Graph::Write::EdgeFake3DType Configuration::getEdgeDepiction(std::size_t i) const { + return IO::Graph::Write::EdgeFake3DType::None; +} + +void Configuration::printCoords(std::ostream &s, const std::vector &vIds) const { + const std::string &name = getGeometryGraph().getGraph()[getGeometryVertex()].name; + s << "\\coordinate[overlay] (\\modIdPrefix v-coord-" << vIds.back() << ") {};\n"; + s << "\\node[at=($(\\modIdPrefix v-coord-" << vIds.back() << ")+(-90:1)+(-90:12pt)$)] {" << name << "};\n"; +} + +std::string Configuration::getEdgeAnnotation(std::size_t i) const { + return ""; +} + +// Any +//------------------------------------------------------------------------------ + +void Any::printCoords(std::ostream &s, const std::vector &vIds) const { + MOD_anyPrintCoords(); +} + +// Linear +//------------------------------------------------------------------------------ + +void Linear::printCoords(std::ostream &s, const std::vector &vIds) const { + Configuration::printCoords(s, vIds); + printSateliteCoord(s, vIds.back(), 180, vIds[0]); + printSateliteCoord(s, vIds.back(), 0, vIds[1]); +} + +// TrigonalPlanar +//------------------------------------------------------------------------------ + +void TrigonalPlanar::printCoords(std::ostream &s, const std::vector &vIds) const { + Configuration::printCoords(s, vIds); + printSateliteCoord(s, vIds.back(), 180, vIds[0]); + printSateliteCoord(s, vIds.back(), 300, vIds[1]); + printSateliteCoord(s, vIds.back(), 60, vIds[2]); +} + +std::string TrigonalPlanar::getEdgeAnnotation(std::size_t i) const { + if(fixed) return ""; + else return "?"; +} + +// Tetrahedral +//------------------------------------------------------------------------------ + +IO::Graph::Write::EdgeFake3DType Tetrahedral::getEdgeDepiction(std::size_t i) const { + switch(i) { + case 0: + case 1: + return lib::IO::Graph::Write::EdgeFake3DType::None; + case 2: + return lib::IO::Graph::Write::EdgeFake3DType::WedgeSL; + case 3: + return lib::IO::Graph::Write::EdgeFake3DType::HashSL; + default: + __builtin_unreachable(); + } +} + +void Tetrahedral::printCoords(std::ostream &s, const std::vector &vIds) const { + Configuration::printCoords(s, vIds); + printSateliteCoord(s, vIds.back(), 90, vIds[0]); + printSateliteCoord(s, vIds.back(), 210, vIds[1]); + printSateliteCoord(s, vIds.back(), -60, vIds[2]); + printSateliteCoord(s, vIds.back(), -10, vIds[3]); +} + +std::string Tetrahedral::getEdgeAnnotation(std::size_t i) const { + if(fixed) return ""; + else return "?"; +} + +} // namespace mod::lib::Stereo \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Stereo/IO/WriteConfiguration.hpp b/libs/libmod/src/mod/lib/Stereo/IO/WriteConfiguration.hpp new file mode 100644 index 0000000..6bce519 --- /dev/null +++ b/libs/libmod/src/mod/lib/Stereo/IO/WriteConfiguration.hpp @@ -0,0 +1,305 @@ +#ifndef MOD_LIG_STEREO_IO_WRITECONFIGURATION_HPP +#define MOD_LIG_STEREO_IO_WRITECONFIGURATION_HPP + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace mod::lib::Stereo::Write { + +template +std::string coords(const GraphInner &gStereo, const Configuration &conf, const std::string &name, + std::map::vertex_descriptor, int> &vMap) { + post::FileHandle s(IO::makeUniqueFilePrefix() + name + "_coord.tex"); + std::vector vIds(num_vertices(gStereo)); + using SVertex = typename boost::graph_traits::vertex_descriptor; + for(SVertex vStereo: asRange(vertices(gStereo))) { + auto vId = get(boost::vertex_index_t(), gStereo, vStereo); + auto iter = vMap.find(vStereo); + assert(iter != end(vMap)); + if(iter->second == -1) vIds[vIds.size() - 1] = vId; + else vIds[iter->second] = vId; + } + conf.printCoords(s, vIds); + return s; +} + +template +std::pair +tikz(const GraphPrint &g, typename boost::graph_traits::vertex_descriptor v, + const Configuration &conf, const std::string &name, const Depict &depict, + const IO::Graph::Write::Options &options, ShownId shownId) { + const bool printLonePairs = true; + using GVertex = typename boost::graph_traits::vertex_descriptor; + using GEdge = typename boost::graph_traits::edge_descriptor; + + using GraphStereo = jla_boost::EdgeIndexedAdjacencyList; + using SVertex = boost::graph_traits::vertex_descriptor; + using SEdge = boost::graph_traits::edge_descriptor; + + // we make a new graph with copies and then the extra lone pairs + GraphStereo gStereo; + std::map vMap; // the order id, though -1 => the center + SVertex vCenter = add_vertex(gStereo); + vMap[vCenter] = -1; + std::map, IO::Graph::Write::EdgeFake3DType> edgeDepiction; + for(std::size_t i = 0; i < conf.degree(); ++i) { + SVertex vStereo = add_vertex(gStereo); + vMap[vStereo] = i; + add_edge(vCenter, vStereo, gStereo); + auto edge3Dtype = conf.getEdgeDepiction(i); + edgeDepiction[std::make_pair(vCenter, vStereo)] = edge3Dtype; + edgeDepiction[std::make_pair(vStereo, vCenter)] = IO::Graph::Write::invertEdgeFake3DType(edge3Dtype); + } + + std::string coordFile = coords(gStereo, conf, name, vMap); + post::FileHandle s(IO::makeUniqueFilePrefix() + name + ".tex"); + + struct DepictorAndAdvOptions { + DepictorAndAdvOptions(const GraphPrint &gOuter, GVertex vOuterCenter, const GraphStereo &g, + const Depict &depict, bool printLonePairs, const std::map &vMap, + const Configuration &conf, + const std::map, IO::Graph::Write::EdgeFake3DType> &edgeDepiction, + ShownId shownId) + : gOuter(gOuter), vOuterCenter(vOuterCenter), + nullVertexOuter(boost::graph_traits::null_vertex()), gInner(g), + depict(depict), printLonePairs(printLonePairs), vMap(vMap), conf(conf), edgeDepiction(edgeDepiction), + shownId(shownId) {} + + GVertex getOuterVertexFromInnerVertex(SVertex vInner) const { + auto iter = vMap.find(vInner); + assert(iter != end(vMap)); + if(iter->second == -1) return vOuterCenter; + const auto &emb = conf.begin()[iter->second]; + if(emb.type != EmbeddingEdge::Type::Edge) return nullVertexOuter; + auto eOuter = emb.getEdge(vOuterCenter, gOuter); + return target(eOuter, gOuter); + } + + GEdge getOuterEdgeFromInnerEdge(SEdge eInner) const { + GVertex vSrcOuter = getOuterVertexFromInnerVertex(source(eInner, gInner)); + GVertex vTarOuter = getOuterVertexFromInnerVertex(target(eInner, gInner)); + assert(vSrcOuter != nullVertexOuter); + assert(vTarOuter != nullVertexOuter); + auto p = edge(vSrcOuter, vTarOuter, gOuter); + assert(p.second); + return p.first; + } + + unsigned char getAtomId(SVertex vInner) const { + GVertex vOuter = getOuterVertexFromInnerVertex(vInner); + if(vOuter == nullVertexOuter) return AtomIds::Invalid; + else return depict.getAtomId(vOuter); + } + + Isotope getIsotope(SVertex vInner) const { + GVertex vOuter = getOuterVertexFromInnerVertex(vInner); + if(vOuter == nullVertexOuter) return Isotope(); + else return depict.getIsotope(vOuter); + } + + char getCharge(SVertex vInner) const { + GVertex vOuter = getOuterVertexFromInnerVertex(vInner); + if(vOuter == nullVertexOuter) return 0; + else return depict.getCharge(vOuter); + } + + bool getRadical(SVertex vInner) const { + GVertex vOuter = getOuterVertexFromInnerVertex(vInner); + if(vOuter == nullVertexOuter) return false; + else return depict.getRadical(vOuter); + } + + BondType getBondData(SEdge eInner) const { + GVertex vSrcOuter = getOuterVertexFromInnerVertex(source(eInner, gInner)); + GVertex vTarOuter = getOuterVertexFromInnerVertex(target(eInner, gInner)); + if(vSrcOuter == nullVertexOuter || vTarOuter == nullVertexOuter) return BondType::Invalid; + else return depict.getBondData(getOuterEdgeFromInnerEdge(eInner)); + } + + AtomData operator()(SVertex v) const { + GVertex vOuter = getOuterVertexFromInnerVertex(v); + if(vOuter == nullVertexOuter) return AtomData(); + else return depict(vOuter); + } + + BondType operator()(SEdge eInner) const { + GVertex vSrcOuter = getOuterVertexFromInnerVertex(source(eInner, gInner)); + GVertex vTarOuter = getOuterVertexFromInnerVertex(target(eInner, gInner)); + if(vSrcOuter == nullVertexOuter || vTarOuter == nullVertexOuter) return BondType::Invalid; + else return depict(getOuterEdgeFromInnerEdge(eInner)); + } + + std::string getVertexLabelNoIsotopeChargeRadical(SVertex vInner) const { + GVertex vOuter = getOuterVertexFromInnerVertex(vInner); + if(vOuter != nullVertexOuter)return depict.getVertexLabelNoIsotopeChargeRadical(vOuter); + auto iter = vMap.find(vInner); + assert(iter != end(vMap)); + assert(iter->second != -1); + const auto &emb = conf.begin()[iter->second]; + switch(emb.type) { + case EmbeddingEdge::Type::Edge: + MOD_ABORT; + case EmbeddingEdge::Type::LonePair: + return "e"; + case EmbeddingEdge::Type::Radical: + return "r"; + } + MOD_ABORT; + } + + std::string getEdgeLabel(SEdge eInner) const { + GVertex vSrcOuter = getOuterVertexFromInnerVertex(source(eInner, gInner)); + GVertex vTarOuter = getOuterVertexFromInnerVertex(target(eInner, gInner)); + if(vSrcOuter == nullVertexOuter || vTarOuter == nullVertexOuter) return ""; + else return depict.getEdgeLabel(getOuterEdgeFromInnerEdge(eInner)); + } + + bool hasImportantStereo(SVertex vInner) const { + return true; + } + + bool getHasCoordinates() const { + return false; + } + + double getX(SVertex v, bool b) const { + return 0; + } + + double getY(SVertex v, bool b) const { + return 0; + } + + bool isVisible(SVertex v) const { + if(printLonePairs) return true; + else + MOD_ABORT; + return true; + } + + std::string getColour(SVertex) const { + return ""; + } + + std::string getColour(SEdge) const { + return ""; + } + + std::string getShownId(SVertex vInner) const { + GVertex vOuter = getOuterVertexFromInnerVertex(vInner); + if(vOuter == nullVertexOuter) MOD_ABORT; + else return boost::lexical_cast(shownId(gOuter, vOuter)); + } + + bool overwriteWithIndex(SVertex vInner) const { + auto vOuter = getOuterVertexFromInnerVertex(vInner); + return vOuter == nullVertexOuter; + } + + IO::Graph::Write::EdgeFake3DType getEdgeFake3DType(SEdge eInner, bool withHydrogen) const { + auto iter = edgeDepiction.find(std::make_pair(source(eInner, gInner), target(eInner, gInner))); + assert(iter != end(edgeDepiction)); + return iter->second; + } + + std::string getRawStereoString(SVertex vInner) const { + return ""; + } + + std::string getPrettyStereoString(SVertex vInner) const { + return ""; + } + + std::string getStereoString(SEdge eInner) const { + return ""; + } + + std::string getEdgeAnnotation(SEdge eInner) const { + SVertex vSrcInner = source(eInner, gInner), vTarInner = target(eInner, gInner); + auto iterSrc = vMap.find(vSrcInner), iterTar = vMap.find(vTarInner); + assert(iterSrc != end(vMap)); + assert(iterTar != end(vMap)); + assert(iterSrc->second == -1 || iterTar->second == -1); + bool swapped = false; + if(iterSrc->second != -1) { + std::swap(vSrcInner, vTarInner); + std::swap(iterSrc, iterTar); + swapped = true; + } + std::string res; + // if the edge category does not correspond to the bond type, then print it + const EmbeddingEdge &emb = conf.begin()[iterTar->second]; + if(emb.type == EmbeddingEdge::Type::Edge) { + auto eOuter = getOuterEdgeFromInnerEdge(eInner); + auto eCatFromBt = bondTypeToEdgeCategory(depict.getBondData(eOuter)); + if(eCatFromBt != emb.cat) { + res += " node[auto] {"; + res += boost::lexical_cast(emb.cat); + res += "}"; + } + } + + // print the offset and whatever the configuration wants + res += " node[auto, pos="; + if(swapped) res += "0.25"; + else res += "0.75"; + res += "] {{\\tiny "; + res += boost::lexical_cast(iterTar->second); + res += conf.getEdgeAnnotation(iterTar->second); + res += "}} "; + return res; + } + + bool disallowHydrogenCollapse(SVertex) const { + return false; + } + + std::string getOpts(SVertex v) const { + return std::string(); + } + public: + int getOutputId(SVertex vInner) const { + return get(boost::vertex_index_t(), gInner, vInner); + } + private: + const GraphPrint &gOuter; + GVertex vOuterCenter, nullVertexOuter; + const GraphStereo &gInner; + const Depict &depict; + bool printLonePairs; + const std::map &vMap; + const Configuration &conf; + const std::map, IO::Graph::Write::EdgeFake3DType> &edgeDepiction; + ShownId shownId; + } depictAndAdvOptions(g, v, gStereo, depict, printLonePairs, vMap, conf, edgeDepiction, shownId); + auto bonusWriter = [&](std::ostream &s) { + }; + lib::IO::Graph::Write::tikz(s, options, gStereo, depictAndAdvOptions, coordFile, depictAndAdvOptions, bonusWriter, + ""); + return std::pair(s, coordFile); +} + +template +std::string pdf(const Graph &g, typename boost::graph_traits::vertex_descriptor v, + const Configuration &conf, const std::string &name, const Depict &depict, + const lib::IO::Graph::Write::Options &options, ShownId shownId) { + const auto p = tikz(g, v, conf, name, depict, options, shownId); + std::string fileTikz = p.first, fileCoords = p.second; + std::string fileNoExt = fileTikz.substr(0, fileTikz.size() - 4); + std::string fileCoordsNoExt = fileCoords.substr(0, fileCoords.size() - 4); + IO::post() << "compileTikz \"" << fileNoExt << "\" \"" << fileCoordsNoExt << "\"" << std::endl; + return fileNoExt + ".pdf"; +} + +} // namespace mod::lib::Stereo::Write + +#endif // MOD_LIG_STEREO_IO_WRITECONFIGURATION_HPP diff --git a/libs/libmod/src/mod/lib/Stereo/Inference.hpp b/libs/libmod/src/mod/lib/Stereo/Inference.hpp index 82b02f7..e911c7d 100644 --- a/libs/libmod/src/mod/lib/Stereo/Inference.hpp +++ b/libs/libmod/src/mod/lib/Stereo/Inference.hpp @@ -15,49 +15,39 @@ #include namespace mod::lib::Stereo { -namespace detail { -template -struct makeInferenceHelper; - -struct InferenceBase { - struct VertexData { - GeometryGraph::Vertex vGeometry = GeometryGraph::nullGeometry(); - std::size_t nextAvailableVirtual; - std::vector edges; - bool explicitEmbedding = false; - Fixation fix = Fixation::free(); - public: - std::unique_ptr configuration; - }; - struct EdgeData { - EdgeCategorySubset valid = EdgeCategorySubset().all(); - EdgeCategory finalCategory; // will be set by finalize - }; +struct InferenceVertexData { + GeometryGraph::Vertex vGeometry = GeometryGraph::nullGeometry(); + int nextAvailableVirtual; + std::vector edges; + bool explicitEmbedding = false; + Fixation fix = Fixation::free(); +public: + std::unique_ptr configuration; }; -} // namespace detail +struct InferenceEdgeData { + EdgeCategorySubset valid = EdgeCategorySubset().all(); + EdgeCategory finalCategory; // will be set by finalize +}; template -struct Inference : private detail::InferenceBase { +struct Inference { using Vertex = typename boost::graph_traits::vertex_descriptor; using Edge = typename boost::graph_traits::edge_descriptor; public: - friend class detail::makeInferenceHelper; - Inference(const Graph &g, PropMolecule &&) = delete; Inference(const Graph &g, const PropMolecule &pMolecule, bool asPattern) : g(g), pMolecule(pMolecule), asPattern(asPattern), - hasFinalized(false), vertexData(num_vertices(g)), edgeData(num_edges(g)) { - for(Vertex v : asRange(vertices(g))) { + vertexData(num_vertices(g)), edgeData(num_edges(g)) { + for(const auto v: asRange(vertices(g))) vertexData[get(boost::vertex_index_t(), g, v)].nextAvailableVirtual = out_degree(v, g); - } } lib::IO::Result<> assignGeometry(Vertex v, GeometryGraph::Vertex vGeometry) { assert(vGeometry != GeometryGraph::nullGeometry()); - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; if(data.vGeometry != GeometryGraph::nullGeometry()) return lib::IO::Result<>::Error("Geometry already assigned."); @@ -66,7 +56,7 @@ struct Inference : private detail::InferenceBase { } lib::IO::Result<> assignEdgeCategory(Edge e, EdgeCategory cat) { - auto eId = get(boost::edge_index_t(), g, e); + const auto eId = get(boost::edge_index_t(), g, e); auto &data = edgeData[eId]; if(!data.valid(cat)) { return lib::IO::Result<>::Error( @@ -79,42 +69,42 @@ struct Inference : private detail::InferenceBase { } void initEmbedding(Vertex v) { - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; data.explicitEmbedding = true; } void addEdge(Vertex v, Edge eOut) { - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; data.explicitEmbedding = true; - auto outRange = out_edges(v, g); - auto iter = std::find(outRange.first, outRange.second, eOut); + const auto outRange = out_edges(v, g); + const auto iter = std::find(outRange.first, outRange.second, eOut); assert(iter != outRange.second); // Add the edge with undefined category. It will be assigned properly in finalization. data.edges.emplace_back(std::distance(outRange.first, iter), EmbeddingEdge::Type::Edge, EdgeCategory::Undefined); } void addLonePair(Vertex v) { - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; data.explicitEmbedding = true; - auto offset = data.nextAvailableVirtual; + const auto offset = data.nextAvailableVirtual; ++data.nextAvailableVirtual; data.edges.emplace_back(offset, EmbeddingEdge::Type::LonePair, EdgeCategory::Single); } void addRadical(Vertex v) { - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; data.explicitEmbedding = true; - auto offset = data.nextAvailableVirtual; + const auto offset = data.nextAvailableVirtual; ++data.nextAvailableVirtual; data.edges.emplace_back(offset, EmbeddingEdge::Type::Radical, EdgeCategory::Single); } void fixSimpleGeometry(Vertex v) { - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; assert(data.explicitEmbedding); // the embedding should have been initialised at this point data.fix = Fixation::simpleFixed(); @@ -127,8 +117,8 @@ struct Inference : private detail::InferenceBase { assert(!hasFinalized); // Finalize edge categories. //-------------------------------------------------------------------------- - for(Edge e : asRange(edges(g))) { - auto eId = get(boost::edge_index_t(), g, e); + for(const auto e: asRange(edges(g))) { + const auto eId = get(boost::edge_index_t(), g, e); auto &data = edgeData[eId]; const EdgeCategorySubset &eCat = data.valid; if(eCat.count() == 0) MOD_ABORT; // should have been handled earlier @@ -136,7 +126,7 @@ struct Inference : private detail::InferenceBase { data.finalCategory = eCat.selectFirst(); } else { // assume chemical - auto eCatChem = bondTypeToEdgeCategory(pMolecule[e]); + const auto eCatChem = bondTypeToEdgeCategory(pMolecule[e]); if(!eCat(eCatChem)) { MOD_ABORT; // TODO: implement } @@ -146,25 +136,24 @@ struct Inference : private detail::InferenceBase { // Assign edge categories to embedding edges. //-------------------------------------------------------------------------- - for(const Vertex v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { const auto vId = get(boost::vertex_index_t(), g, v); - auto &data = vertexData[vId]; - for(auto &emb : data.edges) { + for(auto &emb: vertexData[vId].edges) { if(emb.type != EmbeddingEdge::Type::Edge) continue; - auto e = emb.getEdge(v, g); + const auto e = emb.getEdge(v, g); emb.cat = edgeData[get(boost::edge_index_t(), g, e)].finalCategory; } } - for(const Vertex v : asRange(vertices(g))) { + for(const auto v: asRange(vertices(g))) { auto res = finalizeVertex(warnings, v, vertexPrinter); if(!res) return res; } // Construct the configurations. //-------------------------------------------------------------------------- - for(Vertex v : asRange(vertices(g))) { - auto vId = get(boost::vertex_index_t(), g, v); + for(const auto v: asRange(vertices(g))) { + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; assert(data.vGeometry != GeometryGraph::nullGeometry()); assert(!data.configuration); @@ -180,14 +169,14 @@ struct Inference : private detail::InferenceBase { } private: EdgeCategoryCount addEdgesFromGraph(Vertex v) { - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; - auto d = out_degree(v, g); + const auto d = out_degree(v, g); assert(data.edges.empty()); data.edges.reserve(d); std::size_t offset = 0; EdgeCategoryCount catCount; - for(Edge eOut : asRange(out_edges(v, g))) { + for(const auto eOut: asRange(out_edges(v, g))) { data.edges.emplace_back(offset, EmbeddingEdge::Type::Edge, edgeData[get(boost::edge_index_t(), g, eOut)].finalCategory); ++catCount[edgeData[get(boost::edge_index_t(), g, eOut)].finalCategory]; @@ -208,7 +197,7 @@ struct Inference : private detail::InferenceBase { template lib::IO::Result<> finalizeVertex(lib::IO::Warnings &warnings, Vertex v, VertexPrinter vertexPrinter) { const auto &geo = getGeometryGraph(); - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); auto &data = vertexData[vId]; AtomData ad = pMolecule[v]; EdgeCategoryCount catCount; @@ -222,40 +211,43 @@ struct Inference : private detail::InferenceBase { catCount = addEdgesFromGraph(v); // TODO: shouldn't we deduce radical? } else { - std::vector neighbourPresent(data.edges.size(), false); - if(data.edges.size() < out_degree(v, g)) { + // check if all real edges are there, once + std::vector offsetsUsed(data.edges.size(), false); + int edgeCount = 0; + for(const auto &emb: data.edges) { + if(emb.type != EmbeddingEdge::Type::Edge) continue; + assert(emb.offset < out_degree(v, g)); + if(offsetsUsed[emb.offset]) + return lib::IO::Result<>::Error( + "Duplicate edge in stereo embedding for vertex " + vertexPrinter(v) + "."); + offsetsUsed[emb.offset] = true; + ++edgeCount; + } + if(edgeCount != out_degree(v, g)) { + assert(edgeCount < out_degree(v, g)); return lib::IO::Result<>::Error( - "Too few edges in embedding for vertex " + vertexPrinter(v) + ". Got " + - std::to_string(data.edges.size()) - + ", but the degree is " + std::to_string(out_degree(v, g)) + "." + "Too few edges in stereo embedding for vertex " + vertexPrinter(v) + ". Got " + + std::to_string(edgeCount) + + " edges, but the degree is " + std::to_string(out_degree(v, g)) + "." ); } - for(const auto &emb : data.edges) { - if(emb.offset >= data.edges.size()) { - MOD_ABORT; // error, offset out of bounds - return lib::IO::Result<>::Error(""); - } - if(neighbourPresent[emb.offset]) { - MOD_ABORT; // error, duplicate neighbour - return lib::IO::Result<>::Error(""); - } - if(emb.offset < out_degree(v, g) && emb.type != EmbeddingEdge::Type::Edge) { - MOD_ABORT; // error, [0, d[ are reserved for the real edges - return lib::IO::Result<>::Error(""); - } - neighbourPresent[emb.offset] = true; + + // and now count stuff + for(const auto &emb: data.edges) { + assert(emb.offset < data.edges.size()); switch(emb.type) { case EmbeddingEdge::Type::Edge: ++catCount[emb.cat]; break; case EmbeddingEdge::Type::LonePair: + assert(emb.offset >= out_degree(v, g)); // should not happen, [0, d[ are reserved for the real edges ++numLonePairs; break; case EmbeddingEdge::Type::Radical: - if(radical) { - MOD_ABORT; // only one radical per vertex - return lib::IO::Result<>::Error(""); - } + assert(emb.offset >= out_degree(v, g)); // should not happen, [0, d[ are reserved for the real edges + if(radical) + return lib::IO::Result<>::Error( + "Multiple radicals in stereo embedding for vertex " + vertexPrinter(v) + "."); radical = true; break; } @@ -283,13 +275,14 @@ struct Inference : private detail::InferenceBase { public: // if hasFinalized std::unique_ptr extractConfiguration(Vertex v) { assert(hasFinalized); - auto vId = get(boost::vertex_index_t(), g, v); + const auto vId = get(boost::vertex_index_t(), g, v); assert(vertexData[vId].configuration); return std::move(vertexData[vId].configuration); } EdgeCategory getEdgeCategory(Edge e) const { - auto eId = get(boost::edge_index_t(), g, e); + assert(hasFinalized); + const auto eId = get(boost::edge_index_t(), g, e); assert(eId < num_edges(g)); return edgeData[eId].finalCategory; } @@ -298,33 +291,11 @@ struct Inference : private detail::InferenceBase { const PropMolecule &pMolecule; const bool asPattern; private: - bool hasFinalized; - std::vector vertexData; - std::vector edgeData; + bool hasFinalized = false; + std::vector vertexData; + std::vector edgeData; }; -namespace detail { - -// see http://stackoverflow.com/questions/27835925/overload-between-rvalue-reference-and-const-lvalue-reference-in-template - -template -struct makeInferenceHelper { - static Inference make(const Graph &g, PropMolecule &&pMolecule, bool asPattern) = delete; - - static Inference make(const Graph &g, const PropMolecule &pMolecule, bool asPattern) { - return Inference(g, pMolecule, asPattern); - } -}; - -} // detail - -template -Inference::type, typename std::decay::type> -makeInference(Graph &&g, PropMolecule &&pMolecule, bool asPattern) { - return detail::makeInferenceHelper::type, typename std::decay::type> - ::make(std::forward(g), std::forward(pMolecule), asPattern); -} - } // namespace mod::lib::Stereo #endif // MOD_LIB_STEREO_INFERENCE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Term/IO/Read.cpp b/libs/libmod/src/mod/lib/Term/IO/Read.cpp new file mode 100644 index 0000000..1270393 --- /dev/null +++ b/libs/libmod/src/mod/lib/Term/IO/Read.cpp @@ -0,0 +1,113 @@ +#include "Read.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +namespace mod::lib::Term::Read { +namespace detail { + +struct Structure; + +struct Variable { + std::string name; +}; + +struct Term : x3::variant> { + using base_type::base_type; + using base_type::operator=; +}; + +struct Structure { + std::string name; + std::vector arguments; +}; + +namespace { +namespace parser { +#define FIRST "A-Za-z0-9=#:.+-" +#define SECOND "_" + +const x3::rule term = "term"; +const x3::rule function = "function"; +const x3::rule > termList = "term list"; +const x3::rule variable = "variable"; +const x3::rule identifier = "identifier"; + +const auto term_def = function | variable; +const auto function_def = identifier >> -('(' > termList > ')'); +const auto termList_def = term % ','; +const auto variable_def = (x3::lexeme['_' > identifier] >> x3::eps) | x3::string("*"); +const auto identifier_def = x3::lexeme[x3::char_(FIRST) > *x3::char_(SECOND FIRST)]; + +BOOST_SPIRIT_DEFINE(term, function, termList, variable, identifier) +} // namespace parser +} // namespace + +struct Converter : public boost::static_visitor { + Converter(const Converter &) = delete; + Converter &operator=(const Converter &) = delete; + + Converter(const StringStore &stringStore) : stringStore(stringStore) {} + + RawTerm operator()(const Variable &var) { + std::size_t stringId; + if(var.name == "*") { + for(;; ++nextVar) { + std::string name = "X" + std::to_string(nextVar) + "_"; + if(!stringStore.hasString(name)) { + stringId = stringStore.getIndex(name); + break; + } + } + } else { + stringId = stringStore.getIndex(var.name); + } + return RawVariable{stringId}; + } + + RawTerm operator()(const Structure &str) { + auto stringId = stringStore.getIndex(str.name); + std::vector arguments; + for(const auto &a : str.arguments) + arguments.push_back(boost::apply_visitor(*this, a)); + return RawStructure{stringId, std::move(arguments)}; + } +private: + const StringStore &stringStore; + std::size_t nextVar = 0; +}; + +} // namespace detail + +RawTerm rawTerm(const std::string &data, const StringStore &stringStore) { + detail::Term term; + IO::parse(data.begin(), data.end(), detail::parser::term, term, true, x3::space); + detail::Converter converter(stringStore); + return boost::apply_visitor(converter, term); +} + +} // namespace mod::lib::Term::Read + +BOOST_FUSION_ADAPT_STRUCT(mod::lib::Term::Read::detail::Variable, + (std::string, name)) +BOOST_FUSION_ADAPT_STRUCT(mod::lib::Term::Read::detail::Structure, + (std::string, name) + (std::vector, arguments)) \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Term/IO/Read.hpp b/libs/libmod/src/mod/lib/Term/IO/Read.hpp new file mode 100644 index 0000000..8161de8 --- /dev/null +++ b/libs/libmod/src/mod/lib/Term/IO/Read.hpp @@ -0,0 +1,20 @@ +#ifndef MOD_LIB_TERM_IO_READ_HPP +#define MOD_LIB_TERM_IO_READ_HPP + +#include +#include + +#include +#include + +namespace mod::lib { +struct StringStore; +} // namespace mod::lib +namespace mod::lib::Term::Read { + +// throws lib::IO::ParsingError on error +RawTerm rawTerm(const std::string &data, const StringStore &stringStore); + +} // namespace mod::lib::Term::Read + +#endif // MOD_LIB_TERM_IO_READ_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Term/IO/Write.cpp b/libs/libmod/src/mod/lib/Term/IO/Write.cpp new file mode 100644 index 0000000..a22c3da --- /dev/null +++ b/libs/libmod/src/mod/lib/Term/IO/Write.cpp @@ -0,0 +1,192 @@ +#include "Write.hpp" + +#include + +#include +#include +#include + +#include + +namespace mod::lib::Term::Write { +namespace { + +std::ostream &rawVarFromCell(std::ostream &s, Cell cell) { + assert(cell.tag == Cell::Tag::REF); + switch(cell.REF.addr.type) { + case AddressType::Heap: + return s << "_H" << cell.REF.addr.addr; + case AddressType::Temp: + return s << "_T" << cell.REF.addr.addr; + } + MOD_ABORT; +} + +} // namespace + +std::ostream &rawTerm(const RawTerm &term, const StringStore &strings, std::ostream &s) { + struct Printer { + void operator()(RawVariable v) const { + s << "_" << strings.getString(v.name); + } + + void operator()(const RawStructure &str) const { + s << strings.getString(str.name); + if(!str.args.empty()) { + s << '('; + std::visit(*this, str.args.front()); + for(int i = 1; i != str.args.size(); i++) { + s << ", "; + std::visit(*this, str.args[i]); + } + s << ')'; + } + } + public: + std::ostream &s; + const StringStore &strings; + }; + std::visit(Printer{s, strings}, term); + return s; +} + +std::ostream &element(Cell cell, const StringStore &strings, std::ostream &s) { + switch(cell.tag) { + case Cell::Tag::STR: + return s << "STR " << cell.STR.addr; + case Cell::Tag::Structure: + s << strings.getString(cell.Structure.name); + if(cell.Structure.arity > 0) + s << "/" << cell.Structure.arity; + return s; + case Cell::Tag::REF: + return s << "REF " << cell.REF.addr; + } + __builtin_unreachable(); +} + +void wam(const Wam &machine, const StringStore &strings, std::ostream &s) { + wam(machine, strings, s, [](Address, std::ostream &) {}); +} + +void wam(const Wam &machine, const StringStore &strings, std::ostream &s, + std::function addressCallback) { + s << "Heap:" << std::endl; + for(std::size_t i = 0; i < machine.getHeap().size(); i++) { + Cell cell = machine.getHeap()[i]; + s << std::setw(5) << std::left << i; + element(cell, strings, s); + addressCallback({AddressType::Heap, i}, s); + s << std::endl; + } + s << "-------------------------------------------------" << std::endl; + s << "Temp:" << std::endl; + for(std::size_t i = 0; i < machine.getTemp().size(); i++) { + Cell cell = machine.getTemp()[i]; + s << std::setw(5) << std::left << i; + element(cell, strings, s); + addressCallback({AddressType::Temp, i}, s); + s << std::endl; + } + s << "-------------------------------------------------" << std::endl; +} + +std::ostream &term(const Wam &machine, Address addr, const StringStore &strings, std::ostream &s) { + struct Printer { + Printer(const Wam &machine, const StringStore &strings, std::ostream &s) + : machine(machine), strings(strings), s(s) { + occurred[0].resize(machine.getHeap().size(), 0); + occurred[1].resize(machine.getTemp().size(), 0); + } + + void operator()(Address addr) { + Cell cell = machine.getCell(addr); + switch(cell.tag) { + case Cell::Tag::REF: + if(cell.REF.addr == addr + || occurred[static_cast(cell.REF.addr.type)][cell.REF.addr.addr] != 0 + ) { + rawVarFromCell(s, cell); + } else (*this)(cell.REF.addr); + break; + case Cell::Tag::STR: + (*this)(cell.STR.addr); + break; + case Cell::Tag::Structure: + if(occurred[static_cast(addr.type)][addr.addr] != 0) { + wam(machine, strings, std::cout); + std::cout << "addr.addr = " << addr.addr << std::endl; + std::cout << "occurred:" << std::endl; + for(int aType : {0, 1}) { + for(const auto o : occurred[aType]) { + if(o == 0) continue; + std::cout << " [" << aType << "]: " << o << std::endl; + } + } + } + assert(occurred[static_cast(addr.type)][addr.addr] == 0); + s << strings.getString(cell.Structure.name); + if(cell.Structure.arity > 0) { + ++occurred[static_cast(addr.type)][addr.addr]; + s << "("; + (*this)(addr + 1); + for(std::size_t i = 2; i <= cell.Structure.arity; i++) { + s << ", "; + (*this)(addr + i); + } + s << ")"; + --occurred[static_cast(addr.type)][addr.addr]; + } + break; + } + } + private: + const Wam &machine; + const StringStore &strings; + std::ostream &s; + std::array, 2> occurred; + }; + Printer(machine, strings, s)(addr); + return s; +} + +std::ostream &mgu(const Wam &machine, const MGU &mgu, const StringStore &strings, std::ostream &s) { + switch(mgu.status) { + case MGU::Status::Exists: + s << "Exists: "; + break; + case MGU::Status::Fail: + term(machine, mgu.errorLeft, strings, s << "Fail(") << " != "; + term(machine, mgu.errorRight, strings, s) << ")"; + return s; + } + bool first = true; + for(auto binding : mgu.bindings) { + if(binding.type == AddressType::Heap && binding.addr >= mgu.preHeapSize) continue; + if(!first) s << ", "; + first = false; + Cell cell; + cell.tag = Cell::Tag::REF; + cell.REF.addr = binding; + rawVarFromCell(s, cell) << " = "; + term(machine, binding, strings, s); + } + return s; +} + +} // namespace mod::lib::Term::Write +namespace mod::lib::Term { + +std::ostream &operator<<(std::ostream &s, Address addr) { + switch(addr.type) { + case AddressType::Heap: + s << "H"; + break; + case AddressType::Temp: + s << "T"; + break; + } + return s << "[" << addr.addr << "]"; +} + +} // namespace mod::lib::Term \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Term/IO/Write.hpp b/libs/libmod/src/mod/lib/Term/IO/Write.hpp new file mode 100644 index 0000000..132bb32 --- /dev/null +++ b/libs/libmod/src/mod/lib/Term/IO/Write.hpp @@ -0,0 +1,25 @@ +#ifndef MOD_LIB_TERM_IO_WRITE_HPP +#define MOD_LIB_TERM_IO_WRITE_HPP + +#include +#include + +#include +#include + +namespace mod::lib { +struct StringStore; +} // namespace mod::lib +namespace mod::lib::Term::Write { + +std::ostream &rawTerm(const RawTerm &term, const StringStore &strings, std::ostream &s); +std::ostream &element(Cell cell, const StringStore &strings, std::ostream &s); +void wam(const Wam &machine, const StringStore &strings, std::ostream &s); +void wam(const Wam &machine, const StringStore &strings, std::ostream &s, + std::function addressCallback); +std::ostream &term(const Wam &machine, Address addr, const StringStore &strings, std::ostream &s); +std::ostream &mgu(const Wam &machine, const MGU &mgu, const StringStore &strings, std::ostream &s); + +} // namespace mod::lib::Term::Write + +#endif // MOD_LIB_TERM_IO_WRITE_HPP \ No newline at end of file diff --git a/libs/libmod/src/mod/lib/Term/WAM.hpp b/libs/libmod/src/mod/lib/Term/WAM.hpp index f750a08..f5cf11e 100644 --- a/libs/libmod/src/mod/lib/Term/WAM.hpp +++ b/libs/libmod/src/mod/lib/Term/WAM.hpp @@ -43,7 +43,7 @@ struct Address { return Address{addr.type, addr.addr + k}; } - // implemented in the IO cpp file + // implemented in IO/Write.cpp file friend std::ostream &operator<<(std::ostream &s, Address addr); }; diff --git a/libs/libmod/src/mod/rule/CompositionMatch.cpp b/libs/libmod/src/mod/rule/CompositionMatch.cpp index 3c6b1cd..3557c76 100644 --- a/libs/libmod/src/mod/rule/CompositionMatch.cpp +++ b/libs/libmod/src/mod/rule/CompositionMatch.cpp @@ -58,9 +58,12 @@ Rule::LeftGraph::Vertex CompositionMatch::operator[](Rule::RightGraph::Vertex vF const auto &gSecond = p->rSecond->getRule().getGraph(); const auto vFirstInner = vertices(gFirst).first[vFirst.getId()]; const auto vSecondInner = p->mb.getSecondFromFirst(vFirstInner); + if(vSecondInner == boost::graph_traits::null_vertex()) + return {}; const auto vCore = Rule::Vertex(p->rSecond, get(boost::vertex_index_t(), gSecond, vSecondInner)); - if(!vCore) return {}; - else return vCore.getLeft(); + assert(get(boost::vertex_index_t(), gSecond, vSecondInner) == vCore.getId()); + assert(vCore); + return vCore.getLeft(); } Rule::RightGraph::Vertex CompositionMatch::operator[](Rule::LeftGraph::Vertex vSecond) const { @@ -69,10 +72,13 @@ Rule::RightGraph::Vertex CompositionMatch::operator[](Rule::LeftGraph::Vertex vS const auto &gFirst = p->rFirst->getRule().getGraph(); const auto &gSecond = p->rSecond->getRule().getGraph(); const auto vSecondInner = vertices(gSecond).first[vSecond.getId()]; - const auto vFirstInner = p->mb.getSecondFromFirst(vSecondInner); + const auto vFirstInner = p->mb.getFirstFromSecond(vSecondInner); + if(vFirstInner == boost::graph_traits::null_vertex()) + return {}; const auto vCore = Rule::Vertex(p->rFirst, get(boost::vertex_index_t(), gFirst, vFirstInner)); - if(!vCore) return {}; - else return vCore.getRight(); + assert(get(boost::vertex_index_t(), gFirst, vFirstInner) == vCore.getId()); + assert(vCore); + return vCore.getRight(); } void CompositionMatch::push(Rule::RightGraph::Vertex vFirst, Rule::LeftGraph::Vertex vSecond) { diff --git a/libs/libmod/src/mod/rule/GraphInterface.cpp b/libs/libmod/src/mod/rule/GraphInterface.cpp index b54854d..9a59fbf 100644 --- a/libs/libmod/src/mod/rule/GraphInterface.cpp +++ b/libs/libmod/src/mod/rule/GraphInterface.cpp @@ -3,10 +3,10 @@ #include #include #include +#include #include -#include +#include #include -#include namespace mod::rule { @@ -20,15 +20,15 @@ std::ostream &operator<<(std::ostream &s, const Rule::LeftGraph &g) { std::size_t Rule::LeftGraph::numVertices() const { using boost::vertices; - const auto &graph = get_left(r->getRule().getDPORule()); - const auto &vs = vertices(graph); + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &vs = vertices(getL(rDPO)); return std::distance(vs.first, vs.second); } std::size_t Rule::LeftGraph::numEdges() const { using boost::edges; - const auto &graph = get_left(r->getRule().getDPORule()); - const auto &es = edges(graph); + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &es = edges(getL(rDPO)); return std::distance(es.first, es.second); } @@ -36,51 +36,60 @@ std::size_t Rule::LeftGraph::numEdges() const { // Vertex //------------------------------------------------------------------------------ -MOD_GRAPHPIMPL_Define_Vertex(Rule::LeftGraph, RuleLeftGraph, - get_left(this->g->getRule()->getRule().getDPORule()), g, ->getRule()) +MOD_GRAPHPIMPL_Define_Vertex_noGraph_noId(Rule::LeftGraph, RuleLeftGraph, + getL(this->g->getRule()->getRule().getDPORule().getRule()), g, ->getRule()) +MOD_GRAPHPIMPL_Define_Vertex_graph(Rule::LeftGraph, g) + +std::size_t Rule::LeftGraph::Vertex::getId() const { + return getCore().getId(); +} + MOD_GRAPHPIMPL_Define_Vertex_Undirected(Rule::LeftGraph, - get_left(this->g->getRule()->getRule().getDPORule()), g) + getL(this->g->getRule()->getRule().getDPORule().getRule()), g) const std::string &Rule::LeftGraph::Vertex::getStringLabel() const { if(!*this) throw LogicError("Can not get string label on a null vertex."); - const auto &graph = get_left(getRule()->getRule().getDPORule()); + const auto &lr = g->getRule()->getRule().getDPORule(); + const auto &rDPO = lr.getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return g->getRule()->getRule().getStringState().getLeft()[v]; + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + return get_string(lr).getLeft()[v]; } AtomId Rule::LeftGraph::Vertex::getAtomId() const { if(!*this) throw LogicError("Can not get atom id on a null vertex."); - const auto &graph = get_left(g->getRule()->getRule().getDPORule()); + const auto &lr = g->getRule()->getRule().getDPORule(); + const auto &rDPO = lr.getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return g->getRule()->getRule().getMoleculeState().getLeft()[v].getAtomId(); + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + return get_molecule(lr).getLeft()[v].getAtomId(); } Isotope Rule::LeftGraph::Vertex::getIsotope() const { if(!*this) throw LogicError("Can not get isotope on a null vertex."); - const auto &graph = get_left(g->getRule()->getRule().getDPORule()); + const auto &lr = g->getRule()->getRule().getDPORule(); + const auto &rDPO = lr.getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return g->getRule()->getRule().getMoleculeState().getLeft()[v].getIsotope(); + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + return get_molecule(lr).getLeft()[v].getIsotope(); } Charge Rule::LeftGraph::Vertex::getCharge() const { if(!*this) throw LogicError("Can not get charge on a null vertex."); - const auto &graph = get_left(g->getRule()->getRule().getDPORule()); + const auto &lr = g->getRule()->getRule().getDPORule(); + const auto &rDPO = lr.getRule(); using boost::vertices; - auto iter = vertices(graph).first; - std::advance(iter, vId); - auto v = *iter; - return g->getRule()->getRule().getMoleculeState().getLeft()[v].getCharge(); + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + return get_molecule(g->getRule()->getRule().getDPORule()).getLeft()[v].getCharge(); } bool Rule::LeftGraph::Vertex::getRadical() const { if(!*this) throw LogicError("Can not get radical status on a null vertex."); - const auto &graph = get_left(g->getRule()->getRule().getDPORule()); + const auto &lr = g->getRule()->getRule().getDPORule(); + const auto &rDPO = lr.getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return g->getRule()->getRule().getMoleculeState().getLeft()[v].getRadical(); + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + return get_molecule(lr).getLeft()[v].getRadical(); } std::string Rule::LeftGraph::Vertex::printStereo() const { @@ -94,19 +103,21 @@ std::string Rule::LeftGraph::Vertex::printStereo() const { std::string Rule::LeftGraph::Vertex::printStereo(const graph::Printer &p) const { if(!*this) throw LogicError("Can not print stereo on a null vertex."); - const auto &graph = get_graph(get_labelled_left(g->getRule()->getRule().getDPORule())); + const auto &rDPO = g->getRule()->getRule().getDPORule().getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return lib::IO::Stereo::Write::summary(g->getRule()->getRule(), v, lib::Rules::Membership::Left, p.getOptions()); + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + const auto vCG = get(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), v); + return lib::Rules::Write::stereoSummary(g->getRule()->getRule(), vCG, lib::Rules::Membership::L, p.getOptions()); } Rule::Vertex Rule::LeftGraph::Vertex::getCore() const { if(isNull()) return Rule::Vertex(); - const auto &graph = get_left(g->getRule()->getRule().getDPORule()); + const auto &rDPO = g->getRule()->getRule().getDPORule().getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - auto vCoreId = get(boost::vertex_index_t(), graph, v); - return Rule::Vertex(g->getRule(), vCoreId); + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + const auto vCG = get(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), v); + const auto vCGId = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), vCG); + return Rule::Vertex(g->getRule(), vCGId); } std::shared_ptr Rule::LeftGraph::Vertex::getRule() const { @@ -119,7 +130,7 @@ std::shared_ptr Rule::LeftGraph::Vertex::getRule() const { //------------------------------------------------------------------------------ MOD_GRAPHPIMPL_Define_Indices(Rule::LeftGraph, RuleLeftGraph, - get_left(this->g->getRule()->getRule().getDPORule()), g, ->getRule()) + getL(this->g->getRule()->getRule().getDPORule().getRule()), g, ->getRule()) BOOST_CONCEPT_ASSERT((boost::ForwardIterator)); BOOST_CONCEPT_ASSERT((boost::ForwardIterator)); @@ -127,32 +138,38 @@ BOOST_CONCEPT_ASSERT((boost::ForwardIteratorgetRule()->getRule().getDPORule()); + const auto &lr = g->getRule()->getRule().getDPORule(); + const auto &rDPO = lr.getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - auto e = *std::next(out_edges(v, graph).first, eId); - return g->getRule()->getRule().getStringState().getLeft()[e]; + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + const auto e = *std::next(out_edges(v, getL(rDPO)).first, eId); + return get_string(lr).getLeft()[e]; } BondType Rule::LeftGraph::Edge::getBondType() const { if(!*this) throw LogicError("Can not get bond type on a null edge."); - const auto &graph = get_left(g->getRule()->getRule().getDPORule()); + const auto &lr = g->getRule()->getRule().getDPORule(); + const auto &rDPO = lr.getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - auto e = *std::next(out_edges(v, graph).first, eId); - return g->getRule()->getRule().getMoleculeState().getLeft()[e]; + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + const auto e = *std::next(out_edges(v, getL(rDPO)).first, eId); + return get_molecule(lr).getLeft()[e]; } Rule::Edge Rule::LeftGraph::Edge::getCore() const { if(isNull()) return Rule::Edge(); - const auto &graph = get_left(g->getRule()->getRule().getDPORule()); + const auto &rDPO = g->getRule()->getRule().getDPORule().getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - auto vCoreId = get(boost::vertex_index_t(), graph, v); - auto e = *std::next(out_edges(v, graph).first, eId); - const auto &es = out_edges(v, get_graph(g->getRule()->getRule().getDPORule())); - auto eCoreId = std::distance(es.first, std::find(es.first, es.second, e)); - return Rule::Edge(g->getRule(), vCoreId, eCoreId); + const auto v = *std::next(vertices(getL(rDPO)).first, vId); + const auto e = *std::next(out_edges(v, getL(rDPO)).first, eId); + const auto vCG = get(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), v); + const auto eCG = get(rDPO.getLtoCG(), getL(rDPO), rDPO.getCombinedGraph(), e); + const auto vCGId = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), vCG); + const auto &esCG = out_edges(vCG, rDPO.getCombinedGraph()); + const auto eIter = std::find(esCG.first, esCG.second, eCG); + assert(eIter != esCG.second); + const auto eCGOffset = std::distance(esCG.first, eIter); + return Rule::Edge(g->getRule(), vCGId, eCGOffset); } std::shared_ptr Rule::LeftGraph::Edge::getRule() const { @@ -171,14 +188,14 @@ std::ostream &operator<<(std::ostream &s, const Rule::ContextGraph &g) { std::size_t Rule::ContextGraph::numVertices() const { using boost::vertices; - const auto &graph = get_context(r->getRule().getDPORule()); + const auto &graph = getK(r->getRule().getDPORule().getRule()); const auto &vs = vertices(graph); return std::distance(vs.first, vs.second); } std::size_t Rule::ContextGraph::numEdges() const { using boost::edges; - const auto &graph = get_context(r->getRule().getDPORule()); + const auto &graph = getK(r->getRule().getDPORule().getRule()); const auto &es = edges(graph); return std::distance(es.first, es.second); } @@ -188,17 +205,18 @@ std::size_t Rule::ContextGraph::numEdges() const { //------------------------------------------------------------------------------ MOD_GRAPHPIMPL_Define_Vertex(Rule::ContextGraph, RuleContextGraph, - get_context(this->g->getRule()->getRule().getDPORule()), g, ->getRule()) + getK(this->g->getRule()->getRule().getDPORule().getRule()), g, ->getRule()) MOD_GRAPHPIMPL_Define_Vertex_Undirected(Rule::ContextGraph, - get_context(this->g->getRule()->getRule().getDPORule()), g) + getK(this->g->getRule()->getRule().getDPORule().getRule()), g) Rule::Vertex Rule::ContextGraph::Vertex::getCore() const { if(isNull()) return Rule::Vertex(); - const auto &graph = get_context(g->getRule()->getRule().getDPORule()); + const auto &rDPO = g->getRule()->getRule().getDPORule().getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - auto vCoreId = get(boost::vertex_index_t(), graph, v); - return Rule::Vertex(g->getRule(), vCoreId); + const auto v = *std::next(vertices(getK(rDPO)).first, vId); + const auto vCG = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), v); + const auto vCGId = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), vCG); + return Rule::Vertex(g->getRule(), vCGId); } std::shared_ptr Rule::ContextGraph::Vertex::getRule() const { @@ -211,7 +229,7 @@ std::shared_ptr Rule::ContextGraph::Vertex::getRule() const { //------------------------------------------------------------------------------ MOD_GRAPHPIMPL_Define_Indices(Rule::ContextGraph, RuleContextGraph, - get_context(this->g->getRule()->getRule().getDPORule()), g, ->getRule()) + getK(this->g->getRule()->getRule().getDPORule().getRule()), g, ->getRule()) BOOST_CONCEPT_ASSERT((boost::ForwardIterator)); BOOST_CONCEPT_ASSERT((boost::ForwardIterator)); @@ -219,14 +237,18 @@ BOOST_CONCEPT_ASSERT((boost::ForwardIteratorgetRule().getDPORule()); + const auto &rDPO = g->getRule()->getRule().getDPORule().getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - auto vCoreId = get(boost::vertex_index_t(), graph, v); - auto e = *std::next(out_edges(v, graph).first, eId); - const auto &es = out_edges(v, get_graph(g->getRule()->getRule().getDPORule())); - auto eCoreId = std::distance(es.first, std::find(es.first, es.second, e)); - return Rule::Edge(g->getRule(), vCoreId, eCoreId); + const auto v = *std::next(vertices(getK(rDPO)).first, vId); + const auto e = *std::next(out_edges(v, getK(rDPO)).first, eId); + const auto vCG = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), v); + const auto eCG = get(rDPO.getKtoCG(), getK(rDPO), rDPO.getCombinedGraph(), e); + const auto vCGId = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), vCG); + const auto &esCG = out_edges(vCG, rDPO.getCombinedGraph()); + const auto eIter = std::find(esCG.first, esCG.second, eCG); + assert(eIter != esCG.second); + const auto eCGOffset = std::distance(esCG.first, eIter); + return Rule::Edge(g->getRule(), vCGId, eCGOffset); } std::shared_ptr Rule::ContextGraph::Edge::getRule() const { @@ -244,14 +266,14 @@ std::ostream &operator<<(std::ostream &s, const Rule::RightGraph &g) { std::size_t Rule::RightGraph::numVertices() const { using boost::vertices; - const auto &graph = get_right(r->getRule().getDPORule()); + const auto &graph = getR(r->getRule().getDPORule().getRule()); const auto &vs = vertices(graph); return std::distance(vs.first, vs.second); } std::size_t Rule::RightGraph::numEdges() const { using boost::edges; - const auto &graph = get_right(r->getRule().getDPORule()); + const auto &graph = getR(r->getRule().getDPORule().getRule()); const auto &es = edges(graph); return std::distance(es.first, es.second); } @@ -260,58 +282,65 @@ std::size_t Rule::RightGraph::numEdges() const { // Vertex //------------------------------------------------------------------------------ -MOD_GRAPHPIMPL_Define_Vertex(Rule::RightGraph, RuleRightGraph, - get_right(this->g->getRule()->getRule().getDPORule()), g, ->getRule()) +MOD_GRAPHPIMPL_Define_Vertex_noGraph_noId(Rule::RightGraph, RuleRightGraph, + getR(this->g->getRule()->getRule().getDPORule().getRule()), g, ->getRule()) +MOD_GRAPHPIMPL_Define_Vertex_graph(Rule::RightGraph, g) + +std::size_t Rule::RightGraph::Vertex::getId() const { + return getCore().getId(); +} + MOD_GRAPHPIMPL_Define_Vertex_Undirected(Rule::RightGraph, - get_right(this->g->getRule()->getRule().getDPORule()), g) + getR(this->g->getRule()->getRule().getDPORule().getRule()), g) const std::string &Rule::RightGraph::Vertex::getStringLabel() const { if(!g) throw LogicError("Can not get string label on a null vertex."); - const auto &graph = get_right(getRule()->getRule().getDPORule()); + const auto &graph = getR(getRule()->getRule().getDPORule().getRule()); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return getRule()->getRule().getStringState().getRight()[v]; + const auto v = *std::next(vertices(graph).first, vId); + return get_string(getRule()->getRule().getDPORule()).getRight()[v]; } AtomId Rule::RightGraph::Vertex::getAtomId() const { if(!g) throw LogicError("Can not get atom id on a null vertex."); - const auto &graph = get_right(getRule()->getRule().getDPORule()); + const auto &graph = getR(getRule()->getRule().getDPORule().getRule()); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return getRule()->getRule().getMoleculeState().getRight()[v].getAtomId(); + const auto v = *std::next(vertices(graph).first, vId); + return get_molecule(getRule()->getRule().getDPORule()).getRight()[v].getAtomId(); } Isotope Rule::RightGraph::Vertex::getIsotope() const { if(!g) throw LogicError("Can not get isotope on a null vertex."); - const auto &graph = get_right(getRule()->getRule().getDPORule()); + const auto &graph = getR(getRule()->getRule().getDPORule().getRule()); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return getRule()->getRule().getMoleculeState().getRight()[v].getIsotope(); + const auto v = *std::next(vertices(graph).first, vId); + return get_molecule(getRule()->getRule().getDPORule()).getRight()[v].getIsotope(); } Charge Rule::RightGraph::Vertex::getCharge() const { if(!g) throw LogicError("Can not get charge on a null vertex."); - const auto &graph = get_right(getRule()->getRule().getDPORule()); + const auto &graph = getR(getRule()->getRule().getDPORule().getRule()); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return getRule()->getRule().getMoleculeState().getRight()[v].getCharge(); + const auto v = *std::next(vertices(graph).first, vId); + return get_molecule(getRule()->getRule().getDPORule()).getRight()[v].getCharge(); } bool Rule::RightGraph::Vertex::getRadical() const { if(!g) throw LogicError("Can not get radical status on a null vertex."); - const auto &graph = get_right(getRule()->getRule().getDPORule()); + const auto &graph = getR(getRule()->getRule().getDPORule().getRule()); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return getRule()->getRule().getMoleculeState().getRight()[v].getRadical(); + const auto v = *std::next(vertices(graph).first, vId); + return get_molecule(getRule()->getRule().getDPORule()).getRight()[v].getRadical(); } Rule::Vertex Rule::RightGraph::Vertex::getCore() const { if(isNull()) return Rule::Vertex(); - const auto &graph = get_right(getRule()->getRule().getDPORule()); + const auto &rDPO = g->getRule()->getRule().getDPORule().getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - auto vCoreId = get(boost::vertex_index_t(), graph, v); - return Rule::Vertex(getRule(), vCoreId); + const auto v = *std::next(vertices(getR(rDPO)).first, vId); + const auto vCG = get(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), v); + const auto vCGId = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), vCG); + return Rule::Vertex(getRule(), vCGId); } std::shared_ptr Rule::RightGraph::Vertex::getRule() const { @@ -332,10 +361,12 @@ std::string Rule::RightGraph::Vertex::printStereo() const { std::string Rule::RightGraph::Vertex::printStereo(const graph::Printer &p) const { if(!g) throw LogicError("Can not print stereo on a null vertex."); - const auto &graph = get_graph(get_labelled_right(getRule()->getRule().getDPORule())); + const auto &graph = getR(getRule()->getRule().getDPORule().getRule()); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - return lib::IO::Stereo::Write::summary(getRule()->getRule(), v, lib::Rules::Membership::Right, p.getOptions()); + const auto v = *std::next(vertices(graph).first, vId); + const auto &dpo = g->getRule()->getRule().getDPORule().getRule(); + const auto vCG = get(dpo.getRtoCG(), getR(dpo), dpo.getCombinedGraph(), v); + return lib::Rules::Write::stereoSummary(getRule()->getRule(), vCG, lib::Rules::Membership::R, p.getOptions()); } //------------------------------------------------------------------------------ @@ -343,7 +374,7 @@ std::string Rule::RightGraph::Vertex::printStereo(const graph::Printer &p) const //------------------------------------------------------------------------------ MOD_GRAPHPIMPL_Define_Indices(Rule::RightGraph, RuleRightGraph, - get_right(this->g->getRule()->getRule().getDPORule()), g, ->getRule()) + getR(this->g->getRule()->getRule().getDPORule().getRule()), g, ->getRule()) BOOST_CONCEPT_ASSERT((boost::ForwardIterator)); BOOST_CONCEPT_ASSERT((boost::ForwardIterator)); @@ -351,36 +382,36 @@ BOOST_CONCEPT_ASSERT((boost::ForwardIteratorgetRule().getDPORule()); + const auto &graph = getR(getRule()->getRule().getDPORule().getRule()); using boost::vertices; - auto iter = vertices(graph).first; - std::advance(iter, vId); - auto v = *iter; - auto e = *std::next(out_edges(v, graph).first, eId); - return getRule()->getRule().getStringState().getRight()[e]; + const auto v = *std::next(vertices(graph).first, vId); + const auto e = *std::next(out_edges(v, graph).first, eId); + return get_string(getRule()->getRule().getDPORule()).getRight()[e]; } BondType Rule::RightGraph::Edge::getBondType() const { if(!g) throw LogicError("Can not get bond type on a null edge."); - const auto &graph = get_right(getRule()->getRule().getDPORule()); + const auto &graph = getR(getRule()->getRule().getDPORule().getRule()); using boost::vertices; - auto iter = vertices(graph).first; - std::advance(iter, vId); - auto v = *iter; - auto e = *std::next(out_edges(v, graph).first, eId); - return getRule()->getRule().getMoleculeState().getRight()[e]; + const auto v = *std::next(vertices(graph).first, vId); + const auto e = *std::next(out_edges(v, graph).first, eId); + return get_molecule(getRule()->getRule().getDPORule()).getRight()[e]; } Rule::Edge Rule::RightGraph::Edge::getCore() const { if(isNull()) return Rule::Edge(); - const auto &graph = get_right(getRule()->getRule().getDPORule()); + const auto &rDPO = g->getRule()->getRule().getDPORule().getRule(); using boost::vertices; - auto v = *std::next(vertices(graph).first, vId); - auto vCoreId = get(boost::vertex_index_t(), graph, v); - auto e = *std::next(out_edges(v, graph).first, eId); - const auto &es = out_edges(v, get_graph(getRule()->getRule().getDPORule())); - auto eCoreId = std::distance(es.first, std::find(es.first, es.second, e)); - return Rule::Edge(getRule(), vCoreId, eCoreId); + const auto v = *std::next(vertices(getR(rDPO)).first, vId); + const auto e = *std::next(out_edges(v, getR(rDPO)).first, eId); + const auto vCG = get(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), v); + const auto eCG = get(rDPO.getRtoCG(), getR(rDPO), rDPO.getCombinedGraph(), e); + const auto vCGId = get(boost::vertex_index_t(), rDPO.getCombinedGraph(), vCG); + const auto &esCG = out_edges(vCG, rDPO.getCombinedGraph()); + const auto eIter = std::find(esCG.first, esCG.second, eCG); + assert(eIter != esCG.second); + const auto eCGOffset = std::distance(esCG.first, eIter); + return Rule::Edge(g->getRule(), vCGId, eCGOffset); } std::shared_ptr Rule::RightGraph::Edge::getRule() const { @@ -402,62 +433,72 @@ MOD_GRAPHPIMPL_Define_Vertex_Undirected(Rule, r->getRule().getGraph(), r) Rule::LeftGraph::Vertex Rule::Vertex::getLeft() const { if(isNull()) return LeftGraph::Vertex(); - const auto &dpoRule = r->getRule().getDPORule(); - const auto v = vertex(vId, get_graph(dpoRule)); - if(membership(dpoRule, v) != lib::Rules::Membership::Right) { + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto vCG = vertex(vId, gCombined); + if(gCombined[vCG].membership != lib::Rules::Membership::R) { + const auto vL = get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG); using boost::vertices; - const auto &vs = vertices(get_left(r->getRule().getDPORule())); - auto iter = std::find(vs.first, vs.second, v); - auto vSubId = std::distance(vs.first, iter); - return LeftGraph::Vertex(r, vSubId); + const auto &vs = vertices(getL(rDPO)); + const auto vIter = std::find(vs.first, vs.second, vL); + assert(vIter != vs.second); + const auto vLOffset = std::distance(vs.first, vIter); + return LeftGraph::Vertex(r, vLOffset); } else return LeftGraph::Vertex(); } Rule::ContextGraph::Vertex Rule::Vertex::getContext() const { if(isNull()) return ContextGraph::Vertex(); - const auto &dpoRule = r->getRule().getDPORule(); - const auto v = vertex(vId, get_graph(dpoRule)); - if(membership(dpoRule, v) == lib::Rules::Membership::Context) { + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto vCG = vertex(vId, gCombined); + if(gCombined[vCG].membership == lib::Rules::Membership::K) { + const auto vK = get_inverse(rDPO.getKtoCG(), getK(rDPO), gCombined, vCG); using boost::vertices; - const auto &vs = vertices(get_context(r->getRule().getDPORule())); - auto iter = std::find(vs.first, vs.second, v); - auto vSubId = std::distance(vs.first, iter); - return ContextGraph::Vertex(r, vSubId); + const auto &vs = vertices(getK(rDPO)); + const auto vIter = std::find(vs.first, vs.second, vK); + assert(vIter != vs.second); + const auto vKOffset = std::distance(vs.first, vIter); + return ContextGraph::Vertex(r, vKOffset); } else return ContextGraph::Vertex(); } Rule::RightGraph::Vertex Rule::Vertex::getRight() const { if(isNull()) return RightGraph::Vertex(); - const auto &dpoRule = r->getRule().getDPORule(); - const auto v = vertex(vId, get_graph(dpoRule)); - if(membership(dpoRule, v) != lib::Rules::Membership::Left) { + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto vCG = vertex(vId, gCombined); + if(gCombined[vCG].membership != lib::Rules::Membership::L) { + const auto vR = get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG); using boost::vertices; - const auto &vs = vertices(get_right(r->getRule().getDPORule())); - auto iter = std::find(vs.first, vs.second, v); - auto vSubId = std::distance(vs.first, iter); - return RightGraph::Vertex(r, vSubId); + const auto &vs = vertices(getR(rDPO)); + const auto vIter = std::find(vs.first, vs.second, vR); + + assert(vIter != vs.second); + const auto vROffset = std::distance(vs.first, vIter); + return RightGraph::Vertex(r, vROffset); } else return RightGraph::Vertex(); } double Rule::Vertex::get2DX(bool withHydrogens) { - if(isNull()) throw LogicError("Can not get coordinaes on a null vertex."); - const auto &rule = r->getRule(); - const auto &dpoRule = rule.getDPORule(); - const auto v = vertex(vId, get_graph(dpoRule)); - const auto &depict = rule.getDepictionData(); - double x = depict.getX(v, withHydrogens); - if(std::isnan(x)) throw LogicError("Can not get coordinaes for this vertex."); + if(isNull()) throw LogicError("Can not get coordinates on a null vertex."); + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto vCG = vertex(vId, gCombined); + const auto &depict = r->getRule().getDepictionData(); + const double x = depict.getX(vCG, withHydrogens); + if(std::isnan(x)) throw LogicError("Can not get coordinates for this vertex."); return x; } double Rule::Vertex::get2DY(bool withHydrogens) { - if(isNull()) throw LogicError("Can not get coordinaes on a null vertex."); - const auto &rule = r->getRule(); - const auto &dpoRule = rule.getDPORule(); - const auto v = vertex(vId, get_graph(dpoRule)); - const auto &depict = rule.getDepictionData(); - double y = depict.getY(v, withHydrogens); - if(std::isnan(y)) throw LogicError("Can not get coordinaes for this vertex."); + if(isNull()) throw LogicError("Can not get coordinates on a null vertex."); + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto vCG = vertex(vId, gCombined); + const auto &depict = r->getRule().getDepictionData(); + const double y = depict.getY(vCG, withHydrogens); + if(std::isnan(y)) throw LogicError("Can not get coordinates for this vertex."); return y; } @@ -473,55 +514,67 @@ BOOST_CONCEPT_ASSERT((boost::ForwardIterator)); Rule::LeftGraph::Edge Rule::Edge::getLeft() const { if(isNull()) return LeftGraph::Edge(); - const auto &dpoRule = r->getRule().getDPORule(); - const auto &graph = get_graph(dpoRule); - using boost::vertices; - const auto v = *(vertices(graph).first + vId); - const auto e = *(out_edges(v, graph).first + eId); - if(membership(dpoRule, e) != lib::Rules::Membership::Right) { - const auto &vs = vertices(get_left(r->getRule().getDPORule())); - auto iter = std::find(vs.first, vs.second, v); - auto vSubId = std::distance(vs.first, iter); - const auto &es = out_edges(v, get_left(r->getRule().getDPORule())); - auto eIter = std::find(es.first, es.second, e); - auto eSubId = std::distance(es.first, eIter); - return LeftGraph::Edge(r, vSubId, eSubId); + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto vCG = vertex(vId, gCombined); + const auto eCG = *std::next(out_edges(vCG, gCombined).first, eId); + if(gCombined[eCG].membership != lib::Rules::Membership::R) { + const auto vL = get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, vCG); + const auto eL = get_inverse(rDPO.getLtoCG(), getL(rDPO), gCombined, eCG); + using boost::vertices; + const auto &vs = vertices(getL(rDPO)); + const auto vIter = std::find(vs.first, vs.second, vL); + assert(vIter != vs.second); + const auto vLId = std::distance(vs.first, vIter); + const auto &es = out_edges(vL, getL(rDPO)); + const auto eIter = std::find(es.first, es.second, eL); + assert(eIter != es.second); + const auto eLOffset = std::distance(es.first, eIter); + return LeftGraph::Edge(r, vLId, eLOffset); } else return LeftGraph::Edge(); } Rule::ContextGraph::Edge Rule::Edge::getContext() const { if(isNull()) return ContextGraph::Edge(); - const auto &dpoRule = r->getRule().getDPORule(); - const auto &graph = get_graph(dpoRule); - using boost::vertices; - const auto v = *(vertices(graph).first + vId); - const auto e = *(out_edges(v, graph).first + eId); - if(membership(dpoRule, e) == lib::Rules::Membership::Context) { - const auto &vs = vertices(get_context(r->getRule().getDPORule())); - auto iter = std::find(vs.first, vs.second, v); - auto vSubId = std::distance(vs.first, iter); - const auto &es = out_edges(v, get_context(r->getRule().getDPORule())); - auto eIter = std::find(es.first, es.second, e); - auto eSubId = std::distance(es.first, eIter); - return ContextGraph::Edge(r, vSubId, eSubId); + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto vCG = vertex(vId, gCombined); + const auto eCG = *std::next(out_edges(vCG, gCombined).first, eId); + if(gCombined[eCG].membership == lib::Rules::Membership::K) { + const auto vK = get_inverse(rDPO.getKtoCG(), getK(rDPO), gCombined, vCG); + const auto eK = get_inverse(rDPO.getKtoCG(), getK(rDPO), gCombined, eCG); + using boost::vertices; + const auto &vs = vertices(getK(rDPO)); + const auto vIter = std::find(vs.first, vs.second, vK); + assert(vIter != vs.second); + const auto vKOffset = std::distance(vs.first, vIter); + const auto &es = out_edges(vK, getK(rDPO)); + const auto eIter = std::find(es.first, es.second, eK); + assert(eIter != es.second); + const auto eKOffset = std::distance(es.first, eIter); + return ContextGraph::Edge(r, vKOffset, eKOffset); } else return ContextGraph::Edge(); } Rule::RightGraph::Edge Rule::Edge::getRight() const { if(isNull()) return RightGraph::Edge(); - const auto &dpoRule = r->getRule().getDPORule(); - const auto &graph = get_graph(dpoRule); - using boost::vertices; - const auto v = *(vertices(graph).first + vId); - const auto e = *(out_edges(v, graph).first + eId); - if(membership(dpoRule, e) != lib::Rules::Membership::Left) { - const auto &vs = vertices(get_right(r->getRule().getDPORule())); - auto iter = std::find(vs.first, vs.second, v); - auto vSubId = std::distance(vs.first, iter); - const auto &es = out_edges(v, get_right(r->getRule().getDPORule())); - auto eIter = std::find(es.first, es.second, e); - auto eSubId = std::distance(es.first, eIter); - return RightGraph::Edge(r, vSubId, eSubId); + const auto &rDPO = r->getRule().getDPORule().getRule(); + const auto &gCombined = rDPO.getCombinedGraph(); + const auto vCG = vertex(vId, gCombined); + const auto eCG = *std::next(out_edges(vCG, gCombined).first, eId); + if(gCombined[eCG].membership != lib::Rules::Membership::L) { + const auto vR = get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, vCG); + const auto eR = get_inverse(rDPO.getRtoCG(), getR(rDPO), gCombined, eCG); + using boost::vertices; + const auto &vs = vertices(getR(rDPO)); + const auto vIter = std::find(vs.first, vs.second, vR); + assert(vIter != vs.second); + const auto vRId = std::distance(vs.first, vIter); + const auto &es = out_edges(vR, getR(rDPO)); + const auto eIter = std::find(es.first, es.second, eR); + assert(eIter != es.second); + const auto eROffset = std::distance(es.first, eIter); + return RightGraph::Edge(r, vRId, eROffset); } else return RightGraph::Edge(); } diff --git a/libs/libmod/src/mod/rule/Rule.cpp b/libs/libmod/src/mod/rule/Rule.cpp index 0d50678..64d54ed 100644 --- a/libs/libmod/src/mod/rule/Rule.cpp +++ b/libs/libmod/src/mod/rule/Rule.cpp @@ -4,9 +4,10 @@ #include #include #include -#include -#include #include +#include +#include +#include #include #include @@ -46,7 +47,7 @@ const lib::Rules::Real &Rule::getRule() const { //------------------------------------------------------------------------------ std::size_t Rule::numVertices() const { - return num_vertices(p->r->getGraph()); + return num_vertices(p->r->getDPORule().getRule().getCombinedGraph()); } Rule::VertexRange Rule::vertices() const { @@ -54,7 +55,7 @@ Rule::VertexRange Rule::vertices() const { } std::size_t Rule::numEdges() const { - return num_edges(p->r->getGraph()); + return num_edges(p->r->getDPORule().getRule().getCombinedGraph()); } Rule::EdgeRange Rule::edges() const { @@ -78,21 +79,21 @@ Rule::RightGraph Rule::getRight() const { std::shared_ptr Rule::makeInverse() const { lib::Rules::LabelledRule dpoRule(getRule().getDPORule(), true); if(getConfig().rule.ignoreConstraintsDuringInversion.get()) { - if(dpoRule.leftMatchConstraints.size() > 0 - || dpoRule.rightMatchConstraints.size() > 0) { + if(get_match_constraints(get_labelled_left(dpoRule)).size() > 0 + || get_match_constraints(get_labelled_right(dpoRule)).size() > 0) { std::cout << "WARNING: inversion of rule strips constraints.\n"; } } else { - if(dpoRule.leftMatchConstraints.size() > 0) { + if(get_match_constraints(get_labelled_left(dpoRule)).size() > 0) { throw LogicError("Can not invert rule with left-side component constraints."); } - if(dpoRule.rightMatchConstraints.size() > 0) { + if(get_match_constraints(get_labelled_right(dpoRule)).size() > 0) { throw LogicError("Can not invert rule with right-side component constraints."); } } dpoRule.invert(); - bool ignore = getConfig().rule.ignoreConstraintsDuringInversion.get(); - if(ignore) dpoRule.rightMatchConstraints.clear(); + const bool ignore = getConfig().rule.ignoreConstraintsDuringInversion.get(); + if(ignore) dpoRule.rightData.matchConstraints.clear(); auto rInner = std::make_unique(std::move(dpoRule), getLabelType()); rInner->setName(this->getName() + ", inverse"); return makeRule(std::move(rInner), *p->externalToInternalIds); @@ -116,25 +117,25 @@ Rule::print(const graph::Printer &first, const graph::Printer &second) const { std::pair Rule::print(const graph::Printer &first, const graph::Printer &second, bool printCombined) const { - return lib::IO::Rules::Write::summary(getRule(), first.getOptions(), second.getOptions(), printCombined); + return lib::Rules::Write::summary(getRule(), first.getOptions(), second.getOptions(), printCombined); } void Rule::printTermState() const { - lib::IO::Rules::Write::termState(getRule()); + lib::Rules::Write::termState(getRule()); } std::string Rule::getGMLString(bool withCoords) const { if(withCoords && !getRule().getDepictionData().getHasCoordinates()) throw LogicError("Coordinates are not available for this rule (" + getName() + ")."); std::stringstream ss; - lib::IO::Rules::Write::gml(getRule(), withCoords, ss); + lib::Rules::Write::gml(getRule(), withCoords, ss); return ss.str(); } std::string Rule::printGML(bool withCoords) const { if(withCoords && !getRule().getDepictionData().getHasCoordinates()) throw LogicError("Coordinates are not available for this rule (" + getName() + ")."); - return lib::IO::Rules::Write::gml(*p->r, withCoords); + return lib::Rules::Write::gml(*p->r, withCoords); } const std::string &Rule::getName() const { @@ -150,11 +151,11 @@ std::optional Rule::getLabelType() const { } std::size_t Rule::getNumLeftComponents() const { - return getRule().getDPORule().numLeftComponents; + return get_num_connected_components(get_labelled_left(getRule().getDPORule())); } std::size_t Rule::getNumRightComponents() const { - return getRule().getDPORule().numRightComponents; + return get_num_connected_components(get_labelled_right(getRule().getDPORule())); } namespace { @@ -212,7 +213,7 @@ int Rule::getMaxExternalId() const { namespace { -std::shared_ptr handleLoadedRule(lib::IO::Result dataRes, +std::shared_ptr handleLoadedRule(lib::IO::Result dataRes, lib::IO::Warnings warnings, bool invert, const std::string &dataSource) { @@ -223,7 +224,7 @@ std::shared_ptr handleLoadedRule(lib::IO::ResultpString); if(invert) { - if(!data.rule->leftMatchConstraints.empty()) { + if(!get_match_constraints(get_labelled_left(*data.rule)).empty()) { const bool ignore = getConfig().rule.ignoreConstraintsDuringInversion.get(); std::string msg = "The rule '"; if(data.name) msg += *data.name; @@ -238,7 +239,7 @@ std::shared_ptr handleLoadedRule(lib::IO::ResultleftMatchConstraints.clear(); + data.rule->leftData.matchConstraints.clear(); std::cout << "WARNING: " << msg << '\n'; } } @@ -254,7 +255,7 @@ std::shared_ptr handleLoadedRule(lib::IO::Result Rule::fromGMLString(const std::string &data, bool invert) { lib::IO::Warnings warnings; - auto res = lib::IO::Rules::Read::gml(warnings, data); + auto res = lib::Rules::Read::gml(warnings, data); return handleLoadedRule(std::move(res), std::move(warnings), invert, ""); } @@ -267,10 +268,16 @@ std::shared_ptr Rule::fromGMLFile(const std::string &file, bool invert) { } if(!ifs) throw InputError("Could not open rule GML file '" + file + "'.\n"); lib::IO::Warnings warnings; - auto res = lib::IO::Rules::Read::gml(warnings, {ifs.begin(), ifs.size()}); + auto res = lib::Rules::Read::gml(warnings, {ifs.begin(), ifs.size()}); return handleLoadedRule(std::move(res), std::move(warnings), invert, "file '" + file + "'"); } +std::shared_ptr Rule::fromDFS(const std::string &data, bool invert) { + lib::IO::Warnings warnings; + auto res = lib::Rules::Read::dfs(warnings, data); + return handleLoadedRule(std::move(res), std::move(warnings), invert, ""); +} + std::shared_ptr Rule::makeRule(std::unique_ptr r) { return makeRule(std::move(r), {}); } diff --git a/libs/libmod/src/mod/rule/Rule.hpp b/libs/libmod/src/mod/rule/Rule.hpp index db496b6..a6dbcf4 100644 --- a/libs/libmod/src/mod/rule/Rule.hpp +++ b/libs/libmod/src/mod/rule/Rule.hpp @@ -16,7 +16,12 @@ namespace mod::rule { // rst-class: rule::Rule // rst: -// rst: Model of a transformation rule in the Double Pushout formalism. +// rst: This class models a graph transformation rule in the Double Pushout formalism, +// rst: as the span :math:`L \leftarrow K \rightarrow R`. +// rst: The three graphs are referred to as respectively +// rst: the "left", "context", and "right" graphs of the rule. +// rst: See :ref:`graph-model` for more details. +// rst: // rst: See :ref:`cpp-rule/GraphInterface` for the documentation for the // rst: graph interface for this class. // rst: @@ -187,6 +192,15 @@ struct MOD_DECL Rule { // rst: :throws: :class:`InputError` on bad data and when inversion fails due to constraints. static std::shared_ptr fromGMLString(const std::string &data, bool invert); static std::shared_ptr fromGMLFile(const std::string &file, bool invert); + // rst: .. function:: static std::shared_ptr fromDFS(const std::string &data, bool invert) + // rst: + // rst: Load a rule from a :ref:`RuleDFS ` string, and store either that rule or its inverse. + // rst: The name of the rule is the one specified, though when ``invert=True`` + // rst: the string ", inverse" is appended to the name. + // rst: + // rst: :returns: the loaded (possibly inverted) rule. + // rst: :throws: :class:`InputError` on bad data and when inversion fails due to constraints. + static std::shared_ptr fromDFS(const std::string &data, bool invert); // rst: .. function:: static std::shared_ptr makeRule(std::unique_ptr r) // rst: static std::shared_ptr makeRule(std::unique_ptr r, std::map externalToInternalIds) // rst: diff --git a/libs/libmod/src/mod/rule/internal/Rule.cpp b/libs/libmod/src/mod/rule/internal/Rule.cpp index 863e999..11f6760 100644 --- a/libs/libmod/src/mod/rule/internal/Rule.cpp +++ b/libs/libmod/src/mod/rule/internal/Rule.cpp @@ -13,28 +13,27 @@ lib::Rules::LabelledRule::GraphType &getGraph(lib::Rules::LabelledRule &r) { return get_graph(r); } -std::unique_ptr makePropStringCore(const lib::Rules::GraphType &g) { - return std::make_unique(g); +std::unique_ptr makePropStringCore(const lib::Rules::LabelledRule &rule) { + return std::make_unique(rule.getRule()); } -void add(lib::Rules::PropStringCore &pString, boost::graph_traits::vertex_descriptor v, +void add(lib::Rules::PropString &pString, lib::DPO::CombinedRule::CombinedVertex v, const std::string &valueLeft, const std::string &valueRight) { pString.add(v, valueLeft, valueRight); } -void add(lib::Rules::PropStringCore &pString, boost::graph_traits::edge_descriptor e, +void add(lib::Rules::PropString &pString, lib::DPO::CombinedRule::CombinedEdge e, const std::string &valueLeft, const std::string &valueRight) { pString.add(e, valueLeft, valueRight); } -void setRight(lib::Rules::PropStringCore &pString, - boost::graph_traits::edge_descriptor e, const std::string &value) { +void setRight(lib::Rules::PropString &pString, lib::DPO::CombinedRule::CombinedEdge e, const std::string &value) { pString.setRight(e, value); } -MOD_DECL lib::Rules::PropMoleculeCore -makePropMoleculeCore(const lib::Rules::GraphType &g, const lib::Rules::PropStringCore &str) { - return lib::Rules::PropMoleculeCore(g, str); +lib::Rules::PropMolecule +makePropMoleculeCore(const lib::Rules::LabelledRule &rule, const lib::Rules::PropString &str) { + return lib::Rules::PropMolecule(rule.getRule(), str); } std::shared_ptr makeRule(lib::Rules::LabelledRule &&r) { @@ -42,4 +41,24 @@ std::shared_ptr makeRule(lib::Rules::LabelledRule &&r) { return mod::rule::Rule::makeRule(std::move(rLib)); } +const std::string &getStringLeft(lib::DPO::CombinedRule::CombinedVertex v, + const lib::Rules::PropString &str) { + return str.getLeft()[v]; +} + +const std::string &getStringRight(lib::DPO::CombinedRule::CombinedVertex v, + const lib::Rules::PropString &str) { + return str.getRight()[v]; +} + +BondType getMoleculeLeft(lib::DPO::CombinedRule::CombinedEdge e, + const lib::Rules::PropMolecule &mol) { + return mol.getLeft()[e]; +} + +BondType getMoleculeRight(lib::DPO::CombinedRule::CombinedEdge e, + const lib::Rules::PropMolecule &mol) { + return mol.getRight()[e]; +} + } // namespace mod::rule::internal \ No newline at end of file diff --git a/libs/libmod/src/mod/rule/internal/Rule.hpp b/libs/libmod/src/mod/rule/internal/Rule.hpp index c28e5a6..83f4f9f 100644 --- a/libs/libmod/src/mod/rule/internal/Rule.hpp +++ b/libs/libmod/src/mod/rule/internal/Rule.hpp @@ -10,18 +10,27 @@ namespace mod::rule::internal { MOD_DECL lib::Rules::LabelledRule makeLabelledRule(); MOD_DECL lib::Rules::GraphType &getGraph(lib::Rules::LabelledRule &r); -MOD_DECL std::unique_ptr makePropStringCore(const lib::Rules::GraphType &g); -MOD_DECL void add(lib::Rules::PropStringCore &pString, boost::graph_traits::vertex_descriptor v, +MOD_DECL std::unique_ptr makePropStringCore(const lib::Rules::LabelledRule &rule); +MOD_DECL void add(lib::Rules::PropString &pString, lib::DPO::CombinedRule::CombinedVertex v, const std::string &valueLeft, const std::string &valueRight); -MOD_DECL void add(lib::Rules::PropStringCore &pString, boost::graph_traits::edge_descriptor e, +MOD_DECL void add(lib::Rules::PropString &pString, lib::DPO::CombinedRule::CombinedEdge e, const std::string &valueLeft, const std::string &valueRight); -MOD_DECL void setRight(lib::Rules::PropStringCore &pString, - boost::graph_traits::edge_descriptor e, const std::string &value); -MOD_DECL lib::Rules::PropMoleculeCore -makePropMoleculeCore(const lib::Rules::GraphType &g, const lib::Rules::PropStringCore &str); +MOD_DECL void setRight(lib::Rules::PropString &pString, lib::DPO::CombinedRule::CombinedEdge e, + const std::string &value); +MOD_DECL lib::Rules::PropMolecule makePropMoleculeCore(const lib::Rules::LabelledRule &rule, + const lib::Rules::PropString &str); MOD_DECL std::shared_ptr makeRule(lib::Rules::LabelledRule &&r); +MOD_DECL const std::string &getStringLeft(lib::DPO::CombinedRule::CombinedVertex v, + const lib::Rules::PropString &str); +MOD_DECL const std::string &getStringRight(lib::DPO::CombinedRule::CombinedVertex v, + const lib::Rules::PropString &str); +MOD_DECL BondType getMoleculeLeft(lib::DPO::CombinedRule::CombinedEdge e, + const lib::Rules::PropMolecule &mol); +MOD_DECL BondType getMoleculeRight(lib::DPO::CombinedRule::CombinedEdge e, + const lib::Rules::PropMolecule &mol); + } // namespace mod::rule::internal #endif // MOD_RULE_INTERNAL_RULE_HPP diff --git a/libs/post_mod/bin/mod_post b/libs/post_mod/bin/mod_post index 543bb0a..2232802 100755 --- a/libs/post_mod/bin/mod_post +++ b/libs/post_mod/bin/mod_post @@ -57,6 +57,9 @@ else fi export MOD_PREFIX this="$MOD_PREFIX/bin/$self" +if [ "$this" -ef "$(which $self)" ]; then + this=$self +fi # Args if [ ! -n "$MOD_NUM_POST_THREADS" ]; then @@ -118,8 +121,15 @@ elif test "x$1" != "x--mode"; then exit $res fi - $this --mode invokeMake - exit $? + if [ -e out/disableInvokeMake ]; then + echo "Post-processing disabled by user. Run" + echo " $this --mode invokeMake" + echo "to manually invoke it." + exit 0 + else + $this --mode invokeMake + exit $? + fi else # $1 == --mode mode=$2 if test "x$mode" = "x"; then @@ -169,17 +179,21 @@ else # $1 == --mode echo "$texFile: gen" } function endMakefile { - echo "summary.pdf: $texFile" - if [ -e out/disableSummary ]; then - echo " echo ''" # each target prints a status message - echo " echo 'Summary disabled by user.' | tee summary/summary.pdf" + if [ -e out/disableCompileSummary ]; then + echo " echo ''" + echo " echo 'Summary compilation disabled by user. Run'" + echo " echo ' $this --mode compileSummary'" + echo " echo 'to manually invoke it.'" + echo " $texFile" >> $makefileAllTarget else - echo " $this --mode compileSummary" + echo " summary.pdf" >> $makefileAllTarget fi - echo "include $makefileImpl" >> $makefile - echo "include $makefileDep" >> $makefile - echo "include $makefileAllTarget" >> $makefile - echo "include $makefileClean" >> $makefile + echo "summary.pdf: $texFile" + echo " $this --mode compileSummary" + echo "include $makefileImpl" + echo "include $makefileDep" + echo "include $makefileAllTarget" + echo "include $makefileClean" } function initMakefileImpl { local p=$MOD_PREFIX/share/mod/commonPreamble @@ -217,16 +231,35 @@ else # $1 == --mode } #--------------------------------------------------------------------- - # Summary + # Invocation Control #--------------------------------------------------------------------- - function disableSummary { - touch out/disableSummary + function enableInvokeMake { + rm -f out/disableInvokeMake + } + function disableInvokeMake { + touch out/disableInvokeMake + } + + function enableCompileSummary { + rm -f out/disableCompileSummary + } + function disableCompileSummary { + touch out/disableCompileSummary } function enableSummary { - rm -f out/disableSummary + >&2 echo "WARNING: post command 'enableSummary' is deprecated', use 'ensableInvokeMake' instead." + enableInvokeMake } + function disableSummary { + >&2 echo "WARNING: post command 'disableSummary' is deprecated', use 'disableInvokeMake' instead." + disableInvokeMake + } + + #--------------------------------------------------------------------- + # Summary + #--------------------------------------------------------------------- function summaryChapter { echo " echo '\summaryChapter{$(latexEsc "$1")}' >> $texFile" >> $makefile @@ -371,14 +404,6 @@ else # $1 == --mode echo -n " $fileNoExt.$outType" >> $makefileDep return - extractGVImageDeps $inFile >> $makefileImpl - echo "" >> $makefileImpl - # dotFile - function extractGVImageDeps { - grep image $1 | sed "s/.* image=\"\([^\"]*\)\".*/\1/" | while read f; do - echo -n " $f" - done; - } } # graphType fileNoExt [noOverlay] @@ -388,7 +413,9 @@ else # $1 == --mode local noOverlay=$3 echo -n " $fileNoExt.plain ${fileNoExt}_coord.tex" >> $makefileClean function common { - grep image $fileNoExt.dot | sed "s/.* image=\"\([^\"]*\)\".*/\1/" | while read f; do + reGrep='\[.* image="\([^\"]*\)".*\]' + reSed='s/.*\[.* image="\([^\"]*\)".*\].*/\1/' + grep "$reGrep" $fileNoExt.dot | sed "$reSed" | while read f; do echo -n " $f" >> $makefileImpl done echo "" >> $makefileImpl @@ -451,7 +478,6 @@ else # $1 == --mode echo "" >> $makefileDep echo "" >> $makefileClean - echo " summary.pdf" >> $makefileAllTarget endMakefile >> $makefile return 0 } @@ -518,15 +544,13 @@ else # $1 == --mode break fi done - # hax to prevent files from changing - sed -i "s!/ID .*!/ID [<0> <0>]!" ${fileNoExt}.pdf return $res } gvArgs_base="" - #gvArgs_graph="$gvArgs_base -Kneato" + gvArgs_graph="$gvArgs_base -Kneato" #gvArgs_ruleSide="$gvArgs_base -Kneato -Nfontsize=28 -Epenwidth=3" - #gvArgs_rule="$gvArgs_base -Kneato" + gvArgs_rule="$gvArgs_base -Kneato" gvArgs_ruleCombined="$gvArgs_base -Kneato" gvArgs_dgNonHyper="$gvArgs_base -Kneato -Goverlap=false -Elen=1.1" gvArgs_dgHyper="$gvArgs_base -Kneato -Goverlap=false -Elen=1.5" @@ -592,9 +616,9 @@ BEGIN { if(abs(y) < 1e-4) y = 0 if(noOverlay == "xnoOverlay") { - printf "\\node (v-coord-%s) at (%s, %s) {};\n", name, x, y + printf "\\node (\\modIdPrefix v-coord-%s) at (%s, %s) {};\n", name, x, y } else { - printf "\\coordinate[overlay] (v-coord-%s) at (%s, %s) {};\n", name, x, y + printf "\\coordinate[overlay] (\\modIdPrefix v-coord-%s) at (%s, %s) {};\n", name, x, y } } else { # ignore diff --git a/libs/post_mod/share/mod/figureTemplate.tex b/libs/post_mod/share/mod/figureTemplate.tex index 952e966..73b8ad4 100644 --- a/libs/post_mod/share/mod/figureTemplate.tex +++ b/libs/post_mod/share/mod/figureTemplate.tex @@ -4,16 +4,11 @@ \usepackage{hyperref} % prevent the figures from changing when recompiling -% http://tex.stackexchange.com/questions/5401/how-to-prevent-different-checksums-for-different-typesets -% http://superuser.com/questions/130347/how-do-i-produce-bytewise-consistent-documents-with-pdflatex -\pdfinfo{/CreationDate (FixedForDiffing) -/ModDate (FixedForDiffing)} - -% in some future, use the following -% see http://tug.org/pipermail/pdftex/2015-July/008953.html -%\pdfinfoomitdate=1 -%\pdftrailerid{} - +% https://tex.stackexchange.com/questions/95080/making-an-anonymous-pdf-file-using-pdflatex +\pdfinfo{ /ModDate () /CreationDate () } +\hypersetup{pdfinfo={ Creator={}, Producer={} }} +\pdftrailerid{} %Remove ID +\pdfsuppressptexinfo15 %Suppress PTEX.Fullbanner and info of imported PDFs \begin{document} \begin{preview} diff --git a/libs/pymod/CMakeLists.txt b/libs/pymod/CMakeLists.txt index 4d954c0..39ab30a 100644 --- a/libs/pymod/CMakeLists.txt +++ b/libs/pymod/CMakeLists.txt @@ -16,7 +16,7 @@ target_include_directories(pymod PUBLIC $ $) -target_link_libraries(pymod PUBLIC mod::libmod Boost::${PYTHON_TARGET} Python3::Python) +target_link_libraries(pymod PUBLIC mod::pymodutils) target_link_libraries(pymod PRIVATE $<$:-Wl,--no-undefined> $<$:-Wl,--no-undefined> @@ -25,8 +25,7 @@ target_link_libraries(pymod PRIVATE set_target_properties(pymod PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}") target_compile_options(pymod PRIVATE -Wall -Wextra -pedantic -Wno-unused-parameter - -Wno-comment - -Wno-unused-local-typedefs) + -Wno-comment) set_target_properties(pymod PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON) @@ -63,6 +62,26 @@ install(DIRECTORY lib/mod COMPONENT pymod_run FILES_MATCHING PATTERN "*.py" PATTERN "*.pyi") +# redirector +# ------------------------------------------------------------------------- + +set(exportDir ${CMAKE_CURRENT_BINARY_DIR}/redirector) +file(MAKE_DIRECTORY ${exportDir}/mod) +# TODO: should be file(COPY_FILE redirector/pyproject.toml ${exportDir}/pyproject.toml) when CMake 3.21 is required +configure_file(redirector/pyproject.toml ${exportDir}/pyproject.toml @ONLY) +configure_file(redirector/setup.cfg.in ${exportDir}/setup.cfg @ONLY) +configure_file(redirector/mod/__init__.py.in ${exportDir}/mod/__init__.py @ONLY) +if(${BUILD_PY_MOD_PIP}) + # https://www.scivision.dev/cmake-install-python-package/ + # detect virtualenv and set Pip args accordingly + if(DEFINED ENV{VIRTUAL_ENV} OR DEFINED ENV{CONDA_PREFIX}) + set(_pip_args) + else() + set(_pip_args "--user") + endif() + install(CODE "execute_process(COMMAND ${Python3_EXECUTABLE} -m pip install . ${_pip_args} WORKING_DIRECTORY ${exportDir})" + COMPONENT pymod_run) +endif() # mod # ------------------------------------------------------------------------- diff --git a/libs/pymod/bin/mod.in b/libs/pymod/bin/mod.in index d09ed47..90bb109 100755 --- a/libs/pymod/bin/mod.in +++ b/libs/pymod/bin/mod.in @@ -233,7 +233,7 @@ done # Create precommand (gdb/valgrind/...) precommand="" if [ $profile = "true" -o $memcheck = "true" ]; then - precommand="valgrind" + precommand="valgrind --error-exitcode=42" fi if [ $profile = "true" ]; then precommand="$precommand --tool=callgrind --dump-instr=yes --collect-jumps=yes $vgArgs" @@ -346,6 +346,18 @@ def include(fName, checkDup=True, putDup=True, skipDup=True): else: print("End of code from '", fName, "'", sep="") mod.include = include + +if "LD_PRELOAD" in os.environ and "valgrind" in os.environ["LD_PRELOAD"]: + import atexit + def _valgrindLeakHaxAtExit(): + toRemove = set(globals()) - _initialGlobals + toRemove.remove("_initialGlobals") + print("atexit hax to remove global variables for Valgrind leak check:") + for n in toRemove: + print(" ", n) + del globals()[n] + atexit.register(_valgrindLeakHaxAtExit) + _initialGlobals = set(globals()) EOF if [ ! -z "$MOD_NO_DEPRECATED" ]; then echo "config.common.ignoreDeprecation = False" diff --git a/libs/pymod/lib/mod/__init__.py b/libs/pymod/lib/mod/__init__.py index d84ed4c..18871e9 100644 --- a/libs/pymod/lib/mod/__init__.py +++ b/libs/pymod/lib/mod/__init__.py @@ -1,14 +1,15 @@ -import collections +import collections.abc import ctypes import inspect -import math import sys from typing import ( Any, Callable, cast, Generic, Iterable, List, Optional, Sequence, TextIO, Tuple, Type, TypeVar, Union ) +_redirected = False + _oldFlags = sys.getdlopenflags() sys.setdlopenflags(_oldFlags | ctypes.RTLD_GLOBAL) from . import libpymod @@ -164,6 +165,13 @@ def __call__(self, *args: List[Any]) -> "T": return module._sharedToStd(res) +#---------------------------------------------------------- +# Common stuff +#---------------------------------------------------------- + +_lsString = LabelSettings(LabelType.String, LabelRelation.Isomorphism) + + ########################################################### # Chem ########################################################### @@ -179,6 +187,7 @@ def __call__(self, *args: List[Any]) -> "T": LabelRelation.__str__ = libpymod._LabelRelation__str__ # type: ignore IsomorphismPolicy.__str__ = libpymod._IsomorphismPolicy__str__ # type: ignore SmilesClassPolicy.__str__ = libpymod._SmilesClassPolicy__str__ # type: ignore +Action.__str__ = libpymod._Action__str__ # type: ignore config = getConfig() @@ -222,7 +231,7 @@ def dgDerivations(ders: Iterable[Derivation]) -> DG: return dg def dgRuleComp(graphs: Iterable[Graph], strat: DGStrat, - labelSettings: LabelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism), + labelSettings: LabelSettings=_lsString, ignoreRuleLabelTypes: bool=False) -> DG: _deprecation("dgRuleComp is deprecated. Use the new build interface.") dg = DG(labelSettings=labelSettings, graphDatabase=graphs) @@ -234,6 +243,8 @@ def _DG_calc(dg: DG, printInfo: bool=True) -> None: _deprecation("DG.calc() is deprecated. Use the new build interface.") d = dg._ruleCompData # type: ignore dg.build().execute(d["strat"], ignoreRuleLabelTypes=d["ignoreRuleLabelTypes"]) + object.__setattr__(dg, "_ruleCompData", None) + object.__setattr__(dg, "calc", None) import types object.__setattr__(dg, "calc", types.MethodType(_DG_calc, dg)) return dg @@ -251,9 +262,7 @@ def _DG_load( _DG__init__old = DG.__init__ def _DG__init__(self: DG, *, - labelSettings: LabelSettings = LabelSettings( - LabelType.String, - LabelRelation.Isomorphism), + labelSettings: LabelSettings=_lsString, graphDatabase: List[Graph] = [], graphPolicy: IsomorphismPolicy = IsomorphismPolicy.Check) -> None: return _DG__init__old(self, # type: ignore @@ -340,6 +349,11 @@ def addDerivation(self, d: Derivations, assert self._builder return self._builder.addDerivation(d, graphPolicy) + def addHyperEdge(self, e: DGHyperEdge, + graphPolicy: IsomorphismPolicy = IsomorphismPolicy.Check) -> DGHyperEdge: + assert self._builder + return self._builder.addHyperEdge(e, graphPolicy) + def execute(self, strategy: DGStrat, *, verbosity: int=2, ignoreRuleLabelTypes: bool=False) -> DGExecuteResult: assert self._builder return self._builder.execute(dgStrat(strategy), verbosity, ignoreRuleLabelTypes) # type: ignore @@ -396,7 +410,7 @@ def _makeGraphToVertexCallback(orig, name, func): def callback(self, f, *args, **kwargs): if hasattr(f, "__call__"): import inspect - spec = inspect.getargspec(f) + spec = inspect.getfullargspec(f) if len(spec.args) == 2: _deprecation("The callback for {} seems to take two arguments, a graph and a derivation graph. This is deprecated, the callback should take a single DGVertex argument.".format(name)) fOrig = f @@ -438,6 +452,16 @@ def callback(self, f, *args, **kwargs): DGPrinter.setMirrorOverwrite = ( # type: ignore lambda self, f: _DGPrinter_setMirrorOverwrite_orig(self, _funcWrap(libpymod._Func_BoolGraph, f))) +_DGPrinter_setImageOverwrite_orig = DGPrinter.setImageOverwrite +def _DGPrinter_setImageOverwrite(self, f): + if f is None: + wrapped = None + else: + wrapped = _funcWrap( + libpymod._Func_PairStringStringDGVertexInt, f) + return _DGPrinter_setImageOverwrite_orig(self, wrapped) +DGPrinter.setImageOverwrite = _DGPrinter_setImageOverwrite # type: ignore + #---------------------------------------------------------- # Strategy @@ -506,7 +530,7 @@ def dgStrat(s: _DGStratType) -> DGStrat: return DGStrat.makeRule(s) elif isinstance(s, _DGStrat_sequenceProxy): return DGStrat.makeSequence(s.strats) - elif isinstance(s, collections.Iterable): + elif isinstance(s, collections.abc.Iterable): # do deep dgStrat l = [dgStrat(a) for a in s] return DGStrat.makeParallel(l) @@ -656,12 +680,15 @@ def _Graph_print(self: Graph, first: Optional[GraphPrinter]=None, second: Option Graph.print = _Graph_print # type: ignore _Graph_aut = Graph.aut -Graph.aut = lambda self, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism): _Graph_aut(self, labelSettings) # type: ignore +Graph.aut = lambda self, labelSettings=_lsString: _Graph_aut(self, labelSettings) # type: ignore _Graph_isomorphism = Graph.isomorphism -Graph.isomorphism = lambda self, g, maxNumMatches=1, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism): _Graph_isomorphism(self, g, maxNumMatches, labelSettings) # type: ignore +Graph.isomorphism = lambda self, g, maxNumMatches=1, labelSettings=_lsString: _Graph_isomorphism(self, g, maxNumMatches, labelSettings) # type: ignore _Graph_monomorphism = Graph.monomorphism -Graph.monomorphism = lambda self, g, maxNumMatches=1, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism): _Graph_monomorphism(self, g, maxNumMatches, labelSettings) # type: ignore +Graph.monomorphism = lambda self, g, maxNumMatches=1, labelSettings=_lsString: _Graph_monomorphism(self, g, maxNumMatches, labelSettings) # type: ignore +_Graph_enumerateMonomorphisms = Graph.enumerateMonomorphisms # type: ignore +Graph.enumerateMonomorphisms = lambda self, codomain, *, callback, labelSettings=_lsString: _Graph_enumerateMonomorphisms( # type: ignore + self, codomain, _funcWrap(libpymod._Func_BoolVertexMapGraphGraph, callback), labelSettings) # type: ignore _Graph_getGMLString = Graph.getGMLString Graph.getGMLString = lambda self, withCoords=False: _Graph_getGMLString(self, withCoords) # type: ignore @@ -678,47 +705,92 @@ def _graphLoad(a: Graph, name: Optional[str], add: bool) -> Graph: inputGraphs.append(a) return a -def _graphsLoad(gs, add): +def _graphsLoad(gs: List[Graph], add: bool) -> List[Graph]: us = _unwrap(gs) res = [_graphLoad(a, name=None, add=add) for a in us] return res +def _graphssLoad(gs: List[List[Graph]], add: bool) -> List[List[Graph]]: + us = _unwrap(gs) + res = [_graphsLoad(a, add=add) for a in us] + return res + _Graph_fromGMLString_orig = Graph.fromGMLString _Graph_fromGMLFile_orig = Graph.fromGMLFile _Graph_fromGMLStringMulti_orig = Graph.fromGMLStringMulti _Graph_fromGMLFileMulti_orig = Graph.fromGMLFileMulti _Graph_fromDFS_orig = Graph.fromDFS +_Graph_fromDFSMulti_orig = Graph.fromDFSMulti _Graph_fromSMILES_orig = Graph.fromSMILES _Graph_fromSMILESMulti_orig = Graph.fromSMILESMulti - -def _Graph_fromGMLString(s: str, name: Optional[str] = None, add: bool = True) -> Graph: - return _graphLoad(_Graph_fromGMLString_orig( s), name, add) -def _Graph_fromGMLFile( f: str, name: Optional[str] = None, add: bool = True) -> Graph: - return _graphLoad(_Graph_fromGMLFile_orig(prefixFilename(f)), name, add) -def _Graph_fromGMLStringMulti(s: str, add: bool = True): - return _graphsLoad(_Graph_fromGMLStringMulti_orig( s), add) -def _Graph_fromGMLFileMulti( f: str, add: bool = True): - return _graphsLoad(_Graph_fromGMLFileMulti_orig(prefixFilename(f)), add) -def _Graph_fromDFS( s: str, name: Optional[str] = None, add: bool = True) -> Graph: - return _graphLoad(_Graph_fromDFS_orig( s), name, add) -def _Graph_fromSMILES( s: str, name: Optional[str] = None, allowAbstract: bool = False, classPolicy: SmilesClassPolicy = SmilesClassPolicy.NoneOnDuplicate, add: bool = True) -> Graph: - return _graphLoad(_Graph_fromSMILES_orig( s, allowAbstract, classPolicy), name, add) -def _Graph_fromSMILESMulti(s: str, allowAbstract: bool = False, classPolicy: SmilesClassPolicy = SmilesClassPolicy.NoneOnDuplicate, add: bool = True): - return _graphsLoad(_Graph_fromSMILESMulti_orig( s, allowAbstract, classPolicy), add) +_Graph_fromMOLString_orig = Graph.fromMOLString +_Graph_fromMOLFile_orig = Graph.fromMOLFile +_Graph_fromMOLStringMulti_orig = Graph.fromMOLStringMulti +_Graph_fromMOLFileMulti_orig = Graph.fromMOLFileMulti +_Graph_fromSDString_orig = Graph.fromSDString +_Graph_fromSDFile_orig = Graph.fromSDFile +_Graph_fromSDStringMulti_orig = Graph.fromSDStringMulti +_Graph_fromSDFileMulti_orig = Graph.fromSDFileMulti + +def _Graph_fromGMLString( s: str, name: Optional[str] = None, add: bool = True) -> Graph: + return _graphLoad(_Graph_fromGMLString_orig( s ), name, add) +def _Graph_fromGMLFile( f: str, name: Optional[str] = None, add: bool = True) -> Graph: + return _graphLoad(_Graph_fromGMLFile_orig( prefixFilename(f) ), name, add) +def _Graph_fromGMLStringMulti(s: str, add: bool = True) -> List[Graph]: + return _graphsLoad(_Graph_fromGMLStringMulti_orig( s ), add) +def _Graph_fromGMLFileMulti( f: str, add: bool = True) -> List[Graph]: + return _graphsLoad(_Graph_fromGMLFileMulti_orig(prefixFilename(f) ), add) +def _Graph_fromDFS( s: str, name: Optional[str] = None, add: bool = True) -> Graph: + return _graphLoad(_Graph_fromDFS_orig( s ), name, add) +def _Graph_fromDFSMulti( s: str, add: bool = True) -> List[Graph]: + return _graphsLoad(_Graph_fromDFSMulti_orig( s ), add) +def _Graph_fromSMILES( s: str, name: Optional[str] = None, allowAbstract: bool = False, classPolicy: SmilesClassPolicy = SmilesClassPolicy.NoneOnDuplicate, + add: bool = True) -> Graph: + return _graphLoad(_Graph_fromSMILES_orig( s, allowAbstract, classPolicy ), name, add) +def _Graph_fromSMILESMulti( s: str, allowAbstract: bool = False, classPolicy: SmilesClassPolicy = SmilesClassPolicy.NoneOnDuplicate, + add: bool = True) -> List[Graph]: + return _graphsLoad(_Graph_fromSMILESMulti_orig( s, allowAbstract, classPolicy ), add) +def _Graph_fromMOLString( s: str, name: Optional[str] = None, options: MDLOptions = MDLOptions(), add: bool = True) -> Graph: + return _graphLoad(_Graph_fromMOLString_orig( s, options ), name, add) +def _Graph_fromMOLFile( f: str, name: Optional[str] = None, options: MDLOptions = MDLOptions(), add: bool = True) -> Graph: + return _graphLoad(_Graph_fromMOLFile_orig( prefixFilename(f), options ), name, add) +def _Graph_fromMOLStringMulti(s: str, options: MDLOptions = MDLOptions(), add: bool = True) -> List[Graph]: + return _graphsLoad(_Graph_fromMOLStringMulti_orig( s, options ), add) +def _Graph_fromMOLFileMulti( f: str, options: MDLOptions = MDLOptions(), add: bool = True) -> List[Graph]: + return _graphsLoad(_Graph_fromMOLFileMulti_orig(prefixFilename(f), options ), add) +def _Graph_fromSDString( s: str, options: MDLOptions = MDLOptions(), add: bool = True) -> List[Graph]: + return _graphsLoad(_Graph_fromSDString_orig( s, options ), add) +def _Graph_fromSDFile( f: str, options: MDLOptions = MDLOptions(), add: bool = True) -> List[Graph]: + return _graphsLoad(_Graph_fromSDFile_orig( prefixFilename(f), options ), add) +def _Graph_fromSDStringMulti( s: str, options: MDLOptions = MDLOptions(), add: bool = True) -> List[List[Graph]]: + return _graphssLoad(_Graph_fromSDStringMulti_orig( s, options ), add) +def _Graph_fromSDFileMulti( f: str, options: MDLOptions = MDLOptions(), add: bool = True) -> List[List[Graph]]: + return _graphssLoad(_Graph_fromSDFileMulti_orig(prefixFilename(f), options ), add) Graph.fromGMLString = _Graph_fromGMLString # type: ignore Graph.fromGMLFile = _Graph_fromGMLFile # type: ignore Graph.fromGMLStringMulti = _Graph_fromGMLStringMulti # type: ignore Graph.fromGMLFileMulti = _Graph_fromGMLFileMulti # type: ignore Graph.fromDFS = _Graph_fromDFS # type: ignore +Graph.fromDFSMulti = _Graph_fromDFSMulti # type: ignore Graph.fromSMILES = _Graph_fromSMILES # type: ignore Graph.fromSMILESMulti = _Graph_fromSMILESMulti # type: ignore +Graph.fromMOLString = _Graph_fromMOLString # type: ignore +Graph.fromMOLFile = _Graph_fromMOLFile # type: ignore +Graph.fromMOLStringMulti = _Graph_fromMOLStringMulti # type: ignore +Graph.fromMOLFileMulti = _Graph_fromMOLFileMulti # type: ignore +Graph.fromSDString = _Graph_fromSDString # type: ignore +Graph.fromSDFile = _Graph_fromSDFile # type: ignore +Graph.fromSDStringMulti = _Graph_fromSDStringMulti # type: ignore +Graph.fromSDFileMulti = _Graph_fromSDFileMulti # type: ignore graphGMLString = Graph.fromGMLString graphGML = Graph.fromGMLFile graphDFS = Graph.fromDFS smiles = Graph.fromSMILES +########################################################### + Graph.__repr__ = lambda self: str(self) + "(" + str(self.id) + ")" # type: ignore Graph.__eq__ = lambda self, other: self.id == other.id # type: ignore Graph.__lt__ = lambda self, other: self.id < other.id # type: ignore @@ -733,7 +805,35 @@ def _Graph__setattr__(self: Graph, name: str, value: Any) -> None: ########################################################### -# Rule +# Post +########################################################### + +def _post_command(self, cmd: str) -> None: + _deprecation("'post(cmd)' is deprecated. Use 'post.command(cmd)'.") + return post.command(cmd) +post.__init__ = _post_command # type: ignore + +def postFlush() -> None: + _deprecation("'postFlush()' is deprecated. Use 'post.flushCommands()'.") + return post.flushCommands() +def postDisable() -> None: + _deprecation("'postDisable()' is deprecated. Use 'post.disableCommands()'.") + return post.disableCommands() +def postEnable() -> None: + _deprecation("'postEnable()' is deprecated. Use 'post.enableCommands()'.") + return post.enableCommands() +def postReset() -> None: + _deprecation("'postReset()' is deprecated. Use 'post.reopenCommandFile()'.") + return post.reopenCommandFile() + +def postChapter(heading: str) -> None: + _deprecation("'postChapter(heading)' is deprecated. Use 'post.summaryChapter(heading)'.") + post.summaryChapter(heading) +def postSection(heading: str) -> None: + _deprecation("'postSection(heading)' is deprecated. Use 'post.summarySection(heading)'.") + post.summarySection(heading) + + ########################################################### inputRules = [] @@ -749,9 +849,9 @@ def _Rule_print(self: Rule, first: Optional[GraphPrinter]=None, second: Optional Rule.print = _Rule_print # type: ignore _Rule_isomorphism = Rule.isomorphism -Rule.isomorphism = lambda self, r, maxNumMatches=1, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism): _Rule_isomorphism(self, r, maxNumMatches, labelSettings) # type: ignore +Rule.isomorphism = lambda self, r, maxNumMatches=1, labelSettings=_lsString: _Rule_isomorphism(self, r, maxNumMatches, labelSettings) # type: ignore _Rule_monomorphism = Rule.monomorphism -Rule.monomorphism = lambda self, r, maxNumMatches=1, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism): _Rule_monomorphism(self, r, maxNumMatches, labelSettings) # type: ignore +Rule.monomorphism = lambda self, r, maxNumMatches=1, labelSettings=_lsString: _Rule_monomorphism(self, r, maxNumMatches, labelSettings) # type: ignore _Rule_getGMLString = Rule.getGMLString Rule.getGMLString = lambda self, withCoords=False: _Rule_getGMLString(self, withCoords) # type: ignore @@ -765,14 +865,18 @@ def _ruleLoad(a: Rule, add: bool) -> Rule: _Rule_fromGMLString_orig = Rule.fromGMLString _Rule_fromGMLFile_orig = Rule.fromGMLFile +_Rule_fromDFS_orig = Rule.fromDFS def _Rule_fromGMLString(s: str, invert: bool=False, add: bool=True) -> Rule: return _ruleLoad(_Rule_fromGMLString_orig(s, invert), add) def _Rule_fromGMLFile(f: str, invert: bool=False, add: bool=True) -> Rule: return _ruleLoad(_Rule_fromGMLFile_orig(prefixFilename(f), invert), add) +def _Rule_fromDFS(s: str, invert: bool=False, add: bool=True) -> Rule: + return _ruleLoad(_Rule_fromDFS_orig(s, invert), add) Rule.fromGMLString = _Rule_fromGMLString # type: ignore Rule.fromGMLFile = _Rule_fromGMLFile # type: ignore +Rule.fromDFS = _Rule_fromDFS # type: ignore ruleGMLString = Rule.fromGMLString ruleGML = Rule.fromGMLFile @@ -799,7 +903,7 @@ def _RCEvaluator__getattribute__(self: RCEvaluator, name: str) -> Any: _RCEvaluator_eval = RCEvaluator.eval RCEvaluator.eval = lambda self, exp, *, verbosity=2: _unwrap(_RCEvaluator_eval(self, exp, verbosity)) # type: ignore -def rcEvaluator(rules: Iterable[Rule], labelSettings: LabelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism)) -> RCEvaluator: +def rcEvaluator(rules: Iterable[Rule], labelSettings: LabelSettings=_lsString) -> RCEvaluator: return libpymod._rcEvaluator(_wrap(libpymod._VecRule, rules), labelSettings) @@ -832,7 +936,7 @@ def rcExp(e: _rcExpType) -> RCExpExp: return e elif isinstance(e, RCExpComposeCommon) or isinstance(e, RCExpComposeParallel) or isinstance(e, RCExpComposeSub) or isinstance(e, RCExpComposeSuper): return e - elif isinstance(e, collections.Iterable): + elif isinstance(e, collections.abc.Iterable): return RCExpUnion(_wrap(libpymod._VecRCExpExp, [rcExp(a) for a in e])) else: raise TypeError("Can not convert type '" + str(type(e)) + "' to RCExpExp") @@ -842,7 +946,7 @@ def rcExp(e: _rcExpType) -> RCExpExp: def _rcConvertGraph(g: _GraphOrGraphs, cls: Type[Union[RCExpBind, RCExpId, RCExpUnbind]], f: Callable[[Graph], RCExpExp]) -> RCExpExp: if isinstance(g, Graph): return cls(g) - elif isinstance(g, collections.Iterable): + elif isinstance(g, collections.abc.Iterable): l = [f(a) for a in g] return rcExp(l) else: diff --git a/libs/pymod/lib/mod/latex.py b/libs/pymod/lib/mod/latex.py index 559857b..8c2112e 100644 --- a/libs/pymod/lib/mod/latex.py +++ b/libs/pymod/lib/mod/latex.py @@ -15,14 +15,14 @@ def setTexFile(fName): global _texFile _texFile = open(fName, "w") - mod.post("disableSummary") + mod.post.disableCompileSummary() def setFigFolder(fName): global _figFolder _figFolder = fName _checkSettings() - mod.post("post \"mkdir -p '%s'\"" % _figFolder) + mod.post.command("post \"mkdir -p '%s'\"" % _figFolder) def _checkSettings(): @@ -39,7 +39,7 @@ def outputFile(f, inline=False): assert f.endswith(".pdf") f = f[:-4] f += ".tex" if inline else ".pdf" - mod.post("post \"cp '%s' '%s/'\"" % (f, _figFolder)) + mod.post.command("post \"cp '%s' '%s/'\"" % (f, _figFolder)) res = _figFolder + "/" + os.path.basename(f) return res[:-4] @@ -70,15 +70,15 @@ def graph(id, g, p, inline): def graphGML(id, data, printer, inline=False): - graph(id, mod.graphGML(data), printer, inline) + graph(id, mod.Graph.fromGMLFile(data), printer, inline) def smiles(id, data, printer, inline=False): - graph(id, mod.smiles(data.replace('##', '#')), printer, inline) + graph(id, mod.Graph.fromSMILES(data.replace('##', '#')), printer, inline) def graphDFS(id, data, printer, inline=False): - graph(id, mod.graphDFS(data.replace('##', '#')), printer, inline) + graph(id, mod.Graph.fromDFS(data.replace('##', '#')), printer, inline) # ------------------------------------------------------------------------------ @@ -102,4 +102,4 @@ def rule(id, r, p): def ruleGML(id, data, printer): - rule(id, mod.ruleGML(data), printer) + rule(id, mod.Rule.fromGMLFile(data), printer) diff --git a/libs/pymod/lib/mod/libpymod.pyi b/libs/pymod/lib/mod/libpymod.pyi index 20e5ad9..93c9906 100644 --- a/libs/pymod/lib/mod/libpymod.pyi +++ b/libs/pymod/lib/mod/libpymod.pyi @@ -35,8 +35,12 @@ class _Func_BoolDerivation: class _Func_BoolDGVertex: def __call__(self, v: DGVertex) -> bool: ... +class _Func_DoubleDGVertex: + def __call__(self, v: DGVertex) -> float: ... class _Func_StringDGVertex: def __call__(self, v: DGVertex) -> str: ... +class _Func_StringDGVertexInt: + def __call__(self, v: DGVertex, dupNum: int) -> str: ... class _Func_BoolDGHyperEdge: def __call__(self, e: DGHyperEdge) -> bool: ... @@ -98,6 +102,7 @@ class SmilesClassPolicy(enum.Enum): MapUnique: int def _SmilesClassPolicy__str__(self: SmilesClassPolicy) -> str: ... +class MDLOptions: ... def _getAvailableILPSolvers() -> List[str]: ... @@ -131,47 +136,25 @@ class Derivations: ... def rngUniformReal() -> float: ... - -#----------------------------------------------------------------------------- -# causality +# Post #----------------------------------------------------------------------------- -# Petri -#----------------------------------------------------------------------------- - -class PetriNet: - def __init__(self, dg: DG) -> None: ... - def syncSize(self) -> None: ... - - -class PetriNetMarking: - def __init__(self, net: PetriNet) -> None: ... - def syncSize(self) -> None: ... - def add(self, v: DGVertex, c: int) -> int: ... - def remove(self, v: DGVertex, c: int) -> int: ... - def __getitem__(self, v: DGVertex) -> int: ... - def getAllEnabled(self) -> List[DGHyperEdge]: ... - def getNonZeroPlaces(self) -> List[DGVertex]: ... - def getEmptyPostPlaces(self, e: DGHyperEdge) -> List[DGVertex]: ... - def fire(self, e: DGHyperEdge) -> None: ... - - -class PetriNetMarkingSet: - def addIfNotSubset(self, m: PetriNetMarking) -> bool: ... - - -# Stochsim -#----------------------------------------------------------------------------- - -class MassActionKinetics: - def __init__(self, dg: DG, rate: Callable[[DGHyperEdge], float]) -> None: ... - def draw(self, possibles: List[DGHyperEdge], m: PetriNetMarking) -> Tuple[DGHyperEdge, float]: ... - +class post: + @staticmethod + def command(line: str) -> None: ... + @staticmethod + def flushCommands() -> None: ... + @staticmethod + def disableCommands() -> None: ... + @staticmethod + def enableCommands() -> None: ... + @staticmethod + def reopenCommandFile() -> None: ... -class EventTrace: - def __init__(self, initialState: PetriNetMarking) -> None: ... - def addEdge(self, time: float, e: DGHyperEdge) -> None: ... - def addTransfer(self, time: float, v: DGVertex, count: int) -> None: ... + @staticmethod + def summaryChapter(heading: str) -> None: ... + @staticmethod + def summarySection(heading: str) -> None: ... #----------------------------------------------------------------------------- @@ -198,6 +181,7 @@ class DGBuilder: def __exit__(self, exc_type, exc_val, exc_tb) -> None: ... def addDerivation(self, d: Derivations, graphPolicy: IsomorphismPolicy = ...) -> DGHyperEdge: ... + def addHyperEdge(self, e: DGHyperEdge, graphPolicy: IsomorphismPolicy = ...) -> DGHyperEdge: ... def execute(self, strategy: DGStrat, *, verbosity: int=..., ignoreRuleLabelTypes: bool=...) -> DGExecuteResult: ... def apply(self, graphs: List[Graph], rule: Rule, onlyProper: bool = ..., verbosity: int = ..., graphPolicy: IsomorphismPolicy = ...) -> List[DGHyperEdge]: ... def addAbstract(self, description: str) -> None: ... @@ -225,6 +209,7 @@ class DGPrinter: def pushEdgeColour(self, f: Union[Callable[[DGHyperEdge], str], str]) -> None: ... def setRotationOverwrite(self, f: Union[Callable[[Graph], int], int]) -> None: ... def setMirrorOverwrite(self, f: Union[Callable[[Graph], bool], bool]) -> None: ... + def setImageOverwrite(self, f: Union[Callable[[DGVertex, int], str], str]) -> None: ... class DGPrintData: @@ -288,12 +273,40 @@ class Graph: def fromGMLFileMulti( f: str) -> List[Graph]: ... @staticmethod - def fromDFS(s: str) -> Graph: ... + def fromDFS( s: str) -> Graph: ... + @staticmethod + def fromDFSMulti( s: str) -> List[Graph]: ... @staticmethod - def fromSMILES( s: str, allowAbstract: bool = ..., classPolicy: SmilesClassPolicy = ...) -> Graph: ... + def fromSMILES( s: str, allowAbstract: bool = ..., classPolicy: SmilesClassPolicy = ...) -> Graph: ... @staticmethod - def fromSMILESMulti(s: str, allowAbstract: bool = ..., classPolicy: SmilesClassPolicy = ...) -> List[Graph]: ... + def fromSMILESMulti( s: str, allowAbstract: bool = ..., classPolicy: SmilesClassPolicy = ...) -> List[Graph]: ... + + @staticmethod + def fromMOLString( s: str, options: MDLOptions = ..., add: bool = ...) -> Graph: ... + @staticmethod + def fromMOLFile( f: str, options: MDLOptions = ..., add: bool = ...) -> Graph: ... + @staticmethod + def fromMOLStringMulti(s: str, options: MDLOptions = ..., add: bool = ...) -> List[Graph]: ... + @staticmethod + def fromMOLFileMulti( f: str, options: MDLOptions = ..., add: bool = ...) -> List[Graph]: ... + @staticmethod + def fromSDString( s: str, options: MDLOptions = ..., add: bool = ...) -> List[Graph]: ... + @staticmethod + def fromSDFile( f: str, options: MDLOptions = ..., add: bool = ...) -> List[Graph]: ... + @staticmethod + def fromSDStringMulti( s: str, options: MDLOptions = ..., add: bool = ...) -> List[List[Graph]]: ... + @staticmethod + def fromSDFileMulti( f: str, options: MDLOptions = ..., add: bool = ...) -> List[List[Graph]]: ... + + @staticmethod + def fromRXNString( s: str, options: MDLOptions = ..., add: bool = ...): ... + @staticmethod + def fromRXNFile( f: str, options: MDLOptions = ..., add: bool = ...): ... + @staticmethod + def fromRXNStringMulti(s: str, options: MDLOptions = ..., add: bool = ...): ... + @staticmethod + def fromRXNFileMulti( f: str, options: MDLOptions = ..., add: bool = ...): ... class GraphAutGroup: ... @@ -302,6 +315,15 @@ class GraphAutGroup: ... class GraphPrinter: ... +def graphGMLString(s: str) -> Graph: ... +def graphGML(f: str) -> Graph: ... +def graphDFS(s: str) -> Graph: ... +def smiles(s: str, allowAbstract: bool, classPolicy: SmilesClassPolicy) -> Graph: ... +def mdlMOLString(s: str, addHydrogens: bool) -> Graph: ... +def mdlMOL(f: str, addHydrogens: bool) -> Graph: ... +def mdlSDString(s: str, addHydrogens: bool) -> List[Graph]: ... +def mdlSD(f: str, addHydrogens: bool) -> List[Graph]: ... + #----------------------------------------------------------------------------- # rule @@ -357,6 +379,8 @@ class Rule(RCExpExp): def fromGMLString(s: str, invert: bool=...) -> Rule: ... @staticmethod def fromGMLFile(f: str, invert: bool=...) -> Rule: ... + @staticmethod + def fromDFS(s: str, invert: bool=...) -> Rule: ... def _rcEvaluator(rules: Iterable[Rule], labelSettings: LabelSettings=...) -> RCEvaluator: ... diff --git a/libs/pymod/redirector/mod/__init__.py.in b/libs/pymod/redirector/mod/__init__.py.in new file mode 100644 index 0000000..bd3a76e --- /dev/null +++ b/libs/pymod/redirector/mod/__init__.py.in @@ -0,0 +1,6 @@ +import sys +sys.path.insert(0, "@CMAKE_INSTALL_FULL_LIBDIR@") +del sys.modules['mod'] +import mod +assert not mod._redirected +mod._redirected = True \ No newline at end of file diff --git a/libs/pymod/redirector/pyproject.toml b/libs/pymod/redirector/pyproject.toml new file mode 100644 index 0000000..9787c3b --- /dev/null +++ b/libs/pymod/redirector/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" diff --git a/libs/pymod/redirector/setup.cfg.in b/libs/pymod/redirector/setup.cfg.in new file mode 100644 index 0000000..f39e9d1 --- /dev/null +++ b/libs/pymod/redirector/setup.cfg.in @@ -0,0 +1,10 @@ +[metadata] +name = mod-jakobandersen +version = @mod_VERSION@ +author = @mod_AUTHOR@ +author_email = @mod_AUTHOR_EMAIL@ +description = A package just for redirecting the mod import to the /lib folder. + +[options] +packages = + mod \ No newline at end of file diff --git a/libs/pymod/share/mod/python.supp b/libs/pymod/share/mod/python.supp index b9abc9b..dc36fee 100644 --- a/libs/pymod/share/mod/python.supp +++ b/libs/pymod/share/mod/python.supp @@ -15,6 +15,15 @@ # # See Misc/README.valgrind for more information. +{ + When using Open Babel from Python this appears. + Memcheck:Leak + match-leak-kinds: possible + fun:malloc + ... + fun:_PyObject_GC_Alloc* +} + # all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif { ADDRESS_IN_RANGE/Invalid read of size 4 @@ -102,6 +111,14 @@ fun:_PyObject_GC_Resize #fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING } +{ + Handle PyMalloc confusing valgrind (possibly leaked), manually added + Memcheck:Leak + fun:realloc + ... + fun:_PyObject_GC_Resize + #fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING +} { Handle PyMalloc confusing valgrind (possibly leaked) diff --git a/libs/pymod/src/mod/py/Chem.cpp b/libs/pymod/src/mod/py/Chem.cpp index 7b7fc1e..71472c8 100644 --- a/libs/pymod/src/mod/py/Chem.cpp +++ b/libs/pymod/src/mod/py/Chem.cpp @@ -49,7 +49,7 @@ void Chem_doExport() { // rst: py::class_("Isotope", py::no_init) // rst: .. method:: __init__() - // rst: __init__(i) + // rst: __init__(i) // rst: // rst: Construct a representation of an isotope. // rst: If an isotope number is given, that specific one is constructed, @@ -93,7 +93,7 @@ void Chem_doExport() { // rst: Representation of basic data of an atom. // rst: py::class_("AtomData", py::no_init) - // rst: .. method:: __init__(atomId=AtomIds.Invalid, istotope=Isotope(), charge=Charge(), radical=False) + // rst: .. method:: __init__(atomId=AtomIds.Invalid, isotope=Isotope(), charge=Charge(), radical=False) // rst: // rst: Construct an atom data object. // rst: @@ -156,7 +156,6 @@ void Chem_doExport() { py::def("_bondTypeToString", &bondTypeToString); - // rst: .. class:: AtomIds // rst: // rst: This class contains constants for each chemical element, both as their abbreviations and their full names. diff --git a/libs/pymod/src/mod/py/Collections.cpp b/libs/pymod/src/mod/py/Collections.cpp index d73efae..46d91bb 100644 --- a/libs/pymod/src/mod/py/Collections.cpp +++ b/libs/pymod/src/mod/py/Collections.cpp @@ -54,6 +54,7 @@ bool listCompare(const std::vector &l, py::object r) { template void makePair() { py::to_python_converter, PairToTupleConverter>(); + TupleToPairConverter(); } void Collections_doExport() { @@ -63,6 +64,7 @@ void Collections_doExport() { makeVector(VecDGHyperEdge, dg::DG::HyperEdge); makeVector(VecDGStrat, std::shared_ptr); makeVector(VecGraph, std::shared_ptr); + makeVector(VecVecGraph, std::vector>); makeVector(VecRule, std::shared_ptr); using PairString = std::pair; makeVector(VecPairString, PairString); @@ -76,7 +78,6 @@ void Collections_doExport() { makePair(); makePair(); makePair(); - makePair(); // Optional py::to_python_converter, ToPythonOptionalValue>(); diff --git a/libs/pymod/src/mod/py/Config.cpp b/libs/pymod/src/mod/py/Config.cpp index 9bbe6ca..49db56a 100644 --- a/libs/pymod/src/mod/py/Config.cpp +++ b/libs/pymod/src/mod/py/Config.cpp @@ -23,6 +23,10 @@ std::string SmilesClassPolicy_str(SmilesClassPolicy p) { return boost::lexical_cast(p); } +std::string Action_str(Action a) { + return boost::lexical_cast(a); +} + } // namespace void Config_doExport() { @@ -154,6 +158,127 @@ void Config_doExport() { .value("MapUnique", SmilesClassPolicy::MapUnique); py::def("_SmilesClassPolicy__str__", &SmilesClassPolicy_str); + // rst: .. class:: Action + // rst: + // rst: Utility enum for deciding what to do in certain cases. + // rst: + py::enum_("Action") + // rst: .. attribute:: Error + // rst: + // rst: Abort the function and produce an error message, e.g., through and exception. + .value("Error", Action::Error) + // rst: .. attribute:: Warn + // rst: + // rst: Write a warning, but otherwise do as if it was `Ignore`. + .value("Warn", Action::Warn) + // rst: .. attribute:: Ignore + // rst: + // rst: Ignore the case. The function taking the action as argument should describe what this means. + .value("Ignore", Action::Ignore); + py::def("_Action__str__", &Action_str); + + // rst: .. class:: MDLOptions + // rst: + // rst: An aggregation of options for the various loading functions for MDL formats. + // rst: Generally each option is defaulted to follow the specification of the formats, + // rst: unless it is harmless to deviate (e.g., relaxed white-space parsing). + // rst: + py::class_("MDLOptions") + // rst: .. attribute:: addHydrogens = True + // rst: + // rst: Use the MDL valence model to add hydrogens to atoms with default valence, or disable all hydrogen addition. + // rst: + // rst: :type: bool + .def_readwrite("addHydrogens", &MDLOptions::addHydrogens) + // rst: .. attribute:: allowAbstract = False + // rst: + // rst: Allow non-standard atom symbols. The standard symbols are the element symbols and those specifying wildcard atoms. + // rst: + // rst: :type: bool + .def_readwrite("allowAbstract", &MDLOptions::allowAbstract) + // rst: .. attribute:: applyV2000AtomAliases = True + // rst: + // rst: In MOL V2000 CTAB blocks, replace atom labels by their aliases. + // rst: After application, the atom is considered abstract without errors, and hydrogen addition is suppressed. + // rst: + // rst: :type: bool + .def_readwrite("applyV2000AtomAliases", &MDLOptions::applyV2000AtomAliases) + // rst: .. attribute:: Action onPatternIsotope = Action::Error + // rst: Action onPatternCharge = Action::Error; + // rst: Action onPatternRadical = Action::Error; + // rst: + // rst: What to do when an atom with symbol ``*`` has an isotope, charge, or radical. + // rst: ``Action.Ignore`` means assuming the isotope, charge, or radical was not there. + .def_readwrite("onPatternIsotope", &MDLOptions::onPatternIsotope) + .def_readwrite("onPatternCharge", &MDLOptions::onPatternCharge) + .def_readwrite("onPatternRadical", &MDLOptions::onPatternRadical) + // rst: .. attribute:: onImplicitValenceOnAbstract = Action.Error + // rst: + // rst: What to do when ``addHydrogens and allowAbstract`` and an abstract atom is encountered with implicit valence. + // rst: ``Action.Ignore`` means adding no hydrogens. + // rst: + // rst: :type: Action + .def_readwrite("onImplicitValenceOnAbstract", &MDLOptions::onImplicitValenceOnAbstract) + // rst: .. attribute:: onV2000UnhandledProperty = Action.Warn + // rst: + // rst: What to do when a property line in a V2000 MOL file is not recognized. + // rst: ``Action.Ignore`` means simply ignoring that particular line. + // rst: + // rst: :type: Action + .def_readwrite("onV2000UnhandledProperty", &MDLOptions::onV2000UnhandledProperty) + // rst: .. attribute:: fullyIgnoreV2000UnhandledKnownProperty = False + // rst: + // rst: Warnings are usually stored as "loading warnings", even when they are ignored as during parsing. + // rst: Setting this to ``True`` will act as if ``onV2000UnhandledProperty = Actions::Ignore`` and + // rst: skip the storage, but only for a pre-defined known subset of properties. + // rst: + // rst: :type: bool + .def_readwrite("fullyIgnoreV2000UnhandledKnownProperty", &MDLOptions::fullyIgnoreV2000UnhandledKnownProperty) + // rst: .. attribute:: onV3000UnhandledAtomProperty = Action.Warn + // rst: + // rst: What to do when a property in atom line in a V3000 MOL file is not recognized. + // rst: ``Action.Ignore`` means simply ignoring that particular property. + // rst: + // rst: :type: Action + .def_readwrite("onV3000UnhandledAtomProperty", &MDLOptions::onV3000UnhandledAtomProperty) + // rst: .. attribute:: onV2000Charge4 = Action.Warn + // rst: + // rst: What to do when an atom in a V2000 MOL file has the charge 4 (doublet radical). + // rst: ``Action.Ignore`` means assuming it was charge 0. + // rst: + // rst: :type: Action + .def_readwrite("onV2000Charge4", &MDLOptions::onV2000Charge4) + // rst: .. attribute:: onV2000AbstractISO = Action.Warn + // rst: + // rst: What to do when an abstract atom in a V2000 MOL file has a non-default ISO or mass difference value. + // rst: ``Action.Ignore`` means assuming it had no ISO or mass difference value. + // rst: + // rst: :type: Action + .def_readwrite("onV2000AbstractISO", &MDLOptions::onV2000AbstractISO) + // rst: .. attribute:: onRAD1 = Action.Error + // rst: onRAD3 = Action.Error + // rst: onRAD4 = Action.Error + // rst: onRAD5 = Action.Error + // rst: onRAD6 = Action.Error + // rst: + // rst: What to do when an atom has assigned the indicated radical state. + // rst: ``Action.Ignore`` means pretending the atom has no radical state assigned. + // rst: + // rst: :type: Action + .def_readwrite("onRAD1", &MDLOptions::onRAD1) + .def_readwrite("onRAD3", &MDLOptions::onRAD3) + .def_readwrite("onRAD4", &MDLOptions::onRAD4) + .def_readwrite("onRAD5", &MDLOptions::onRAD5) + .def_readwrite("onRAD6", &MDLOptions::onRAD6) + // rst: .. attribute:: onUnsupportedQueryBondType = Action.Error + // rst: + // rst: What to do when a bond type 5, 6, or 7 are encountered (constrained query bond types). + // rst: ``Action.Ignore`` means assigning a term variable, as if the type was 8. + // rst: + // rst: :type: Action + .def_readwrite("onUnsupportedQueryBondType", &MDLOptions::onUnsupportedQueryBondType); + + #define NSIter(rNS, dataNS, tNS) \ BOOST_PP_SEQ_FOR_EACH_I(SettingIter, ~, \ BOOST_PP_TUPLE_ELEM(MOD_CONFIG_DATA_NS_SIZE(), 2, tNS)) diff --git a/libs/pymod/src/mod/py/Function.cpp b/libs/pymod/src/mod/py/Function.cpp index c02d20e..3a464a2 100644 --- a/libs/pymod/src/mod/py/Function.cpp +++ b/libs/pymod/src/mod/py/Function.cpp @@ -1,9 +1,9 @@ -#include - -#include "Function.hpp" +#include #include +#include #include +#include #include #include #include @@ -19,7 +19,11 @@ void Function_doExport() { exportFunc("_Func_StringDerivation"); // DG::Vertex -> X exportFunc("_Func_BoolDGVertex"); + exportFunc("_Func_DoubleDGVertex"); exportFunc("_Func_StringDGVertex"); + // DG::Vertex x int -> X + exportFunc(dg::DG::Vertex, int)>( + "_Func_PairStringStringDGVertexInt"); // DG::HyperEdge -> X exportFunc("_Func_BoolDGHyperEdge"); exportFunc("_Func_StringDGHyperEdge"); @@ -28,10 +32,6 @@ void Function_doExport() { exportFunc)>("_Func_BoolGraph"); exportFunc)>("_Func_IntGraph"); exportFunc)>("_Func_StringGraph"); - // Graph x DG -> X - exportFunc, std::shared_ptr)>("_Func_StringGraphDG"); - // Graph x DG x bool -> X - exportFunc, std::shared_ptr, bool)>("_Func_StringGraphDGBool"); // Graph x Strategy::GraphState -> X exportFunc, const dg::Strategy::GraphState &)>( "_Func_BoolGraphDGStratGraphState"); @@ -43,6 +43,8 @@ void Function_doExport() { "_Func_BoolGraphGraphDGStratGraphState"); // Strategy::GraphState -> X exportFunc("_Func_VoidDGStratGraphState"); + + exportFunc)>("_Func_BoolVertexMapGraphGraph"); } } // namespace mod::Py diff --git a/libs/pymod/src/mod/py/Post.cpp b/libs/pymod/src/mod/py/Post.cpp index 2b5d562..8feb73b 100644 --- a/libs/pymod/src/mod/py/Post.cpp +++ b/libs/pymod/src/mod/py/Post.cpp @@ -5,19 +5,90 @@ namespace mod::post::Py { void Post_doExport() { - py::def("post", &command); - py::def("postReset", &reset); - py::def("postFlush", &flush); - py::def("postDisable", &disable); - - py::def("postChapter", &summaryChapter); - py::def("postSection", &summarySection); - // rst: .. function:: makeUniqueFilePrefix() // rst: - // rst: :returns: a unique file prefix from the ``out/`` folder. + // rst: :returns: a string on the form ``out/iii_`` where ```iii``` is the next zero-padded integer + // rst: from an internal counter. // rst: :rtype: str py::def("makeUniqueFilePrefix", &makeUniqueFilePrefix); + + struct Post { + // a fake class to hax a "submodule" + }; + // rst: .. class:: post + // rst: + // rst: This class (which will be a submodule in the future) + // rst: contains various functions to manipulate post-processing (:ref:`mod_post`). + // rst: Commands for the post-processor are written to the command file ``out/post.sh`` + // rst: which the post-processor executes internally as a Bash script. + // rst: + py::class_("post", py::no_init) + // rst: .. staticmethod:: command(line) + // rst: + // rst: Write the given text to the command file and write a newline character. + // rst: + // rst: :param str line: the text to be written. + // rst: + // rst: .. warning:: The contents of the command file is executed without any security checks. + .def("command", &command).staticmethod("command") + // rst: .. staticmethod:: flushCommands() + // rst: + // rst: Flush the command file buffer. + .def("flushCommands", &flushCommands).staticmethod("flushCommands") + // rst: .. staticmethod:: disableCommands() + // rst: + // rst: Disable command writing and flushing, also for commands emitted internally in the library. + .def("disableCommands", &disableCommands).staticmethod("disableCommands") + // rst: .. staticmethod:: enableCommands() + // rst: + // rst: Enable command writing and flushing, also for commands emitted internally in the library. + .def("enableCommands", &enableCommands).staticmethod("enableCommands") + // rst: .. staticmethod:: reopenCommandFile() + // rst: + // rst: Reopen the command file, which may be useful if it was modified externally while open by the library. + .def("reopenCommandFile", &reopenCommandFile).staticmethod("reopenCommandFile") + // rst: .. staticmethod:: summaryChapter(heading) + // rst: + // rst: Command the post-processor to insert a ``\chapter`` macro in the summary. + // rst: + // rst: :param str heading: the chapter heading to insert. + .def("summaryChapter", &summaryChapter).staticmethod("summaryChapter") + // rst: .. staticmethod:: summarySection(heading) + // rst: + // rst: Command the post-processor to insert a ``\section`` macro in the summary. + // rst: + // rst: :param str heading: the section heading to insert. + .def("summarySection", &summarySection).staticmethod("summarySection") + // rst: .. staticmethod:: summaryRaw(latexCode, file=...) + // rst: + // rst: Command the post-processor to insert the given code verbatim in the summary. + // rst: + // rst: :param str latexCode: the code to insert. + // rst: :param str file: if given then that will be appended to a unique prefix for the final filename the code is stored in. + .def("summaryRaw", static_cast(&summaryRaw)) + .def("summaryRaw", static_cast(&summaryRaw)) + .staticmethod("summaryRaw") + // rst: .. staticmethod:: summaryInput(filename) + // rst: + // rst: Command the post-processor to insert a ``\input`` macro in the summary. + // rst: + // rst: :param str filename: the filename to input. + .def("summaryInput", &summaryInput).staticmethod("summaryInput") + // rst: .. staticmethod:: disableInvokeMake() + // rst: enableInvokeMake() + // rst: + // rst: Disable/enable the invocation of Make in the post-processor. + // rst: The processing of commands and generation of Makefiles will still be carried out, + // rst: and Make invocation can be done manually afterwards through the post-processor + .def("disableInvokeMake", &disableInvokeMake).staticmethod("disableInvokeMake") + .def("enableInvokeMake", &enableInvokeMake).staticmethod("enableInvokeMake") + // rst: .. staticmethod:: disableCompileSummary() + // rst: enableCompileSummary() + // rst: + // rst: Disable/enable the compilation of the final summary during post-processing. + // rst: The compilation can be invoked manually afterwards through the post-processor. + .def("disableCompileSummary", &disableCompileSummary).staticmethod("disableCompileSummary") + .def("enableCompileSummary", &enableCompileSummary).staticmethod("enableCompileSummary"); } } // namespace mod::post::Py \ No newline at end of file diff --git a/libs/pymod/src/mod/py/dg/Builder.cpp b/libs/pymod/src/mod/py/dg/Builder.cpp index 01e1bf1..6eea411 100644 --- a/libs/pymod/src/mod/py/dg/Builder.cpp +++ b/libs/pymod/src/mod/py/dg/Builder.cpp @@ -18,6 +18,7 @@ Builder_execute(std::shared_ptr b, std::shared_ptr strategy, void Builder_doExport() { using AddDerivation = DG::HyperEdge (Builder::*)(const Derivations &, IsomorphismPolicy); + using AddHyperEdge = DG::HyperEdge (Builder::*)(const DG::HyperEdge &, IsomorphismPolicy); using Apply = std::vector (Builder::*)(const std::vector > &, std::shared_ptr, bool, int, IsomorphismPolicy); @@ -54,6 +55,21 @@ void Builder_doExport() { // rst: is different but isomorphic to another given graph object or to a graph object already // rst: in the internal graph database in the associated derivation graph. .def("addDerivation", static_cast(&Builder::addDerivation)) + // rst: .. method:: addHyperEdge(e, graphPolicy=IsomorphismPolicy.Check) + // rst: + // rst: Adds a hyperedge to the associated :class:`DG` from a copy of the given hyperedge + // rst: (from a different :class:`DG`). + // rst: If it already exists, only add the rules to the edge. + // rst: + // rst: :param DGHyperEdge e: a hyperedge to copy. + // rst: :param IsomorphismPolicy graphPolicy: the isomorphism policy for adding the given graphs. + // rst: :returns: the hyperedge corresponding to the copy of the given hyperedge. + // rst: :rtype: DGHyperEdge + // rst: :raises: :class:`LogicError` if ``e`` is a null edge. + // rst: :raises: :class:`LogicError` if ``graphPolicy == IsomorphismPolicy.Check`` and a given graph object + // rst: is different but isomorphic to another given graph object or to a graph object already + // rst: in the internal graph database in the associated derivation graph. + .def("addHyperEdge", static_cast(&Builder::addHyperEdge)) // rst: .. method:: execute(strategy, *, verbosity=2, ignoreRuleLabelTypes=False) // rst: // rst: Execute the given strategy (:ref:`dgStrat`) and as a side-effect add diff --git a/libs/pymod/src/mod/py/dg/DG.cpp b/libs/pymod/src/mod/py/dg/DG.cpp index d75e20b..81b1284 100644 --- a/libs/pymod/src/mod/py/dg/DG.cpp +++ b/libs/pymod/src/mod/py/dg/DG.cpp @@ -210,7 +210,7 @@ void DG_doExport() { // rst: // rst: :raises: :class:`LogicError` if the DG has not been calculated. .def("listStats", &DG::listStats) - // rst: .. method:: load(graphDatabase, ruleDatabase, f, graphPolicy=IsomorphismPolicy.Check, verbosity=2) + // rst: .. staticmethod:: load(graphDatabase, ruleDatabase, f, graphPolicy=IsomorphismPolicy.Check, verbosity=2) // rst: // rst: Load a derivation graph dump as a locked object. // rst: Use :func:`DGBuilder.load` to load a dump into a derivation graph under construction. diff --git a/libs/pymod/src/mod/py/dg/Printer.cpp b/libs/pymod/src/mod/py/dg/Printer.cpp index 5a6b532..38dae1b 100644 --- a/libs/pymod/src/mod/py/dg/Printer.cpp +++ b/libs/pymod/src/mod/py/dg/Printer.cpp @@ -44,6 +44,12 @@ Printer_setMirrorOverwrite(Printer &printer, std::shared_ptr( + DG::Vertex, int)>> f) { + printer.setImageOverwrite(mod::toStdFunction(f)); +} + } // namespace void Printer_doExport() { @@ -322,6 +328,29 @@ void Printer_doExport() { // rst: :param f: the function called on each graph to retrieve the mirror to render it with. // rst: :type f: Callable[[Graph], bool] or bool .def("setMirrorOverwrite", &Printer_setMirrorOverwrite) + // rst: .. method:: setImageOverwrite(f) + // rst: + // rst: Overwrite the image generation for graphs depicted in the vertices of the printed derivation graph. + // rst: For each duplicate of each vertex to be depicted, the given callback is called. + // rst: It must then return two strings: + // rst: + // rst: 1. Either + // rst: + // rst: - an empty string if the standard depiction should be used, in which case the second string is ignored, or + // rst: - the filename of the PDF that should be included in the final compiled figure. + // rst: + // rst: 2. Either + // rst: + // rst: - an empty string if no additional post-processing command is needed, or + // rst: - a string of Bash code which will be inserted in the post-processing instructions. + // rst: For example, one can write out source code in the callback, and then return a command that compiles + // rst: that code into the PDF needed by inclusion. + // rst: + // rst: The image overwrite can be removed by calling with ``None``. + // rst: + // rst: :param f: the callback to use, or ``None`` to remove an existing callback. + // rst: :type f: Callable[[DGVertex, int], tuple[str, str]] or None + .def("setImageOverwrite", &Printer_setImageOverride) // rst: .. attribute:: graphvizPrefix // rst: // rst: The string that will be inserted into generated DOT files, diff --git a/libs/pymod/src/mod/py/graph/Graph.cpp b/libs/pymod/src/mod/py/graph/Graph.cpp index d6dbf34..cc40d5c 100644 --- a/libs/pymod/src/mod/py/graph/Graph.cpp +++ b/libs/pymod/src/mod/py/graph/Graph.cpp @@ -1,10 +1,13 @@ #include +#include #include #include #include #include +#include + #include namespace mod::graph::Py { @@ -19,8 +22,7 @@ void Graph_doExport() { // rst: // rst: This class models an undirected graph with labels on vertices and edges, // rst: without loops and without parallel edges. - // rst: Certain labels are regarded as models of chemical atoms and bonds. - // rst: See :ref:`mol-enc` for more information on this. + // rst: See :ref:`graph-model` for more details. // rst: // rst: The class implements the :class:`protocols.LabelledGraph`. // rst: See :ref:`py-Graph/GraphInterface` for additional guarantees. @@ -109,14 +111,14 @@ void Graph_doExport() { py::make_function(&Graph::getSmilesWithIds, py::return_value_policy())) // rst: .. attribute:: graphDFS // rst: - // rst: (Read-only) This is a :ref:`GraphDFS ` of the graph. + // rst: (Read-only) This is a :ref:`GraphDFS ` of the graph. // rst: // rst: :type: str .add_property("graphDFS", py::make_function(&Graph::getGraphDFS, py::return_value_policy())) // rst: .. attribute:: graphDFSWithIds // rst: - // rst: (Read-only) This is a :ref:`GraphDFS ` of the graph, where each vertices have an explicit id, + // rst: (Read-only) This is a :ref:`GraphDFS ` of the graph, where each vertices have an explicit id, // rst: corresponding to its internal vertex id. // rst: // rst: :type: str @@ -124,7 +126,7 @@ void Graph_doExport() { py::return_value_policy())) // rst: .. attribute:: linearEncoding // rst: - // rst: (Read-only) If the graph models a molecule this is the :ref:`SMILES string ` string, otherwise it is the :ref:`GraphDFS ` string. + // rst: (Read-only) If the graph models a molecule this is the :ref:`SMILES string ` string, otherwise it is the :ref:`GraphDFS ` string. // rst: // rst: :type: str .add_property("linearEncoding", py::make_function(&Graph::getLinearEncoding, @@ -170,22 +172,39 @@ void Graph_doExport() { // rst: :returns: the number of edges in the graph with the given label. // rst: :rtype: int .def("eLabelCount", &Graph::eLabelCount) - // rst: .. method:: isomorphism(other, maxNumMatches=1, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism)) + // rst: .. method:: isomorphism(codomain, maxNumMatches=1, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism)) // rst: - // rst: :param Graph other: the codomain :class:`Graph` for finding morphisms. + // rst: :param Graph codomain: the codomain graph for finding morphisms. // rst: :param int maxNumMatches: the maximum number of isomorphisms to search for. // rst: :param LabelSettings labelSettings: the label settings to use during the search. // rst: :returns: the number of isomorphisms from this graph to ``other``, but at most ``maxNumMatches``. // rst: :rtype: int + // rst: :raises LogicError: if ``codomain`` is null. .def("isomorphism", &Graph::isomorphism) - // rst: .. method:: monomorphism(other, maxNumMatches=1, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism)) + // rst: .. method:: monomorphism(codomain, maxNumMatches=1, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism)) // rst: - // rst: :param Graph other: the codomain :class:`Graph` for finding morphisms. + // rst: :param Graph codomain: the codomain graph for finding morphisms. // rst: :param int maxNumMatches: the maximum number of monomorphisms to search for. // rst: :param LabelSettings labelSettings: the label settings to use during the search. // rst: :returns: the number of monomorphisms from this graph to ``other``, though at most ``maxNumMatches``. // rst: :rtype: int + // rst: :raises LogicError: if ``codomain`` is null. .def("monomorphism", &Graph::monomorphism) + // rst: .. method:: enumerateMonomorphisms(codomain, callback, labelSettings=LabelSettings(LabelType.String, LabelRelation.Isomorphism)) + // rst: + // rst: Perform substructure search of this graph into the given codomain graph. + // rst: Whenever a match is found, the corresponding monomorphism is copied into a vertex map + // rst: and the given callback is invoked with it. + // rst: + // rst: :param Graph codomain: the codomain graph for finding morphisms. + // rst: :param callback: the function to call with each found monomorphism. + // rst: If ``False`` is returned from it, then the search is stopped. + // rst: :type callback: Callable[[protocols.VertexMap], bool] + // rst: :param LabelSettings labelSettings: the label settings to use during the search. + // rst: + // rst: :raises LogicError: if ``codomain`` is null. + // rst: :raises LogicError: if ``callback`` is null. + .def("enumerateMonomorphisms", &Graph::enumerateMonomorphisms) // rst: .. method:: makePermutation() // rst: // rst: :returns: a graph isomorphic to this, but with the vertex indices randomly permuted. @@ -261,7 +280,7 @@ void Graph_doExport() { // rst: Load a graph in :ref:`GML ` format from a given string, ``s``, // rst: or given file ``f``. // rst: The graph must be connected. - // rst: If not, use :meth:`Graph.fromGMLStringMulti` or :meth:`Graph.fromGMLFileMulti`. + // rst: If not, use :meth:`fromGMLStringMulti` or :meth:`fromGMLFileMulti`. // rst: // rst: :param str s: the string with the :ref:`GML ` data to load from. // rst: :param f: name of the :ref:`GML ` file to be loaded. @@ -282,7 +301,7 @@ void Graph_doExport() { // rst: or given file ``f``, // rst: with each graph being a connected component of the graph specified in the GML data. // rst: - // rst: See :meth:`Graph.fromGMLString` and :meth:`Graph.fromGMLFile` + // rst: See :meth:`fromGMLString` and :meth:`fromGMLFile` // rst: for a description of the parameters and exceptions. // rst: // rst: :returns: a list of the loaded graphs. @@ -293,9 +312,11 @@ void Graph_doExport() { .staticmethod("fromGMLFileMulti") // rst: .. staticmethod:: Graph.fromDFS(s, name=None, add=True) // rst: - // rst: Load a graph from a :ref:`GraphDFS ` string. + // rst: Load a graph from a :ref:`GraphDFS ` string. + // rst: The graph must be connected. + // rst: If not, use :meth:`Graph.fromDFSMulti`. // rst: - // rst: :param str s: the :ref:`GraphDFS ` string to parse. + // rst: :param str s: the :ref:`GraphDFS ` string to parse. // rst: :param str name: the name of the graph. If none is given the default name is used. // rst: :param bool add: whether to append the graph to :data:`inputGraphs` or not. // rst: :returns: the loaded graph. @@ -303,10 +324,22 @@ void Graph_doExport() { // rst: :raises: :class:`InputError` on bad input. .def("fromDFS", &Graph::fromDFS) .staticmethod("fromDFS") + // rst: .. staticmethod:: Graph.fromDFSMulti(s, add=True) + // rst: + // rst: Load a set of graphs from a :ref:`GraphDFS ` string, + // rst: with each graph being a connected component of the graph specified in the DFS data. + // rst: + // rst: :param str s: the :ref:`GraphDFS ` string to parse. + // rst: :param bool add: whether to append the graphs to :data:`inputGraphs` or not. + // rst: :returns: the loaded graphs. + // rst: :rtype: list[Graph] + // rst: :raises: :class:`InputError` on bad input. + .def("fromDFSMulti", &Graph::fromDFSMulti) + .staticmethod("fromDFSMulti") // rst: .. staticmethod:: Graph.fromSMILES(s, name=None, allowAbstract=False, classPolicy=SmilesClassPolicy.NoneOnDuplicate, add=True) // rst: // rst: Load a molecule from a :ref:`SMILES ` string. - // rst: The molecule must be a connected graph. If not, use :meth:`Graph.fromSMILESMulti`. + // rst: The molecule must be a connected graph. If not, use :meth:`fromSMILESMulti`. // rst: // rst: :param str s: the :ref:`SMILES ` string to parse. // rst: :param str name: the name of the graph. If none is given the default name is used. @@ -323,13 +356,87 @@ void Graph_doExport() { // rst: Load a set of molecules from a :ref:`SMILES ` string, // rst: with each molecule being a connected component of the graph specified in the SMILES string. // rst: - // rst: See :meth:`Graph.fromSMILES` for a description of the parameters and exceptions. + // rst: See :meth:`fromSMILES` for a description of the parameters and exceptions. // rst: // rst: :returns: a list of the loaded molecules. // rst: :rtype: list[Graph] .def("fromSMILESMulti", static_cast>(*)(const std::string &, bool, SmilesClassPolicy)>(&Graph::fromSMILESMulti)) - .staticmethod("fromSMILESMulti"); + .staticmethod("fromSMILESMulti") + // rst: .. staticmethod:: Graph.fromMOLString(s, name=None, options=MDLOptions(), add=True) + // rst: Graph.fromMOLFile(f, name=None, options=MDLOptions(), add=True) + // rst: + // rst: Load a molecule in :ref:`MOL ` format from a given string or file. + // rst: The molecule must be a connected graph. + // rst: If not, use :meth:`fromMOLStringMulti` and :meth:`fromMOLFileMulti`. + // rst: + // rst: :param str s: the string to parse. + // rst: :param f: name of the file to load. + // rst: :type f: str or CWDPath + // rst: :param str name: the name of the graph. If none is given the default name is used. + // rst: :param MDLOptions options: the options to use for loading. + // rst: :param bool add: whether to append the graph to :data:`inputGraphs` or not. + // rst: :returns: the loaded molecule. + // rst: :rtype: :class:`Graph` + // rst: :raises: :class:`InputError` on bad input. + .def("fromMOLString", &Graph::fromMOLString) + .staticmethod("fromMOLString") + .def("fromMOLFile", &Graph::fromMOLFile) + .staticmethod("fromMOLFile") + // rst: .. staticmethod:: Graph.fromMOLStringMulti(s, options=MDLOptions(), add=True) + // rst: Graph.fromMOLFileMulti(f, options=MDLOptions(), add=True) + // rst: + // rst: Load a set of molecules from a given string or file with :ref:`MOL ` data, + // rst: with each molecule being a connected component of the graph specified in the data. + // rst: + // rst: See :meth:`fromMOLString` and :meth:`fromMOLFile` + // rst: for a description of the parameters and exceptions. + // rst: + // rst: :returns: a list of the loaded molecules. + // rst: :rtype: list[Graph] + .def("fromMOLStringMulti", &Graph::fromMOLStringMulti) + .staticmethod("fromMOLStringMulti") + .def("fromMOLFileMulti", &Graph::fromMOLFileMulti) + .staticmethod("fromMOLFileMulti") + // rst: .. staticmethod:: Graph.fromSDString(s, options=MDLOptions(), add=True) + // rst: Graph.fromSDFile(f, options=MDLOptions(), add=True) + // rst: + // rst: Load a list of molecules in :ref:`SD ` format from a given string or file, + // rst: with each molecule being a connected component of each of the the graphs specified in the data. + // rst: If any graph is not connected, use :meth:`fromSDStringMulti` and :meth:`fromSDFileMulti` instead. + // rst: + // rst: :param str s: the string to parse. + // rst: :param f: name of the file to load. + // rst: :type f: str or CWDPath + // rst: :param MDLOptions options: the options to use for loading. + // rst: :param bool add: whether to append the graphs to :data:`inputGraphs` or not. + // rst: :returns: a list of the loaded molecules. + // rst: :rtype: list of :class:`Graph` + // rst: :raises: :class:`InputError` on bad input. + .def("fromSDString", &Graph::fromSDString) + .staticmethod("fromSDString") + .def("fromSDFile", &Graph::fromSDFile) + .staticmethod("fromSDFile") + // rst: .. staticmethod:: Graph.fromSDStringMulti(s, options=MDLOptions(), add=True) + // rst: Graph.fromSDFileMulti(f, options=MDLOptions(), add=True) + // rst: + // rst: Load a list of molecules in :ref:`SD ` format from a given string or file. + // rst: Each molecule is returned as a list of graphs, with each corresponding to a connected + // rst: component of the MOL entry. + // rst: + // rst: See :meth:`fromSDString` and :meth:`fromSDFile` + // rst: for a description of the parameters and exceptions. + // rst: + // rst: :returns: a list of lists of the loaded molecules. + // rst: The items of the outer list correspond to each MOL entry in the SD data. + // rst: :rtype: list[list[Graph]] + .def("fromSDStringMulti", &Graph::fromSDStringMulti) + .staticmethod("fromSDStringMulti") + .def("fromSDFileMulti", &Graph::fromSDFileMulti) + .staticmethod("fromSDFileMulti"); + + mod::Py::exportVertexMap>("VertexMapGraphGraph"); + // rst: .. method:: graphGMLString(s, name=None, add=True) // rst: diff --git a/libs/pymod/src/mod/py/graph/Printer.cpp b/libs/pymod/src/mod/py/graph/Printer.cpp index 4c54998..3b5445f 100644 --- a/libs/pymod/src/mod/py/graph/Printer.cpp +++ b/libs/pymod/src/mod/py/graph/Printer.cpp @@ -21,95 +21,121 @@ void Printer_doExport() { py::class_("GraphPrinter") .def(py::self == py::self) .def(py::self != py::self) - // rst: .. method:: setMolDefault() - // rst: - // rst: Shortcut for enabling all but thickening and index printing. + // rst: .. method:: setMolDefault() + // rst: + // rst: Shortcut for enabling all but thickening and index printing. .def("setMolDefault", &Printer::setMolDefault) - // rst: .. method:: setReactionDefault() - // rst: - // rst: Shortcut for enabling all but thickening, index printing and simplification of carbon atoms. + // rst: .. method:: setReactionDefault() + // rst: + // rst: Shortcut for enabling all but thickening, index printing and simplification of carbon atoms. .def("setReactionDefault", &Printer::setReactionDefault) - // rst: .. method:: disableAll() - // rst: - // rst: Disable all special printing features. + // rst: .. method:: disableAll() + // rst: + // rst: Disable all special printing features. .def("disableAll", &Printer::disableAll) - // rst: .. method:: enableAll() - // rst: - // rst: Enable all special printing features, except typewriter font. + // rst: .. method:: enableAll() + // rst: + // rst: Enable all special printing features, except typewriter font. .def("enableAll", &Printer::enableAll) - // rst: .. attribute:: edgesAsBonds - // rst: - // rst: Control whether edges with special labels are drawn as chemical bonds. - // rst: - // rst: :type: bool + // rst: .. attribute:: edgesAsBonds + // rst: + // rst: Control whether edges with special labels are drawn as chemical bonds. + // rst: + // rst: :type: bool .add_property("edgesAsBonds", &Printer::getEdgesAsBonds, &Printer::setEdgesAsBonds) - // rst: .. attribute:: collapseHydrogens - // rst: - // rst: Control whether vertices representing hydrogen atoms are collapsed into their neighbours labels. - // rst: - // rst: :type: bool + // rst: .. attribute:: collapseHydrogens + // rst: + // rst: Control whether vertices representing hydrogen atoms are collapsed into their neighbours labels. + // rst: + // rst: :type: bool .add_property("collapseHydrogens", &Printer::getCollapseHydrogens, &Printer::setCollapseHydrogens) - // rst: .. attribute:: raiseCharges - // rst: - // rst: Control whether a vertex label suffix encoding a charge is written as a superscript to the rest of the label. - // rst: - // rst: :type: bool + // rst: .. attribute:: raiseIsotopes + // rst: + // rst: Control whether a vertex label prefix encoding an isotope is written as a superscript to the rest of the label. + // rst: + // rst: :type: bool + .add_property("raiseIsotopes", &Printer::getRaiseIsotopes, &Printer::setRaiseIsotopes) + // rst: .. attribute:: raiseCharges + // rst: + // rst: Control whether a vertex label suffix encoding a charge is written as a superscript to the rest of the label. + // rst: + // rst: :type: bool .add_property("raiseCharges", &Printer::getRaiseCharges, &Printer::setRaiseCharges) - // rst: .. attribute:: simpleCarbons - // rst: - // rst: Control whether some vertices encoding carbon atoms are depicted without any label. - // rst: - // rst: :type: bool + // rst: .. attribute:: simpleCarbons + // rst: + // rst: Control whether some vertices encoding carbon atoms are depicted without any label. + // rst: + // rst: :type: bool .add_property("simpleCarbons", &Printer::getSimpleCarbons, &Printer::setSimpleCarbons) - // rst: .. attribute:: thick - // rst: - // rst: Control whether all edges are drawn thicker than normal and all labels are written in bold. - // rst: - // rst: :type: bool + // rst: .. attribute:: thick + // rst: + // rst: Control whether all edges are drawn thicker than normal and all labels are written in bold. + // rst: + // rst: :type: bool .add_property("thick", &Printer::getThick, &Printer::setThick) - // rst: .. attribute:: withColour - // rst: - // rst: Control whether colour is applied to certain elements of the graph which are molecule-like. - // rst: - // rst: :type: bool + // rst: .. attribute:: withColour + // rst: + // rst: Control whether colour is applied to certain elements of the graph which are molecule-like. + // rst: + // rst: :type: bool .add_property("withColour", &Printer::getWithColour, &Printer::setWithColour) - // rst: .. attribute:: withIndex - // rst: - // rst: Control whether the underlying indices of the vertices are printed. - // rst: - // rst: :type: bool + // rst: .. attribute:: withIndex + // rst: + // rst: Control whether the underlying indices of the vertices are printed. + // rst: + // rst: :type: bool .add_property("withIndex", &Printer::getWithIndex, &Printer::setWithIndex) - // rst: .. attribute:: withTexttt - // rst: - // rst: Control whether the vertex and edge labels are written with typewriter font. - // rst: - // rst: :type: bool + // rst: .. attribute:: withTexttt + // rst: + // rst: Control whether the vertex and edge labels are written with typewriter font. + // rst: + // rst: :type: bool .add_property("withTexttt", &Printer::getWithTexttt, &Printer::setWithTexttt) - // rst: .. attribute:: withRawStereo - // rst: - // rst: Control whether the vertices and edges are annotated with the raw stereo properties. - // rst: - // rst: :type: bool + // rst: .. attribute:: withRawStereo + // rst: + // rst: Control whether the vertices and edges are annotated with the raw stereo properties. + // rst: + // rst: :type: bool .add_property("withRawStereo", &Printer::getWithRawStereo, &Printer::setWithRawStereo) - // rst: .. attribute:: withPrettyStereo - // rst: - // rst: Control whether the vertices and edges are annotated with stylized stereo properties. - // rst: - // rst: :type: bool + // rst: .. attribute:: withPrettyStereo + // rst: + // rst: Control whether the vertices and edges are annotated with stylized stereo properties. + // rst: + // rst: :type: bool .add_property("withPrettyStereo", &Printer::getWithPrettyStereo, &Printer::setWithPrettyStereo) - // rst: .. attribute:: rotation - // rst: - // rst: Rotation of internally computed coordinates. - // rst: - // rst: :type: int + // rst: .. attribute:: rotation + // rst: + // rst: Rotation of internally computed coordinates. + // rst: + // rst: :type: int .add_property("rotation", &Printer::getRotation, &Printer::setRotation) - // rst: .. attribute:: mirror - // rst: - // rst: Mirror internally computed coordinates in the y-axis. - // rst: - // rst: :type: bool + // rst: .. attribute:: mirror + // rst: + // rst: Mirror internally computed coordinates in the y-axis. + // rst: + // rst: :type: bool .add_property("mirror", &Printer::getMirror, &Printer::setMirror) - ; + // rst: .. attribute:: withGraphvizCoords + // rst: + // rst: Do not use Open Babel for coordinate generation, but only the Graphviz fallback + // rst: during post-processing. + // rst: When setting this to ``True`` consider setting `simpleCarbons = False`` to avoid + // rst: misleading depictions due to colinear carbon chains. + // rst: + // rst: :type: bool + .add_property("withGraphvizCoords", &Printer::getWithGraphvizCoords, &Printer::setWithGraphvizCoords) + // =================================================================== + // rst: .. attribute:: graphvizPrefix + // rst: + // rst: The string that will be inserted into generated DOT files, + // rst: just after the graph declaration. + // rst: DOT files are only generated when ``withGraphvizCoords == True``. + // rst: + // rst: :type: str + .add_property("graphvizPrefix", + py::make_function(&Printer::getGraphvizPrefix, + py::return_value_policy()), + &Printer::setGraphvizPrefix); } } // namespace mod::graph::Py \ No newline at end of file diff --git a/libs/pymod/src/mod/py/graph/Union.cpp b/libs/pymod/src/mod/py/graph/Union.cpp index f22382f..a2410c1 100644 --- a/libs/pymod/src/mod/py/graph/Union.cpp +++ b/libs/pymod/src/mod/py/graph/Union.cpp @@ -40,6 +40,7 @@ void exportClass() { // rst: // rst: :param graphs: the list of graphs to adapt. // rst: :type graphs: list[Graph] + // rst: :raises LogicError: if a given graph is ``None``. .def("__init__", py::make_constructor(&make)) .def(py::self == py::self) .def(py::self != py::self) diff --git a/libs/pymod/src/mod/py/rule/Rule.cpp b/libs/pymod/src/mod/py/rule/Rule.cpp index 0188038..8997fa7 100644 --- a/libs/pymod/src/mod/py/rule/Rule.cpp +++ b/libs/pymod/src/mod/py/rule/Rule.cpp @@ -22,10 +22,11 @@ void Rule_doExport() { // rst: .. class:: Rule // rst: - // rst: Model of a transformation rule in the Double Pushout formalism, + // rst: This class models a graph transformation rule in the Double Pushout formalism, // rst: as the span :math:`L \leftarrow K \rightarrow R`. // rst: The three graphs are referred to as respectively // rst: the "left", "context", and "right" graphs of the rule. + // rst: See :ref:`graph-model` for more details. // rst: // rst: The class implements the :class:`protocols.Graph` protocol, // rst: which gives access to a graph view of the rule which has the left, context, and right graphs @@ -210,7 +211,20 @@ void Rule_doExport() { .def("fromGMLString", &Rule::fromGMLString) .staticmethod("fromGMLString") .def("fromGMLFile", &Rule::fromGMLFile) - .staticmethod("fromGMLFile"); + .staticmethod("fromGMLFile") + // rst: .. staticmethod:: Rule.fromDFS(s, invert=False, add=True) + // rst: + // rst: Load a rule from a :ref:`RuleDFS ` string. + // rst: + // rst: :param str s: the :ref:`RuleDFS ` string to parse. + // rst: :param bool invert: whether or not to invert the loaded rule. + // rst: :param str name: the name of the rule. If none is given the default name is used. + // rst: :param bool add: whether to append the rule to :data:`inputRules` or not. + // rst: :returns: the loaded rule. + // rst: :rtype: Rule + // rst: :raises: :class:`InputError` on bad input. + .def("fromDFS", &Rule::fromDFS) + .staticmethod("fromDFS"); // rst: .. function:: ruleGMLString(s, invert=False, add=True) // rst: diff --git a/libs/pymodutils/CMakeLists.txt b/libs/pymodutils/CMakeLists.txt new file mode 100644 index 0000000..c8466d3 --- /dev/null +++ b/libs/pymodutils/CMakeLists.txt @@ -0,0 +1,33 @@ +if(NOT BUILD_PY_MOD) + return() +endif() + +########################################################################### +# Targets and Artefacts +########################################################################### + +# pymodutils +# ------------------------------------------------------------------------- +add_library(pymodutils INTERFACE) +# TODO: when CMake 3.19 is required, add the sources +# https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries +# ${mod_pymodutils_INCLUDE_FILES} +# ${mod_pymodtils_SRC_FILES}) +add_library(mod::pymodutils ALIAS pymodutils) +target_include_directories(pymodutils INTERFACE + $ + $) +target_link_libraries(pymodutils INTERFACE + mod::libmod + Boost::${PYTHON_TARGET} + Python3::Python) + +install(TARGETS pymodutils + EXPORT PROJECT_exports + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/mod COMPONENT pymodutils_lib + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/mod COMPONENT pymodutils_lib + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT pymodutils_lib) +install(DIRECTORY src/mod + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT pymodutils_dev + FILES_MATCHING PATTERN "*.hpp") \ No newline at end of file diff --git a/libs/pymod/src/mod/py/Common.hpp b/libs/pymodutils/src/mod/py/Common.hpp similarity index 52% rename from libs/pymod/src/mod/py/Common.hpp rename to libs/pymodutils/src/mod/py/Common.hpp index 56e1426..98eb263 100644 --- a/libs/pymod/src/mod/py/Common.hpp +++ b/libs/pymodutils/src/mod/py/Common.hpp @@ -3,7 +3,9 @@ // TODO: https://github.com/boostorg/python/pull/296 #define BOOST_BIND_GLOBAL_PLACEHOLDERS + #include + #undef BOOST_BIND_GLOBAL_PLACEHOLDERS #include @@ -16,7 +18,7 @@ struct AttributeIsNotReadable { inline void noGet(AttributeIsNotReadable) {} -// From https://wiki.python.org/moin/boost.python/HowTo#Dynamic_template_to-python_converters. +// From https://wiki.python.org/moin/boost.python/HowTo#Dynamic_template_to-python_converters template struct PairToTupleConverter { static PyObject *convert(const std::pair &pair) { @@ -24,6 +26,27 @@ struct PairToTupleConverter { } }; +// From https://stackoverflow.com/questions/16497889/how-to-expose-stdpair-to-python-using-boostpython +template +struct TupleToPairConverter { + TupleToPairConverter() { + py::converter::registry::push_back(&convertible, &construct, py::type_id>()); + } + + static void *convertible(PyObject *obj) { + if(!PyTuple_CheckExact(obj)) return nullptr; + if(PyTuple_Size(obj) != 2) return nullptr; + return obj; + } + + static void construct(PyObject *obj, py::converter::rvalue_from_python_stage1_data *data) { + py::tuple tuple(py::borrowed(obj)); + void *storage = reinterpret_cast> *>(data)->storage.bytes; + storage = new(storage) std::pair(py::extract(tuple[0]), py::extract(tuple[1])); + data->convertible = storage; + } +}; + // https://stackoverflow.com/questions/36485840/wrap-boostoptional-using-boostpython template struct ToPythonOptionalValue { diff --git a/libs/pymod/src/mod/py/Function.hpp b/libs/pymodutils/src/mod/py/Function.hpp similarity index 95% rename from libs/pymod/src/mod/py/Function.hpp rename to libs/pymodutils/src/mod/py/Function.hpp index 24e2183..3d19b49 100644 --- a/libs/pymod/src/mod/py/Function.hpp +++ b/libs/pymodutils/src/mod/py/Function.hpp @@ -1,5 +1,5 @@ -#ifndef MOD_PY_FUNCTION_H -#define MOD_PY_FUNCTION_H +#ifndef MOD_PY_FUNCTION_HPP +#define MOD_PY_FUNCTION_HPP // The wrapping and haxing of reference counts could probably be done simpler. // The arg wrapping should also be looked into. @@ -7,7 +7,7 @@ #include -#include +#include #include @@ -102,4 +102,4 @@ void exportFunc(const char *name) { } // namespace mod::Py -#endif /* MOD_PY_FUNCTION_H */ +#endif // MOD_PY_FUNCTION_HPP \ No newline at end of file diff --git a/libs/pymod/src/mod/py/VertexMap.hpp b/libs/pymodutils/src/mod/py/VertexMap.hpp similarity index 100% rename from libs/pymod/src/mod/py/VertexMap.hpp rename to libs/pymodutils/src/mod/py/VertexMap.hpp diff --git a/scripts/checkJsonVisibility.sh b/scripts/checkJsonVisibility.sh index ea0584b..8b7ad84 100755 --- a/scripts/checkJsonVisibility.sh +++ b/scripts/checkJsonVisibility.sh @@ -6,10 +6,10 @@ if [ $? -ne 0 ]; then echo "grep failed:" grep -Rn '#include.*nlohman' fi -grep -Rn '#include.*nlohmann' | grep -v "^libmod/src/mod/lib/IO/JsonUtils.hpp" &> /dev/null +grep -Rn '#include.*nlohmann' | grep -v "^libmod/src/mod/lib/IO/Json.hpp" &> /dev/null if [ $? -eq 0 ]; then - echo "nlohmann json(_schema) may only be included via ibmod/src/mod/lib/IO/JsonUtils.hpp" + echo "nlohmann json(_schema) may only be included via libmod/src/mod/lib/IO/Json.hpp" echo "Found these other instances:" - grep -Rn '#include.*nlohmann' | grep -v "^libmod/src/mod/lib/IO/JsonUtils.hpp" + grep -Rn '#include.*nlohmann' | grep -v "^libmod/src/mod/lib/IO/Json.hpp" exit 1 fi diff --git a/scripts/flake8.sh b/scripts/flake8.sh index 8e2e007..a9a976e 100755 --- a/scripts/flake8.sh +++ b/scripts/flake8.sh @@ -8,7 +8,7 @@ if [ $res -ne 0 ]; then fi cd $root mkdir -p build/checkPython/mod -rm build/checkPython/mod/* +rm -f build/checkPython/mod/* for f in $(ls libs/pymod/lib/mod); do ln -s -T ../../../libs/pymod/lib/mod/$f build/checkPython/mod/$f done diff --git a/scripts/makePyExamples.py b/scripts/makePyExamples.py new file mode 100755 index 0000000..615a130 --- /dev/null +++ b/scripts/makePyExamples.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +import sys +import os +import json + +def processExamples(topSrcDir): + root = topSrcDir + "/examples/py/" + sections = [] + for sec in sorted(os.listdir(root)): + if not os.path.isdir(root + sec): + continue + with open(root + sec + "/meta.json") as meta: + section = json.load(meta) + assert "title" in section + section['id'] = sec + section['exs'] = [] + for ex in os.listdir(root + sec): + exFull = root + sec + "/" + ex + assert os.path.isfile(exFull), exFull + if not ex.endswith(".py"): + continue + with open(exFull, encoding='utf-8') as f: + desc = "" + endLine = 1 + for line in f.readlines(): + if not line.startswith("# rst"): + endLine += 1 + continue + line = line[5:] + if line.startswith("-name: "): + title = line[7:-1] + else: + assert line.startswith(": ") + desc += line[2:] + section['exs'].append({ + "id": ex, "title": title, "desc": desc, "endLine": endLine + }) + sections.append(section) + return {"sections": sections} + + +def outputRST(topSrcDir): + data = processExamples(topSrcDir) + root = topSrcDir + "/doc/source/" + os.makedirs(root + "examples", exist_ok=True) + with open(root + "examples/index.rst", "w") as f: + f.truncate() + f.write(".. Autogenerated in conf.py\n\n") + f.write(".. _examples:\n\n") + f.write("Examples\n########\n\n") + f.write(".. toctree::\n\n") + for sec in data["sections"]: + f.write("\t{}\n".format(sec["id"])) + + for sec in data['sections']: + with open(root + "examples/{}.rst".format(sec['id']), + "w", encoding='utf-8') as f: + f.truncate() + f.write(".. Autogenerated in conf.py\n\n") + for id_ in sec['oldIds']: + f.write(".. _examples-{}:\n".format(id_)) + f.write(".. _examples-{}:\n\n".format(sec['id'])) + f.write("{}\n".format(sec["title"])) + f.write(80*"#" + "\n\n") + for ex in sec['exs']: + f.write("\n\n{}\n".format(ex['title'])) + f.write(80*"^" + "\n\n") + f.write(ex['desc']) + f.write(""" +`Explore in the playground `__. + +.. literalinclude:: /_static/examples/py/{0}/{1} + :language: python + :linenos: + :lines: 1-{2} + :tab-width: 3 +""".format(sec['id'], ex['id'], ex['endLine'] - 1)) + + +def outputJSON(topSrcDir): + data = processExamples(topSrcDir) + for sec in data['sections']: + del sec['oldIds'] + for ex in sec['exs']: + del ex['desc'] + del ex['endLine'] + print(json.dumps(data, indent=1)) + + +if __name__ == '__main__': + topSrcDir = sys.argv[1] + outType = sys.argv[2] + if outType == 'rst': + outputRST(topSrcDir) + elif outType == 'json': + outputJSON(topSrcDir) + else: + print("Unknown output type '{}'.".format(outType)) + sys.exit(1) diff --git a/scripts/mypy.sh b/scripts/mypy.sh index 24adf3c..be6a7e7 100755 --- a/scripts/mypy.sh +++ b/scripts/mypy.sh @@ -8,7 +8,7 @@ if [ $res -ne 0 ]; then fi cd $root mkdir -p build/checkPython/mod -rm build/checkPython/mod/* +rm -f build/checkPython/mod/* for f in $(ls libs/pymod/lib/mod); do ln -s -T ../../../libs/pymod/lib/mod/$f build/checkPython/mod/$f done diff --git a/scripts/printDepGraph.py b/scripts/printDepGraph.py index 9365596..04d3d18 100644 --- a/scripts/printDepGraph.py +++ b/scripts/printDepGraph.py @@ -52,7 +52,6 @@ def clusterComponentFromFilename(f): ('interface', 'mod'), ('lib', 'lib'), ('lib', 'Chem'), - ('lib', 'IO'), ) clusterColour = { 'interface': 'green', diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7294449..943d63e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -36,6 +36,11 @@ if(BUILD_PY_MOD) string(REPLACE "/" "__" testName "${fileName}") make_py_test(${fileName} ${testName} "") endforeach() + + add_test(NAME pymod-module-direct + COMMAND ${CMAKE_INSTALL_FULL_BINDIR}/mod --nopost -e "assert not mod._redirected") + add_test(NAME pymod-module-redirected + COMMAND ${Python3_EXECUTABLE} -c "import mod\nassert mod._redirected") endif() add_subdirectory(cmake_add_subdirectory) diff --git a/test/cpp/dg/printer.cpp b/test/cpp/dg/printer.cpp index 28bfe3c..9d8d533 100644 --- a/test/cpp/dg/printer.cpp +++ b/test/cpp/dg/printer.cpp @@ -7,24 +7,35 @@ using namespace mod; + template -void test(F f, const Ts& ...ts) { +void test(F f, const std::string &msg, const Ts &...ts) { dg::Printer p; try { (p.*f)({}, ts...); assert(false); } catch(const LogicError &e) { - assert(e.what() == std::string("Can not push empty callback.")); + assert(e.what() == msg); } } +template +void testPush(F f, const Ts &...ts) { + test(f, "Can not push empty callback.", ts...); +} + +template +void testSet(F f, const Ts &...ts) { + test(f, "Can not set empty callback.", ts...); +} + int main() { - test(&dg::Printer::pushVertexVisible); - test(&dg::Printer::pushEdgeVisible); - test(&dg::Printer::pushVertexLabel); - test(&dg::Printer::pushEdgeLabel); - test(&dg::Printer::pushVertexColour, true); - test(&dg::Printer::pushEdgeColour); - test(&dg::Printer::setRotationOverwrite); - test(&dg::Printer::setMirrorOverwrite); + testPush(&dg::Printer::pushVertexVisible); + testPush(&dg::Printer::pushEdgeVisible); + testPush(&dg::Printer::pushVertexLabel); + testPush(&dg::Printer::pushEdgeLabel); + testPush(&dg::Printer::pushVertexColour, true); + testPush(&dg::Printer::pushEdgeColour); + testSet(&dg::Printer::setRotationOverwrite); + testSet(&dg::Printer::setMirrorOverwrite); } \ No newline at end of file diff --git a/test/cpp/graph/graph.cpp b/test/cpp/graph/graph.cpp new file mode 100644 index 0000000..312b760 --- /dev/null +++ b/test/cpp/graph/graph.cpp @@ -0,0 +1,19 @@ +#include +#include + +#undef NDEBUG + +#include + +using namespace mod; + +int main() { + auto dom = graph::Graph::fromSMILES("O"); + auto codom = dom; + try { + dom->enumerateMonomorphisms(dom, {}, LabelSettings(LabelType::String, LabelRelation::Isomorphism)); + assert(false); + } catch(const LogicError &e) { + assert(e.what() == std::string("callback is null.")); + } +} \ No newline at end of file diff --git a/test/py/chem.py b/test/py/chem.py index e50669d..45cc544 100644 --- a/test/py/chem.py +++ b/test/py/chem.py @@ -1,32 +1,61 @@ -post("disableSummary") -print("AtomId\n----------") -a = AtomId() -print(a) -a = AtomId(42) -print(a) -print(int(a)) - -print("Charge\n----------") -a = Charge() -print(a) -a = Charge(-42) -print(a) -print(int(a)) -assert a == -42 - -print("AtomData\n----------") -a = AtomData() -print(a.atomId) -print(a.isotope) -print(a.charge) -print(a.radical) +include("xxx_helpers.py") + +print("AtomId") +print("=" * 80) +assert AtomId() == AtomId(0) +fail(lambda: AtomId().symbol, "AtomId::Invalid has no symbol.") +assert int(AtomId()) == 0 +assert str(AtomId()) == str(int(AtomId())) + +assert AtomId(1) == AtomIds.H +assert AtomId(1) == AtomIds.Hydrogen +assert AtomId(1).symbol == "H" +assert int(AtomId(1)) == 1 +assert str(AtomId(1)) == str(int(AtomId(1))) + + +print("Isotope") +print("=" * 80) +assert Isotope() == Isotope(-1) +assert int(Isotope()) == -1 +assert str(Isotope()) == str(int(Isotope())) +assert Isotope() == -1 + +assert int(Isotope(42)) == 42 +assert str(Isotope(42)) == str(int(Isotope(42))) +assert Isotope(42) == 42 + + +print("Charge") +print("=" * 80) +assert Charge() == Charge(0) +assert int(Charge()) == 0 +assert str(Charge()) == str(int(Charge())) +assert Charge() == 0 + +assert int(Charge(2)) == 2 +assert str(Charge(2)) == str(int(Charge(2))) +assert Charge(2) == 2 + + +print("AtomData") +print("=" * 80) +assert AtomData().atomId == AtomId() +assert AtomData().isotope == Isotope() +assert AtomData().charge == Charge() +assert AtomData().radical == False + a = AtomData(AtomId(42), Isotope(60), Charge(-9), True) -print("AtomData:", a) +assert a.atomId == AtomId(42) +assert a.isotope == 60 +assert a.charge == -9 +assert a.radical == True + b = AtomData(AtomId(42), Isotope(60), Charge(-9), True) assert a == b + g = graphDFS("[60Mo9-.]") v = next(iter(g.vertices)) -print("Vertex:", v.atomId, v.isotope, v.charge, v.radical) assert v.atomId == a.atomId assert v.isotope == a.isotope assert v.charge == a.charge @@ -35,15 +64,21 @@ assert AtomData() < AtomData(AtomId(1)) assert AtomData(AtomId(1)) > AtomData() -print("BondType\n----------") -for a in [BondType.Invalid, BondType.Single, BondType.Aromatic, BondType.Double, BondType.Triple]: - print(repr(a)) - if a != BondType.Invalid: - print(a) -print(BondType.values) - -print("AtomId\n----------") -print(AtomIds.Invalid) -print(AtomIds.Max) -for k, v in AtomIds.__dict__.items(): - print(k, ":", v) + +print("BondType") +print("=" * 80) +fail(lambda: str(BondType.Invalid), "Can not print BondType::Invalid.") +assert int(BondType.Single) == 1 +assert int(BondType.Aromatic) == 2 +assert int(BondType.Double) == 3 +assert int(BondType.Triple) == 4 + +assert str(BondType.Single) == "-" +assert str(BondType.Aromatic) == ":" +assert str(BondType.Double) == "=" +assert str(BondType.Triple) == "#" + + +print("AtomIds") +print("=" * 80) +assert AtomIds.Invalid == AtomId() diff --git a/test/py/config.py b/test/py/config.py index abef0d3..3bc660a 100644 --- a/test/py/config.py +++ b/test/py/config.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() # LabelType assert LabelType() == LabelType.String @@ -70,3 +70,10 @@ assert SmilesClassPolicy() == SmilesClassPolicy.NoneOnDuplicate assert str(SmilesClassPolicy.NoneOnDuplicate) == "noneOnDuplicate" assert str(SmilesClassPolicy.ThrowOnDuplicate) == "throwOnDuplicate" +assert str(SmilesClassPolicy.MapUnique) == "mapUnique" + +# Action +assert Action() == Action.Error +assert str(Action.Error) == "error" +assert str(Action.Warn) == "warn" +assert str(Action.Ignore) == "ignore" diff --git a/test/py/derivation.py b/test/py/derivation.py index b924d7a..6a62429 100644 --- a/test/py/derivation.py +++ b/test/py/derivation.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() g1 = smiles('O', name="g1") g2 = smiles('C', name="g2") r = ruleGMLString("""rule [ ruleID "r" context [ node [ id 0 label "O" ] ] ]""") diff --git a/test/py/dg/021_build_addHyperEdge.py b/test/py/dg/021_build_addHyperEdge.py new file mode 100644 index 0000000..c4b073d --- /dev/null +++ b/test/py/dg/021_build_addHyperEdge.py @@ -0,0 +1,27 @@ +include("xx0_helpers.py") + +g1 = smiles('O', name="g1") +g2 = smiles('C', name="g2") +r1 = ruleGMLString('rule [ ruleID "r1" context [ node [ id 0 label "O" ] ] ]') +r2 = ruleGMLString('rule [ ruleID "r2" context [ node [ id 0 label "C" ] ] ]') + + +dg = DG() +with dg.build() as b: + d = Derivations() + d.left = [g1] + d.rules = [r1, r2] + d.right = [g2] + e = b.addDerivation(d) +assert dg.numEdges == 1 + + +dg2 = DG() +with dg2.build() as b: + fail(lambda: b.addHyperEdge(DGHyperEdge()), "The hyperedge is null.") + + e2 = b.addHyperEdge(e) + +assert set(v.graph for v in e.sources) == set(v.graph for v in e2.sources) +assert set(v.graph for v in e.targets) == set(v.graph for v in e2.targets) +assert set(e.rules) == set(e2.rules) diff --git a/test/py/dg/030_build_apply.py b/test/py/dg/030_build_apply.py index 4cabe45..bacaf98 100644 --- a/test/py/dg/030_build_apply.py +++ b/test/py/dg/030_build_apply.py @@ -199,3 +199,12 @@ assert e.numTargets == 2 ts = [v.graph for v in e.targets] assert ts == [g2, g2] + + +print("Empty result") +gC = smiles('[C]', "gC") +rRemove = ruleGMLString('rule [ left [ node [ id 0 label "C" ] ] ]') +dg = DG() +with dg.build() as builder: + res = builder.apply([gC], rRemove, verbosity=4) + assert len(res) == 0 diff --git a/test/py/dg/035_build_apply_nonProper.py b/test/py/dg/035_build_apply_nonProper.py index b8a4818..ac0451f 100644 --- a/test/py/dg/035_build_apply_nonProper.py +++ b/test/py/dg/035_build_apply_nonProper.py @@ -15,6 +15,12 @@ ]""") DG().build().apply([], r, onlyProper=False) +DG().build().apply([a], ruleGMLString("""rule [ + right [ + node [ id 0 label "O" ] + ] +]"""), onlyProper=False) + fail(lambda: DG().build().apply([None], r, onlyProper=False), "One of the graphs is a null pointer.") fail(lambda: DG().build().apply([], None, onlyProper=False), "The rule is a null pointer.") fail(lambda: DG(graphDatabase=[a]).build().apply([aa], r, onlyProper=False), "Isomorphic graphs. Candidate graph 'gaa' is isomorphic to 'ga' in the graph database.") @@ -136,3 +142,12 @@ assert e.numTargets == 2 ts = [v.graph for v in e.targets] assert ts == [g2, g2] + + +print("Empty result") +gC = smiles('[C]', "gC") +rRemove = ruleGMLString('rule [ left [ node [ id 0 label "C" ] ] ]') +dg = DG() +with dg.build() as builder: + res = builder.apply([gC], rRemove, onlyProper=False, verbosity=4) + assert len(res) == 0 diff --git a/test/py/dg/121_build_execute_rule.py b/test/py/dg/121_build_execute_rule.py index 0dc02f6..5b2695f 100644 --- a/test/py/dg/121_build_execute_rule.py +++ b/test/py/dg/121_build_execute_rule.py @@ -39,3 +39,6 @@ exeStrat(addSubset(gO) >> addUniverse(gC) >> rConnectOC, [gOC], [gO, gC, gOC], graphDatabase=inputGraphs, verbosity=10) +print("Empty result") +rRemove = ruleGMLString('rule [ left [ node [ id 0 label "C" ] ] ]') +exeStrat(addSubset(gC) >> rRemove, [], [gC], verbosity=10) diff --git a/test/py/dg/401_printer_using.py b/test/py/dg/401_printer_using.py index f9bf6d0..170672d 100644 --- a/test/py/dg/401_printer_using.py +++ b/test/py/dg/401_printer_using.py @@ -1,5 +1,5 @@ include("../xxx_helpers.py") -post("enableSummary") +post.enableInvokeMake() dg = DG() fail(lambda: dg.print(), "Can not create print data. The DG is not locked yet.") @@ -13,6 +13,10 @@ right [ node [ id 0 label "S" ] ] ]''') b.addDerivation(d) + d = Derivation() + d.left = [graphDFS("[image1]", name="image1")] + d.right = [graphDFS("[image2]", name="image2")] + b.addDerivation(d) s = "A + hide -> B\n" for i in range(1, 4): s += "{i} A{i} -> {i} B\n".format(i=i) @@ -22,19 +26,19 @@ b.addAbstract(s) dg.print() -postSection("withIndex, withShortcutEdges, labelsAsLatexMath") +post.summarySection("withIndex, withShortcutEdges, labelsAsLatexMath") p = DGPrinter() p.graphPrinter.withIndex = True p.withShortcutEdges = False p.labelsAsLatexMath = False dg.print(p) -postSection("withGraphImages") +post.summarySection("withGraphImages") p = DGPrinter() p.withGraphImages = False dg.print(p) -postSection("vertexVisible") +post.summarySection("vertexVisible") p = DGPrinter() p.pushVertexVisible(False) dg.print(p) @@ -42,14 +46,12 @@ p.pushVertexVisible(lambda v: v.graph.name != "hide") dg.print(p) p.popVertexVisible() -# deprecated -config.common.ignoreDeprecation = True -p.pushVertexVisible(lambda g, dg: g.name != "hide") -config.common.ignoreDeprecation = False +checkDeprecated(lambda: + p.pushVertexVisible(lambda g, dg: g.name != "hide")) dg.print(p) p.popVertexVisible() -postSection("edgeVisible") +post.summarySection("edgeVisible") p = DGPrinter() p.pushEdgeVisible(False) dg.print(p) @@ -58,19 +60,19 @@ dg.print(p) p.popEdgeVisible() -postSection("withShortcutEdgesAfterVisibility") +post.summarySection("withShortcutEdgesAfterVisibility") p = DGPrinter() p.pushVertexVisible(lambda v: v.graph.name != "hide") p.withShortcutEdgesAfterVisibility = True dg.print(p) -postSection("vertexLabelSep") +post.summarySection("vertexLabelSep") p = DGPrinter() p.pushVertexLabel("vLabelConstant") p.vertexLabelSep = " sep " dg.print(p) -postSection("vertexLabel") +post.summarySection("vertexLabel") p = DGPrinter() p.pushVertexLabel("vLabelConstant") dg.print(p) @@ -78,20 +80,18 @@ p.pushVertexLabel(lambda v: "vLabelCallback") dg.print(p) p.popVertexLabel() -# deprecated -config.common.ignoreDeprecation = True -p.pushVertexLabel(lambda g, dg: "vLabelCallbackDep") -config.common.ignoreDeprecation = False +checkDeprecated(lambda: + p.pushVertexLabel(lambda g, dg: "vLabelCallbackDep")) dg.print(p) p.popVertexLabel() -postSection("edgeLabelSep") +post.summarySection("edgeLabelSep") p = DGPrinter() p.pushEdgeLabel("eLabelConstant") p.edgeLabelSep = " sep " dg.print(p) -postSection("edgeLabel") +post.summarySection("edgeLabel") p = DGPrinter() p.pushEdgeLabel("eLabelConstant") dg.print(p) @@ -100,18 +100,18 @@ dg.print(p) p.popEdgeLabel() -postSection("withGraphName, withRuleName, withRuleId") +post.summarySection("withGraphName, withRuleName, withRuleId") p = DGPrinter() p.withGraphName = False p.withRuleName = True p.withRuleId = False dg.print(p) -postSection("withInlineGraphs") +post.summarySection("withInlineGraphs") p = DGPrinter() p.withInlineGraphs = True f = dg.print(p) -post("summaryInput {}.tex".format(f[0][:-4])) +post.summaryInput("{}.tex".format(f[0][:-4])) e = next(e for e in dg.edges if len(e.rules) > 0) src = next(iter(e.sources)) tar = next(iter(e.targets)) @@ -123,9 +123,9 @@ \draw[blue] (v-{dgSrc}-0-v-{gSrc}) to[bend left=45] (v-{dgTar}-0-v-{gTar}); \end{{tikzpicture}} """.format(dgSrc=src.id, dgTar=tar.id, gSrc=vSrc.id, gTar=vTar.id)) -post("summaryInput out/extra.tex") +post.summaryInput("out/extra.tex") -postSection("vertexColour") +post.summarySection("vertexColour") p = DGPrinter() p.pushVertexColour("blue") dg.print(p) @@ -133,10 +133,8 @@ p.pushVertexColour(lambda v: "green") dg.print(p) p.popVertexColour() -# deprecated -config.common.ignoreDeprecation = True -p.pushVertexColour(lambda g, dg: "red") -config.common.ignoreDeprecation = False +checkDeprecated(lambda: + p.pushVertexColour(lambda g, dg: "red")) dg.print(p) p.popVertexColour() @@ -144,7 +142,7 @@ p.pushVertexColour("blue", extendToEdges=False) dg.print(p) -postSection("edgeColour") +post.summarySection("edgeColour") p = DGPrinter() p.pushEdgeColour("blue") dg.print(p) @@ -153,14 +151,14 @@ dg.print(p) p.popEdgeColour() -postSection("rotationOverwrite") +post.summarySection("rotationOverwrite") p = DGPrinter() p.setRotationOverwrite(45) dg.print(p) p.setRotationOverwrite(lambda g: -45) dg.print(p) -postSection("mirrorOverwrite") +post.summarySection("mirrorOverwrite") p = DGPrinter() p.setMirrorOverwrite(True) dg.print(p) @@ -168,12 +166,27 @@ p.setMirrorOverwrite(lambda g: True) dg.print(p) -postSection("graphvizPrefix") +post.summarySection("imageOverride") +p = DGPrinter() +def customImage(v, dupNum): + if v.graph.name != "image1": + return ("", "") + with open("out/custom.tex", "w") as f: + f.write("""\\begin{tikzpicture} +\\node {custom}; +\\end{tikzpicture}""") + return ("out/custom", "compileTikz \"out/custom\" \"out/custom\"") +p.setImageOverwrite(customImage) +dg.print(p) +p.setImageOverwrite(None) +dg.print(p) + +post.summarySection("graphvizPrefix") p = DGPrinter() p.graphvizPrefix = 'layout = "dot";' dg.print(p) -postSection("tikzpictureOption") +post.summarySection("tikzpictureOption") p = DGPrinter() p.tikzpictureOption += ', draw=blue' dg.print(p) diff --git a/test/py/dg/402_printer_latex_escape.py b/test/py/dg/402_printer_latex_escape.py new file mode 100644 index 0000000..a6b278b --- /dev/null +++ b/test/py/dg/402_printer_latex_escape.py @@ -0,0 +1,25 @@ +include("../xxx_helpers.py") +post.enableInvokeMake() + +dg = DG() +with dg.build() as b: + d = Derivation() + d.left = [smiles("CO", name="g#_x")] + d.right = [smiles("CS")] + d.rule = ruleGMLString('''rule [ + ruleID "r#_x" + left [ node [ id 0 label "O" ] ] + right [ node [ id 0 label "S" ] ] + ]''') + b.addDerivation(d) + +p = DGPrinter() +p.withRuleName = True + +post.summarySection("labelsAsLatexMath=false") +p.labelsAsLatexMath = True +dg.print(p) + +post.summarySection("labelsAsLatexMath=true") +p.labelsAsLatexMath = True +dg.print(p) diff --git a/test/py/dg/411_printData_using.py b/test/py/dg/411_printData_using.py index 615fc1e..bcd2704 100644 --- a/test/py/dg/411_printData_using.py +++ b/test/py/dg/411_printData_using.py @@ -1,5 +1,5 @@ include("../xxx_helpers.py") -post("enableSummary") +post.enableInvokeMake() dg = DG() with dg.build() as b: @@ -14,7 +14,7 @@ for v in dg.vertices: globals()[v.graph.name] = v -postSection("makeDuplicate") +post.summarySection("makeDuplicate") d = DGPrintData(dg) d.makeDuplicate(e1, 1) dg.print(data=d) @@ -22,18 +22,18 @@ p.withShortcutEdges = False dg.print(data=d, printer=p) -postSection("removeDuplicate") +post.summarySection("removeDuplicate") d = DGPrintData(dg) d.removeDuplicate(e1, 0) dg.print(data=d) -postSection("reconnectSource") +post.summarySection("reconnectSource") d = DGPrintData(dg) d.makeDuplicate(e2, 1) d.reconnectSource(e2, 1, C, 1) dg.print(data=d) -postSection("reconnectTarget") +post.summarySection("reconnectTarget") d = DGPrintData(dg) d.makeDuplicate(e2, 1) d.reconnectTarget(e2, 1, E, 1) diff --git a/test/py/dg/449_printNonHyper.py b/test/py/dg/449_printNonHyper.py index 8d2c957..125f21a 100644 --- a/test/py/dg/449_printNonHyper.py +++ b/test/py/dg/449_printNonHyper.py @@ -1,5 +1,5 @@ include("../xxx_helpers.py") -post("enableSummary") +post.enableInvokeMake() dg = DG() diff --git a/test/py/dg/450_print_dpo.py b/test/py/dg/450_print_dpo_fail.py similarity index 70% rename from test/py/dg/450_print_dpo.py rename to test/py/dg/450_print_dpo_fail.py index 5defba6..b6da175 100644 --- a/test/py/dg/450_print_dpo.py +++ b/test/py/dg/450_print_dpo_fail.py @@ -1,5 +1,4 @@ include("../xxx_helpers.py") -post("enableSummary") e = DGHyperEdge() fail(lambda: e.print(), "Can not print null edge.") @@ -34,10 +33,13 @@ fail(lambda: eFail.print(), "No derivation exists for rule {}.".format(r.name)) -e.print() -p = GraphPrinter() -p.withIndex = True -e.print(printer=p) -e.print(nomatchColour="yellow") -e.print(matchColour="red") -e.print(nomatchColour="yellow", matchColour="red") +def printOk(withGraphvizCoords: bool): + post.enableInvokeMake() + p = GraphPrinter() + p.withGraphvizCoords = withGraphvizCoords + e.print(p) + e.print(p, nomatchColour="yellow") + e.print(p, matchColour="red") + e.print(p, nomatchColour="yellow", matchColour="red") + p.withIndex = True + e.print(p) diff --git a/test/py/dg/451_print_dpo_no_openbabel.py b/test/py/dg/451_print_dpo_no_openbabel.py deleted file mode 100644 index 111d9e6..0000000 --- a/test/py/dg/451_print_dpo_no_openbabel.py +++ /dev/null @@ -1,45 +0,0 @@ -include("../xxx_helpers.py") -post("enableSummary") - -config.io.useOpenBabelCoords = False - -e = DGHyperEdge() -fail(lambda: e.print(), "Can not print null edge.") - -dg = DG() -dg.build().addAbstract("A -> B") -e = next(iter(dg.edges)) -fail(lambda: e.print(), "The edge has no rules.") - -r = ruleGMLString("""rule [ - left [ node [ id 1 label "B" ] ] - context [ - node [ id 0 label "A" ] - edge [ source 0 target 1 label "-" ] - ] - right [ node [ id 1 label "Q" ] ] -]""") -g = graphDFS("[A][B][C]") -gFail = smiles("O") - -dg = DG() -with dg.build() as b: - es = b.apply([g], r) - assert len(es) == 1 - e = es[0] - - d = Derivation() - d.left = [gFail] - d.right = [gFail] - d.rule = r - eFail = b.addDerivation(d) - -fail(lambda: eFail.print(), "No derivation exists for rule {}.".format(r.name)) - -e.print() -p = GraphPrinter() -p.withIndex = True -e.print(printer=p) -e.print(nomatchColour="yellow") -e.print(matchColour="red") -e.print(nomatchColour="yellow", matchColour="red") diff --git a/test/py/dg/451_print_dpo_open_babel.py b/test/py/dg/451_print_dpo_open_babel.py new file mode 100644 index 0000000..0af4e83 --- /dev/null +++ b/test/py/dg/451_print_dpo_open_babel.py @@ -0,0 +1,2 @@ +include("450_print_dpo_fail.py") +printOk(withGraphvizCoords=False) diff --git a/test/py/dg/452_print_dpo_graphviz.py b/test/py/dg/452_print_dpo_graphviz.py new file mode 100644 index 0000000..2f3a4ce --- /dev/null +++ b/test/py/dg/452_print_dpo_graphviz.py @@ -0,0 +1,2 @@ +include("450_print_dpo_fail.py") +printOk(withGraphvizCoords=True) diff --git a/test/py/formoseCommon/test.py b/test/py/formoseCommon/test.py new file mode 100644 index 0000000..0af265a --- /dev/null +++ b/test/py/formoseCommon/test.py @@ -0,0 +1,21 @@ +include("grammar.py") + +r1 = ketoEnol_F +r2 = aldolAdd_F + +c1_1K = r1.getVertexFromExternalId(1) +c1_2K = r1.getVertexFromExternalId(2) +c2_1K = r2.getVertexFromExternalId(1) +c2_2K = r2.getVertexFromExternalId(2) +o2_3K = r2.getVertexFromExternalId(3) + +m = RCMatch(r1, r2) +print("hmm:", c1_1K.right.id, c2_2K.left.id) +m.push(c1_1K.right, c2_2K.left) +print("hmm:", c1_2K.right.id, c2_1K.left.id) +m.push(c1_2K.right, c2_1K.left) +res = m.compose() +res.print() + +for a in inputRules: + a.print() diff --git a/test/py/function.py b/test/py/function.py index 723d3a0..4dcb3b5 100644 --- a/test/py/function.py +++ b/test/py/function.py @@ -4,16 +4,16 @@ dg.calc() printer = DGPrinter() -postSection("Const False") +post.summarySection("Const False") printer.pushVertexVisible(False) dg.print(printer) printer.popVertexVisible() -postSection("Const True") +post.summarySection("Const True") printer.pushVertexVisible(True) dg.print(printer) printer.popVertexVisible() -postSection("Func") +post.summarySection("Func") def f(g, dg): return all(g != a for a in inputGraphs) printer.pushVertexVisible(f) dg.print(printer) @@ -23,7 +23,7 @@ def f(g, dg): return all(g != a for a in inputGraphs) dg.print(printer) printer.popVertexVisible() -postSection("Lambda") +post.summarySection("Lambda") printer.pushVertexVisible(lambda g, dg: all(g != a for a in inputGraphs)) dg.print(printer) printer.popVertexVisible() diff --git a/test/py/graph/010_basic_loading.py b/test/py/graph/010_basic_loading.py index 3f3d6b8..5b65484 100644 --- a/test/py/graph/010_basic_loading.py +++ b/test/py/graph/010_basic_loading.py @@ -5,8 +5,6 @@ with open(fGML, "w") as f: f.write(dataGML) fGML = CWDPath(fGML) -dataDFS = "C" -dataSMILES = dataDFS def check(f, arg): @@ -25,24 +23,77 @@ def makeList(a): f(arg, add=False) assert inputGraphs == res + res2 + +############################################################################### + check(Graph.fromGMLString, dataGML) assert Graph.fromGMLString == graphGMLString -check(Graph.fromGMLStringMulti, dataGML) - check(Graph.fromGMLFile, fGML) assert Graph.fromGMLFile == graphGML +fail(lambda: Graph.fromGMLFile("doesNotExist.gml"), + "Could not open GML file ", err=InputError, isSubstring=True) +check(Graph.fromGMLStringMulti, dataGML) check(Graph.fromGMLFileMulti, fGML) +fail(lambda: Graph.fromGMLFileMulti("doesNotExist.gml"), + "Could not open GML file ", err=InputError, isSubstring=True) + +############################################################################### + +dataDFS = "C" check(Graph.fromDFS, dataDFS) assert Graph.fromDFS == graphDFS +############################################################################### + +dataSMILES = dataDFS + check(Graph.fromSMILES, dataSMILES) assert Graph.fromSMILES == smiles check(Graph.fromSMILESMulti, dataSMILES) +############################################################################### -fail(lambda: Graph.fromGMLFile("doesNotExist.gml"), - "Could not open graph GML file ", err=InputError, isSubstring=True) +dataMOL = """\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 END ATOM +M V30 END CTAB +M END""" +fMOL = 'myGraph.mol' +with open(fMOL, "w") as f: + f.write(dataMOL) +fMOL = CWDPath(fMOL) + +check(Graph.fromMOLString, dataMOL) +check(Graph.fromMOLFile, fMOL) +fail(lambda: Graph.fromMOLFile("doesNotExist.mol"), + "Could not open MOL file ", err=InputError, isSubstring=True) + +Graph.fromMOLStringMulti(dataMOL) +Graph.fromMOLFileMulti(fMOL) +fail(lambda: Graph.fromMOLFileMulti("doesNotExist.mol"), + "Could not open MOL file ", err=InputError, isSubstring=True) + +############################################################################### + +dataSD = dataMOL + "\n$$$$" +fSD = 'myGraphs.sd' +with open(fSD, "w") as f: + f.write(dataSD) +fSD = CWDPath(fSD) + +check(Graph.fromSDString, dataSD) +check(Graph.fromSDFile, fSD) +fail(lambda: Graph.fromSDFile("doesNotExist.sd"), + "Could not open SD file ", err=InputError, isSubstring=True) + +Graph.fromSDStringMulti(dataSD) +Graph.fromSDFileMulti(fSD) +fail(lambda: Graph.fromSDFileMulti("doesNotExist.sd"), + "Could not open SD file ", err=InputError, isSubstring=True) diff --git a/test/py/graph/020_graphDFS.py b/test/py/graph/020_graphDFS.py deleted file mode 100644 index 91fd527..0000000 --- a/test/py/graph/020_graphDFS.py +++ /dev/null @@ -1,53 +0,0 @@ -def check(s, withIds=False): - if withIds: - toString = lambda a: a.graphDFSWithIds - else: - toString = lambda a: a.graphDFS - a = graphDFS(s) - a.print() - s1 = toString(a) - print(s1) - a1 = graphDFS(s1) - s2 = toString(a1) - a2 = graphDFS(s2) - iso1 = a.isomorphism(a1) - iso2 = a.isomorphism(a2) - if iso1 == 0 or iso2 == 0: - print("s: ", s) - print("s1:", s1) - print("s2:", s2) - postSection("Error, iso1=%d, iso2=%d" % (iso1, iso2)) - a.print() - a1.print() - a2.print() - assert False - return a - -postSection("Implicit vertices") -print("Implicit vertices") -for s in {"B", "C", "N", "O", "P", "S", "F", "Cl", "Br", "I"}: - a = check(s) - print(a.id, a.graphDFS) - a.printGML() - -postSection("Explicit implicit vertices") -print("Explicit implicit vertices") -for s in {"B", "C", "N", "O", "P", "S", "F", "Cl", "Br", "I"}: - a = check("[" + s + "]") - print(a.id, a.graphDFS) - a.printGML() - -postSection("Ids and Ring Closures") -print("Ids and Ring Closures") -check("O1CCC-1N") -check("O1CCC1N") - -postSection("More Stuff") -print("More Stuff") -check("C1CCC1") -check("[C\]]") -check("C{a\}b}C") - -postSection("All With Ids") -print("All With Ids") -check("NCC(O)C(O)=O", withIds=True) diff --git a/test/py/graph/020_graphDFS/common.py b/test/py/graph/020_graphDFS/common.py new file mode 100644 index 0000000..a019908 --- /dev/null +++ b/test/py/graph/020_graphDFS/common.py @@ -0,0 +1,39 @@ +include("../../xxx_helpers.py") + +def check(s): + def iso(g1, g2): + if g1.isomorphism(g2) == 0: + print(" Error") + g1.print() + g2.print() + post.enableInvokeMake() + assert False + + + print("check({})".format(s)) + a = Graph.fromDFS(s) + + s1 = a.graphDFS + print(" min. ids:", s1) + a1 = Graph.fromDFS(s1) + s2 = a1.graphDFS + s2id = a1.graphDFSWithIds + print(" min. ids:", s2) + print(" all ids: ", s2id) + iso(a, a1) + + + s1 = a.graphDFSWithIds + print(" all ids: ", s1) + a1 = Graph.fromDFS(s1) + s2 = a1.graphDFS + s2id = a1.graphDFSWithIds + print(" min. ids:", s2) + print(" all ids: ", s2id) + iso(a, a1) + + return a + +def checkMulti(s): + print("checkMulti({})".format(s)) + gs = Graph.fromDFSMulti(s) diff --git a/test/py/graph/020_graphDFS/dot.py b/test/py/graph/020_graphDFS/dot.py new file mode 100644 index 0000000..d6c7515 --- /dev/null +++ b/test/py/graph/020_graphDFS/dot.py @@ -0,0 +1,34 @@ +include("common.py") + +a = Graph.fromDFS("[C]{.}[C]") +assert a.numVertices == 2 +assert a.numEdges == 1 +assert next(iter(a.edges)).stringLabel == '.' + +fail(lambda: Graph.fromDFS("[C]1.[O]2"), "not connected", + err=InputError, isSubstring=True) +gs = Graph.fromDFSMulti("[C]1.[O]2") +assert len(gs) == 2 +v1_0 = gs[0].getVertexFromExternalId(1) +assert v1_0.stringLabel == "C" +v2_0 = gs[0].getVertexFromExternalId(2) +assert not v2_0 +v1_1 = gs[1].getVertexFromExternalId(1) +assert not v1_1 +v2_1 = gs[1].getVertexFromExternalId(2) +assert v2_1.stringLabel == "O" + +Graph.fromDFS("C1C.CC1") +gs = Graph.fromDFSMulti("C1C.CC1") +assert len(gs) == 1 + +Graph.fromDFS("C1CCCC.1") +gs = Graph.fromDFSMulti("C1CCCC.1") +assert len(gs) == 1 + +gs = Graph.fromDFSMulti("[A]1.[B].1[X]") +assert len(gs) == 2 +assert gs[0].numVertices == 1 +assert next(iter(gs[0].vertices)).stringLabel == "A" +assert gs[1].numVertices == 2 +assert tuple(sorted(v.stringLabel for v in gs[1].vertices)) == ("B", "X") diff --git a/test/py/graph/020_graphDFS/other.py b/test/py/graph/020_graphDFS/other.py new file mode 100644 index 0000000..73e1edc --- /dev/null +++ b/test/py/graph/020_graphDFS/other.py @@ -0,0 +1,7 @@ +include("common.py") + +a = smiles("[CH3][CH2][CH2][CH2][CH2][C@H]([OH])/[CH]=[CH]/[C@H]1[C@H]2[CH2][C@H]([O][O]2)[C@@H]1[CH2]/[CH]=[CH]\\[CH2][CH2][CH2][C](=[O])[O-]") +b = graphDFS(a.graphDFS) +assert a.isomorphism(b) != 0 +c = graphDFS(a.graphDFSWithIds) +assert a.isomorphism(c) != 0 diff --git a/test/py/graph/020_graphDFS/tests.py b/test/py/graph/020_graphDFS/tests.py new file mode 100644 index 0000000..882b177 --- /dev/null +++ b/test/py/graph/020_graphDFS/tests.py @@ -0,0 +1,28 @@ +include("common.py") + +def sec(s): + post.summarySection(s) + print("="*80) + print(s) + +sec("Implicit vertices") +for s in {"B", "C", "N", "O", "P", "S", "F", "Cl", "Br", "I"}: + a = check(s) + +sec("Explicit implicit vertices") +for s in {"B", "C", "N", "O", "P", "S", "F", "Cl", "Br", "I"}: + a = check("[" + s + "]") + +sec("Ids and Ring Closures") +check("O1CCC-1N") +check("O1CCC1N") +check("[A]1[B][C]-1([S])") +check("[A]1[B][C]1") + +sec("Branches") +check("NCC(O)C(O)=O") + +sec("More Stuff") +check("C1CCC1") +check(r"[C\]]") +check(r"C{a\}b}C") diff --git a/test/py/graph/020_graphDFS/topo.py b/test/py/graph/020_graphDFS/topo.py new file mode 100644 index 0000000..ecef6b2 --- /dev/null +++ b/test/py/graph/020_graphDFS/topo.py @@ -0,0 +1,80 @@ +include("common.py") + +print("Missing ring closure") +fail(lambda: check("[A]-1"), "Ring closure ID 1 not found.", + err=InputError) + +print("Loop edges") +fail(lambda: check("[A]1-1"), "Loop edge in DFS on vertex with ID 1.", + err=InputError) +check("[A]1.1") + +fail(lambda: check("[A][B]1-1"), "Loop edge in DFS on vertex with ID 1.", + err=InputError) +check("[A][B]1.1") + +fail(lambda: check("[A]1(-1)"), "Loop edge in DFS on vertex with ID 1.", + err=InputError) +check("[A]1(.1)") + +fail(lambda: check("[A]([B]1-1)"), "Loop edge in DFS on vertex with ID 1.", + err=InputError) +check("[A]([B]1.1)") + + +print("Parallel Edges") +def bad(s): + fail(lambda: check(s), "Parallel edge in DFS. Back-edge is to vertex with ID 1.", err=InputError) + +print("="*80) +print("without extra ring closures") +for first in ( + "[A]1", # chain + "[Q][A]1", # tail + "[Q]([A]1", # branch + ): + for second in ( + "{}[B]", # tail + "({}[B]", # branch + ): + suffix = '' + suffix += ')' if '(' in first else '' + suffix += ')' if '(' in second else '' + print("-"*80) + print("Bad:", first + second) + for third in ("-1", "(1)", "1"): # tail, branch, ring-closure + bad(first + second.format('') + third + suffix) + check(first + second.format('.') + third + suffix) + for third in (".1", "(.1)"): + check(first + second.format('') + third + suffix) + checkMulti(first + second.format('.') + third + suffix) + +print("="*80) +print("with extra ring closures") +for first in ( + "[X]2[Y][Z][A]1", # tail + "[X]2[Y][Z]([A]1", # branch + ): + for second in ( + "{}[B]", # tail + "({}[B]", # branch + ): + suffix = '' + suffix += ')' if '(' in first else '' + suffix += ')' if '(' in second else '' + print("-"*80) + print("Bad:", first + second) + for third in ("-1", "(1)", "1"): # tail, branch, ring-closure + bad(first + second.format('-2') + third + suffix) + check(first + second.format('-2.') + third + suffix) + for third in ("-2-1", "-2(1)"): # tail, branch + bad(first + second.format('') + third + suffix) + check(first + second.format('.') + third + suffix) + bad(first + second.format('-2') + third + suffix) + check(first + second.format('-2.') + third + suffix) + for third in (".1", "(.1)"): + check(first + second.format('-2') + third + suffix) + checkMulti(first + second.format('-2.') + third + suffix) + for third in ("-2.1", "-2(.1)"): + check(first + second.format('') + third + suffix) + checkMulti(first + second.format('.') + third + suffix) diff --git a/test/py/graph/020_graphDFS/whitespace.py b/test/py/graph/020_graphDFS/whitespace.py new file mode 100644 index 0000000..c5fcdc8 --- /dev/null +++ b/test/py/graph/020_graphDFS/whitespace.py @@ -0,0 +1,8 @@ +include("common.py") + +a1 = Graph.fromDFSMulti("[A] 1 ( [B] ) [C] [D] {E} [F] . [G]") +a2 = Graph.fromDFSMulti("[A]1([B])[C][D]{E}[F].[G]") +assert a1[0].isomorphism(a2[0]) != 0 +assert a1[1].isomorphism(a2[1]) != 0 +assert len(a1) == 2 +assert len(a2) == 2 diff --git a/test/py/graph/030_smiles/abstract.py b/test/py/graph/030_smiles/abstract.py index daf7cb6..faa38ab 100644 --- a/test/py/graph/030_smiles/abstract.py +++ b/test/py/graph/030_smiles/abstract.py @@ -34,10 +34,13 @@ def check(p, name): config.graph.appendSmilesClass = False check("{}", "abc") +check("{}", "[]") check("{}", "[def]") +check("{}", "abc[def]") check("{}", "[def]efg") check("{}", "abc[def]efg") -check("{}", "[]") +check("{}", "abc[def]ghi[jkl]mno") +check("{}", "[abc][def][ghi]") check("{}", "42") check("{}", "42Heblah") diff --git a/test/py/graph/030_smiles/components.py b/test/py/graph/030_smiles/components.py index 535bb0b..01775dd 100644 --- a/test/py/graph/030_smiles/components.py +++ b/test/py/graph/030_smiles/components.py @@ -18,5 +18,5 @@ assert len(gs) == 1 fail(lambda: Graph.fromSMILES("C.1CCCC.1"), - "Error in graph loading from smiles string", + "Error in loading from inline SMILES string", err=InputError, isSubstring=True) diff --git a/test/py/graph/030_smiles/mass/genSymbolJumpTable.py b/test/py/graph/030_smiles/mass/genSymbolJumpTable.py index f7505d2..e5035ad 100644 --- a/test/py/graph/030_smiles/mass/genSymbolJumpTable.py +++ b/test/py/graph/030_smiles/mass/genSymbolJumpTable.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() data = list() data.append((1, "H", "Hydrogen")) data.append((2, "He", "Helium")) diff --git a/test/py/graph/030_smiles/mass/problematic.py b/test/py/graph/030_smiles/mass/problematic.py index 82d26c9..01c2893 100644 --- a/test/py/graph/030_smiles/mass/problematic.py +++ b/test/py/graph/030_smiles/mass/problematic.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() smiles("C(C1C(C=O)=C(C)C=1(CO))") smiles("CC1=CC(C)=C1(O)") smiles(R"CN(C)C1=NC(=C2C=CC=C12)N(C)C") diff --git a/test/py/graph/030_smiles/mass/rhea.py b/test/py/graph/030_smiles/mass/rhea.py index 91389a3..00a5e9c 100644 --- a/test/py/graph/030_smiles/mass/rhea.py +++ b/test/py/graph/030_smiles/mass/rhea.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() smiles(R"[Ag+]") smiles(R"[As](=O)([O-])(C)C") smiles(R"[Br-]") diff --git a/test/py/graph/030_smiles/mass/smiles.py b/test/py/graph/030_smiles/mass/smiles.py index 5cab780..9ac6987 100644 --- a/test/py/graph/030_smiles/mass/smiles.py +++ b/test/py/graph/030_smiles/mass/smiles.py @@ -6,7 +6,7 @@ #config.canon.printStats = True include("loadGraphs.py") -post("disableSummary") +post.disableInvokeMake() if not "n" in globals(): n = 100 @@ -39,7 +39,7 @@ molLike.withIndex = True a.print(graphLike, molLike) b.print(graphLike, molLike) - post("enableSummary") + post.enableInvokeMake() sys.exit(1) for i in range(1, n): aPerm = a.makePermutation() @@ -52,7 +52,7 @@ aPerm.name = "aPerm" a.print() aPerm.print() - post("enableSummary") + post.enableInvokeMake() sys.exit(1) ls = LabelSettings(LabelType.String, LabelRelation.Isomorphism) if a.isomorphism(aPerm, labelSettings=ls) != 1: diff --git a/test/py/graph/030_smiles/mass/smilesMetacycReactionSmiles.py b/test/py/graph/030_smiles/mass/smilesMetacycReactionSmiles.py index 8bf1f81..c991872 100644 --- a/test/py/graph/030_smiles/mass/smilesMetacycReactionSmiles.py +++ b/test/py/graph/030_smiles/mass/smilesMetacycReactionSmiles.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() smiles('[a proteinH:4]', allowAbstract=True) smiles('[a proteinH:9]', allowAbstract=True) smiles('[C:100][C:123]([C:101])([C:118]([O:134])[C:121](=[O:135])[N:126][C:103][C:102][C:112](=[O:131])[N:125][C:104][C:105][S:144][C:114](=[O:132])[C:106][C:113](=[O:5])[O-:5])[C:108][O:137][P:143](=[O:8])([O:140][P:142](=[O:7])([O:136][C:107][C:111]1([C:117]([O:139][P:141]([O-:6])(=[O:6])[O-:6])[C:116]([O:133])[C:122]([O:138]1)[N:130]3([C:120]2(=[C:115]([C:119]([N:124])=[N:127][C:109]=[N:128]2)[N:129]=[C:110]3))))[O-:7])[O-:8]', allowAbstract=True) diff --git a/test/py/graph/030_smiles/mass/smiles_cansmi_roundtrip.py b/test/py/graph/030_smiles/mass/smiles_cansmi_roundtrip.py index 234dd2f..f585956 100644 --- a/test/py/graph/030_smiles/mass/smiles_cansmi_roundtrip.py +++ b/test/py/graph/030_smiles/mass/smiles_cansmi_roundtrip.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() smiles(R"[B@]123[C@@]45[B@]67[B@@]89[C@]1(B2468)B3579") smiles(R"[BH3-][N@+]12CN3CN(CN(C3)C2)C1") smiles(R"[BH3-][N+]12CN3CN(CN(C3)C2)C1") diff --git a/test/py/graph/030_smiles/mass/smiles_nci.py b/test/py/graph/030_smiles/mass/smiles_nci.py index ca4c4bc..eb84d9b 100644 --- a/test/py/graph/030_smiles/mass/smiles_nci.py +++ b/test/py/graph/030_smiles/mass/smiles_nci.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() smiles("B1(O)OB(OB2OB(O)O2)O1") smiles("B(O[C@H](C)CC(C)C)(O[C@H](C)CC(C)C)O[C@H](C)CC(C)C") smiles("Brc1cc(c(c(Br)c1)NC(=S)N)Br") diff --git a/test/py/graph/050_mdl/allSmiles.py b/test/py/graph/050_mdl/allSmiles.py new file mode 100644 index 0000000..cbe6735 --- /dev/null +++ b/test/py/graph/050_mdl/allSmiles.py @@ -0,0 +1,104 @@ +post.disableInvokeMake() + +from openbabel import pybel +from openbabel import openbabel as ob + +include("../030_smiles/mass/loadGraphs.py") + +def loadSmiles(s): + mol = pybel.readstring("smi", s) + mol.addh() + return mol + +def loadMol(s): + mol = pybel.readstring("mol", s) + mol.addh() + return mol + +def bonds(mol): + for b in ob.OBMolBondIter(mol.OBMol): + yield b + +def gmlFromOb(mol): + gml = "graph [\n" + for a in mol.atoms: + aid = AtomId(a.atomicnum) + iso = Isotope(a.isotope) if a.isotope != 0 else Isotope() + c = Charge(a.formalcharge) + spinMult = a.spin + if spinMult == 0: + rad = False + elif spinMult == 2: + rad = True + else: + rad = False + print("Warning: non-zero spin-multiplicity,", spinMult) + ad = AtomData(aid, iso, c, rad) + gml += "\tnode [ id %d label \"%s\" ]\n" % (a.idx, str(ad)) + for b in bonds(mol): + src = b.GetBeginAtom().GetIdx() + tar = b.GetEndAtom().GetIdx() + b = b.GetBondOrder() + if b == 1: + bt = BondType.Single + elif b == 2: + bt = BondType.Double + elif b == 3: + bt = BondType.Triple + else: + print(bt) + assert False + gml += "\tedge [ source %d target %d label \"%s\" ]\n" % (src, tar, str(bt)) + gml += "]\n" + return gml + + +gs = inputGraphs[:] +count = 0 +for g in gs: + count += 1 + try: + s = g.smiles + except LogicError: + # an abstract SMILES string, so skip + continue + # Open Babel may mangle the molecule as it gets loaded, + # so the base for comparison is the loaded OBMol. + obInput = loadSmiles(g.smiles) + # Writing the MOL and reading it can yield a different molecule. + # We just want to test MOL parsing, + # so let's do that round-trip in OB as well. + # The OB V3000 read/write does not handle the valence field. + obInput = loadMol(obInput.write("mol")) + obInput3 = loadMol(obInput.write("mol", opt={"3": None})) + molInput = obInput.write("mol") + mol3Input = obInput3.write("mol", opt={"3": None}) + gFromInput = graphGMLString(gmlFromOb(obInput)) + gFromInput3 = graphGMLString(gmlFromOb(obInput3)) + + # actual check + try: + gFromMol = Graph.fromMOLString(molInput) + except: + print(molInput) + raise + if gFromInput.isomorphism(gFromMol) == 0: + print("gFromInput:", gFromInput.smiles) + print("gFromMol: ", gFromMol.smiles) + gFromInput.print() + gFromMol.print() + assert False + + # actual check + try: + gFromMol3 = Graph.fromMOLString(mol3Input) + except: + print(mol3Input) + raise + if gFromInput3.isomorphism(gFromMol3) == 0: + print("gFromInput3:", gFromInput.smiles) + print("gFromMol3: ", gFromMol3.smiles) + post.enableInvokeMake() + gFromInput.print() + gFromMol3.print() + assert False diff --git a/test/py/graph/050_mdl/mol2000.py b/test/py/graph/050_mdl/mol2000.py new file mode 100644 index 0000000..8baf97d --- /dev/null +++ b/test/py/graph/050_mdl/mol2000.py @@ -0,0 +1,384 @@ +post.disableInvokeMake() + +def fail(s, options=MDLOptions()): + try: + Graph.fromMOLString(s, options=options) + assert False + except InputError as e: + print(e) + +def sep(name): + print("\n%s\n" % name + "="*80) + + +sep("Header") +fail("") +fail("$MDL\n") +fail("$$$$") +fail("$RXN") +fail("$RDFILE") + +fail("\n") + +fail("\n\n") + +fail("\n\n\n") + +sep("Counts") +fail("\n\n\n\n") +fail("\n\n\n 1 0 0 0 0 0 0 0 0 0999 a2000") + +fail("\n\n\n 0 0 0 0 0 0 0 0 0999 V2000") +fail("\n\n\n 1 0 0 0 0 0 0 0 0999 V2000") +fail("\n\n\n1 1 0 0 0 0 0 0 0 0 0999 V2000") +fail("\n\n\n 11 0 0 0 0 0 0 0 0 0999 V2000") + +sep("Atoms") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 0 0 0 0 0 0 0 0 0 0 0 0 +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 LP 0 0 0 0 0 0 0 0 0 0 0 0 +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 L 0 0 0 0 0 0 0 0 0 0 0 0 +""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 1 0 0 0 0 0 0 0 0 0 0 0 +""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 - 0 0 0 0 0 0 0 0 0 0 +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 4 0 0 0 0 0 0 0 0 0 0 +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 8 0 0 0 0 0 0 0 0 0 0 +""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0abc 0 0 0 0 0 0 +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 16 0 0 0 0 0 0 +""") + + +sep("Charge 4") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 4 0 0 0 0 0 0 0 0 0 0 +""") +o = MDLOptions() +o.onV2000Charge4 = Action.Warn +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 4 0 0 0 0 0 0 0 0 0 0 +M END""", options=o) +o.onV2000Charge4 = Action.Ignore +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 4 0 0 0 0 0 0 0 0 0 0 +M END""", options=o) + +sep("Abstract ISO") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 * 1 0 0 0 0 0 0 0 0 0 0 0 +M END""") + +o = MDLOptions() +o.onImplicitValenceOnAbstract = Action.Warn +o.onV2000AbstractISO = Action.Warn +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 * 1 0 0 0 0 0 0 0 0 0 0 0 +M END""", options=o) +o.onV2000AbstractISO = Action.Ignore +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 * 1 0 0 0 0 0 0 0 0 0 0 0 +M END""", options=o) + + +sep("Bonds") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +""") + +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2 1 0 0 0 0 +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0 2 1 0 0 0 0 +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 3 2 1 0 0 0 0 +""") + +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 1 0 0 0 0 +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 0 1 0 0 0 0 +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 3 1 0 0 0 0 +""") + +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 0 0 0 0 +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 0 0 0 0 0 +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 9 0 0 0 0 +""") + +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 5 0 0 0 0 +""") +o = MDLOptions() +o.onUnsupportedQueryBondType = Action.Warn +Graph.fromMOLString("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 5 0 0 0 0 +M END +""", options=o) +o.onUnsupportedQueryBondType = Action.Ignore +Graph.fromMOLString("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 5 0 0 0 0 +M END +""", options=o).isomorphism(graphDFS("C([H])([H])([H]){_Q_1_2_5}C([H])([H])([H])")) == 1 + + +sep("Properties") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +blah +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M CHGxx +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M CHG +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M CHG 112345678x +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M CHG 1 xxxx +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M CHG 1 0xxxx +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M CHG 1 3xxxx +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M CHG 1 1 +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M CHG 1 1 - +""") + +sep("End") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +""") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +M END +a +""") + +sep("Missing M End") +fail("""\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +""") + + +sep("Unhandled property") +def makeInput(p): + return """\n\n\n 2 1 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 +{} +M END""".format(p) + +o = MDLOptions() +a = Graph.fromMOLString(makeInput("blah"), options=o) +assert len(a.loadingWarnings) == 1 + +o.fullyIgnoreV2000UnhandledKnownProperty = True +a = Graph.fromMOLString(makeInput("blah"), options=o) +assert len(a.loadingWarnings) == 1 +a = Graph.fromMOLString(makeInput("M REG 123456489"), options=o) +assert len(a.loadingWarnings) == 0 + +o.onV2000UnhandledProperty = Action.Error +a = Graph.fromMOLString(makeInput("M REG 123456489"), options=o) +assert len(a.loadingWarnings) == 0 + + +sep("Implicit valence of abstract atom") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 +M END +""") +o = MDLOptions() +o.onImplicitValenceOnAbstract = Action.Ignore +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 +M END +""", options=o) +o.onImplicitValenceOnAbstract = Action.Warn +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 +M END +""", options=o) + + +for rad in 1, 3, 4, 5, 6: + sep("RAD {}".format(rad)) + data = """\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +M RAD 1 1 {} +M END""".format(rad) + fail(data) + o = MDLOptions() + setattr(o, "onRAD{}".format(rad), Action.Ignore) + Graph.fromMOLString(data, options=o) + setattr(o, "onRAD{}".format(rad), Action.Warn) + Graph.fromMOLString(data, options=o) + + +sep("Atom alias") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +A +M END +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +A 1 +M END +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +A x +M END +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +A 0 +M END +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +A 2 +M END +""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +A 1 +""") +assert Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +A 1 +$strangeLabel +M END +""").isomorphism(graphDFS("[$strangeLabel]")) == 1 +o = MDLOptions() +o.applyV2000AtomAliases = False +assert Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 +A 1 +$strangeLabel +M END +""", options=o).isomorphism(smiles("C")) == 1 + + +sep("Parallel Edges") +fail("""\n\n\n 2 2 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 1 1 0 0 0 0 +M END""") + + +sep("Empty") +fail("""\n\n\n 0 0 0 0 0 0 0 0 0 0999 V2000 +M END""") + + +sep("Pattern Atoms") +for s, opt in (('ISO', 'onPatternIsotope'), ('CHG', 'onPatternCharge'), ('RAD', 'onPatternRadical')): + data = """\n\n\n 1 0 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 * 0 0 0 0 0 0 0 0 0 0 0 0 +M {} 1 1 2 +M END""".format(s) + fail(data) + o = MDLOptions() + o.onImplicitValenceOnAbstract = Action.Ignore + setattr(o, opt, Action.Warn) + Graph.fromMOLString(data, options=o) + setattr(o, opt, Action.Ignore) diff --git a/test/py/graph/050_mdl/mol3000.py b/test/py/graph/050_mdl/mol3000.py new file mode 100644 index 0000000..f444841 --- /dev/null +++ b/test/py/graph/050_mdl/mol3000.py @@ -0,0 +1,640 @@ +post.disableInvokeMake() + +def fail(s, options=MDLOptions()): + try: + Graph.fromMOLString(s, options=options) + assert False + except InputError as e: + print(e) + +def sep(name): + print("\n%s\n" % name + "="*80) + + +sep("CTAB") +fail("\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 """) +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAx""") + +sep("CTAB counts") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 blah""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 2 3 4""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1a 2 3 4 5""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 2a 3 4 5""") + +sep("Atom block") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 2 3 4 5""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 2 3 4 5 +M V30 BEGIn ATOM""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 2 3 4 5 +M V30 BEGIN ATOm""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 0 0 0 0 +M V30 BEGIN ATOM""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 0 0 0 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 x 0 0 0 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 0 0 0 0 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 "blah" 0 0 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 blah 0 0 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 x""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 1 C 0 0 0 0""") + +sep("Atom optional arguments") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 blah""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 blah""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 blah=""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 CHG=""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 CHG=x""") + + +sep("Bond block") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 0 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 0 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BONd""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 0 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 0 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BONd""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 0 0 0""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 x 0 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 0 0 0 0""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 0 x 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 0 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 5 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 6 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 7 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 9 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 10 0 0""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 1 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 x 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 1 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 0 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 1 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 3 0""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 1 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 x""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 1 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 0""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 1 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 3""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 1 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 1""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 3 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 3 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 1 1 2 3""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 3 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 3 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 1 2 3""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 3 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 3 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 1 2 3 +M V30 END BONd""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 3 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 3 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 5 1 2 +M V30 END BOND +M V30 END CTAB""") +o = MDLOptions() +o.onUnsupportedQueryBondType = Action.Warn +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 1 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 5 1 2 +M V30 END BOND +M V30 END CTAB +M END""", options=o) +o.onUnsupportedQueryBondType = Action.Ignore +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 1 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 5 1 2 +M V30 END BOND +M V30 END CTAB +M END""", options=o).isomorphism(graphDFS("C([H])([H])([H]){_Q_1_2_5}C([H])([H])([H])")) == 1 + +sep("CTAB end") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 3 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 3 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 1 2 3 +M V30 END BOND""") + + +sep("End") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 3 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 3 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 1 2 3 +M V30 END BOND +M V30 END CTAB""") # TODO: may be allowed later +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 3 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 3 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 1 2 3 +M V30 END BOND +M V30 END CTAB +M ENd""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 3 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 3 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 1 2 3 +M V30 END BOND +M V30 END CTAB +M END +x""") + +sep("Implicit valence of abstract atom") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 * 0 0 0 0 +M V30 END ATOM +M V30 END CTAB +M END""") +o = MDLOptions() +o.onImplicitValenceOnAbstract = Action.Ignore +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 * 0 0 0 0 +M V30 END ATOM +M V30 END CTAB +M END""", options=o) +o.onImplicitValenceOnAbstract = Action.Warn +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 * 0 0 0 0 +M V30 END ATOM +M V30 END CTAB +M END""", options=o) + + +for rad in 1, 3, 4, 5, 6: + sep("RAD {}".format(rad)) + data = """\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 RAD={} +M V30 END ATOM +M V30 END CTAB +M END""".format(rad) + fail(data) + o = MDLOptions() + setattr(o, "onRAD{}".format(rad), Action.Ignore) + Graph.fromMOLString(data, options=o) + setattr(o, "onRAD{}".format(rad), Action.Warn) + Graph.fromMOLString(data, options=o) + + +sep("Parallel Edges") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 2 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 C 0 0 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 1 2 1 +M V30 END BOND +M V30 END CTAB +M END""") + + +sep("Pattern Atoms") +for s, opt in (('MASS', 'onPatternIsotope'), ('CHG', 'onPatternCharge'), ('RAD', 'onPatternRadical')): + data = """\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 * 0 0 0 0 {}=2 +M V30 END ATOM +M V30 END CTAB +M END""".format(s) + fail(data) + o = MDLOptions() + o.onImplicitValenceOnAbstract = Action.Ignore + setattr(o, opt, Action.Warn) + Graph.fromMOLString(data, options=o) + setattr(o, opt, Action.Ignore) + + +sep("Posval vs. keyword val") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 CHG=1 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 CHG=1 0 RAD=2 +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 CHG= +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""") +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 CHG= +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""") + +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 RGROUPS=(1 1 +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""") + +o = MDLOptions() +assert o.onV3000UnhandledAtomProperty == Action.Warn +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 RGROUPS=(1 1) RGROUPS=(2 1 1) +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""") +o.onV3000UnhandledAtomProperty = Action.Error +fail("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 RGROUPS=(1 1) RGROUPS=(2 1 1) +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""", options=o) +o.onV3000UnhandledAtomProperty = Action.Ignore +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 RGROUPS=(1 1) RGROUPS=(2 1 1) +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""", options=o) + + +sep("String args") +o = MDLOptions() +o.onV3000UnhandledAtomProperty = Action.Ignore +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 Something="hello ( ) ) = world" +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""", options=o) +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 SomethingElse="hello "" ( ) ) = world" +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""", options=o) + + +sep("Multiline") +Graph.fromMOLString("""\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1- +M V30 - +M V30 C 0 - +M V30 0 0 0 CHG=2 - +M V30 RAD=2 RGROUPS=(- +M V30 1 1) +M V30 END ATOM +M V30 BEGIN BOND +M V30 END BOND +M V30 END CTAB +M END""", options=o) + + +sep("Empty") +fail("""\n\n\n 0 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 0 0 0 0 0 +M V30 BEGIN ATOM +M V30 END ATOM +M V30 END CTAB +M END""") diff --git a/test/py/graph/050_mdl/molMult.py b/test/py/graph/050_mdl/molMult.py new file mode 100644 index 0000000..a8f77ef --- /dev/null +++ b/test/py/graph/050_mdl/molMult.py @@ -0,0 +1,15 @@ +include("../../xxx_helpers.py") + +dataDis = """\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 O 0 0 0 0 +M V30 END ATOM +M V30 END CTAB +M END""" +fail(lambda: Graph.fromMOLString(dataDis), "not connected", + err=InputError, isSubstring=True) +gs = Graph.fromMOLStringMulti(dataDis) +assert len(gs) == 2 diff --git a/test/py/graph/050_mdl/sd.py b/test/py/graph/050_mdl/sd.py new file mode 100644 index 0000000..afff885 --- /dev/null +++ b/test/py/graph/050_mdl/sd.py @@ -0,0 +1,34 @@ +post.disableInvokeMake() + +mol = """ + OpenBabel03141812452D + + 3 2 0 0 0 0 0 0 0 0999 V2000 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0000 0.0000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 +M END +""" + +def fail(s): + try: + res = Graph.fromSDString(s) + assert False, res + except InputError as e: + print(e) + +fail(mol) +Graph.fromSDString(mol + "$$$$") +Graph.fromSDString(mol + "$$$$\n") +fail(mol + "\n") +fail(mol + "a") +fail(mol + ">") +fail(mol + ">\na") +fail(mol + ">\n>") +fail(mol + ">\n>\n\n") +fail(mol + ">\n>\n\na") +Graph.fromSDString(mol + ">\n>\n\n$$$$") +Graph.fromSDString(mol + ">\n>\n\n$$$$\n") +fail(mol + ">\n>\n\n$$$$\na") diff --git a/test/py/graph/050_mdl/sdMulti.py b/test/py/graph/050_mdl/sdMulti.py new file mode 100644 index 0000000..6bd508f --- /dev/null +++ b/test/py/graph/050_mdl/sdMulti.py @@ -0,0 +1,37 @@ +include("../../xxx_helpers.py") + +mol = """\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 1 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 N 0 0 0 0 +M V30 END ATOM +M V30 END CTAB +M END +""" +molDis = """\n\n\n 1 0 0 0 0 0 0 0 0 0999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 2 0 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0 0 0 0 +M V30 2 O 0 0 0 0 +M V30 END ATOM +M V30 END CTAB +M END +""" + +dc2 = molDis + "$$$$\n" +dc1c2 = mol + "$$$$\n" + molDis + "$$$$\n" + +fail(lambda: Graph.fromSDString(dc2), "not connected", + err=InputError, isSubstring=True) +fail(lambda: Graph.fromSDString(dc1c2), + "not connected", err=InputError, isSubstring=True) + +gc2 = Graph.fromSDStringMulti(dc2) +assert len(gc2) == 1 +assert len(gc2[0]) == 2 +gc1c2 = Graph.fromSDStringMulti(dc1c2) +assert len(gc1c2) == 2 +assert len(gc1c2[0]) == 1 +assert len(gc1c2[1]) == 2 diff --git a/test/py/graph/201_morphisms.py b/test/py/graph/201_morphisms.py new file mode 100644 index 0000000..118e54f --- /dev/null +++ b/test/py/graph/201_morphisms.py @@ -0,0 +1,67 @@ +include("../xxx_helpers.py") + +g = smiles("O") +fail(lambda: g.isomorphism(None), "codomain is null.") +fail(lambda: g.monomorphism(None), "codomain is null.") + +fail(lambda: g.enumerateMonomorphisms(None, callback=False), + "codomain is null.") + + +co2 = smiles("O=C=O", "CO2") +co = smiles("[C]=O", name="CO") +co2_2 = smiles("O=C=O", "CO2 2") + +assert(co2.isomorphism(co2_2) > 0) +assert(co2.isomorphism(co) == 0) +assert(co.monomorphism(co2) > 0) +assert(co2.monomorphism(co2_2) > 0) +assert(co2.monomorphism(co) == 0) + +def check(f, codom, maps, numMaps=2**30): + exp = list(sorted(maps)) + res = [] + def c(m): + if len(res) >= numMaps: + return False + m_ = [] + for v in m.domain.vertices: + assert m[v] + assert m.inverse(m[v]) == v + m_.append((v.id, m[v].id)) + res.append(m_) + if len(res) >= numMaps: + return False + return True + f(codom, callback=c) + res = list(sorted(res)) + if len(res) != len(exp): + print("res:", res) + print("exp:", exp) + + +check(co2.enumerateMonomorphisms, co2_2, [ + [(0, 0), (1, 1), (2, 2)], + [(0, 0), (1, 2), (1, 2)], +]) +check(co2.enumerateMonomorphisms, co2_2, [], numMaps=0) +check(co2.enumerateMonomorphisms, co, []) +check(co.enumerateMonomorphisms, co2, [ + [(0, 0), (1, 1)], + [(0, 0), (1, 2)], +]) +check(co.enumerateMonomorphisms, co2, [], numMaps=0) +check(co2.enumerateMonomorphisms, co, []) + +# check that the graphs and maps are still there +res = [] +def c(m): + res.append(m) +smiles('[C]', 'C').enumerateMonomorphisms(smiles('[C]', 'C 2'), + callback=c) +assert res[0].domain.name == 'C' +assert res[0].codomain.name == 'C 2' +assert res[0][next(iter(res[0].domain.vertices))] == \ + next(iter(res[0].codomain.vertices)) +assert res[0].inverse(next(iter(res[0].codomain.vertices))) == \ + next(iter(res[0].domain.vertices)) diff --git a/test/py/graph/130_aut.py b/test/py/graph/250_aut.py similarity index 96% rename from test/py/graph/130_aut.py rename to test/py/graph/250_aut.py index 174f045..1ca3621 100644 --- a/test/py/graph/130_aut.py +++ b/test/py/graph/250_aut.py @@ -41,4 +41,4 @@ pairs.add((s.id, t.id)) f.write("\\path[modRCMatchEdge] (v-%d) to[bend left=15] (v-%d);\n" % (v.id, vImg.id)) f.write("\\end{tikzpicture}\n") -post("summaryInput \"%s\"" % fName) +post.summaryInput(fName) diff --git a/test/py/graph/301_image.py b/test/py/graph/301_image.py new file mode 100644 index 0000000..33ab1b4 --- /dev/null +++ b/test/py/graph/301_image.py @@ -0,0 +1,11 @@ +a = smiles("C") +a.print() +def customImage(): + with open("out/custom.tex", "w") as f: + f.write("""\\begin{tikzpicture} +\\node {custom}; +\\end{tikzpicture}""") + return "out/custom" +a.image = customImage +a.imageCommand = "compileTikz \"out/custom\" \"out/custom\"" +a.print() diff --git a/test/py/graph/800_printer.py b/test/py/graph/800_printer.py new file mode 100644 index 0000000..f17f124 --- /dev/null +++ b/test/py/graph/800_printer.py @@ -0,0 +1,88 @@ +include("../xxx_helpers.py") + +p = GraphPrinter() +assert p.edgesAsBonds +assert not p.collapseHydrogens +assert p.raiseIsotopes +assert p.raiseCharges +assert not p.simpleCarbons +assert not p.thick +assert not p.withColour +assert not p.withIndex +assert not p.withTexttt +assert not p.withRawStereo +assert not p.withPrettyStereo +assert p.rotation == 0 +assert not p.mirror +assert not p.withGraphvizCoords +assert p.graphvizPrefix == "" + +p.disableAll() +assert not p.edgesAsBonds +assert not p.collapseHydrogens +assert not p.raiseIsotopes +assert not p.raiseCharges +assert not p.simpleCarbons +assert not p.thick +assert not p.withColour +assert not p.withIndex +assert not p.withTexttt +assert not p.withRawStereo +assert not p.withPrettyStereo +assert p.rotation == 0 +assert not p.mirror +assert not p.withGraphvizCoords +assert p.graphvizPrefix == "" + +p.enableAll() +assert p.edgesAsBonds +assert p.collapseHydrogens +assert p.raiseIsotopes +assert p.raiseCharges +assert p.simpleCarbons +assert p.thick +assert p.withColour +assert p.withIndex +assert not p.withTexttt +assert not p.withRawStereo +assert not p.withPrettyStereo +assert p.rotation == 0 +assert not p.mirror +assert not p.withGraphvizCoords +assert p.graphvizPrefix == "" + +p.disableAll() +p.setMolDefault() +assert p.edgesAsBonds +assert p.collapseHydrogens +assert p.raiseIsotopes +assert p.raiseCharges +assert p.simpleCarbons +assert not p.thick +assert p.withColour +assert not p.withIndex +assert not p.withTexttt +assert not p.withRawStereo +assert not p.withPrettyStereo +assert p.rotation == 0 +assert not p.mirror +assert not p.withGraphvizCoords +assert p.graphvizPrefix == "" + +p.disableAll() +p.setReactionDefault() +assert p.edgesAsBonds +assert p.collapseHydrogens +assert p.raiseIsotopes +assert p.raiseCharges +assert not p.simpleCarbons +assert not p.thick +assert p.withColour +assert not p.withIndex +assert not p.withTexttt +assert not p.withRawStereo +assert not p.withPrettyStereo +assert p.rotation == 0 +assert not p.mirror +assert not p.withGraphvizCoords +assert p.graphvizPrefix == "" diff --git a/test/py/graph/801_printer_graphviz.py b/test/py/graph/801_printer_graphviz.py new file mode 100644 index 0000000..07f7575 --- /dev/null +++ b/test/py/graph/801_printer_graphviz.py @@ -0,0 +1,24 @@ +include("../xxx_helpers.py") +post.enableInvokeMake() + +a = smiles("C1CCCCC1CCC") + +post.summaryChapter("First") +p = GraphPrinter() +f11 = a.print(p) +p.withGraphvizCoords = True +f12 = a.print(p) +p.graphvizPrefix = 'layout = "dot"'; +f13 = a.print(p) + +post.summaryChapter("Second") +p = GraphPrinter() +f21 = a.print(p) +p.withGraphvizCoords = True +f22 = a.print(p) +p.graphvizPrefix = 'layout = "dot"'; +f23 = a.print(p) + +assert f11 == f21 +assert f12 == f22 +assert f13 == f23 diff --git a/test/py/graph/900_union.py b/test/py/graph/900_union.py index 90c4230..8c8cc7e 100644 --- a/test/py/graph/900_union.py +++ b/test/py/graph/900_union.py @@ -1,6 +1,9 @@ include("../xxx_helpers.py") include("../xxx_graphInterface.py") +fail(lambda: UnionGraph([None]), "A graph is null.") + + O = smiles("O") def check(ug, gs): diff --git a/test/py/graph/graph.py b/test/py/graph/graph.py index 6628656..4061ff2 100644 --- a/test/py/graph/graph.py +++ b/test/py/graph/graph.py @@ -34,16 +34,6 @@ a = smiles("C") print("Energy:", a.energy) -host = smiles("O=C=O", "CO2") -pattern = smiles("[C]=O", name="Pattern") -host2 = smiles("O=C=O", "CO2 2") - -assert(host.isomorphism(host2) > 0) -assert(host.isomorphism(pattern) == 0) -assert(pattern.monomorphism(host) > 0) -assert(host.monomorphism(host2) > 0) -assert(host2.monomorphism(pattern) == 0) - a = smiles("O=C(O)C(CC(=O)O)C(O)C(=O)O", name="Isocitrate") aPerm = a.makePermutation() @@ -51,16 +41,3 @@ a.print() a = smiles("N[*]N") a.print() - - -a = smiles("C") -a.print() -def customImage(): - with open("out/custom.tex", "w") as f: - f.write("""\\begin{tikzpicture} -\\node {custom}; -\\end{tikzpicture}""") - return "out/custom" -a.image = customImage -a.imageCommand = "compileTikz \"out/custom\" \"out/custom\"" -a.print() diff --git a/test/py/matchConstraints/vertexAdjacency/main.py b/test/py/matchConstraints/vertexAdjacency/main.py index e367d3d..0b22126 100644 --- a/test/py/matchConstraints/vertexAdjacency/main.py +++ b/test/py/matchConstraints/vertexAdjacency/main.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() lString = LabelSettings(LabelType.String, LabelRelation.Unification) lTerm = LabelSettings(LabelType.Term, LabelRelation.Unification) diff --git a/test/py/matchConstraints/vertexAdjacency/termConversion.py b/test/py/matchConstraints/vertexAdjacency/termConversion.py index d7750ca..b723df2 100644 --- a/test/py/matchConstraints/vertexAdjacency/termConversion.py +++ b/test/py/matchConstraints/vertexAdjacency/termConversion.py @@ -22,25 +22,25 @@ ] ]""" a = ruleGMLString(rStr) -postChapter("TermState") +post.summaryChapter("TermState") a.printTermState() #b = a.makeInverse() -postChapter("Compose first") +post.summaryChapter("Compose first") rc = rcEvaluator([], ls) res = rc.eval(a *rcSuper* rId) for b in res: b.print() b.printTermState() -postChapter("Compose second") +post.summaryChapter("Compose second") rc = rcEvaluator([], ls) res = rc.eval(rIdInv *rcSub* a) for b in res: b.print() b.printTermState() -postChapter("DGRuleComp") +post.summaryChapter("DGRuleComp") graphDFS("C[Q]") dg = dgRuleComp([], addSubset(inputGraphs) >> a, ls) dg.calc() diff --git a/test/py/molDepiction.py b/test/py/molDepiction.py index 6e45a20..d7bca6a 100644 --- a/test/py/molDepiction.py +++ b/test/py/molDepiction.py @@ -1,4 +1,4 @@ -postSection("Hydrogen moving and charges") +post.summarySection("Hydrogen moving and charges") for b in ["", "=", "#"]: for c in ["", "+", ".", "+."]: for l in [6, 8, 10, 12]: @@ -16,7 +16,7 @@ for a in inputGraphs: a.print(p) inputGraphs[:] = [] -postSection("Double/Triple/Lablled bond") +post.summarySection("Double/Triple/Lablled bond") smiles("O=C=C") smiles("CCCC#N") smiles("CCC(O)=O") @@ -24,13 +24,13 @@ graphDFS("C{a}C") for a in inputGraphs: a.print() inputGraphs[:] = [] -postSection("middle C -> simple C + hidden H") +post.summarySection("middle C -> simple C + hidden H") smiles("CCC") smiles("CC(O)C") smiles("N=CC") for a in inputGraphs: a.print() inputGraphs[:] = [] -postSection("collapse H") +post.summarySection("collapse H") smiles("[H][H]") graphDFS("[Q][H][R]") graphDFS("[H]=[R]") @@ -38,12 +38,12 @@ smiles("CNC") for a in inputGraphs: a.print() inputGraphs[:] = [] -postSection("Aromatic") +post.summarySection("Aromatic") smiles("c1ccccc1") smiles("c1[nH]c2c(ncnc2n1)N") for a in inputGraphs: a.print() inputGraphs[:] = [] -postSection("Charges") +post.summarySection("Charges") smiles("[OH-]") graphDFS("[O2-]") smiles("[H+]") diff --git a/test/py/papers/21_tcs/calc.py b/test/py/papers/21_tcs/calc.py index 4d9a531..d9563f1 100644 --- a/test/py/papers/21_tcs/calc.py +++ b/test/py/papers/21_tcs/calc.py @@ -120,19 +120,19 @@ def handleResult(r1: Rule, r2: Rule, res: Tuple[CompRes, CompRes], pIndex.withIndex = True def printRules(rs, txt): - postChapter(txt) + post.summaryChapter(txt) if printGood: - postSection("Good") + post.summarySection("Good") for r in rs[0]: r.print(p) if printBad: - postSection("Bad") + post.summarySection("Bad") for r in rs[1]: r.print(p) # Insert visualizations of the input into the summary. if printInput: - postChapter("Input") + post.summaryChapter("Input") r1.print(p) r2.print(p) # Insert visualisations of the results into the summary. @@ -173,21 +173,21 @@ def compute(r1: Rule, r2: Rule, rc: RCEvaluator): # invariant. # Use our machinery from above: - postChapter("UseBoostCommonSubgraph = True") + post.summaryChapter("UseBoostCommonSubgraph = True") print("UseBoostCommonSubgraph = True") print("=" * 80) config.rc.useBoostCommonSubgraph = True res = compose(r1, r2, rc) handleResult(r1, r2, res) - postChapter("UseBoostCommonSubgraph = False") + post.summaryChapter("UseBoostCommonSubgraph = False") print("UseBoostCommonSubgraph = False") print("=" * 80) config.rc.useBoostCommonSubgraph = False res = compose(r1, r2, rc) handleResult(r1, r2, res) - post("disableSummary") # it takes quite a while due to all the matches + post.disableInvokeMake() # it takes quite a while due to all the matches # First load our two rules: # - an identity rule with the left-hand side of the Aldol Addition rule, and @@ -290,14 +290,14 @@ def compute(r1: Rule, r2: Rule, rc: RCEvaluator): rc = rcEvaluator(inputRules) -postChapter("No no-edge 'constraints'") +post.summaryChapter("No no-edge 'constraints'") print("No no-edge 'constraints'") print("#" * 80) compute(aldolAdd_F_id, aldolAdd_F, rc) -postChapter("No-edge 'constraints'") +post.summaryChapter("No-edge 'constraints'") print("No-edge 'constraints'") print("#" * 80) compute(aldolAdd_F_id_noEdge, aldolAdd_F_noEdge, rc) -#post("enableSummary") +#post.enableInvokeMake() diff --git a/test/py/post.py b/test/py/post.py new file mode 100644 index 0000000..0402b87 --- /dev/null +++ b/test/py/post.py @@ -0,0 +1,39 @@ +include("xxx_helpers.py") + +assert type(makeUniqueFilePrefix()) == str + +post.command("echo hello") +checkDeprecated(lambda: post("echo hello")) + +post.flushCommands() +checkDeprecated(lambda: postFlush()) + +post.disableCommands() +checkDeprecated(lambda: postDisable()) + +post.enableCommands() +checkDeprecated(lambda: postEnable()) + +post.reopenCommandFile() +checkDeprecated(lambda: postReset()) + + +post.enableCommands() + +post.summaryChapter("Some chapter") +checkDeprecated(lambda: postChapter("Some other chapter")) + +post.summarySection("Some section") +checkDeprecated(lambda: postSection("Some other section")) + +post.summaryRaw(r"$\frac{a}{b}$") +post.summaryRaw(r"$\frac{c}{d}$", "secondRaw.whatever") + +with open("out/input.txt", "w") as f: + f.write(r"$\frac{e}{f}$") +post.summaryInput("out/input.txt") + +post.disableCompileSummary() +post.enableCompileSummary() +post.disableInvokeMake() +post.enableInvokeMake() diff --git a/test/py/rc/000_rules.py b/test/py/rc/000_rules.py deleted file mode 100644 index 7668eb5..0000000 --- a/test/py/rc/000_rules.py +++ /dev/null @@ -1,363 +0,0 @@ -include("../xxx_helpers.py") - -_ = ruleGMLString("""rule [ ruleID "->" -]"""); -_A = ruleGMLString("""rule [ ruleID "-> A" - right [ - node [ id 0 label "A" ] - ] -]"""); -_AeA = ruleGMLString("""rule [ ruleID "-> A A" - right [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] -]"""); -_AAA = ruleGMLString("""rule [ ruleID "-> AAA" - right [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -_ABA = ruleGMLString("""rule [ ruleID "-> ABA" - right [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - edge [ source 0 target 1 label "B" ] - ] -]"""); -_AAB = ruleGMLString("""rule [ ruleID "-> AAB" - right [ - node [ id 0 label "A" ] - node [ id 1 label "B" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -_B = ruleGMLString("""rule [ ruleID "-> B" - right [ - node [ id 0 label "B" ] - ] -]"""); -A_ = ruleGMLString("""rule [ ruleID "A ->" - left [ - node [ id 0 label "A" ] - ] -]"""); -A__A = ruleGMLString("""rule [ ruleID "A -> other A" - left [ - node [ id 0 label "A" ] - ] - right [ - node [ id 1 label "A" ] - ] -]"""); -A_A = ruleGMLString("""rule [ ruleID "A -> A" - context [ - node [ id 0 label "A" ] - ] -]"""); -A_AeA = ruleGMLString("""rule [ ruleID "A -> A A" - context [ - node [ id 0 label "A" ] - ] - right [ - node [ id 1 label "A" ] - ] -]"""); -A_AAA = ruleGMLString("""rule [ ruleID "A -> AAA" - context [ - node [ id 0 label "A" ] - ] - right [ - node [ id 1 label "A" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -A_ABA = ruleGMLString("""rule [ ruleID "A -> ABA" - context [ - node [ id 0 label "A" ] - ] - right [ - node [ id 1 label "A" ] - edge [ source 0 target 1 label "B" ] - ] -]"""); -A_AAB = ruleGMLString("""rule [ ruleID "A -> AAB" - context [ - node [ id 0 label "A" ] - ] - right [ - node [ id 1 label "B" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -A_B = ruleGMLString("""rule [ ruleID "A -> B" - left [ - node [ id 0 label "A" ] - ] - right [ - node [ id 0 label "B" ] - ] -]"""); -A_BAA = ruleGMLString("""rule [ ruleID "A -> BAA" - left [ - node [ id 0 label "A" ] - ] - right [ - node [ id 0 label "B" ] - node [ id 1 label "A" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -A_C = ruleGMLString("""rule [ ruleID "A -> C" - left [ - node [ id 0 label "A" ] - ] - right [ - node [ id 0 label "C" ] - ] -]"""); -AeA_ = ruleGMLString("""rule [ ruleID "A A ->" - left [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] -]"""); -AeA_A = ruleGMLString("""rule [ ruleID "A A -> A" - left [ - node [ id 0 label "A" ] - ] - context [ - node [ id 1 label "A" ] - ] -]"""); -AeA_AeA = ruleGMLString("""rule [ ruleID "A A -> A A" - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] -]"""); -AeA_AAA = ruleGMLString("""rule [ ruleID "A A -> AAA" - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] - right [ - edge [ source 0 target 1 label "A" ] - ] -]"""); -AeA_ABA = ruleGMLString("""rule [ ruleID "A A -> ABA" - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] - right [ - edge [ source 0 target 1 label "B" ] - ] -]"""); -AeB_AAB = ruleGMLString("""rule [ ruleID "AeB -> AAB" - context [ - node [ id 0 label "A" ] - node [ id 1 label "B" ] - ] - right [ - edge [ source 0 target 1 label "A" ] - ] -]"""); -AAA_ = ruleGMLString("""rule [ ruleID "AAA ->" - left [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -AAA_A = ruleGMLString("""rule [ ruleID "AAA -> A" - left [ - node [ id 1 label "A" ] - edge [ source 0 target 1 label "A" ] - ] - context [ - node [ id 0 label "A" ] - ] -]"""); -AAA_AeA = ruleGMLString("""rule [ ruleID "AAA -> A A" - left [ - edge [ source 0 target 1 label "A" ] - ] - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] -]"""); -AAA_AAA = ruleGMLString("""rule [ ruleID "AAA -> AAA" - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -AAA_ABA = ruleGMLString("""rule [ ruleID "AAA -> ABA" - left [ - edge [ source 0 target 1 label "A" ] - ] - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] - right [ - edge [ source 0 target 1 label "B" ] - ] -]"""); -AAA_ACA = ruleGMLString("""rule [ ruleID "AAA -> ACA" - left [ - edge [ source 0 target 1 label "A" ] - ] - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] - right [ - edge [ source 0 target 1 label "C" ] - ] -]"""); -AAB_ = ruleGMLString("""rule [ ruleID "AAB ->" - left [ - node [ id 0 label "A" ] - node [ id 1 label "B" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -AAB_AeB = ruleGMLString("""rule [ ruleID "AAB -> A B" - left [ - edge [ source 0 target 1 label "A" ] - ] - context [ - node [ id 0 label "A" ] - node [ id 1 label "B" ] - ] -]"""); -AAB_AAB = ruleGMLString("""rule [ ruleID "AAB -> AAB" - context [ - node [ id 0 label "A" ] - node [ id 1 label "B" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); -AAB_ABB = ruleGMLString("""rule [ ruleID "AAB -> ABB" - left [ - edge [ source 0 target 1 label "A" ] - ] - context [ - node [ id 0 label "A" ] - node [ id 1 label "B" ] - ] - right [ - edge [ source 0 target 1 label "B" ] - ] -]"""); -ABA_ = ruleGMLString("""rule [ ruleID "ABA ->" - left [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - edge [ source 0 target 1 label "B" ] - ] -]"""); -ABA__AAA = ruleGMLString("""rule [ ruleID "ABA -> AAA other" - left [ - node [ id 1 label "A" ] - edge [ source 0 target 1 label "B" ] - ] - context [ - node [ id 0 label "A" ] - ] - right [ - node [ id 2 label "A" ] - edge [ source 0 target 2 label "A" ] - ] -]"""); -ABA_A = ruleGMLString("""rule [ ruleID "ABA -> A" - left [ - node [ id 1 label "A" ] - edge [ source 0 target 1 label "B" ] - ] - context [ - node [ id 0 label "A" ] - ] -]"""); -ABA_AeA = ruleGMLString("""rule [ ruleID "ABA -> A A" - left [ - edge [ source 0 target 1 label "B" ] - ] - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] -]"""); -ABA_AAA = ruleGMLString("""rule [ ruleID "ABA -> AAA" - left [ - edge [ source 0 target 1 label "B" ] - ] - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] - right [ - edge [ source 0 target 1 label "A" ] - ] -]"""); -ABA_ABA = ruleGMLString("""rule [ ruleID "ABA -> ABA" - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - edge [ source 0 target 1 label "B" ] - ] -]"""); -ABA_ACA = ruleGMLString("""rule [ ruleID "ABA -> ACA" - left [ - edge [ source 0 target 1 label "B" ] - ] - context [ - node [ id 0 label "A" ] - node [ id 1 label "A" ] - ] - right [ - edge [ source 0 target 1 label "C" ] - ] -]"""); - -B_ = ruleGMLString("""rule [ ruleID "B ->" - left [ - node [ id 0 label "B" ] - ] -]"""); -B_A = ruleGMLString("""rule [ ruleID "B -> A" - left [ - node [ id 0 label "B" ] - ] - right [ - node [ id 0 label "A" ] - ] -]"""); -B_B = ruleGMLString("""rule [ ruleID "B -> B" - context [ - node [ id 0 label "B" ] - ] -]"""); -B_C = ruleGMLString("""rule [ ruleID "B -> C" - left [ - node [ id 0 label "B" ] - ] - right [ - node [ id 0 label "C" ] - ] -]"""); -B_BAA = ruleGMLString("""rule [ ruleID "B -> BAA" - context [ - node [ id 0 label "B" ] - ] - right [ - node [ id 1 label "A" ] - edge [ source 0 target 1 label "A" ] - ] -]"""); diff --git a/test/py/rc/010_base_test.py b/test/py/rc/010_base_test.py new file mode 100644 index 0000000..39a6498 --- /dev/null +++ b/test/py/rc/010_base_test.py @@ -0,0 +1,62 @@ +include("xxx_base_checks.py") +initialVerbose = False + +def c(s1, s2, mId, sExpected, ls): + print("- first: ", s1) + print(" second: ", s2) + print(" matchId: ", mId) + print(" expected:", sExpected) + r1 = Rule.fromDFS(s1) + r2 = Rule.fromDFS(s2) + rExpected = None if sExpected is None else Rule.fromDFS(sExpected) + m = RCMatch(r1, r2, labelSettings=ls) + for i1, i2 in mId.items(): + m.push(r1.getVertexFromExternalId(i1).right, + r2.getVertexFromExternalId(i2).left) + + rs = [r1, r2] + if rExpected is not None: + rs.append(rExpected) + + rRes = m.compose(verbose=initialVerbose) + if rExpected is None: + if rRes is not None: + post.summarySection("Check Error, Result") + print("ERROR expected None, but got rule") + print("Composing again with verbose=True") + rRes.name = "rRes" + rRes.print() + m.compose(verbose=True) + post.enableInvokeMake() + assert False + else: + if rRes is None: + post.summarySection("Check Error, Result") + print("ERROR expected rule, but got None") + print("Composing again with verbose=True") + rExpected.name = "rExpected" + rExpected.print() + m.compose(verbose=True) + post.enableInvokeMake() + assert False + if rRes.isomorphism(rExpected, labelSettings=ls) != 1: + post.summarySection("Check Error, Result") + print("ERROR non-isomorphic result") + print("Composing again with verbose=True") + rRes.name = "rRes" + rExpected.name = "rExpected" + rRes.print() + rExpected.print() + m.compose(verbose=True) + post.enableInvokeMake() + assert False + +def testSettings(ls): + print("#" * 80) + print(ls) + print("#" * 80) + doChecks(lambda *args: c(*args, ls=ls)) + +testSettings(LabelSettings(LabelType.String, LabelRelation.Specialisation)) +testSettings(LabelSettings(LabelType.Term, LabelRelation.Specialisation)) +#testSettings(LabelSettings(LabelType.String, LabelRelation.Isomorphism, LabelRelation.Isomorphism)) diff --git a/test/py/rc/500_compositionMatch.py b/test/py/rc/500_compositionMatch.py index 66133ce..1dcb392 100644 --- a/test/py/rc/500_compositionMatch.py +++ b/test/py/rc/500_compositionMatch.py @@ -3,41 +3,14 @@ def vertex(id_, r): return r.getVertexFromExternalId(id_) -_ = ruleGMLString("""rule [ ruleID "->" - context [ node [ id 0 label "A" ] ] -]"""); -A_B = ruleGMLString("""rule [ ruleID "A -> B" - left [ - node [ id 0 label "A" ] - node [ id 1 label "X" ] - edge [ source 0 target 1 label "-" ] - ] - context [ - node [ id 2 label "P" ] - edge [ source 0 target 2 label "-" ] - ] - right [ - node [ id 0 label "B" ] - node [ id 1 label "Y" ] - edge [ source 0 target 1 label "=" ] - ] -]"""); -B_C = ruleGMLString("""rule [ ruleID "B -> C" - left [ - node [ id 0 label "B" ] - node [ id 1 label "Y" ] - edge [ source 0 target 1 label "=" ] - ] - context [ - node [ id 2 label "Q" ] - edge [ source 0 target 2 label "-" ] - ] - right [ - node [ id 0 label "C" ] - node [ id 1 label "Z" ] - edge [ source 0 target 1 label "#" ] - ] -]"""); +_ = Rule.fromDFS("[A]0>>[A]0") +_.name = "->" + +A_B = Rule.fromDFS("[P]2[A]0[X]1>>[P]2[B]0{=}[Y]1") +A_B.name = "A -> B" + +B_C = Rule.fromDFS("[Q]2[B]0{=}[Y]1>>[Q]2[C]0{#}[Z]1") +B_C.name = "B -> C" fail(lambda: RCMatch(None, _), "rFirst is null.") fail(lambda: RCMatch(_, None), "rSecond is null.") diff --git a/test/py/rc/checks.py b/test/py/rc/checks.py deleted file mode 100644 index 1853697..0000000 --- a/test/py/rc/checks.py +++ /dev/null @@ -1,358 +0,0 @@ -include("../xxx_helpers.py") - -def doChecks(): - print("############################################") - # Rules - # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - # Let ABC = {A, B, C} - # Let ABCe = {A, B, C, } - # We only enumerate connected rules - # ==== ==== ================================== ============ - # L R Label variations Id Pattern - # ==== ==== ================================== ============ - # (empty) _ - # O ABC l_ - # O ABC _l - # O O ABC l_l - # $ $ ABC x ABC l_l - # ---- ---- ---------------------------------- ------------ - # O-O ABC x ABC c ABC lel_ - # O-O' O ABC x ABC x ABC lel_l - # O-$ $ ABC x ABC x ABC x ABC lel_l - # O-O ABC x ABC x ABC _lel - # O O-O' ABC x ABC x ABC l_lel - # $ O-$ ABC x ABC x ABC x ABC l_lel - # ---- ---- ---------------------------------- ------------ - # O-O' O-O' ABC x ABC x ABCe x ABC lel_lel - # O-$ O-$ ABC x ABC x ABC x ABCe x ABC lel_lel - # $-$' $-$' ABC x ABC x ABC x ABC x ABCe x ABC lel_lel - # ==== ==== ================================== ============ - - # ======= ======= ======= ========================= - # First Second New - # === === === === === === ========================= - # L R L R L R Note - # === === === === === === ========================= - print("(empty) with everything"); checkNum = 1 - # A A - # A A - # A A A A - # A B A B - check(_ *rcParallel* A_, {iso(0, A_)}) - check(_ *rcParallel* _A, {iso(0, _A)}) - check(_ *rcParallel* A_A, {iso(0, A_A)}) - check(_ *rcParallel* A_B, {iso(0, A_B)}) - # AAA AAA - # AAA A AAA A - # AAA AAA - # A AAA A AAA - check(_ *rcParallel* AAA_, {iso(0, AAA_)}) - check(_ *rcParallel* AAA_A, {iso(0, AAA_A)}) - check(_ *rcParallel* _AAA, {iso(0, _AAA)}) - check(_ *rcParallel* A_AAA, {iso(0, A_AAA)}) - # AAA A A AAA A A - # A A AAA A A AAA - # AAA AAA AAA AAA - # AAA ABA AAA ABA - check(_ *rcParallel* AAA_AeA, {iso(0, AAA_AeA)}) - check(_ *rcParallel* AeA_AAA, {iso(0, AeA_AAA)}) - check(_ *rcParallel* AAA_AAA, {iso(0, AAA_AAA)}) - check(_ *rcParallel* AAA_ABA, {iso(0, AAA_ABA)}) - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("everything with (empty)"); checkNum = 1 - # A A - # A A - # A A A A - # A B A B - # AAA A A AAA A A - # A A AAA A A AAA - # AAA AAA AAA AAA - # AAA ABA AAA ABA - check(A_ *rcParallel* _, {iso(0, A_)}) - check(_A *rcParallel* _, {iso(0, _A)}) - check(A_A *rcParallel* _, {iso(0, A_A)}) - check(A_B *rcParallel* _, {iso(0, A_B)}) - check(AAA_AeA *rcParallel* _, {iso(0, AAA_AeA)}) - check(AeA_AAA *rcParallel* _, {iso(0, AeA_AAA)}) - check(AAA_AAA *rcParallel* _, {iso(0, AAA_AAA)}) - check(AAA_ABA *rcParallel* _, {iso(0, AAA_ABA)}) - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("O -> with everything"); checkNum = 1 - # There would be no non-empty match - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("-> O with everything"); checkNum = 1 - # A A Delete node - # A A A A - # A A B B - # A AAB Invalid, eSecond dangling in L - # A A AAB AAB - # A AAB A B Invalid, eSecond dangling in L - # A A B AAB B AAB (B_BAA) - # A AAB AAB Invalid, eSecond dangling in L - # A AAB ABB Invalid, eSecond dangling in L - check(_A *rcCommon* A_, {iso(0, _)}) - check(_A *rcCommon* A_A, {iso(0, _A)}) - check(_A *rcCommon* A_B, {iso(0, _B)}) - check(_A *rcCommon* AAB_, {}, 0) - check(_A *rcCommon* A_AAB, {iso(0, _AAB)}) - check(_A *rcCommon* AAB_AeB, {}, 0) - check(_A *rcCommon* AeB_AAB, {iso(0, B_BAA)}) - check(_A *rcCommon* AAB_AAB, {}, 0) - check(_A *rcCommon* AAB_ABB, {}, 0) - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("O -> O with everything"); checkNum = 1 - # A A A A - # A A A A A A - # A A A B A B - # A A AAB AAB - # A A A AAB A AAB - # A A AAB A B AAB A B - # A A A B AAB A B AAB - # A A AAB AAB AAB AAB - # A A AAB ABB AAB ABB - check(A_A *rcCommon* A_, {iso(0, A_)}) - check(A_A *rcCommon* A_A, {iso(0, A_A)}) - check(A_A *rcCommon* A_B, {iso(0, A_B)}) - check(A_A *rcCommon* AAB_, {iso(0, AAB_)}) - check(A_A *rcCommon* A_AAB, {iso(0, A_AAB)}) - check(A_A *rcCommon* AAB_AeB, {iso(0, AAB_AeB)}) - check(A_A *rcCommon* AeB_AAB, {iso(0, AeB_AAB)}) - check(A_A *rcCommon* AAB_AAB, {iso(0, AAB_AAB)}) - check(A_A *rcCommon* AAB_ABB, {iso(0, AAB_ABB)}) - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("$ -> $ with everything"); checkNum = 1 - # A B B A - # A B B B A B - # A B B A A A - # A B B C A C - check(A_B *rcCommon* B_, {iso(0, A_)}) - check(A_B *rcCommon* B_B, {iso(0, A_B)}) - check(A_B *rcCommon* B_A, {iso(0, A_A)}) - check(A_B *rcCommon* B_C, {iso(0, A_C)}) - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("O-O -> with everything"); checkNum = 1 - # There would be no non-empty match - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("O-O' -> O with everything"); checkNum = 1 - # This would be similar to just O -> O - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("O-$ -> $ with everything"); checkNum = 1 - # This would be similar to just $ -> $ - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("-> O-O with everything"); checkNum = 1 - # A A AAA Invalid, eSecond dangling in L - # A A A A AAA AAA - check(_AeA *rcSub* AAA_, {}, 0) - check(_AeA *rcSub* AeA_AAA, {iso(0, _AAA)}) - # AAA A Invalid, eFirst dangling in R - # AAA A A AAA - # AAA A B BAA (AAB) - # AAA A A Invalid, eFirst dangling in R - # AAA AAA (All deleted) - # AAA ABA (Only one vertex would be matched) Invalid, both edges dangling - check(_AAA *rcCommon* A_, {}, 0) - check(_AAA *rcCommon* A_A, {iso(0, _AAA)}) - check(_AAA *rcCommon* A_B, {iso(0, _AAB)}) - check(_AAA *rcSuper* AeA_, {}, 0) - check(_AAA *rcCommon* AAA_, {iso(0, _)}) - check(_AAA *rcCommon* ABA_, {}, 0) - # AAA A A A Invalid, eFirst dangling in R - # AAA AAA A A - # AAA ABA A (Only one vertex would be matched) Invalid, eSecond dangling in L - check(_AAA *rcSuper* AeA_A, {}, 0) - check(_AAA *rcCommon* AAA_A, {iso(0, _A)}) - check(_AAA *rcCommon* ABA_A, {}, 0) - # AAA A A AAA Invalid, duplicate edge in R - # AAA AAA A A A A - # AAA AAA AAA AAA - # AAA AAA ABA ABA - check(_AAA *rcSuper* AeA_AAA, {}, 0) - check(_AAA *rcSuper* AAA_AeA, {iso(0, _AeA)}) - check(_AAA *rcSuper* AAA_AAA, {iso(0, _AAA)}) - check(_AAA *rcSuper* AAA_ABA, {iso(0, _ABA)}) - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("O -> O-O with everything"); checkNum = 1 - # A A A AAA Invalid, eSecond dangling in L - # A A A AAA A Invalid, eSecond dangling in L - # A A A A A AAA A AAA - check(A_AeA *rcSub* AAA_, {}, 0) - check(A_AeA *rcSub* AAA_A, {}, 0) - check(A_AeA *rcSub* AeA_AAA, {iso(0, A_AAA)}) - # A AAA A Invalid, eFirst dangling in R - # A AAA A A A AAA - # A AAA A B A BAA - # A AAB - # A AAA A A Invalid, eFirst dangling in R - # A AAA AAA A - # A AAA ABA (Only one vertex would be matched) Invalid, both edges dangling - check(A_AAA *rcCommon* A_, {}, 0) - check(A_AAA *rcCommon* A_A, {iso(0, A_AAA)}) - check(A_AAA *rcCommon* A_B, {iso(0, A_AAB), iso(1, A_BAA)}, 2) - check(A_AAA *rcSuper* AeA_, {}, 0) - check(A_AAA *rcCommon* AAA_, {iso(0, A_)}) - check(A_AAA *rcCommon* ABA_, {}, 0) - # A AAA A A A Invalid, eFirst dangling in R - # A AAA AAA A A A - # A A - # A AAA ABA A ABA AAA' (Only one vertex would be matched, AB->A<- and AA->A<- are not the same) - check(A_AAA *rcSuper* AeA_A, {}, 0) - check(A_AAA *rcCommonMax* AAA_A, {iso(0, A__A), iso(1, A_A)}, 2) - check(A_AAA *rcCommonMax* ABA_A, {iso(0, ABA__AAA)}) - # A AAA A A AAA Invalid, duplicate edge in R - # A AAA AAA A A A A A - # A AAA AAA AAA A AAA - # A AAA AAA ABA A ABA - check(A_AAA *rcSuper* AeA_AAA, {}, 0) - check(A_AAA *rcSuper* AAA_AeA, {iso(0, A_AeA)}) - check(A_AAA *rcSuper* AAA_AAA, {iso(0, A_AAA)}) - check(A_AAA *rcSuper* AAA_ABA, {iso(0, A_ABA)}) - # --------------------------------------------------- - # L R L R L R Note - # === === === === === === ========================= - print("O-O -> O-O with everything"); checkNum = 1 - # A A A A A A AAA A A AAA - # A A A A AAA A A AAA A A - # A A A A AAA A AAA A - # A A A A AAA AAA AAA AAA - # A A A A AAA ABA AAA ABA - print("Group 1"); checkNum = 1 - check(AeA_AeA *rcSuper* AeA_AAA, {iso(0, AeA_AAA)}) - check(AeA_AeA *rcSub* AAA_AeA, {iso(0, AAA_AeA)}) - check(AeA_AeA *rcSub* AAA_A, {iso(0, AAA_A)}) - check(AeA_AeA *rcSub* AAA_AAA, {iso(0, AAA_AAA)}) - check(AeA_AeA *rcSub* AAA_ABA, {iso(0, AAA_ABA)}) - # A A AAA A Invalid, eFirst dangling in R - # A A AAA A A A A AAA - # A A AAA A A Invalid, eFirst dangling in R - # A A AAA AAA A A - # A A AAA A A A Invalid, eFirst dangling in R - # A A AAA AAA A A A A - print("Group 2"); checkNum = 1 - check(AeA_AAA *rcSuper* A_, {}, 0) - check(AeA_AAA *rcSuper* A_A, {iso(0, AeA_AAA)}) - check(AeA_AAA *rcSuper* AeA_, {}, 0) - check(AeA_AAA *rcSuper* AAA_, {iso(0, AeA_)}) - check(AeA_AAA *rcSuper* AeA_A, {}, 0) - check(AeA_AAA *rcSuper* AAA_A, {iso(0, AeA_A)}) - # A A AAA A A A A A A AAA - # A A AAA A A AAA Invalid, duplicate edge in R - # A A AAA AAA A A A A A A - # A A AAA AAA AAA A A AAA - # A A AAA AAA ABA A A ABA - print("Group 3"); checkNum = 1 - check(AeA_AAA *rcSuper* AeA_AeA, {iso(0, AeA_AAA)}) - check(AeA_AAA *rcSuper* AeA_AAA, {}, 0) - check(AeA_AAA *rcSuper* AAA_AeA, {iso(0, AeA_AeA)}) - check(AeA_AAA *rcSuper* AAA_AAA, {iso(0, AeA_AAA)}) - check(AeA_AAA *rcSuper* AAA_ABA, {iso(0, AeA_ABA)}) - # AAA A A A AAA A - # AAA A A A A A AAA A - # AAA A A AAA A Invalid, duplicate edge in L - print("Group 4"); checkNum = 1 - check(AAA_AeA *rcSuper* A_, {iso(0, AAA_A)}) - check(AAA_AeA *rcSuper* AeA_A, {iso(0, AAA_A)}) - check(AAA_AeA *rcSuper* AAA_A, {}, 0) - # AAA A A A A A A AAA A A - # AAA A A A A AAA AAA AAA - # AAA A A A A ABA AAA ABA - # AAA A A AAA A A Invalid, duplicate edge in L - # AAA A A AAA AAA Invalid, duplicate edge in L - # AAA A A AAA ABA Invalid, duplicate edge in L - print("Group 5"); checkNum = 1 - check(AAA_AeA *rcSuper* AeA_AeA, {iso(0, AAA_AeA)}) - check(AAA_AeA *rcSuper* AeA_AAA, {iso(0, AAA_AAA)}) - check(AAA_AeA *rcSuper* AeA_ABA, {iso(0, AAA_ABA)}) - check(AAA_AeA *rcSub* AAA_AeA, {}, 0) - check(AAA_AeA *rcSub* AAA_AAA, {}, 0) - check(AAA_AeA *rcSub* AAA_ABA, {}, 0) - # AAA AAA A Invalid, eFirst dangling in R - # AAA AAA A A AAA AAA - # AAA AAA A A A Invalid, eFirst dangling in R - # AAA AAA AAA A AAA A - print("Group 6"); checkNum = 1 - check(AAA_AAA *rcSuper* A_, {}, 0) - check(AAA_AAA *rcSuper* A_A, {iso(0, AAA_AAA)}) - check(AAA_AAA *rcSuper* AeA_A, {}, 0) - check(AAA_AAA *rcSuper* AAA_A, {iso(0, AAA_A)}) - # AAA AAA A A AAA Invalid, duplicate edge in R - # AAA AAA AAA A A AAA A A - # AAA AAA AAA AAA AAA AAA - # AAA AAA AAA ABA AAA ABA - print("Group 7"); checkNum = 1 - check(AAA_AAA *rcSuper* AeA_AAA, {}, 0) - check(AAA_AAA *rcSuper* AAA_AeA, {iso(0, AAA_AeA)}) - check(AAA_AAA *rcSuper* AAA_AAA, {iso(0, AAA_AAA)}) - check(AAA_AAA *rcSuper* AAA_ABA, {iso(0, AAA_ABA)}) - # AAA ABA A A AAA Invalid, duplicate edge in R - # AAA ABA ABA A A AAA A A - # AAA ABA ABA ABA AAA ABA - # AAA ABA ABA AAA AAA AAA - # AAA ABA ABA ACA AAA ACA - print("Group 8"); checkNum = 1 - check(AAA_ABA *rcSuper* AeA_AAA, {}, 0) - check(AAA_ABA *rcSuper* ABA_AeA, {iso(0, AAA_AeA)}) - check(AAA_ABA *rcSuper* ABA_ABA, {iso(0, AAA_ABA)}) - check(AAA_ABA *rcSuper* ABA_AAA, {iso(0, AAA_AAA)}) - check(AAA_ABA *rcSuper* ABA_ACA, {iso(0, AAA_ACA)}) - - - if False: - # ===== ===== ===== ============== =========== - # First Sec New Note - # == == == == == == ================================================= - # L R L R L R - # == == == == == == ================================================== - - # ===== ===== ===== ================== - # First Sec New Note - # == == == == == == ================================================= - # L R L R L R - # == == == == == == ================================================== - # B B Del - # B B B B - # B B A A - # B B C C - check(_B *rcSuper* B_, {iso(0, _)}) - check(_B *rcSuper* B_B, {iso(0, _B)}) - check(_B *rcSuper* B_A, {iso(0, _A)}) - check(_B *rcSuper* B_C, {iso(0, _C)}) - # -- -- -- -- -- -- ------------------------------------------------ - # B B B B - # B B B B B B - # B B B A B A - # B B B C B C - check(B_B *rcSuper* B_, {iso(0, B_)}) - check(B_B *rcSuper* B_B, {iso(0, B_B)}) - check(B_B *rcSuper* B_A, {iso(0, B_A)}) - check(B_B *rcSuper* B_C, {iso(0, B_C)}) - # -- -- -- -- -- -- ------------------------------------------------ - # A B B A - # A B B B A B - # A B B A A A - # A B B C A C - check(A_B *rcSuper* B_, {iso(0, A_)}) - check(A_B *rcSuper* B_B, {iso(0, A_B)}) - check(A_B *rcSuper* B_A, {iso(0, A_A)}) - check(A_B *rcSuper* B_C, {iso(0, A_C)}) - # ===== ===== ===== ================== - diff --git a/test/py/rc/test.py b/test/py/rc/test.py deleted file mode 100644 index a386ea2..0000000 --- a/test/py/rc/test.py +++ /dev/null @@ -1,57 +0,0 @@ -include("../xxx_helpers.py") - -include("000_rules.py") -#for a in inputRules: -# a.print() -# a.printTermState() - -rc = 'dummy' -iso = 'dummy' - -checkNum = 0 -def check(exp, checks, resSize=1): - global checkNum - print("Check", checkNum, exp) - checkNum += 1 - res = list(set(rc.eval(exp))) - def redo(): - rc.eval(exp, verbosity=20) - if len(rc.products) != 0: - postSection("Product Error, Result") - print("ERROR") - print("=============================================================") - for a in res: a.print() - redo() - assert False - if len(res) != resSize: - postSection("Len Error, Result") - print("ERROR") - print("=============================================================") - for a in res: a.print() - redo() - assert False - for a in checks: - if not a(res): - postSection("Check Error, Result") - print("ERROR") - print("=============================================================") - for a in res: a.print() - redo() - assert False -rcSub = rcSub(allowPartial=False) -rcSuper = rcSuper(allowPartial=False) -rcCommonMax = rcCommon(maximum=True, connected=False) -rcCommon = rcCommon(maximum=False, connected=False) - -def testSettings(settings): - global rc - global iso - global checkNum - checkNum = 0 - rc = rcEvaluator(inputRules, labelSettings=settings) - iso = lambda index, rule: lambda res: res[index].isomorphism(rule, labelSettings=settings) > 0 - include("checks.py", checkDup=False, skipDup=False) - doChecks() -testSettings(LabelSettings(LabelType.String, LabelRelation.Isomorphism)) -testSettings(LabelSettings(LabelType.Term, LabelRelation.Isomorphism)) -#testSettings(LabelSettings(LabelType.String, LabelRelation.Isomorphism, LabelRelation.Isomorphism)) diff --git a/test/py/rc/xxx_base_checks.py b/test/py/rc/xxx_base_checks.py new file mode 100644 index 0000000..627caf6 --- /dev/null +++ b/test/py/rc/xxx_base_checks.py @@ -0,0 +1,595 @@ +include("../xxx_helpers.py") + +# Rules +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# Let ABC = {A, B, C} +# Let ABCe = {A, B, C, } +# ==== ==== ================================== +# L R Label variations +# ==== ==== ================================== +# (empty) +# ---- ---- ---------------------------------- +# O ABC +# O ABC +# O O ABC +# $ $ ABC x ABC +# ---- ---- ---------------------------------- +# O-O ABC x ABC x ABC +# O-O' O ABC x ABC x ABC +# O-$ $ ABC x ABC x ABC x ABC +# O-O ABC x ABC x ABC +# O O-O' ABC x ABC x ABC +# $ O-$ ABC x ABC x ABC x ABC +# ---- ---- ---------------------------------- +# O-O' O-O' ABC x ABC x ABCe x ABC +# O-$ O-$ ABC x ABC x ABC x ABCe x ABC +# $-$' $-$' ABC x ABC x ABC x ABC x ABCe x ABC +# ==== ==== ================================== +# Different basic rules: +# L R +# === === +# (empty) +# A +# A +# A A +# A B +# AEX +# AEX A +# AEX A X +# AEX +# A AEX +# A X AEX +# AEX AEX +# AEX AFX + + +def _check_0_vertexOverlap(c): + print() + print("0-vertex overlap") + print("#" * 80) + # Try all parallel compositions between all types of rules. + # L R + # === === + # (empty) + # A + # A + # A A + # A B + rs1 = (">>", "[A]1>>", ">>[A]1", "[A]1>>[A]1", "[A]1>>[B]1", + # AEX + # AEX A + # AEX A X + "[A]1{E}[X]2>>", + "[A]1{E}[X]2>>[A]1", + "[A]1{E}[X]2>>[A]1.[X]2", + # AEX + # A AEX + # A X AEX + " >>[A]1{E}[X]2", + "[A]1 >>[A]1{E}[X]2", + "[A]1.[X]2>>[A]1{E}[X]2", + # AEX AEX + # AEX AFX + "[A]1{E}[X]2>>[A]1{E}[X]2", + "[A]1{E}[X]2>>[A]1{F}[X]2") + # and another copy, with different IDs so we can construct a combined res + rs2 = (">>", "[A]3>>", ">>[A]3", "[A]3>>[A]3", "[A]3>>[B]3", + "[A]3{E}[X]4>>", + "[A]3{E}[X]4>>[A]3", + "[A]3{E}[X]4>>[A]3.[X]4", + " >>[A]3{E}[X]4", + "[A]3 >>[A]3{E}[X]4", + "[A]3. [X]4>>[A]3{E}[X]4", + "[A]3{E}[X]4>>[A]3{E}[X]4", + "[A]3{E}[X]4>>[A]3{F}[X]4") + + for r1 in rs1: + for r2 in rs2: + r2L, r2R = r2.split(">>") + res = r1 + if res.lstrip()[0] == ">": + res = r2L + res + elif r2L.strip() != "": + res = r2L + "." + res + if res.rstrip()[-1] == ">": + res += r2R + elif r2R.strip() != "": + res += "." + r2R + c(r1, r2, {}, res) + + +def _check_1_vertexOverlap(c): + print() + print("1-vertex overlap") + print("#" * 80) + o = {1: 1} + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + # (empty) no overlaps + # A no overlaps + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print(" >>A with everything") + print("-" * 80) + r1 = ">>[A]1" + # A (empty) no overlaps + # A A (empty) + c(r1, "[A]1>>", o, ">>") + # A A no overlaps + # A A A A + # A A B B + c(r1, "[A]1>>[A]1", o, ">>[A]") + c(r1, "[A]1>>[B]1", o, ">>[B]") + # A AEX dangling condition + # A AEX A dangling condition + # A AEX A X dangling condition + c(r1, "[A]1{E}[X]2>>", o, None) + c(r1, "[A]1{E}[X]2>>[A]1", o, None) + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, None) + # A AEX no overlaps + # A A AEX AEX + # A A X AEX X AEX + c(r1, "[A]1 >>[A]1{E}[X]2", o, " >>[A]{E}[X]") + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, "[X]2>>[A]{E}[X]2") + # A AEX AEX dangling condition + # A AEX AFX dangling condition + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, None) + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, None) + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("A >>A with everything") + print("-" * 80) + r1 = "[A]1>>[A]1" + # A A (empty) no overlaps + # A A A A + c(r1, "[A]1>>", o, "[A]1>>") + # A A A no overlaps + # A A A A A A + # A A A B A B + c(r1, "[A]1>>[A]1", o, "[A]1>>[A]1") + c(r1, "[A]1>>[B]1", o, "[A]1>>[B]1") + # A A AEX AEX + # A A AEX A AEX A + # A A AEX A X AEX A X + c(r1, "[A]1{E}[X]2>>", o, "[A]1{E}[X]2>>") + c(r1, "[A]1{E}[X]2>>[A]1", o, "[A]1{E}[X]2>>[A]1") + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, "[A]1{E}[X]2>>[A]1.[X]2") + # A A AEX no overlaps + # A A A AEX A AEX + # A A A X AEX A X AEX + c(r1, "[A]1 >>[A]1{E}[X]2", o, "[A]1 >>[A]1{E}[X]") + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, "[A]1.[X]2>>[A]1{E}[X]2") + # A A AEX AEX AEX AEX + # A A AEX AFX AEX AFX + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, "[A]1{E}[X]2>>[A]1{E}[X]2") + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, "[A]1{E}[X]2>>[A]1{F}[X]2") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("Q >>A with everything") + print("-" * 80) + r1 = "[Q]1>>[A]1" + # Q A (empty) no overlaps + # Q A A Q + c(r1, "[A]1>>", o, "[Q]1>>") + # Q A A no overlaps + # Q A A A Q A + # Q A A B Q B + c(r1, "[A]1>>[A]1", o, "[Q]1>>[A]1") + c(r1, "[A]1>>[B]1", o, "[Q]1>>[B]1") + # Q A AEX QEX + # Q A AEX A QEX A + # Q A AEX A X QEX A X + c(r1, "[A]1{E}[X]2>>", o, "[Q]1{E}[X] >>") + c(r1, "[A]1{E}[X]2>>[A]1", o, "[Q]1{E}[X] >>[A]1") + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, "[Q]1{E}[X]2>>[A]1.[X]2") + # Q A AEX no overlaps + # Q A A AEX Q AEX + # Q A A X AEX Q X AEX + c(r1, "[A]1 >>[A]1{E}[X]2", o, "[Q]1 >>[A]1{E}[X]") + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, "[Q]1.[X]2>>[A]1{E}[X]2") + # Q A AEX AEX QEX AEX + # Q A AEX AFX QEX AFX + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, "[Q]1{E}[X]2>>[A]1{E}[X]2") + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, "[Q]1{E}[X]2>>[A]1{F}[X]2") + + # AEX no overlaps + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("AEX>>A with everything") + print("-" * 80) + r1 = "[A]1{E}[X]>>[A]1" + # AEX A (empty) no overlaps + # AEX A A AEX + c(r1, "[A]1>>", o, "[A]{E}[X]>>") + # AEX A A no overlaps + # AEX A A A AEX A + # AEX A A B AEX B + c(r1, "[A]1>>[A]1", o, "[A]1{E}[X]>>[A]1") + c(r1, "[A]1>>[B]1", o, "[A]1{E}[X]>>[B]1") + # AEX A AEY A(EY)EX + # AEX A AEY A A(EY)EX A + # AEX A AEY A Y A(EY)EX A Y + c(r1, "[A]1{E}[Y]2>>", o, "[A]1({E}[Y] ){E}[X]>>") + c(r1, "[A]1{E}[Y]2>>[A]1", o, "[A]1({E}[Y] ){E}[X]>>[A]1") + c(r1, "[A]1{E}[Y]2>>[A]1.[Y]2", o, "[A]1({E}[Y]2){E}[X]>>[A]1.[Y]2") + # AEX A AEY no overlaps + # AEX A A AEY AEX AEY + # AEX A A Y AEY A( Y)EX AEY + c(r1, "[A]1 >>[A]1{E}[Y]2", o, "[A]1 {E}[X]>>[A]1{E}[Y]") + c(r1, "[A]1.[Y]2>>[A]1{E}[Y]2", o, "[A]1(.[Y]2){E}[X]>>[A]1{E}[Y]2") + # AEX A AEY AEY A(EY)EX AEY + # AEX A AEY AFY A(EY)EX AFY + c(r1, "[A]1{E}[Y]2>>[A]1{E}[Y]2", o, "[A]1({E}[Y]2){E}[X]>>[A]1{E}[Y]2") + c(r1, "[A]1{E}[Y]2>>[A]1{F}[Y]2", o, "[A]1({E}[Y]2){E}[X]>>[A]1{F}[Y]2") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("AEX>>A X with everything") + print("-" * 80) + r1 = "[A]1{E}[X]2>>[A]1.[X]2" + # AEX A X (empty) no overlaps + # AEX A X A AEX X + c(r1, "[A]1>>", o, "[A]{E}[X]2>>[X]2") + # AEX A X A no overlaps + # AEX A X A A AEX A X + # AEX A X A B AEX B X + c(r1, "[A]1>>[A]1", o, "[A]1{E}[X]2>>[A]1.[X]2") + c(r1, "[A]1>>[B]1", o, "[A]1{E}[X]2>>[B]1.[X]2") + # AEX A X AEY A(EY)EX X + # AEX A X AEY A A(EY)EX A X + # AEX A X AEY A Y A(EY)EX A Y X + c(r1, "[A]1{E}[Y]2>>", o, "[A]1({E}[Y] ){E}[X]3>> [X]3") + c(r1, "[A]1{E}[Y]2>>[A]1", o, "[A]1({E}[Y] ){E}[X]3>>[A]1 .[X]3") + c(r1, "[A]1{E}[Y]2>>[A]1.[Y]2", o, "[A]1({E}[Y]2){E}[X]3>>[A]1.[Y]2.[X]3") + # AEX A X AEY no overlaps + # AEX A X A AEY AEX AEY X + # AEX A X A Y AEY A( Y)EX AEY X + c(r1, "[A]1 >>[A]1{E}[Y]2", o, "[A]1 {E}[X]3>>[A]1{E}[Y] .[X]3") + c(r1, "[A]1.[Y]2>>[A]1{E}[Y]2", o, "[A]1(.[Y]2){E}[X]3>>[A]1{E}[Y]2.[X]3") + # AEX A X AEY AEY A(EY)EX AEY X + # AEX A X AEY AFY A(EY)EX AFY X + c(r1, "[A]1{E}[Y]2>>[A]1{E}[Y]2", o, "[A]1({E}[Y]2){E}[X]3>>[A]1{E}[Y]2.[X]3") + c(r1, "[A]1{E}[Y]2>>[A]1{F}[Y]2", o, "[A]1({E}[Y]2){E}[X]3>>[A]1{F}[Y]2.[X]3") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print(" >>AEX with everything") + print("-" * 80) + r1 = ">>[A]1{E}[X]2" + # AEX (empty) no overlaps + # AEX A dangling condition + c(r1, "[A]1>>", o, None) + # AEX A no overlaps + # AEX A A AEX + # AEX A B BEX + c(r1, "[A]1>>[A]1", o, ">>[A]{E}[X]") + c(r1, "[A]1>>[B]1", o, ">>[B]{E}[X]") + # AEX AEY dangling condition + # AEX AEY A dangling condition + # AEX AEY A Y dangling conditoin + c(r1, "[A]1{E}[Y]2>>", o, None) + c(r1, "[A]1{E}[Y]2>>[A]1", o, None) + c(r1, "[A]1{E}[Y]2>>[A]1.[Y]2", o, None) + # AEX AEY no overlaps + # AEX A AEY A(EY)EX + # AEX A Y AEY Y A(EY)EX + c(r1, "[A]1 >>[A]1{E}[Y]2", o, " >>[A]({E}[Y] ){E}[X]") + c(r1, "[A]1.[Y]2>>[A]1{E}[Y]2", o, "[Y]2>>[A]({E}[Y]2){E}[X]") + # AEX AEY AEY dangling condition + # AEX AEY AFY dangling condition + c(r1, "[A]1{E}[Y]2>>[A]1{E}[Y]2", o, None) + c(r1, "[A]1{E}[Y]2>>[A]1{F}[Y]2", o, None) + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("A >>AEX with everything") + print("-" * 80) + r1 = "[A]1>>[A]1{E}[X]2" + # A AEX (empty) no overlaps + # A AEX A dangling condition + c(r1, "[A]1>>", o, None) + # A AEX A no overlaps + # A AEX A A A AEX + # A AEX A B A BEX + c(r1, "[A]1>>[A]1", o, "[A]1>>[A]1{E}[X]") + c(r1, "[A]1>>[B]1", o, "[A]1>>[B]1{E}[X]") + # A AEX AEY dangling condition + # A AEX AEY A AEY AEX + # A AEX AEY A Y AEY A( Y)EX + c(r1, "[A]1{E}[Y]2>>", o, None) + c(r1, "[A]1{E}[Y]2>>[A]1", o, "[A]1{E}[Y] >>[A]1 {E}[X]") + c(r1, "[A]1{E}[Y]2>>[A]1.[Y]2", o, "[A]1{E}[Y]2>>[A]1(.[Y]2){E}[X]") + # A AEX AEY no overlaps + # A AEX A AEY A A(EY)EX + # A AEX A Y AEY A Y A(EY)EX + c(r1, "[A]1 >>[A]1{E}[Y]2", o, "[A]1 >>[A]1({E}[Y] ){E}[X]") + c(r1, "[A]1.[Y]2>>[A]1{E}[Y]2", o, "[A]1.[Y]2>>[A]1({E}[Y]2){E}[X]") + # A AEX AEY AEY AEY A(EY)EX + # A AEX AEY AFY AEY A(FY)EX + c(r1, "[A]1{E}[Y]2>>[A]1{E}[Y]2", o, "[A]1{E}[Y]2>>[A]1({E}[Y]2){E}[X]") + c(r1, "[A]1{E}[Y]2>>[A]1{F}[Y]2", o, "[A]1{E}[Y]2>>[A]1({F}[Y]2){E}[X]") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("A X>>AEX with everything") + print("-" * 80) + r1 = "[A]1.[X]2>>[A]1{E}[X]2" + # A X AEX (empty) no overlaps + # A X AEX A dangling condition + c(r1, "[A]1>>", o, None) + # A X AEX A no overlaps + # A X AEX A A A X AEX + # A X AEX A B A X BEX + c(r1, "[A]1>>[A]1", o, "[A]1.[X]2>>[A]1{E}[X]2") + c(r1, "[A]1>>[B]1", o, "[A]1.[X]2>>[B]1{E}[X]2") + # A X AEX AEY dangling condition + # A X AEX AEY A AEY X AEX + # A X AEX AEY A X AEY X A( Y)EX + c(r1, "[A]1{E}[Y]2>>", o, None) + c(r1, "[A]1{E}[Y]2>>[A]1", o, "[A]1{E}[Y] .[X]3>>[A]1 {E}[X]3") + c(r1, "[A]1{E}[Y]2>>[A]1.[Y]2", o, "[A]1{E}[Y]2.[X]3>>[A]1(.[Y]2){E}[X]3") + # A X AEX AEY no overlaps + # A X AEX A AEY A X A(EY)EX + # A X AEX A Y AEY A Y X A(EY)EX + c(r1, "[A]1 >>[A]1{E}[Y]2", o, "[A]1 .[X]3>>[A]1({E}[Y] ){E}[X]3") + c(r1, "[A]1.[Y]2>>[A]1{E}[Y]2", o, "[A]1.[Y]2.[X]3>>[A]1({E}[Y]2){E}[X]3") + # A X AEX AEY AEY AEY X A(EY)EX + # A X AEX AEY AFY AEY X A(FY)EX + c(r1, "[A]1{E}[Y]2>>[A]1{E}[Y]2", o, "[A]1{E}[Y]2.[X]3>>[A]1({E}[Y]2){E}[X]3") + c(r1, "[A]1{E}[Y]2>>[A]1{F}[Y]2", o, "[A]1{E}[Y]2.[X]3>>[A]1({F}[Y]2){E}[X]3") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("AEX>>AEX with everything") + print("-" * 80) + r1 = "[A]1{E}[X]2>>[A]1{E}[X]2" + # AEX AEX (empty) no overlaps + # AEX AEX A dangling condition + c(r1, "[A]1>>", o, None) + # AEX AEX A no overlaps + # AEX AEX A A AEX AEX + # AEX AEX A B AEX BEX + c(r1, "[A]1>>[A]1", o, "[A]1{E}[X]2>>[A]1{E}[X]2") + c(r1, "[A]1>>[B]1", o, "[A]1{E}[X]2>>[B]1{E}[X]2") + # AEX AEX AEY dangling condition + # AEX AEX AEY A A(EY)EX AEX + # AEX AEX AEY A Y A(EY)EX A( Y)EX + c(r1, "[A]1{E}[Y]2>>", o, None) + c(r1, "[A]1{E}[Y]2>>[A]1", o, "[A]1({E}[Y] ){E}[X]3>>[A]1 {E}[X]3") + c(r1, "[A]1{E}[Y]2>>[A]1.[Y]2", o, "[A]1({E}[Y]2){E}[X]3>>[A]1(.[Y]2){E}[X]3") + # AEX AEX AEY no overlaps + # AEX AEX A AEY AEX A(EY)EX + # AEX AEX A Y AEY A( Y)EX A(EY)EX + c(r1, "[A]1 >>[A]1{E}[Y]2", o, "[A]1 {E}[X]3>>[A]1({E}[Y] ){E}[X]3") + c(r1, "[A]1.[Y]2>>[A]1{E}[Y]2", o, "[A]1(.[Y]2){E}[X]3>>[A]1({E}[Y]2){E}[X]3") + # AEX AEX AEY AEY A(EY)EX A(EY)EX + # AEX AEX AEY AFY A(EY)EX A(FY)EX + c(r1, "[A]1{E}[Y]2>>[A]1{E}[Y]2", o, "[A]1({E}[Y]2){E}[X]3>>[A]1({E}[Y]2){E}[X]3") + c(r1, "[A]1{E}[Y]2>>[A]1{F}[Y]2", o, "[A]1({E}[Y]2){E}[X]3>>[A]1({F}[Y]2){E}[X]3") + + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("AQX>>AEX with everything") + print("-" * 80) + r1 = "[A]1{Q}[X]2>>[A]1{E}[X]2" + # AQX AEX (empty) no overlaps + # AQX AEX A dangling condition + c(r1, "[A]1>>", o, None) + # AQX AEX A no overlaps + # AQX AEX A A AQX AEX + # AQX AEX A B AQX BEX + c(r1, "[A]1>>[A]1", o, "[A]1{Q}[X]2>>[A]1{E}[X]2") + c(r1, "[A]1>>[B]1", o, "[A]1{Q}[X]2>>[B]1{E}[X]2") + # AQX AEX AEY dangling condition + # AQX AEX AEY A A(EY)QX AEX + # AQX AEX AEY A Y A(EY)QX A( Y)EX + c(r1, "[A]1{E}[Y]2>>", o, None) + c(r1, "[A]1{E}[Y]2>>[A]1", o, "[A]1({E}[Y] ){Q}[X]3>>[A]1 {E}[X]3") + c(r1, "[A]1{E}[Y]2>>[A]1.[Y]2", o, "[A]1({E}[Y]2){Q}[X]3>>[A]1(.[Y]2){E}[X]3") + # AQX AEX AEY no overlaps + # AQX AEX A AEY AQX A(EY)EX + # AQX AEX A Y AEY A( Y)QX A(EY)EX + c(r1, "[A]1 >>[A]1{E}[Y]2", o, "[A]1 {Q}[X]3>>[A]1({E}[Y] ){E}[X]3") + c(r1, "[A]1.[Y]2>>[A]1{E}[Y]2", o, "[A]1(.[Y]2){Q}[X]3>>[A]1({E}[Y]2){E}[X]3") + # AQX AEX AEY AEY A(EY)QX A(EY)EX + # AQX AEX AEY AFY A(EY)QX A(FY)EX + c(r1, "[A]1{E}[Y]2>>[A]1{E}[Y]2", o, "[A]1({E}[Y]2){Q}[X]3>>[A]1({E}[Y]2){E}[X]3") + c(r1, "[A]1{E}[Y]2>>[A]1{F}[Y]2", o, "[A]1({E}[Y]2){Q}[X]3>>[A]1({F}[Y]2){E}[X]3") + + +def _check_2_vertexOverlap(c): + print() + print("2-vertex overlap") + print("#" * 80) + o = {1: 1, 2: 2} + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + # (empty) no overlaps + # A no overlaps + # A no overlaps + # A A no overlaps + # A B no overlaps + # AEX no overlaps + # AEX A no overlaps + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("AEX>>A X with everything") + print("-" * 80) + r1 = "[A]1{E}[X]2>>[A]1.[X]2" + # AEX A X (empty) no overlaps + # AEX A X A no overlaps + # AEX A X A no overlaps + # AEX A X A A no overlaps + # AEX A X A B no overlaps + # AEX A X AEX parallel edge in L + # AEX A X AEX A parallel edge in L + # AEX A X AEX A X parallel edge in L + c(r1, "[A]1{E}[X]2>>", o, None) + c(r1, "[A]1{E}[X]2>>[A]1", o, None) + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, None) + # AEX A X AEX no overlaps + # AEX A X A AEX no overlaps + # AEX A X A X AEX AEX AEX + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, "[A]1{E}[X]2>>[A]1{E}[X]2") + # AEX A X AEX AEX parallel edge in L + # AEX A X AEX AFX parallel edge in L + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, None) + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, None) + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + # AEX + print(" >>AEX with everything") + print("-" * 80) + r1 = ">>[A]1{E}[X]2" + # AEX (empty) no overlaps + # AEX A no overlaps + # AEX A no overlaps + # AEX A A no overlaps + # AEX A B no overlaps + # AEX AEX (empty) + # AEX AEX A A + # AEX AEX A X A X + c(r1, "[A]1{E}[X]2>>", o, ">>") + c(r1, "[A]1{E}[X]2>>[A]1", o, ">>[A]") + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, ">>[A].[X]") + # AEX AEX no overlaps + # AEX A AEX no overlaps + # AEX A X AEX parallel edge in R + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, None) + # AEX AEX AEX AEX + # AEX AEX AFX AFX + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, ">>[A]{E}[X]") + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, ">>[A]{F}[X]") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("A >>AEX with everything") + print("-" * 80) + r1 = "[A]1>>[A]1{E}[X]2" + # A AEX (empty) no overlaps + # A AEX A no overlaps + # A AEX A no overlaps + # A AEX A A no overlaps + # A AEX A B no overlaps + # A AEX AEX A + # A AEX AEX A A A + # A AEX AEX A X A A X + c(r1, "[A]1{E}[X]2>>", o, "[A]1>>") + c(r1, "[A]1{E}[X]2>>[A]1", o, "[A]1>>[A]1") + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, "[A]1>>[A]1.[X]") + # A AEX AEX no overlaps + # A AEX A AEX no overlaps + # A AEX A X AEX parallel edge in R + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, None) + # A AEX AEX AEX A AEX + # A AEX AEX AFX A AFX + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, "[A]1>>[A]1{E}[X]") + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, "[A]1>>[A]1{F}[X]") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("A X>>AEX with everything") + print("-" * 80) + r1 = "[A]1.[X]2>>[A]1{E}[X]2" + # A X AEX (empty) no overlaps + # A X AEX A no overlaps + # A X AEX A no overlaps + # A X AEX A A no overlaps + # A X AEX A B no overlaps + # A X AEX AEX A X + # A X AEX AEX A A X A + # A X AEX AEX A X A X A X + c(r1, "[A]1{E}[X]2>>", o, "[A]1.[X]2>>") + c(r1, "[A]1{E}[X]2>>[A]1", o, "[A]1.[X]2>>[A]1") + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, "[A]1.[X]2>>[A]1.[X]2") + # A X AEX AEX no overlaps + # A X AEX A AEX no overlaps + # A X AEX A X AEX parallel edge in R + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, None) + # A X AEX AEX AEX A X AEX + # A X AEX AEX AFX A X AFX + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, "[A]1.[X]2>>[A]1{E}[X]2") + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, "[A]1.[X]2>>[A]1{F}[X]2") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("AEX>>AEX with everything") + print("-" * 80) + r1 = "[A]1{E}[X]2>>[A]1{E}[X]2" + # AEX AEX (empty) no overlaps + # AEX AEX A no overlaps + # AEX AEX A no overlaps + # AEX AEX A A no overlaps + # AEX AEX A B no overlaps + # AEX AEX AEX AEX + # AEX AEX AEX A AEX A + # AEX AEX AEX A X AEX A X + c(r1, "[A]1{E}[X]2>>", o, "[A]1{E}[X]2>>") + c(r1, "[A]1{E}[X]2>>[A]1", o, "[A]1{E}[X]2>>[A]1") + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, "[A]1{E}[X]2>>[A]1.[X]2") + # AEX AEX AEX no overlaps + # AEX AEX A AEX no overlaps + # AEX AEX A X AEX parallel edge in R + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, None) + # AEX AEX AEX AEX AEX AEX + # AEX AEX AEX AFX AEX AFX + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, "[A]1{E}[X]2>>[A]1{E}[X]2") + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, "[A]1{E}[X]2>>[A]1{F}[X]2") + + # --------------------------------------------------- + # L R L R L R Note + # === === === === === === ========================= + print("AQX>>AEX with everything") + print("-" * 80) + r1 = "[A]1{Q}[X]2>>[A]1{E}[X]2" + # AQX AEX (empty) no overlaps + # AQX AEX A no overlaps + # AQX AEX A no overlaps + # AQX AEX A A no overlaps + # AQX AEX A B no overlaps + # AQX AEX AEX AQX + # AQX AEX AEX A AQX A + # AQX AEX AEX A X AQX A X + c(r1, "[A]1{E}[X]2>>", o, "[A]1{Q}[X]2>>") + c(r1, "[A]1{E}[X]2>>[A]1", o, "[A]1{Q}[X]2>>[A]1") + c(r1, "[A]1{E}[X]2>>[A]1.[X]2", o, "[A]1{Q}[X]2>>[A]1.[X]2") + # AQX AEX AEX no overlaps + # AQX AEX A AEX no overlaps + # AQX AEX A X AEX parallel edge in R + c(r1, "[A]1.[X]2>>[A]1{E}[X]2", o, None) + # AQX AEX AEX AEX AQX AEX + # AQX AEX AEX AFX AQX AFX + c(r1, "[A]1{E}[X]2>>[A]1{E}[X]2", o, "[A]1{Q}[X]2>>[A]1{E}[X]2") + c(r1, "[A]1{E}[X]2>>[A]1{F}[X]2", o, "[A]1{Q}[X]2>>[A]1{F}[X]2") + + +def doChecks(c): + _check_0_vertexOverlap(c) + _check_1_vertexOverlap(c) + _check_2_vertexOverlap(c) diff --git a/test/py/rcEval/common.py b/test/py/rcEval/common.py index 46d7eaa..e2e4ef0 100644 --- a/test/py/rcEval/common.py +++ b/test/py/rcEval/common.py @@ -13,8 +13,13 @@ def handleExp(exp, printRules=False): print("Database:\t", rc.ruleDatabase) print("Products:\t", rc.products) if printRules: - for a in rc.ruleDatabase: a.print() - postSection("Products") - for a in rc.products: a.print() + for a in rc.ruleDatabase: + print(" Printing", a) + a.print() + post.summarySection("Products") + for a in rc.products: + print(" Printing", a) + a.print() + print("Printing", rc) rc.print() inputRules.extend(a for a in rc.products) diff --git a/test/py/rcEval/composeCommon.py b/test/py/rcEval/composeCommon.py index d61f2be..74f4451 100644 --- a/test/py/rcEval/composeCommon.py +++ b/test/py/rcEval/composeCommon.py @@ -9,7 +9,7 @@ print("Implicit 2 -----------------------------------------------------------") handleExp(ketoEnol_F * rcCommon * aldolAdd_F) -post("disableSummary") +post.disableInvokeMake() rc = rcEvaluator(inputRules) resWithout = rc.eval(ketoEnol_F *rcCommon* ketoEnol_B) diff --git a/test/py/rcEval/convertGraph.py b/test/py/rcEval/convertGraph.py index 645e140..e20e4f7 100644 --- a/test/py/rcEval/convertGraph.py +++ b/test/py/rcEval/convertGraph.py @@ -1,7 +1,7 @@ include("common.py") def convert(name, cls, f): - postSection(name) + post.summarySection(name) print(name + ", explicit -------------------------------------------------") handleExp(cls(formaldehyde)) print(name + ", implicit -------------------------------------------------") diff --git a/test/py/rule/004_basic_loading.py b/test/py/rule/001_gml_interface.py similarity index 96% rename from test/py/rule/004_basic_loading.py rename to test/py/rule/001_gml_interface.py index 8995ad9..81e6d94 100644 --- a/test/py/rule/004_basic_loading.py +++ b/test/py/rule/001_gml_interface.py @@ -1,4 +1,4 @@ -include("../xxx_helpers.py") +include("xxx_helpers.py") data = """rule [ left [ node [ id 0 label "L" ] ] diff --git a/test/py/rule/001_loadFail.py b/test/py/rule/002_gml_loadFail.py similarity index 52% rename from test/py/rule/001_loadFail.py rename to test/py/rule/002_gml_loadFail.py index 2430d4a..fcd249a 100644 --- a/test/py/rule/001_loadFail.py +++ b/test/py/rule/002_gml_loadFail.py @@ -1,66 +1,44 @@ include("xxx_helpers.py") # for Boost < 1.77 it's column 12, for 1.77 it's 13 -ruleFail('blah', "Parsing failed at 1:") +gmlFail('blah', "Parsing failed at 1:") -ruleFail('labelType "blah"', "Unknown labelType 'blah'.") +gmlFail('labelType "blah"', "Unknown labelType 'blah'.") # Vertex properties # ============================================================================ -ruleFail(''' +gmlFail(''' left [ node [ id 0 label "A" ] ] context [ node [ id 0 label "A" ] ] ''', "Vertex 0 has a label both in 'context' and 'left'.") -ruleFail(''' +gmlFail(''' right [ node [ id 0 label "A" ] ] context [ node [ id 0 label "A" ] ] ''', "Vertex 0 has a label both in 'context' and 'right'.") -ruleFail(''' - left [ node [ id 0 stereo "any" ] ] - context [ node [ id 0 label "A" stereo "any" ] ] -''', "Vertex 0 has stereo both in 'context' and 'left'.") -ruleFail(''' - right [ node [ id 0 stereo "any" ] ] - context [ node [ id 0 label "A" stereo "any" ] ] -''', "Vertex 0 has stereo both in 'context' and 'right'.") - -ruleFail('left [ node [ id 0 ] ]', "Vertex 0 is in L, but has no label.") -ruleFail('right [ node [ id 0 ] ]', "Vertex 0 is in R, but has no label.") +gmlFail('left [ node [ id 0 ] ]', "Vertex 0 is in L, but has no label.") +gmlFail('right [ node [ id 0 ] ]', "Vertex 0 is in R, but has no label.") # Edge properties # ============================================================================ -ruleFail(''' +gmlFail(''' left [ edge [ source 0 target 1 label "-" ] ] context [ edge [ source 0 target 1 label "-" ] node [ id 0 label "A" ] node [ id 1 label "B" ] ]''', "Edge (0, 1) has a label both in 'context' and 'left'.") -ruleFail(''' +gmlFail(''' right [ edge [ source 0 target 1 label "-" ] ] context [ edge [ source 0 target 1 label "-" ] node [ id 0 label "A" ] node [ id 1 label "B" ] ]''', "Edge (0, 1) has a label both in 'context' and 'right'.") -ruleFail(''' - left [ edge [ source 0 target 1 stereo "" ] ] - context [ edge [ source 0 target 1 label "-" stereo "" ] - node [ id 0 label "A" ] - node [ id 1 label "B" ] - ]''', "Edge (0, 1) has stereo both in 'context' and 'left'.") -ruleFail(''' - right [ edge [ source 0 target 1 stereo "" ] ] - context [ edge [ source 0 target 1 label "-" stereo "" ] - node [ id 0 label "A" ] - node [ id 1 label "B" ] - ]''', "Edge (0, 1) has stereo both in 'context' and 'right'.") - -ruleFail(''' +gmlFail(''' left [ edge [ source 0 target 1 ] ] context [ node [ id 0 label "A" ] node [ id 1 label "B" ] ]''', "Edge (0, 1) is in L, but has no label.") -ruleFail(''' +gmlFail(''' right [ edge [ source 0 target 1 ] ] context [ node [ id 0 label "A" ] node [ id 1 label "B" ] ]''', "Edge (0, 1) is in R, but has no label.") diff --git a/test/py/rule/002_loadTopoFail.py b/test/py/rule/002_loadTopoFail.py deleted file mode 100644 index 79261f7..0000000 --- a/test/py/rule/002_loadTopoFail.py +++ /dev/null @@ -1,29 +0,0 @@ -include("xxx_helpers.py") - -for side in "left", "context", "right": - ruleFail(''' %s [ - node [ id 0 label "A" ] - edge [ source 0 target 1 label "-" ] - ]''' % side, "Edge endpoint '1' does not exist for edge (0, 1).") - ruleFail('''%s [ - node [ id 0 label "A" ] - edge [ source 1 target 0 label "-" ] - ]''' % side, "Edge endpoint '1' does not exist for edge (0, 1).") - -for side in "left", "context", "right": - ruleFail(""" %s [ - node [ id 0 label "C" ] - edge [ source 0 target 0 label "-" ] - ]""" % side, "Loop edge (on 0, in") - - ruleFail(""" %s [ - node [ id 0 label "C" ] - node [ id 1 label "C" ] - edge [ source 0 target 1 label "-" ] - edge [ source 1 target 0 label "-" ] - ] """ % side, "Duplicate edge (1, 0) in ") - - ruleFail(""" %s [ - node [ id 0 label "C" ] - node [ id 0 label "C" ] - ] """ % side, "Duplicate vertex 0 in ") diff --git a/test/py/rule/003_gml_loadTopoFail.py b/test/py/rule/003_gml_loadTopoFail.py new file mode 100644 index 0000000..3e0452c --- /dev/null +++ b/test/py/rule/003_gml_loadTopoFail.py @@ -0,0 +1,53 @@ +include("xxx_helpers.py") + +for side in "left", "context", "right": + gmlFail(''' %s [ + node [ id 0 label "A" ] + edge [ source 0 target 1 label "-" ] + ]''' % side, "Edge endpoint '1' does not exist for edge (0, 1).") + gmlFail('''%s [ + node [ id 0 label "A" ] + edge [ source 1 target 0 label "-" ] + ]''' % side, "Edge endpoint '1' does not exist for edge (0, 1).") + +for side in "left", "context", "right": + gmlFail(""" %s [ + node [ id 0 label "C" ] + edge [ source 0 target 0 label "-" ] + ]""" % side, "Loop edge (on 0, in") + + gmlFail(""" %s [ + node [ id 0 label "C" ] + node [ id 1 label "C" ] + edge [ source 0 target 1 label "-" ] + edge [ source 1 target 0 label "-" ] + ] """ % side, "Duplicate edge (1, 0) in ") + + gmlFail(""" %s [ + node [ id 0 label "C" ] + node [ id 0 label "C" ] + ] """ % side, "Duplicate vertex 0 in ") + +msg = "Edge (0, 1) dangling: edge is present in {} but endpoint {} only present in {}." + +gmlFail(""" +left [ edge [ source 0 target 1 label "-" ] ] +context [ node [ id 0 label "A" ] ] +right [ node [ id 1 label "B" ] ] +""", msg.format("L", 1, "R")) +gmlFail(""" +left [ edge [ source 0 target 1 label "-" ] ] +context [ node [ id 1 label "A" ] ] +right [ node [ id 0 label "B" ] ] +""", msg.format("L", 0, "R")) + +gmlFail(""" +left [ node [ id 1 label "B" ] ] +context [ node [ id 0 label "A" ] ] +right [ edge [ source 0 target 1 label "-" ] ] +""", msg.format("R", 1, "L")) +gmlFail(""" +left [ node [ id 0 label "B" ] ] +context [ node [ id 1 label "A" ] ] +right [ edge [ source 0 target 1 label "-" ] ] +""", msg.format("R", 0, "L")) diff --git a/test/py/rule/003_loadConstraintsFail.py b/test/py/rule/004_gml_loadConstraintsFail.py similarity index 72% rename from test/py/rule/003_loadConstraintsFail.py rename to test/py/rule/004_gml_loadConstraintsFail.py index a121b48..fb5c80a 100644 --- a/test/py/rule/003_loadConstraintsFail.py +++ b/test/py/rule/004_gml_loadConstraintsFail.py @@ -2,8 +2,8 @@ # ConstrainAdj # ============================================================================ -ruleFail('''constrainAdj [ id 0 op "=" count 0 ]''', +gmlFail('''constrainAdj [ id 0 op "=" count 0 ]''', "Vertex 0 in adjacency constraint does not exist.") -ruleFail('''context [ node [ id 0 label "A" ] ] +gmlFail('''context [ node [ id 0 label "A" ] ] constrainAdj [ id 0 op "a" count 0 ]''', "Unknown operator 'a' in adjacency constraint.") diff --git a/test/py/rule/010_gml_basic_structure.py b/test/py/rule/010_gml_basic_structure.py new file mode 100644 index 0000000..8bb7f15 --- /dev/null +++ b/test/py/rule/010_gml_basic_structure.py @@ -0,0 +1,83 @@ +include("xxx_helpers.py") + +data = """rule [ + left [ + node [ id 11 label "vL1" ] + node [ id 12 label "vL2" ] + edge [ source 11 target 12 label "eL" ] + + node [ id 41 label "vCL1" ] + node [ id 42 label "vCL2" ] + edge [ source 41 target 42 label "eCL" ] + ] + context [ + node [ id 31 label "vK1" ] + node [ id 32 label "vK2" ] + edge [ source 31 target 32 label "eK" ] + ] + right [ + node [ id 21 label "vR1" ] + node [ id 22 label "vR2" ] + edge [ source 21 target 22 label "eR" ] + + node [ id 41 label "vCR1" ] + node [ id 42 label "vCR2" ] + edge [ source 41 target 42 label "eCR" ] + ] +]""" + +r = Rule.fromGMLString(data) +for i in (1, 2, 3, 4): + globals()["v{}1".format(i)] = r.getVertexFromExternalId(i * 10 + 1) + globals()["v{}2".format(i)] = r.getVertexFromExternalId(i * 10 + 2) + +assert r.numVertices == 8 +assert r.numEdges == 4 +assert r.left.numVertices == 6 +assert r.left.numEdges == 3 +assert r.context.numVertices == 4 +assert r.context.numEdges == 2 +assert r.right.numVertices == 6 +assert r.right.numEdges == 3 + +assert v11.left +assert not v11.right +assert not v11.context +assert v11.left.stringLabel == "vL1" +assert v12.left +assert not v12.right +assert not v12.context +assert v12.left.stringLabel == "vL2" + +assert not v21.left +assert v21.right +assert not v21.context +assert v21.right.stringLabel == "vR1" +assert not v22.left +assert v22.right +assert not v22.context +assert v22.right.stringLabel == "vR2" + +assert v31.left +assert v31.right +assert v31.context +assert v31.left.stringLabel == "vK1" +assert v31.right.stringLabel == "vK1" +assert v32.left +assert v32.right +assert v32.context +assert v32.left.stringLabel == "vK2" +assert v32.right.stringLabel == "vK2" + +assert v41.left +assert v41.right +assert v41.context +assert v41.left.stringLabel == "vCL1" +assert v41.right.stringLabel == "vCR1" +assert v42.left +assert v42.right +assert v42.context +assert v42.left.stringLabel == "vCL2" +assert v42.right.stringLabel == "vCR2" + +commonChecks(r) diff --git a/test/py/rule/030_gml_stereo_loadFail.py b/test/py/rule/030_gml_stereo_loadFail.py new file mode 100644 index 0000000..1e7eca3 --- /dev/null +++ b/test/py/rule/030_gml_stereo_loadFail.py @@ -0,0 +1,96 @@ +include("xxx_helpers.py") + +# Vertex Properties +# ============================================================================ +gmlFail(''' + left [ node [ id 0 stereo "any" ] ] + context [ node [ id 0 label "A" stereo "any" ] ] +''', "Vertex 0 has stereo both in 'context' and 'left'.") +gmlFail(''' + right [ node [ id 0 stereo "any" ] ] + context [ node [ id 0 label "A" stereo "any" ] ] +''', "Vertex 0 has stereo both in 'context' and 'right'.") + +# check that context data is copied to left and right +gmlFail('context [ node [ id 0 label "A" stereo "blah" ] ]', + "Error in stereo data for vertex 0 in L. Invalid geometry 'blah'.") + + +# Edge properties +# ============================================================================ +gmlFail(''' + left [ edge [ source 0 target 1 stereo "" ] ] + context [ edge [ source 0 target 1 label "-" stereo "" ] + node [ id 0 label "A" ] + node [ id 1 label "B" ] + ]''', "Edge (0, 1) has stereo both in 'context' and 'left'.") +gmlFail(''' + right [ edge [ source 0 target 1 stereo "" ] ] + context [ edge [ source 0 target 1 label "-" stereo "" ] + node [ id 0 label "A" ] + node [ id 1 label "B" ] + ]''', "Edge (0, 1) has stereo both in 'context' and 'right'.") + +# check that context data is copied to left and right +gmlFail('''context [ + node [ id 0 label "C" ] + node [ id 1 label "C" ] + edge [ source 0 target 1 label "-" stereo "blah" ] +]''', "Error in stereo data for edge (0, 1) in L. Parsing error in stereo data 'blah'.") + + +# Embedding +# ============================================================================ + +# parsing error +gmlFail('left [ node [ id 0 label "C" stereo "" ] ]', + "Error in stereo data for vertex 0 in L. Parsing failed:") +gmlFail('left [ node [ id 0 label "C" stereo " " ] ]', + "Error in stereo data for vertex 0 in L. Parsing failed:") +gmlFail('left [ node [ id 0 label "C" stereo "tetrahedral[" ] ]', + "Error in stereo data for vertex 0 in L. Parsing failed:") +gmlFail('left [ node [ id 0 label "C" stereo "tetrahedral[1" ] ]', + "Error in stereo data for vertex 0 in L. Parsing failed:") +gmlFail('left [ node [ id 0 label "C" stereo "tetrahedral[.]" ] ]', + "Error in stereo data for vertex 0 in L. Parsing failed:") +# higher level parsing error +gmlFail('left [ node [ id 0 label "C" stereo "tetrahedral[a]" ] ]', + "Error in graph GML. Virtual neighbour in stereo embedding for vertex 0 in L has unknown type 'a'.") + +# not all edges, do offset to force the vertexPrinter to fail if wrong mapping +gmlFail('''right [ + node [ id 0 label "offset" ] + ] + left [ + node [ id 10 label "C" stereo "tetrahedral[]" ] + node [ id 11 label "H" ] edge [ source 10 target 11 label "-" ] + node [ id 12 label "H" ] edge [ source 10 target 12 label "-" ] + node [ id 13 label "H" ] edge [ source 10 target 13 label "-" ] + node [ id 14 label "H" ] edge [ source 10 target 14 label "-" ] +]''', "Too few edges in stereo embedding for vertex 10 in L. Got 0 edges, but the degree is 4.") +gmlFail('''left [ + node [ id 0 label "offset" ] + ] + right [ + node [ id 10 label "C" stereo "tetrahedral[]" ] + node [ id 11 label "H" ] edge [ source 10 target 11 label "-" ] + node [ id 12 label "H" ] edge [ source 10 target 12 label "-" ] + node [ id 13 label "H" ] edge [ source 10 target 13 label "-" ] + node [ id 14 label "H" ] edge [ source 10 target 14 label "-" ] +]''', "Too few edges in stereo embedding for vertex 10 in R. Got 0 edges, but the degree is 4.") + +# duplicate edge +gmlFail('''left [ + node [ id 10 label "C" stereo "tetrahedral[11, 12, 13, 13]" ] + node [ id 11 label "H" ] edge [ source 10 target 11 label "-" ] + node [ id 12 label "H" ] edge [ source 10 target 12 label "-" ] + node [ id 13 label "H" ] edge [ source 10 target 13 label "-" ] + node [ id 14 label "H" ] edge [ source 10 target 14 label "-" ] +]''', "Duplicate edge in stereo embedding for vertex 10 in L.") + +# duplicate radical +gmlFail('''left [ + node [ id 10 label "C" stereo "tetrahedral[11, 12, r, r]" ] + node [ id 11 label "H" ] edge [ source 10 target 11 label "-" ] + node [ id 12 label "H" ] edge [ source 10 target 12 label "-" ] +]''', "Multiple radicals in stereo embedding for vertex 10 in L.") diff --git a/test/py/rule/110_extId.py b/test/py/rule/049_gml_externalIds.py similarity index 90% rename from test/py/rule/110_extId.py rename to test/py/rule/049_gml_externalIds.py index 0460e20..335fcf7 100644 --- a/test/py/rule/110_extId.py +++ b/test/py/rule/049_gml_externalIds.py @@ -16,7 +16,7 @@ def check(a): # GML -a = ruleGMLString("""rule [ context [ +a = Rule.fromGMLString("""rule [ context [ node [ id 42 label "C" ] node [ id 1337 label "O" ] node [ id 0 label "U" ] @@ -24,3 +24,5 @@ def check(a): edge [ source 1337 target 0 label "-" ] ] ]""") check(a) + +commonChecks(a) diff --git a/test/py/rule/100_ruleInterface.py b/test/py/rule/100_ruleInterface.py deleted file mode 100644 index 007a0ff..0000000 --- a/test/py/rule/100_ruleInterface.py +++ /dev/null @@ -1,146 +0,0 @@ -include("../xxx_graphInterface.py") - -a = ruleGMLString("""rule [ - left [ - node [ id 0 label "C+." ] - edge [ source 100 target 0 label "-" ] - ] - context [ - node [ id 2 label "H-." ] - edge [ source 100 target 2 label ":" ] - node [ id 100 label "U" ] - ] - right [ - node [ id 1 label "O-." ] - edge [ source 100 target 1 label "=" ] - ] -]""") - - -checkGraph(a, string="'{}'".format(a.name), - vertexString="RuleVertex", edgeString="RuleEdge") -checkLabelledGraph(a.left, string="RuleLeftGraph('{}')".format(a.name), - vertexString="RuleLeftGraphVertex", edgeString="RuleLeftGraphEdge", - graphNameInElements=str(a), vIdFull=False) -checkGraph(a.context, string="RuleContextGraph('{}')".format(a.name), - vertexString="RuleContextGraphVertex", edgeString="RuleContextGraphEdge", - graphNameInElements=str(a), vIdFull=False) -checkLabelledGraph(a.right, string="RuleRightGraph('{}')".format(a.name), - vertexString="RuleRightGraphVertex", edgeString="RuleRightGraphEdge", - graphNameInElements=str(a), vIdFull=False) - - - - -print("Core\n" + "="*80) -print("numVertices:", a.numVertices) -for v in a.vertices: - print("Vertex:", v.rule, v.id, v.degree) - if v.left.isNull(): - print("\tLeft: N/A") - else: - print("\tLeft:", v.left, v.left.stringLabel) - assert v.left.core == v - if v.context.isNull(): - print("\tContext: N/A") - else: - print("\tContext:", v.context) - assert v.context.core == v - if v.right.isNull(): - print("\tRight: N/A") - else: - print("\tRight:", v.right, v.right.stringLabel) - assert v.right.core == v - for e in v.incidentEdges: - print("\tEdge:", e.target.id) -print("numEdges:", a.numEdges) -for e in a.edges: - print("Edge:", e.rule, e.source.id, e.target.id) - if e.left.isNull(): - print("Left: N/A") - else: - print("Left:", e.left) - assert e.left.core == e - if e.context.isNull(): - print("Context: N/A") - else: - print("Context:", e.context) - assert e.context.core == e - if e.right.isNull(): - print("Right: N/A") - else: - print("Right:", e.right) - assert e.right.core == e - - -def checkGraph(a, withLabels, name): - print(name + "\n" + "="*80) - print("rule:", a.rule.name) - print("numVertices:", a.numVertices) - for v in a.vertices: - if withLabels: - print("Vertex:", v.rule, v.id, v.degree, v.stringLabel, v.atomId, v.isotope, v.charge, v.radical) - else: - print("Vertex:", v.rule, v.id, v.degree) - assert v.id == v.core.id - for e in v.incidentEdges: - print("\tEdge:", e.target.id) - print("numEdges:", a.numEdges) - for e in a.edges: - if withLabels: - print("Edge:", e.rule, e.source.id, e.target.id, e.stringLabel, e.bondType) - else: - print("Edge:", e.rule, e.source.id, e.target.id) - print("Core:", e.core) - -checkGraph(a.left, True, "Left") -checkGraph(a.context, False, "Context") -checkGraph(a.right, True, "Right") - - - -a = ruleGMLString("""rule [ - context [ - node [ id 0 label "A" ] - node [ id 1 label "B" ] - edge [ source 0 target 1 label "-" ] - ] -]""") -v0 = a.vertices[0] -v1 = a.vertices[1] -assert v0 == v0 -assert v0 != v1 -e1 = list(v0.incidentEdges)[0] -e2 = list(v1.incidentEdges)[0] -assert e1 == e2 -assert not e1 != e2 - - - -a = ruleGMLString("""rule [ - left [ - node [ id 1 label "B" ] - ] - context [ - node [ id 0 label "A" ] - ] - right [ - node [ id 2 label "C" ] - ] -]""") -v0 = a.getVertexFromExternalId(0) -v1 = a.getVertexFromExternalId(1) -v2 = a.getVertexFromExternalId(2) -assert v0.id == 0, v0.id -assert v1.id == 1, v1.id -assert v2.id == 2, v2.id - -assert v0.left.id == 0, v0.left.id -assert v0.context.id == 0, v0.context.id -assert v0.right.id == 0, v0.right.id -assert v1.left.id == 1, v1.left.id -assert v2.right.id == 2, v2.right.id - -print(v0.left.stringLabel, v0.right.stringLabel) -print(v1.left.stringLabel) -print(v2.right.stringLabel) diff --git a/test/py/rule/101_dfs_basic_loading.py b/test/py/rule/101_dfs_basic_loading.py new file mode 100644 index 0000000..e4a32e5 --- /dev/null +++ b/test/py/rule/101_dfs_basic_loading.py @@ -0,0 +1,11 @@ +include("xxx_helpers.py") + +data = "[L]1>>[R]1" + +inputRules[:] = [] +r1 = Rule.fromDFS(data) +assert inputRules == [r1] +r2 = Rule.fromDFS(data) +assert inputRules == [r1, r2] +Rule.fromDFS(data, add=False) +assert inputRules == [r1, r2] diff --git a/test/py/rule/102_dfs_loadFail.py b/test/py/rule/102_dfs_loadFail.py new file mode 100644 index 0000000..4f0a3f5 --- /dev/null +++ b/test/py/rule/102_dfs_loadFail.py @@ -0,0 +1,4 @@ +include("xxx_helpers.py") + +dfsFail('', "Parsing failed at 1:1:") +dfsFail('[C]', "Parsing failed at 1:4:") diff --git a/test/py/rule/103_dfs_topo.py b/test/py/rule/103_dfs_topo.py new file mode 100644 index 0000000..0e762ac --- /dev/null +++ b/test/py/rule/103_dfs_topo.py @@ -0,0 +1,26 @@ +include("xxx_helpers.py") + +# The tests here assume that the Graph DFS tests cover all details. +# Here we just test that the issues are caught in Rule DFS as well. + +# Missing ring closure +dfsFail("[A]-1>>", "Ring closure ID 1 not found, in the left side.") +dfsFail(">>[A]-1", "Ring closure ID 1 not found, in the right side.") +dfsFail("[A]-1>>[A]-1", "Ring closure ID 1 not found, in the left side.") + +# Loop edges +dfsFail("[A]1-1>>", + "Loop edge in DFS on vertex in the left side with ID 1.") +dfsFail(">>[A]1-1", + "Loop edge in DFS on vertex in the right side with ID 1.") +dfsFail("[A]1-1>>[A]1-1", + "Loop edge in DFS on vertex in the left side with ID 1.") + +# Parallel +dfsFail("[A]1[B]-1>>", + "Parallel edge in DFS in the left side. Back-edge is to vertex with ID 1.") +dfsFail(">>[A]1[B]-1", + "Parallel edge in DFS in the right side. Back-edge is to vertex with ID 1.") +dfsFail("[A]1[B]-1>>[A]1[B]-1", + "Parallel edge in DFS in the left side. Back-edge is to vertex with ID 1.") + diff --git a/test/py/rule/110_dfs_basic_structure.py b/test/py/rule/110_dfs_basic_structure.py new file mode 100644 index 0000000..31aa246 --- /dev/null +++ b/test/py/rule/110_dfs_basic_structure.py @@ -0,0 +1,121 @@ +include("xxx_helpers.py") + +dfsCheck(">>", '') + +dfsCheck("[A]>>", 'left [ node [ id 1 label "A" ] ]') +dfsCheck(">>[A]", 'right [ node [ id 1 label "A" ] ]') + +dfsCheck("[A]1>>", 'left [ node [ id 1 label "A" ] ]') +dfsCheck(">>[A]1", 'right [ node [ id 1 label "A" ] ]') + +dfsCheck("[A].[B]>>", '''left [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] +]''') +dfsCheck(">>[A].[B]", '''right [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] +]''') + +dfsCheck("[A][B]>>", '''left [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] + edge [ source 1 target 2 label "-" ] +]''') +dfsCheck(">>[A][B]", '''right [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] + edge [ source 1 target 2 label "-" ] +]''') + +dfsCheck("[A]1>>[A]1", 'context [ node [ id 1 label "A" ] ]') +dfsCheck("[A]1>>[C]1", ''' + left [ node [ id 1 label "A" ] ] + right [ node [ id 1 label "C" ] ] +''') + +dfsCheck("[A]1.[B]>>[C]1", ''' + left [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] + ] + right [ node [ id 1 label "C" ] ] +''') +dfsCheck("[A]1>>[C]1.[D]", ''' + left [ node [ id 1 label "A" ] ] + right [ + node [ id 1 label "C" ] + node [ id 2 label "D" ] + ] +''') + +dfsCheck("[A]1[B]>>[C]1", ''' + left [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] + edge [ source 1 target 2 label "-" ] + ] + right [ node [ id 1 label "C" ] ] +''') +dfsCheck("[A]1>>[C]1[D]", ''' + left [ node [ id 1 label "A" ] ] + right [ + node [ id 1 label "C" ] + node [ id 2 label "D" ] + edge [ source 1 target 2 label "-" ] + ] +''') + +dfsCheck("[A]1[B]>>[C]1[D]", ''' + left [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] + edge [ source 1 target 2 label "-" ] + ] + right [ + node [ id 1 label "C" ] + node [ id 3 label "D" ] + edge [ source 1 target 3 label "-" ] + ] +''') + +dfsCheck("[A]1[B]2>>[C]1[D]2", ''' + left [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] + ] + context [ + edge [ source 1 target 2 label "-" ] + ] + right [ + node [ id 1 label "C" ] + node [ id 2 label "D" ] + ] +''') + +dfsCheck("[A]1{-}[B]2>>[C]1{=}[D]2", ''' + left [ + node [ id 1 label "A" ] + node [ id 2 label "B" ] + edge [ source 1 target 2 label "-" ] + ] + right [ + node [ id 1 label "C" ] + node [ id 2 label "D" ] + edge [ source 1 target 2 label "=" ] + ] +''') + +# other checks +dfsCheck("[A]1[C]>>[B][C]1", ''' + left [ + node [ id 1 label "A" ] + node [ id 2 label "C" ] + edge [ source 1 target 2 label "-" ] + ] + right [ + node [ id 3 label "B" ] + node [ id 1 label "C" ] + edge [ source 3 target 1 label "-" ] + ] +''') diff --git a/test/py/rule/111_dfs_load_implicit.py b/test/py/rule/111_dfs_load_implicit.py new file mode 100644 index 0000000..b384fa4 --- /dev/null +++ b/test/py/rule/111_dfs_load_implicit.py @@ -0,0 +1,6 @@ +include("xxx_helpers.py") + +msg = "Vertices with implicit hydrogen atoms currently not supported." +dfsFail("C>>", msg) +dfsFail(">>C", msg) +dfsFail("C1>>C1", msg) diff --git a/test/py/rule/112_dfs_whitespace.py b/test/py/rule/112_dfs_whitespace.py new file mode 100644 index 0000000..29cf49c --- /dev/null +++ b/test/py/rule/112_dfs_whitespace.py @@ -0,0 +1,37 @@ +include("xxx_helpers.py") + +r1 = Rule.fromDFS("[A] 1 ( [B] ) [C] [D] {E} [F] . [G] >> [H]") +r2 = Rule.fromDFS("[A]1([B])[C][D]{E}[F].[G]>>[H]") + +def cmpGraphs(g1, g2): + assert g1.numVertices == g2.numVertices + assert g1.numEdges == g2.numEdges + assert len(list(g1.vertices)) == len(list(g2.vertices)) + assert len(list(g1.edges)) == len(list(g2.edges)) + for v1, v2 in zip(g1.vertices, g2.vertices): + assert v1.id == v2.id + assert v1.degree == v2.degree + assert len(list(v1.incidentEdges)) == len(list(v2.incidentEdges)) + for e1, e2 in zip(v1.incidentEdges, v2.incidentEdges): + assert e1.source.id == e2.source.id + assert e1.target.id == e2.target.id + for e1, e2 in zip(g1.edges, g2.edges): + assert e1.source.id == e2.source.id + assert e1.target.id == e2.target.id + +def cmpGraphsLab(g1, g2): + cmpGraphs(g1, g2) + for v1, v2 in zip(g1.vertices, g2.vertices): + assert v1.stringLabel == v2.stringLabel + for e1, e2 in zip(v1.incidentEdges, v2.incidentEdges): + assert e1.stringLabel == e2.stringLabel + for e1, e2 in zip(g1.edges, g2.edges): + assert e1.stringLabel == e2.stringLabel + +cmpGraphs(r1, r2) +cmpGraphsLab(r1.left, r2.left) +cmpGraphs(r1.context, r2.context) +cmpGraphsLab(r1.right, r2.right) + +commonChecks(r1) +commonChecks(r2) diff --git a/test/py/rule/149_dfs_externalIds.py b/test/py/rule/149_dfs_externalIds.py new file mode 100644 index 0000000..ed48055 --- /dev/null +++ b/test/py/rule/149_dfs_externalIds.py @@ -0,0 +1,24 @@ +include("xxx_helpers.py") + +r = Rule.fromDFS("[A]1[B]2>>[A]1[C]3") + +v1 = r.getVertexFromExternalId(1) +assert v1 +assert v1.left +assert v1.left.stringLabel == "A" +assert v1.right +assert v1.right.stringLabel == "A" + +v2 = r.getVertexFromExternalId(2) +assert v2 +assert v2.left +assert v2.left.stringLabel == "B" +assert not v2.right + +v3 = r.getVertexFromExternalId(3) +assert v3 +assert not v3.left +assert v3.right +assert v3.right.stringLabel == "C" + +commonChecks(r) diff --git a/test/py/rule/010_basic.py b/test/py/rule/500_basic.py similarity index 62% rename from test/py/rule/010_basic.py rename to test/py/rule/500_basic.py index c6ef5be..4b8e154 100644 --- a/test/py/rule/010_basic.py +++ b/test/py/rule/500_basic.py @@ -15,12 +15,3 @@ r.print(p, printCombined=True) r.print(p, p) r.print(p, p, printCombined=True) - - -data = """rule [ -left [ node [ id 0 label "L" ] ] -right [ node [ id 0 label "R" ] ] -]""" -r = Rule.fromGMLString(data) -ri = Rule.fromGMLString(data, invert=True) -assert r.makeInverse().isomorphism(ri) != 0 diff --git a/test/py/rule/501_numComponents.py b/test/py/rule/501_numComponents.py new file mode 100644 index 0000000..3c3a329 --- /dev/null +++ b/test/py/rule/501_numComponents.py @@ -0,0 +1,22 @@ +include("xxx_helpers.py") + +def c(dfs, numL, numR): + r = Rule.fromDFS(dfs) + assert r.numLeftComponents == numL + assert r.numRightComponents == numR + commonChecks(r) + +# L +c("[X]>>", 1, 0) +c("[X][Y]>>", 1, 0) +c("[X].[Y]>>", 2, 0) + +# K +c("[X]1>>[X]1", 1, 1) +c("[X]1[Y]2>>[X]1[Y]2", 1, 1) +c("[X]1.[Y]2>>[X]1.[Y]2", 2, 2) + +# R +c(">>[X]", 0, 1) +c(">>[X][Y]", 0, 1) +c(">>[X].[Y]", 0, 2) diff --git a/test/py/rule/510_ruleInterface.py b/test/py/rule/510_ruleInterface.py new file mode 100644 index 0000000..5563d94 --- /dev/null +++ b/test/py/rule/510_ruleInterface.py @@ -0,0 +1,234 @@ +include("../xxx_graphInterface.py") +include("xxx_helpers.py") +import math + +def _getOrNan(f): + try: + return f() + except LogicError: + return float('nan') + +def checkCore(a): + print("Core") + print("-" * 80) + print("numVertices:", a.numVertices) + for v in a.vertices: + print("Vertex:", v.rule, v.id, v.degree) + xn = _getOrNan(lambda: v.get2DX(False)) + yn = _getOrNan(lambda: v.get2DY(False)) + xh = _getOrNan(lambda: v.get2DX(True)) + yh = _getOrNan(lambda: v.get2DY(True)) + x = _getOrNan(lambda: v.get2DX()) + y = _getOrNan(lambda: v.get2DY()) + assert math.isnan(xn) == math.isnan(yn) + assert math.isnan(xh) == math.isnan(yh) + assert math.isnan(x) == math.isnan(y) + + print(" coords without hydrogens: x={}, y={}".format(xn, yn)) + print(" coords with hydrogens: x={}, y={}".format(xh, yh)) + assert math.isnan(x) == math.isnan(xh) + assert math.isnan(y) == math.isnan(yh) + assert math.isnan(x) or x == xh, (x, xh) + assert math.isnan(y) or y == yh, (y, yh) + + if v.left.isNull(): + print("\tLeft: N/A") + else: + print("\tLeft:", v.left, v.left.stringLabel) + assert v.left.core == v + if v.context.isNull(): + print("\tContext: N/A") + else: + print("\tContext:", v.context) + assert v.context.core == v + if v.right.isNull(): + print("\tRight: N/A") + else: + print("\tRight:", v.right, v.right.stringLabel) + assert v.right.core == v + for e in v.incidentEdges: + print("\tEdge:", e.target.id) + print("numEdges:", a.numEdges) + for e in a.edges: + print("Edge:", e.rule, e.source.id, e.target.id) + if e.left.isNull(): + print("Left: N/A") + else: + print("Left:", e.left) + assert e.left.core == e + if e.context.isNull(): + print("Context: N/A") + else: + print("Context:", e.context) + assert e.context.core == e + if e.right.isNull(): + print("Right: N/A") + else: + print("Right:", e.right) + assert e.right.core == e + + +def checkSide(a, withLabels, side): + print(side) + print("-" * 80) + print("rule:", a.rule.name) + print("numVertices:", a.numVertices) + for v in a.vertices: + if withLabels: + print("Vertex:", v.rule, v.id, v.degree, v.stringLabel, v.atomId, v.isotope, v.charge, v.radical) + else: + print("Vertex:", v.rule, v.id, v.degree) + assert v.id == v.core.id + for e in v.incidentEdges: + print("\tEdge:", e.target.id) + print("numEdges:", a.numEdges) + for e in a.edges: + if withLabels: + try: + b = str(e.bondType) + except LogicError: + b = "I" + print("Edge:", e.rule, e.source.id, e.target.id, e.stringLabel, b) + else: + print("Edge:", e.rule, e.source.id, e.target.id) + print("Core:", e.core) + + +def checkRule(name, a): + print("#" * 80) + print(name) + print("#" * 80) + a.name = name + + checkGraph(a, string="'{}'".format(a.name), + vertexString="RuleVertex", edgeString="RuleEdge") + checkLabelledGraph(a.left, string="RuleLeftGraph('{}')".format(a.name), + vertexString="RuleLeftGraphVertex", edgeString="RuleLeftGraphEdge", + graphNameInElements=str(a), vIdFull=False) + checkGraph(a.context, string="RuleContextGraph('{}')".format(a.name), + vertexString="RuleContextGraphVertex", edgeString="RuleContextGraphEdge", + graphNameInElements=str(a), vIdFull=False) + checkLabelledGraph(a.right, string="RuleRightGraph('{}')".format(a.name), + vertexString="RuleRightGraphVertex", edgeString="RuleRightGraphEdge", + graphNameInElements=str(a), vIdFull=False) + + checkCore(a) + checkSide(a.left, True, "L") + checkSide(a.context, False, "K") + checkSide(a.right, True, "R") + + commonChecks(a) + + +def checkGML(name, gml): + return checkRule(name, Rule.fromGMLString("rule [ {} ]".format(gml))) + +def checkDFS(name, dfs): + return checkRule(name, Rule.fromDFS(dfs)) + + +checkGML("L only", """left [ node [ id 0 label "C" ] ]""") + +checkGML("orig example", """ + left [ + node [ id 0 label "C+." ] + edge [ source 100 target 0 label "-" ] + ] + context [ + node [ id 2 label "H-." ] + edge [ source 100 target 2 label ":" ] + node [ id 100 label "U" ] + ] + right [ + node [ id 1 label "O-." ] + edge [ source 100 target 1 label "=" ] + ]""") + +# K +checkDFS("K, vertex change", "[x]1{e}[y]2>>[a]1{e}[b]2") +checkDFS("K, vertex and edge change", "[x]1{q}[y]2>>[a]1{r}[b]2") +checkDFS("K, chemical vertex change", "[C]1-[S]2>>[N]1-[O]2") +checkDFS("K, chemical vertex and edge change", "[C]1=[S]2>>[N]1#[O]2") + +# L +checkDFS("L", "[x]{q}[y]>>") +checkDFS("L, chemical", "[C]=[H]>>") + +# R +checkDFS("R", ">>[x]{q}[y]") +checkDFS("R, chemical", ">>[C]=[H]") + +# Combinations K +# offset a side to provoke index out of bounds problems +checkDFS("Combinations K, offset L, vertex change", + "[ox]{oq}[oy].[x]1{e}[y]2>>[a]1{e}[b]2") +checkDFS("Combinations K, offset L, vertex and edge change", + "[ox]{oq}[oy].[x]1{q}[y]2>>[a]1{r}[b]2") +checkDFS("Combinations K, offset L, chemical vertex chagne", + "[ox]{oq}[oy].[C]1-[S]2>>[N]1-[O]2") +checkDFS("Combinations K, offset L, chemical vertex and edge change", + "[ox]{oq}[oy].[C]1=[S]2>>[N]1#[O]2") +checkDFS("Combinations K, offset R, vertex change", + "[x]1{e}[y]2>>[ox]{oq}[oy].[a]1{e}[b]2") +checkDFS("Combinations K, offset R, vertex and edge change", + "[x]1{q}[y]2>>[ox]{oq}[oy].[a]1{r}[b]2") +checkDFS("Combinations K, offset R, chemical vertex change", + "[C]1-[S]2>>[ox]{oq}[oy].[N]1-[O]2") +checkDFS("Combinations K, offset R, chemical vertex and edge change", + "[C]1=[S]2>>[ox]{oq}[oy].[N]1#[O]2") + +# lots of hydrogens, to check coord difference +checkDFS("Hydrogens, coords", + "{0}>>{0}".format("[C]1([H]2)([H]3)([H]4)[C]5([H]6)([H]7)[C]8([H]9)([H]10)([H]11)")) + + +# Some structural checks +a = Rule.fromGMLString("""rule [ + context [ + node [ id 0 label "A" ] + node [ id 1 label "B" ] + edge [ source 0 target 1 label "-" ] + ] +]""") +v0 = a.vertices[0] +v1 = a.vertices[1] +assert v0 == v0 +assert v0 != v1 +e1 = list(v0.incidentEdges)[0] +e2 = list(v1.incidentEdges)[0] +assert e1 == e2 +assert not e1 != e2 + +commonChecks(a) + + +# Some ID checks +a = Rule.fromGMLString("""rule [ + left [ + node [ id 1 label "B" ] + ] + context [ + node [ id 0 label "A" ] + ] + right [ + node [ id 2 label "C" ] + ] +]""") +v0 = a.getVertexFromExternalId(0) +v1 = a.getVertexFromExternalId(1) +v2 = a.getVertexFromExternalId(2) +assert v0.id == 0, v0.id +assert v1.id == 1, v1.id +assert v2.id == 2, v2.id + +assert v0.left.id == 0, v0.left.id +assert v0.context.id == 0, v0.context.id +assert v0.right.id == 0, v0.right.id +assert v1.left.id == 1, v1.left.id +assert v2.right.id == 2, v2.right.id + +print(v0.left.stringLabel, v0.right.stringLabel) +print(v1.left.stringLabel) +print(v2.right.stringLabel) + +commonChecks(a) diff --git a/test/py/rule/520_inverse.py b/test/py/rule/520_inverse.py new file mode 100644 index 0000000..f18e4f4 --- /dev/null +++ b/test/py/rule/520_inverse.py @@ -0,0 +1,88 @@ +include("xxx_helpers.py") + +def check(s1, s2): + print("#" * 80) + data1 = "rule [ left [ {} ] right [ {} ] ]".format(s1, s2) + data2 = "rule [ left [ {} ] right [ {} ] ]".format(s2, s1) + print("data1:") + print(data1) + print("data2:") + print(data2) + print("=" * 80) + print("r1 load") + print("-" * 80) + r1 = Rule.fromGMLString(data1) + print("r1 invert") + print("-" * 80) + r1inv = r1.makeInverse() + + print("=" * 80) + print("r2 load") + print("-" * 80) + r2 = Rule.fromGMLString(data2) + print("r2 invert") + print("-" * 80) + r2inv = r2.makeInverse() + + print("=" * 80) + print("r1inv iso r2") + print("-" * 80) + assert r1inv.isomorphism(r2) != 0 + print("r2inv iso r1") + print("-" * 80) + assert r2inv.isomorphism(r1) != 0 + + commonChecks(r1) + commonChecks(r2) + +# vertices +check('node [ id 0 label "A" ]', '') +check('', 'node [ id 0 label "A" ]') +check('node [ id 0 label "A" ]', 'node [ id 0 label "B" ]') +check('node [ id 0 label "C" ]', 'node [ id 0 label "C" ]') +# edges +check(''' + node [ id 0 label "X" ] + node [ id 1 label "Y" ] + edge [ source 0 target 1 label "A" ] +''', ''' + node [ id 0 label "X" ] + node [ id 1 label "Y" ] +''') +check(''' + node [ id 0 label "X" ] + node [ id 1 label "Y" ] +''', ''' + node [ id 0 label "X" ] + node [ id 1 label "Y" ] + edge [ source 0 target 1 label "A" ] +''') +check(''' + node [ id 0 label "X" ] + node [ id 1 label "Y" ] + edge [ source 0 target 1 label "A" ] +''', ''' + node [ id 0 label "X" ] + node [ id 1 label "Y" ] + edge [ source 0 target 1 label "B" ] +''') +check(''' + node [ id 0 label "X" ] + node [ id 1 label "Y" ] + edge [ source 0 target 1 label "C" ] +''', ''' + node [ id 0 label "X" ] + node [ id 1 label "Y" ] + edge [ source 0 target 1 label "C" ] +''') + +# TODO: stereo tests + +# constraints +data = """rule [ + left [ node [ id 0 label "X" ] ] + constrainAdj [ id 0 count 0 op "=" ] +]""" +r = Rule.fromGMLString(data) +fail(lambda: Rule.fromGMLString(data, invert=True), err=InputError, + pattern="has matching constraints and can not be reversed. Use Rule::ignoreConstraintsDuringInversion == true to strip constraints.") diff --git a/test/py/rule/800_morphism.py b/test/py/rule/800_morphism.py new file mode 100644 index 0000000..8ad1142 --- /dev/null +++ b/test/py/rule/800_morphism.py @@ -0,0 +1,42 @@ +include("xxx_helpers.py") + +data = """rule [ + left [ + node [ id 11 label "vL1" ] + node [ id 12 label "vL2" ] + edge [ source 11 target 12 label "eL" ] + + node [ id 41 label "vCL1" ] + node [ id 42 label "vCL2" ] + edge [ source 41 target 42 label "eCL" ] + ] + context [ + node [ id 31 label "vK1" ] + node [ id 32 label "vK2" ] + edge [ source 31 target 32 label "eK" ] + ] + right [ + node [ id 21 label "vR1" ] + node [ id 22 label "vR2" ] + edge [ source 21 target 22 label "eR" ] + + node [ id 41 label "vCR1" ] + node [ id 42 label "vCR2" ] + edge [ source 41 target 42 label "eCR" ] + ] +]""" +print("Loading r1") +print("=" * 80) +r1 = Rule.fromGMLString(data) +print("Loading r2") +print("=" * 80) +r2 = Rule.fromGMLString(data) +print("Isomorphism") +print("=" * 80) +assert r1.isomorphism(r2) != 0 +print("Monoomorphism") +print("=" * 80) +assert r1.monomorphism(r2) != 0 + +commonChecks(r1) +commonChecks(r2) diff --git a/test/py/rule/200_isomorphismLeftRight.py b/test/py/rule/850_isomorphismLeftRight.py similarity index 74% rename from test/py/rule/200_isomorphismLeftRight.py rename to test/py/rule/850_isomorphismLeftRight.py index d01df0f..e735240 100644 --- a/test/py/rule/200_isomorphismLeftRight.py +++ b/test/py/rule/850_isomorphismLeftRight.py @@ -1,13 +1,13 @@ include("xxx_helpers.py") -rId = ruleGMLString("""rule [ +rId = Rule.fromGMLString("""rule [ ruleID "id" context [ node [ id 0 label "A" ] node [ id 1 label "B" ] ] ]""") -rSwap = ruleGMLString("""rule [ +rSwap = Rule.fromGMLString("""rule [ ruleID "id" left [ node [ id 0 label "A" ] @@ -20,3 +20,6 @@ ]""") assert rId.isomorphism(rSwap) == 0 assert rId.isomorphicLeftRight(rSwap) + +commonChecks(rId) +commonChecks(rSwap) diff --git a/test/py/rule/500_print.py b/test/py/rule/900_print.py similarity index 79% rename from test/py/rule/500_print.py rename to test/py/rule/900_print.py index 440eb5b..eaf22dc 100644 --- a/test/py/rule/500_print.py +++ b/test/py/rule/900_print.py @@ -1,4 +1,4 @@ -a = ruleGMLString('rule [ context [ node [ id 0 label "C" ] ] ]') +a = Rule.fromGMLString('rule [ context [ node [ id 0 label "C" ] ] ]') fs = a.print() assert len(fs) == 2 assert fs[0] != fs[1] diff --git a/test/py/rule/901_print_graphviz.py b/test/py/rule/901_print_graphviz.py new file mode 100644 index 0000000..cebc81d --- /dev/null +++ b/test/py/rule/901_print_graphviz.py @@ -0,0 +1,20 @@ +include("../xxx_helpers.py") +post.enableInvokeMake() +a = Rule.fromDFS("[O][C][C][C]1[C]2[C]3[C]4[C]5[C][S][P]>>[N][C][C][C]1[C]2[C]3[C]4[C]5[C][S][P]") +a = Rule.fromGMLString('rule [ context [ node [ id 0 label "C" ] ] ]') + +post.summaryChapter("First") +p = GraphPrinter() +f11 = a.print(p) +p.withGraphvizCoords = True +f12 = a.print(p) +p.graphvizPrefix = 'layout = "dot"'; +f13 = a.print(p) + +post.summaryChapter("Second") +p = GraphPrinter() +f21 = a.print(p) +p.withGraphvizCoords = True +f22 = a.print(p) +p.graphvizPrefix = 'layout = "dot"'; +f23 = a.print(p) diff --git a/test/py/rule/910_depict.py b/test/py/rule/910_depict.py new file mode 100644 index 0000000..0a64bdd --- /dev/null +++ b/test/py/rule/910_depict.py @@ -0,0 +1,42 @@ +def p(dfs): + print(dfs) + post.summarySection(dfs) + r = Rule.fromDFS(dfs) + p = GraphPrinter() + r.print(p, printCombined=True) + p.withGraphvizCoords = True + r.print(p) + r.printTermState() + +print("=" * 80) +print("K") +print("-" * 80) +p("[x]1{e}[y]2>>[a]1{e}[b]2") +p("[x]1{q}[y]2>>[a]1{r}[b]2") +p("[C]1-[S]2>>[N]1-[O]2") +p("[C]1=[S]2>>[N]1#[O]2") + +print("=" * 80) +print("L") +print("-" * 80) +p("[x]{q}[y]>>") +p("[C]=[H]>>") + +print("=" * 80) +print("R") +print("-" * 80) +p(">>[x]{q}[y]") +p(">>[C]=[H]") + +print("=" * 80) +print("Combinations K") +print("-" * 80) +# offset a side to provoke index out of bounds problems +p("[ox]{oq}[oy].[x]1{e}[y]2>>[a]1{e}[b]2") +p("[ox]{oq}[oy].[x]1{q}[y]2>>[a]1{r}[b]2") +p("[ox]{oq}[oy].[C]1-[S]2>>[N]1-[O]2") +p("[ox]{oq}[oy].[C]1=[S]2>>[N]1#[O]2") +p("[x]1{e}[y]2>>[ox]{oq}[oy].[a]1{e}[b]2") +p("[x]1{q}[y]2>>[ox]{oq}[oy].[a]1{r}[b]2") +p("[C]1-[S]2>>[ox]{oq}[oy].[N]1-[O]2") +p("[C]1=[S]2>>[ox]{oq}[oy].[N]1#[O]2") diff --git a/test/py/rule/911_depict_hydrogen_collapse.py b/test/py/rule/911_depict_hydrogen_collapse.py new file mode 100644 index 0000000..ee8fbd8 --- /dev/null +++ b/test/py/rule/911_depict_hydrogen_collapse.py @@ -0,0 +1,16 @@ +def p(dfs): + print(dfs) + post.summarySection(dfs) + r = Rule.fromDFS(dfs) + pr = GraphPrinter() + r.print(pr) + pr.setReactionDefault() + r.print(pr) + +p("[C]1[H]2>>[N]1[O]2") +p("[N]1[O]2>>[C]1[H]2") + +p("[C]1[H]2>>[C]1[H]2") + +p("[ox]{oq}[oy].[C]1[H]2>>[C]1[H]2") +p("[C]1[H]2>>[ox]{oq}[oy].[C]1[H]2") diff --git a/test/py/rule/920_depict_stereo.py b/test/py/rule/920_depict_stereo.py new file mode 100644 index 0000000..830df8f --- /dev/null +++ b/test/py/rule/920_depict_stereo.py @@ -0,0 +1,28 @@ +include("xxx_helpers.py") +include("xxx_gml_stereo.py") + +def f(conf, group, title, gml): + r = Rule.fromGMLString(gml) + r.name = title + p = GraphPrinter() + p.setReactionDefault() + r.print(p) + + p = GraphPrinter() + p.setReactionDefault() + p.withPrettyStereo = True + r.print(p) + + p = GraphPrinter() + p.setReactionDefault() + p.withRawStereo = True + r.print(p) + + p = GraphPrinter() + p.setReactionDefault() + p.withIndex = True + r.print(p) + + commonChecks(r) + +foreachStereoExample(f) diff --git a/test/py/rule/rule.gml b/test/py/rule/rule.gml deleted file mode 100644 index 5963eac..0000000 --- a/test/py/rule/rule.gml +++ /dev/null @@ -1,27 +0,0 @@ -rule [ - ruleID "Test Rule" - left [ - node [ id 1 label "L" ] - node [ id 4 label "LC_A" ] - edge [ source 1 target 4 label "-" ] - ] - context [ - node [ id 3 label "C" ] - edge [ source 3 target 4 label "-" ] - ] - right [ - node [ id 2 label "R" ] - node [ id 4 label "LC_B" ] - edge [ source 2 target 4 label "-" ] - ] - constrainAdj [ - id 1 count 0 op "=" - nodeLabels [ label "A" label "B" ] - edgeLabels [ label "Q" label "R" ] - ] - constrainAdj [ - id 4 count 2 op "<" - nodeLabels [ label "C" label "D" ] - edgeLabels [ label "S" label "T" ] - ] -] diff --git a/test/py/rule/rule.py b/test/py/rule/rule.py deleted file mode 100644 index 92bfaef..0000000 --- a/test/py/rule/rule.py +++ /dev/null @@ -1,53 +0,0 @@ -include("xxx_helpers.py") - -testRule = ruleGMLString(""" - rule [ ruleID "inline rule" context [ node [ id 0 label "A" ] ] ] -""") -testRuleInverse = testRule.makeInverse() -ruleGML("rule.gml") -a = ruleGML("rule.gml") -print("a:\t", a) -print("inputRules:\t", inputRules) -with open("myRule.gml", "w") as f: - f.write(a.getGMLString()) -a = ruleGML(CWDPath("myRule.gml")) - -try: - b = a.makeInverse() -except LogicError: - pass -else: - assert False - -a.printGML() -print(a.getGMLString()) - -print("name:\t", a.name) -a.name = "New Name" -print("name:\t", a.name) - -print("numLeftComponents:\t", a.numLeftComponents) -print("numRightComponents:\t", a.numRightComponents) - -# Check eq operator -inputRules[:] = [] -a = ruleGML("rule.gml") -rs = inputRules # TODO: make rs something comming from C++ to get difference Python object with same ptr -b = rs[0] -print("a:", a) -print("b:", b) -assert a == b -assert a.isomorphism(b) > 0 -assert a.isomorphism(testRule) == 0 - -a = ruleGMLString("""rule [ context [ node [ id 0 label "C" ] ] ]""") - - -r = ruleGMLString("""rule [ - context [ - node [ id 0 label "*" ] - node [ id 1 label "*" ] - edge [ source 0 target 1 label "*" ] - ] -]""") -r.printTermState() diff --git a/test/py/rule/xxx_gml_stereo.py b/test/py/rule/xxx_gml_stereo.py new file mode 100644 index 0000000..855a575 --- /dev/null +++ b/test/py/rule/xxx_gml_stereo.py @@ -0,0 +1,68 @@ +_gmlStereoExamples = {} + +def foreachStereoExample(f): + for conf, confExamples in _gmlStereoExamples.items(): + print("#" * 80) + print(conf) + print("#" * 80) + post.summaryChapter(conf) + for group, examples in confExamples.items(): + print("=" * 80) + print(group) + print("=" * 80) + post.summarySection(group) + for title, gml in examples: + print(title) + print("-" * 80) + f(conf, group, title, gml) + +# ========================================================================= + +offset = """ + node [ id 0 label "ox" ] + node [ id 1 label "oy" ] + edge [ source 0 target 1 label "oq" stereo "*" ] +""" + +# ========================================================================= +# Tetrahedral +# ========================================================================= + +tetrahedralPattern = """ + {} + node [ id 101 label "r" ] + node [ id 102 label "s" ] + node [ id 103 label "t" ] + edge [ source 100 target 101 label "-" stereo "*" ] + edge [ source 100 target 102 label "-" stereo "*" ] + edge [ source 100 target 103 label "-" stereo "*" ] +""" +tetrahedral = tetrahedralPattern.format( + 'node [ id 100 label "q" stereo "tetrahedral[101, 102, 103, e]!" ]') +tetrahedralAnti = tetrahedralPattern.format( + 'node [ id 100 label "q" stereo "tetrahedral[101, 102, e, 103]!" ]') + +_gmlStereoExamples["Tetrahedral" ] = { + "K": [ + ("context", "rule [ context [ {} ] ]".format(tetrahedral)), + ("left + right", "rule [ left [ {} ] right [ {} ] ]".format( + tetrahedral, tetrahedralAnti)), + ], + "L": [ + ("left", "rule [ left [ {} ] ]".format(tetrahedral)), + ], + "R": [ + ("right", "rule [ right [ {} ] ]".format(tetrahedral)), + ], + "Combinations K": [ + # offset a side to provoke index out of bounds problems + ("context, offset left", """rule [ + left [ {} ] + context [ {} ] + ]""".format(offset, tetrahedral)), + ("context, offset right", """rule [ + right [ {} ] + context [ {} ] + ]""".format(offset, tetrahedral)), + ], +} diff --git a/test/py/rule/xxx_helpers.py b/test/py/rule/xxx_helpers.py index 73edd11..d1b65ba 100644 --- a/test/py/rule/xxx_helpers.py +++ b/test/py/rule/xxx_helpers.py @@ -1,5 +1,43 @@ include("../xxx_helpers.py") -def ruleFail(s, pattern): - fail(lambda: ruleGMLString("rule [ %s ]" % s), +def commonChecks(r): + # round-trip as GML + s = r.getGMLString() + rCopy = Rule.fromGMLString(s) + assert r.isomorphism(rCopy) != 0 + sCopy = rCopy.getGMLString() + if s != sCopy: + print("=" * 80) + print("Originally generated GML:") + print(s) + print("=" * 80) + print("Generated GML from reloaded GML:") + print(sCopy) + assert False + + +def gmlFail(s, pattern): + fail(lambda: Rule.fromGMLString("rule [ %s ]" % s), + pattern=pattern, err=InputError, isSubstring=True) + + +def dfsFail(s, pattern): + fail(lambda: Rule.fromDFS(s), pattern=pattern, err=InputError, isSubstring=True) + + +def dfsCheck(dfsInput, gmlInput): + print("DFS:", dfsInput) + dfs = Rule.fromDFS(dfsInput) + gml = Rule.fromGMLString("rule [ %s ]" % gmlInput) + if dfs.isomorphism(gml) != 1: + print("DFS Input:", dfs) + print("GML Input: rule [\n%s\n]" % gmlInput) + dfs.name = "DFS" + gml.name = "GML" + dfs.print() + gml.print() + post.enableInvokeMake() + assert False, "Run mod_post to see rules." + + commonChecks(dfs) diff --git a/test/py/stereo/00_geometryAndEmbedding.py b/test/py/stereo/00_geometryAndEmbedding.py index 40b5105..998ec87 100644 --- a/test/py/stereo/00_geometryAndEmbedding.py +++ b/test/py/stereo/00_geometryAndEmbedding.py @@ -14,12 +14,12 @@ def gmlNode(i, edge="-"): data['any'] = 'node [ id 0 label "Q" stereo "any[1, 2, 3, 4]" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3) + gmlNode(4) data['tetrahedral'] = 'node [ id 0 label "C" stereo "tetrahedral[1, 2, 3, 4]" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3) + gmlNode(4) -postChapter("Graph") +post.summaryChapter("Graph") for n, d in data.items(): - postSection(n) + post.summarySection(n) gGML(d) for side in ["context", "left", "right"]: - postChapter("Rule " + side) + post.summaryChapter("Rule " + side) for n, d in data.items(): - postSection(n + " " + side) + post.summarySection(n + " " + side) rGML(d, side) diff --git a/test/py/stereo/01_geometry.py b/test/py/stereo/01_geometry.py index 5f6c150..734967e 100644 --- a/test/py/stereo/01_geometry.py +++ b/test/py/stereo/01_geometry.py @@ -25,14 +25,14 @@ def gmlNode(i, edge="-"): data.append(('tetrahedral', 'node [ id 0 label "P" stereo "tetrahedral" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3, "=") + gmlNode(4))) # for testing initialisation data.append(('tetrahedral', 'node [ id 0 label "P" stereo "tetrahedral" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3) + gmlNode(4, "="))) # for testing initialisation -postChapter("Graph") +post.summaryChapter("Graph") for n, d in data: - postSection(n) + post.summarySection(n) gGML(d) for side in ["context", "left", "right"]: - postChapter("Rule " + side) + post.summaryChapter("Rule " + side) for n, d in data: # TODO: this should work too if n == "trigonalPlanar2": continue - postSection(n + " " + side) + post.summarySection(n + " " + side) rGML(d, side) diff --git a/test/py/stereo/02_embedding.py b/test/py/stereo/02_embedding.py index f684f08..5fa9bd3 100644 --- a/test/py/stereo/02_embedding.py +++ b/test/py/stereo/02_embedding.py @@ -30,12 +30,12 @@ def gmlNode(i, edge="-"): data.append(('tetrahedral', 'node [ id 0 label "S" stereo "[1, 2, 3, 4]" ]' + gmlNode(1) + gmlNode(2, "=") + gmlNode(3) + gmlNode(4, "="))) # for testing initialisation data.append(('tetrahedral', 'node [ id 0 label "S" stereo "[1, 2, 3, 4]" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3, "=") + gmlNode(4, "="))) # for testing initialisation -postChapter("Graph") +post.summaryChapter("Graph") for n, d in data: - postSection(n) + post.summarySection(n) gGML(d) for side in ["context", "left", "right"]: - postChapter("Rule " + side) + post.summaryChapter("Rule " + side) for n, d in data: - postSection(n + " " + side) + post.summarySection(n + " " + side) rGML(d, side) diff --git a/test/py/stereo/03_completeDeduce.py b/test/py/stereo/03_completeDeduce.py index e4b01c6..b8f9f27 100644 --- a/test/py/stereo/03_completeDeduce.py +++ b/test/py/stereo/03_completeDeduce.py @@ -23,9 +23,9 @@ data.append(('tetrahedral', dfs("[Z]P([Z])([Z])=[Z]"))) # for testing initialisation for side in ["context", "left", "right"]: - postChapter("Rule " + side) + post.summaryChapter("Rule " + side) for n, a in data: - postSection(n + " " + side) + post.summarySection(n + " " + side) gml = a.getGMLString() gml = gml[7:-2] rGML(gml, side) diff --git a/test/py/stereo/04_graphFix.py b/test/py/stereo/04_graphFix.py index 3950f1e..acd7908 100644 --- a/test/py/stereo/04_graphFix.py +++ b/test/py/stereo/04_graphFix.py @@ -3,29 +3,29 @@ def gmlNode(i, edge="-"): return 'node [ id %d label "Z" ] edge [ source 0 target %d label "%s" ]' % (i, i, edge) -postSection('any') +post.summarySection('any') gGML('node [ id 0 label "Q" stereo "[]!" ]') gGML('node [ id 0 label "H+" stereo "[]!" ]') gGML('node [ id 0 label "H" stereo "[1]!" ]' + gmlNode(1)) gGML('node [ id 0 label "Q" stereo "[1, 2]!" ]' + gmlNode(1) + gmlNode(2)) -postSection('linear') +post.summarySection('linear') gGML('node [ id 0 label "C" stereo "[1, 2]!" ]' + gmlNode(1, "#") + gmlNode(2)) gGML('node [ id 0 label "C" stereo "[1, 2]!" ]' + gmlNode(1, "=") + gmlNode(2, "=")) -postSection('any') +post.summarySection('any') gGML('node [ id 0 label "Q" stereo "[1, 2, 3]!" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3)) -postSection('trigonalPlanar') +post.summarySection('trigonalPlanar') gGML('node [ id 0 label "C" stereo "[1, 2, 3]!" ]' + gmlNode(1, "=") + gmlNode(2) + gmlNode(3)) gGML('node [ id 0 label "C" stereo "[1, 2, 3]!" ]' + gmlNode(1, ":") + gmlNode(2, ":") + gmlNode(3, ":")) gGML('node [ id 0 label "N" stereo "[1, 2, 3]!" ]' + gmlNode(1, ":") + gmlNode(2, ":") + gmlNode(3)) gGML('node [ id 0 label "N" stereo "[1, 2, e]!" ]' + gmlNode(1, ":") + gmlNode(2, ":")) -postSection('any') +post.summarySection('any') gGML('node [ id 0 label "Q" stereo "[1, 2, 3, 4]!" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3) + gmlNode(4)) -postSection('tetrahedral') +post.summarySection('tetrahedral') gGML('node [ id 0 label "C" stereo "[1, 2, 3, 4]!" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3) + gmlNode(4)) gGML('node [ id 0 label "N" stereo "[1, 2, 3, e]!" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3)) gGML('node [ id 0 label "N+" stereo "[1, 2, 3, 4]!" ]' + gmlNode(1) + gmlNode(2) + gmlNode(3) + gmlNode(4)) diff --git a/test/py/stereo/20_morphismGraph.py b/test/py/stereo/20_morphismGraph.py index ff09729..05dda7a 100644 --- a/test/py/stereo/20_morphismGraph.py +++ b/test/py/stereo/20_morphismGraph.py @@ -35,10 +35,10 @@ def makeGraphDict(): for n2, g2 in sorted(graphs2.items()): def check(res): if not res: - postChapter("Error: %s, %s" % (n1, n2)) - postSection("Stereo, %s" % n1) + post.summaryChapter("Error: %s, %s" % (n1, n2)) + post.summarySection("Stereo, %s" % n1) printStereo(g1) - postSection("Stereo, %s" % n2) + post.summarySection("Stereo, %s" % n2) printStereo(g2) assert False if n1 == n2: @@ -74,7 +74,7 @@ def check(res): if n1 == n2: def check(res): if not res: - postChapter("Error") + post.summaryChapter("Error") printStereo(g1) printStereo(g2) assert False @@ -86,7 +86,7 @@ def check(res): def check(res, n1, n2): isOk = (n1, n2) in ok if isOk != res: - postChapter("Error: %d" % res) + post.summaryChapter("Error: %d" % res) print(n1, "vs.", n2) print("isOk:", isOk) print("res:", res) @@ -112,7 +112,7 @@ def check(res, n1, n2): tb2 = graphs2["t_b"] = gGML('node [ id 0 label "Q" stereo "tetrahedral[1, 2, 4, 3]!" ]' + gmlNodeIdx(1) + gmlNodeIdx(2) + gmlNodeIdx(3) + gmlNodeIdx(4)) print("\tIsomorphism") -postChapter("Tetrahedral Isomorphism") +post.summaryChapter("Tetrahedral Isomorphism") assert tf1.isomorphism(tf2, labelSettings=isoLabelSettings) > 0 assert tf1.isomorphism(ta2, labelSettings=isoLabelSettings) == 0 assert tf1.isomorphism(tb2, labelSettings=isoLabelSettings) == 0 @@ -124,7 +124,7 @@ def check(res, n1, n2): assert tb1.isomorphism(tb2, labelSettings=isoLabelSettings) > 0 print("\tSpecialisation") -postChapter("Tetrahedral Specialisation") +post.summaryChapter("Tetrahedral Specialisation") assert tf1.isomorphism(tf2, labelSettings=specLabelSettings) > 0 assert tf1.isomorphism(ta2, labelSettings=specLabelSettings) > 0 assert tf1.isomorphism(tb2, labelSettings=specLabelSettings) > 0 diff --git a/test/py/stereo/graphDepiction.py b/test/py/stereo/graphDepiction.py index 3cc851d..f55143f 100644 --- a/test/py/stereo/graphDepiction.py +++ b/test/py/stereo/graphDepiction.py @@ -10,7 +10,7 @@ def doPrint(a): a.print(pg, pm) -postSection("All") +post.summarySection("All") a = graphGMLString("""graph [ node [ id 0 label "C" stereo "[1, 2, 3, 4]!" ] node [ id 1 label "O" ] @@ -36,7 +36,7 @@ def doPrint(a): ]""") doPrint(a) -postSection("Lone Pair") +post.summarySection("Lone Pair") a = graphGMLString("""graph [ node [ id 0 label "N" stereo "[1, 2, 3, e]!" ] node [ id 1 label "O" ] @@ -69,7 +69,7 @@ def doPrint(a): ]""") doPrint(a) -postSection("H") +post.summarySection("H") a = graphGMLString("""graph [ node [ id 0 label "C" stereo "[1, 2, 3, 4]!" ] node [ id 1 label "O" ] @@ -109,7 +109,7 @@ def doPrint(a): doPrint(a) -postSection("All H") +post.summarySection("All H") a = graphGMLString("""graph [ node [ id 0 label "C" stereo "[1, 2, 3, 4]!" ] node [ id 1 label "H" ] diff --git a/test/py/stereo/ruleDepiction.py b/test/py/stereo/ruleDepiction.py index c22d608..744116b 100644 --- a/test/py/stereo/ruleDepiction.py +++ b/test/py/stereo/ruleDepiction.py @@ -10,7 +10,7 @@ def doPrint(a): a.print(pg, pm) -postSection("Context") +post.summarySection("Context") a = ruleGMLString("""rule [ context [ node [ id 0 label "C" stereo "[1, 2, 3, 4]!" ] @@ -25,7 +25,7 @@ def doPrint(a): ] ]""") doPrint(a) -postSection("Left and right") +post.summarySection("Left and right") a = ruleGMLString("""rule [ left [ node [ id 0 stereo "[1, 2, 3, 4]!" ] @@ -80,7 +80,7 @@ def doPrint(a): ]""") doPrint(a) -postSection("Lone Pair") +post.summarySection("Lone Pair") a = graphGMLString("""graph [ node [ id 0 label "N" stereo "[1, 2, 3, e]!" ] node [ id 1 label "O" ] @@ -113,7 +113,7 @@ def doPrint(a): ]""") doPrint(a) -postSection("H") +post.summarySection("H") a = graphGMLString("""graph [ node [ id 0 label "C" stereo "[1, 2, 3, 4]!" ] node [ id 1 label "O" ] @@ -153,7 +153,7 @@ def doPrint(a): doPrint(a) -postSection("All H") +post.summarySection("All H") a = graphGMLString("""graph [ node [ id 0 label "C" stereo "[1, 2, 3, 4]!" ] node [ id 1 label "H" ] diff --git a/test/py/stereo/smiles.py b/test/py/stereo/smiles.py index a6233db..f2e621f 100644 --- a/test/py/stereo/smiles.py +++ b/test/py/stereo/smiles.py @@ -1,24 +1,24 @@ -postSection("prev, branch, branch, tail") +post.summarySection("prev, branch, branch, tail") smiles("O[C@](N)(P)S").print() smiles("O[C@@](N)(P)S").print() -postSection("prev, branch, branch, branch") +post.summarySection("prev, branch, branch, branch") smiles("O[C@](N)(P)(S)").print() smiles("O[C@@](N)(P)(S)").print() -postSection("branch, branch, branch, tail") +post.summarySection("branch, branch, branch, tail") smiles("[C@](O)(N)(P)S").print() smiles("[C@@](O)(N)(P)S").print() -postSection("prev, H, branch, tail") +post.summarySection("prev, H, branch, tail") smiles("O[C@H](P)S").print() smiles("O[C@@H](P)S").print() -postSection("prev, H, branch, branch") +post.summarySection("prev, H, branch, branch") smiles("O[C@H](P)(S)").print() smiles("O[C@@H](P)(S)").print() -postSection("branch, H, branch, tail") +post.summarySection("branch, H, branch, tail") smiles("[C@H](N)(P)S").print() smiles("[C@@H](N)(P)S").print() -postSection("ring closure") +post.summarySection("ring closure") inputGraphs[:] = [] smiles("C1O[C@](N)(P)S1", name="1").print() smiles("O1[C@](N)(P)SC1", name="2").print() @@ -34,6 +34,6 @@ print("{}: {}".format(b.name, b.smiles)) assert False -postSection("from OpenSMILES") +post.summarySection("from OpenSMILES") smiles("FC1C[C@](Br)(Cl)CCC1").print() smiles("[C@]1(Br)(Cl)CCCC(F)C1").print() diff --git a/test/py/stereo/smilesAll.py b/test/py/stereo/smilesAll.py index dbd806c..c739e97 100644 --- a/test/py/stereo/smilesAll.py +++ b/test/py/stereo/smilesAll.py @@ -1,7 +1,7 @@ include("../graph/030_smiles/mass/loadGraphs.py") sys.exit(0) for a in inputGraphs: - postSection(a.name) + post.summarySection(a.name) for v in a.vertices: try: v.printStereo() diff --git a/test/py/stereo/testTetraRule.py b/test/py/stereo/testTetraRule.py index 7cd6e5c..0a31685 100644 --- a/test/py/stereo/testTetraRule.py +++ b/test/py/stereo/testTetraRule.py @@ -18,7 +18,7 @@ ]""") g = smiles("[O][C@]([C@H3])([C@H2][C@H3])([C@H2][C@H3])") -postChapter("DG") +post.summaryChapter("DG") ls = LabelSettings(LabelType.String, LabelRelation.Isomorphism, LabelRelation.Specialisation) dg = DG(graphDatabase=inputGraphs, labelSettings=ls) dg.build().execute(addSubset(g) >> r, verbosity=100) @@ -26,13 +26,13 @@ for a in dg.graphDatabase: a.print() -postChapter("RC") +post.summaryChapter("RC") rc = rcEvaluator([r], labelSettings=ls) res = rc.eval(rcBind(g) *rcSuper* r) print("Res:", len(res)) for a in res: a.print() -postChapter("Iso") +post.summaryChapter("Iso") g1 = smiles("[O][C@]([C@H3])([C@H2][C@H3])([C@H2][C@H]([C@H3])([C@H3]))") g2 = smiles("[O][C@@]([C@H3])([C@H2][C@H3])([C@H2][C@H]([C@H3])([C@H3]))") p = GraphPrinter() diff --git a/test/py/term/counter.py b/test/py/term/counter.py index 0c9a9b7..245a326 100644 --- a/test/py/term/counter.py +++ b/test/py/term/counter.py @@ -1,4 +1,3 @@ -config.io.useOpenBabelCoords = False a = graphDFS("[f(0)][sd(0)]") incFirst = ruleGMLString("""rule [ ruleID "Inc first" @@ -24,11 +23,13 @@ ]""") incSecond.printTermState() -dg = dgRuleComp(inputGraphs, +dg = DG(graphDatabase=inputGraphs, + labelSettings=LabelSettings(LabelType.Term, LabelRelation.Unification)) +dg.build().execute( addSubset(a) >> repeat[1]([incSecond, incFirst]) - >> incSecond, - labelSettings=LabelSettings(LabelType.Term, LabelRelation.Unification) + >> incSecond ) -dg.calc() -dg.print() +p = DGPrinter() +p.graphPrinter.withGraphvizCoords = True +dg.print(p) diff --git a/test/py/term/makeVars.py b/test/py/term/makeVars.py index a9879e5..ad89c3d 100644 --- a/test/py/term/makeVars.py +++ b/test/py/term/makeVars.py @@ -29,18 +29,18 @@ dg = dgRuleComp(inputGraphs, addSubset(inputGraphs) >> inputRules, labelSettings=LabelSettings(LabelType.Term, LabelRelation.Unification)) dg.calc() dg.print() -postSection("Input Graphs") +post.summarySection("Input Graphs") for a in inputGraphs: a.print() a.printTermState() -postSection("Input Rules") +post.summarySection("Input Rules") for a in inputRules: a.print() a.printTermState() -postSection("Vertex Graphs") +post.summarySection("Vertex Graphs") for a in set((v.graph for v in dg.vertices)) - set(inputGraphs): a.print() a.printTermState() -postSection("Derivations") +post.summarySection("Derivations") for e in dg.edges: e.print() diff --git a/test/py/term/parsing.py b/test/py/term/parsing.py index 60a0bf0..84720fd 100644 --- a/test/py/term/parsing.py +++ b/test/py/term/parsing.py @@ -1,33 +1,37 @@ +include("../xxx_helpers.py") + ls = LabelSettings(LabelType.Term, LabelRelation.Unification) -def fail(f): - try: - f() - assert False - except TermParsingError as e: - print(e) -g = graphDFS("[C/][C]") -r = ruleGMLString("""rule [ - context [ - node [ id 0 label "C/" ] - ] -]""") +g = Graph.fromDFS("[C/][C]") +r = Rule.fromGMLString("""rule [ context [ node [ id 0 label "C/" ] ] ]""") for a in inputGraphs: - fail(lambda: a.printTermState()) + fail(lambda: a.printTermState(), "Parsing failed", err=TermParsingError, + isSubstring=True) for a in inputRules: - fail(lambda: a.printTermState()) + fail(lambda: a.printTermState(), "Parsing failed", err=TermParsingError, + isSubstring=True) -fail(lambda: dgRuleComp(inputGraphs, addSubset(lambda: []), ls)) -fail(lambda: dgRuleComp([], addSubset(inputGraphs), ls).calc()) -dg = dgRuleComp([], addSubset(lambda: inputGraphs), ls) -fail(lambda: dg.calc()) -dg = dgRuleComp([], inputRules, ls) -fail(lambda: dg.calc()) -fail(lambda: g.isomorphism(g, 1, ls)) -fail(lambda: g.monomorphism(g, 1, ls)) -fail(lambda: r.isomorphism(r, 1, ls)) -fail(lambda: r.monomorphism(r, 1, ls)) -fail(lambda: rcEvaluator(inputRules, ls)) +fail(lambda: DG(graphDatabase=inputGraphs, labelSettings=ls).build().execute(addSubset(lambda: [])), + "Parsing failed", err=TermParsingError, isSubstring=True) +fail(lambda: DG(graphDatabase=[], labelSettings=ls).build().execute(addSubset(inputGraphs)), + "Parsing failed", err=TermParsingError, isSubstring=True) +dg = DG(graphDatabase=[], labelSettings=ls) +fail(lambda: dg.build().execute(addSubset(lambda: inputGraphs)), + "Parsing failed", err=TermParsingError, isSubstring=True) +dg = DG(graphDatabase=[], labelSettings=ls) +fail(lambda: dg.build().execute(inputRules), + "Parsing failed", err=TermParsingError, isSubstring=True) +fail(lambda: g.isomorphism(g, 1, ls), + "Parsing failed", err=TermParsingError, isSubstring=True) +fail(lambda: g.monomorphism(g, 1, ls), + "Parsing failed", err=TermParsingError, isSubstring=True) +fail(lambda: r.isomorphism(r, 1, ls), + "Parsing failed", err=TermParsingError, isSubstring=True) +fail(lambda: r.monomorphism(r, 1, ls), + "Parsing failed", err=TermParsingError, isSubstring=True) +fail(lambda: rcEvaluator(inputRules, ls), + "Parsing failed", err=TermParsingError, isSubstring=True) rc = rcEvaluator([], ls) -fail(lambda: rc.eval(inputRules *rcSuper* inputRules)) +fail(lambda: rc.eval(inputRules *rcSuper* inputRules), + "Parsing failed", err=TermParsingError, isSubstring=True) diff --git a/test/py/term/relations.py b/test/py/term/relations.py index d936130..dedc129 100644 --- a/test/py/term/relations.py +++ b/test/py/term/relations.py @@ -1,7 +1,6 @@ -postChapter("DG") +post.summaryChapter("DG") print("DG") print("="*80) -config.io.useOpenBabelCoords = False a = graphDFS("[f(a)][f(b)]") b = ruleGMLString("""rule [ ruleID "b" @@ -13,12 +12,12 @@ ]""") b.printTermState() -dg = dgRuleComp(inputGraphs, - addSubset(a) >> b, - labelSettings=LabelSettings(LabelType.Term, LabelRelation.Unification) -) -dg.calc() -dg.print() +dg = DG(graphDatabase=inputGraphs, + labelSettings=LabelSettings(LabelType.Term, LabelRelation.Unification)) +dg.build().execute(addSubset(a) >> b) +p = DGPrinter() +p.graphPrinter.withGraphvizCoords = True +dg.print(p) def doRelations(xy1, xy2, xx): all = [xy1, xy2, xx] @@ -44,7 +43,7 @@ def doRelations(xy1, xy2, xx): assert xy1.isomorphism(xx, labelSettings=LabelSettings(LabelType.Term, LabelRelation.Unification)) > 0 assert xx.isomorphism(xy1, labelSettings=LabelSettings(LabelType.Term, LabelRelation.Unification)) > 0 -postChapter("Graph") +post.summaryChapter("Graph") print("Graph") print("="*80) xy1 = graphDFS("[f(_X, _Y)][a]") @@ -52,7 +51,7 @@ def doRelations(xy1, xy2, xx): xx = graphDFS("[a][f(_X, _X)]") doRelations(xy1, xy2, xx) -postChapter("Rule") +post.summaryChapter("Rule") print("Rule") print("="*80) xy1 = rcEvaluator([]).eval(rcId(xy1))[0] diff --git a/test/py/uniqueFilePrefix.py b/test/py/uniqueFilePrefix.py deleted file mode 100644 index 1b4af4a..0000000 --- a/test/py/uniqueFilePrefix.py +++ /dev/null @@ -1,5 +0,0 @@ -post("disableSummary") - -for _ in range(10): - filePrefix = makeUniqueFilePrefix() - print(filePrefix) diff --git a/test/py/xxx_graphInterface.py b/test/py/xxx_graphInterface.py index d81de16..79234bc 100644 --- a/test/py/xxx_graphInterface.py +++ b/test/py/xxx_graphInterface.py @@ -1,5 +1,5 @@ include("xxx_helpers.py") -postDisable() +post.disableCommands() def checkGraph(g, *, string: str, vertexString: str, edgeString: str, graphNameInElements: str = None, diff --git a/test/py/xxx_helpers.py b/test/py/xxx_helpers.py index 16a8946..a8b4f8e 100644 --- a/test/py/xxx_helpers.py +++ b/test/py/xxx_helpers.py @@ -1,4 +1,4 @@ -post("disableSummary") +post.disableInvokeMake() def fail(f, pattern, err=LogicError, isSubstring=False): try: @@ -16,6 +16,20 @@ def fail(f, pattern, err=LogicError, isSubstring=False): raise +def checkDeprecated(f): + old = config.common.ignoreDeprecation + config.common.ignoreDeprecation = True + res = f() + config.common.ignoreDeprecation = False + try: + f() + assert False + except DeprecationWarning: + pass + config.common.ignoreDeprecation = old + return res + + def _compareFiles(f1, f2): import difflib with open(f1, 'rb') as file1, open(f2, 'rb') as file2: From 9c17993b14eb7b3f189a0bc8733c891e3b71af58 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 21 Nov 2022 14:45:45 +0100 Subject: [PATCH 02/12] Update Github CI --- .github/workflows/Checks.yml | 2 +- .github/workflows/Conda.yml | 8 +++--- .github/workflows/Docker.yml | 24 +++++++++--------- .github/workflows/Main.yml | 49 +++++++++++++++++++++++++++++------- 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/.github/workflows/Checks.yml b/.github/workflows/Checks.yml index cca04e0..6a5056e 100644 --- a/.github/workflows/Checks.yml +++ b/.github/workflows/Checks.yml @@ -2,7 +2,7 @@ name: Checks on: [push, pull_request] jobs: Checks: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 diff --git a/.github/workflows/Conda.yml b/.github/workflows/Conda.yml index 6533c08..3a56210 100644 --- a/.github/workflows/Conda.yml +++ b/.github/workflows/Conda.yml @@ -2,7 +2,7 @@ name: Conda on: [push, pull_request] jobs: Conda-Linux: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -19,9 +19,9 @@ jobs: run: sudo apt install -y $(bindep -b | tr '\n' ' ') - name: Install Boost run: | - wget https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.tar.gz - tar -xf boost_1_74_0.tar.gz - cd boost_1_74_0 + wget https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz + tar -xf boost_1_80_0.tar.gz + cd boost_1_80_0 ./bootstrap.sh --with-python=python3 --prefix=/opt/boost ./b2 -j 2 --with-python --with-graph --with-iostreams install - name: Bootstrap diff --git a/.github/workflows/Docker.yml b/.github/workflows/Docker.yml index ee2dd8f..ed6cfe9 100644 --- a/.github/workflows/Docker.yml +++ b/.github/workflows/Docker.yml @@ -2,7 +2,7 @@ name: Docker on: [push, pull_request] jobs: Ubuntu: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -19,9 +19,9 @@ jobs: run: sudo apt install -y $(bindep -b | tr '\n' ' ') - name: Install Boost run: | - wget https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.tar.gz - tar -xf boost_1_74_0.tar.gz - cd boost_1_74_0 + wget https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz + tar -xf boost_1_80_0.tar.gz + cd boost_1_80_0 ./bootstrap.sh --with-python=python3 --prefix=/opt/boost ./b2 -j 2 --with-python --with-graph --with-iostreams install - name: Bootstrap @@ -36,7 +36,7 @@ jobs: - name: Build run: docker build -t jakobandersen/mod:ubuntu-test -f docker/Ubuntu.Dockerfile --build-arg j=2 . Fedora: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -53,9 +53,9 @@ jobs: run: sudo apt install -y $(bindep -b | tr '\n' ' ') - name: Install Boost run: | - wget https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.tar.gz - tar -xf boost_1_74_0.tar.gz - cd boost_1_74_0 + wget https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz + tar -xf boost_1_80_0.tar.gz + cd boost_1_80_0 ./bootstrap.sh --with-python=python3 --prefix=/opt/boost ./b2 -j 2 --with-python --with-graph --with-iostreams install - name: Bootstrap @@ -70,7 +70,7 @@ jobs: - name: Build run: docker build -t jakobandersen/mod:fedora-test -f docker/Fedora.Dockerfile --build-arg j=2 . Arch: - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -87,9 +87,9 @@ jobs: run: sudo apt install -y $(bindep -b | tr '\n' ' ') - name: Install Boost run: | - wget https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.tar.gz - tar -xf boost_1_74_0.tar.gz - cd boost_1_74_0 + wget https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz + tar -xf boost_1_80_0.tar.gz + cd boost_1_80_0 ./bootstrap.sh --with-python=python3 --prefix=/opt/boost ./b2 -j 2 --with-python --with-graph --with-iostreams install - name: Bootstrap diff --git a/.github/workflows/Main.yml b/.github/workflows/Main.yml index 78341d4..871c17c 100644 --- a/.github/workflows/Main.yml +++ b/.github/workflows/Main.yml @@ -2,7 +2,7 @@ name: Main on: [push, pull_request] jobs: Ubuntu: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -19,9 +19,9 @@ jobs: run: sudo apt install -y $(bindep -b | tr '\n' ' ') - name: Install Boost run: | - wget https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.tar.gz - tar -xf boost_1_74_0.tar.gz - cd boost_1_74_0 + wget https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz + tar -xf boost_1_80_0.tar.gz + cd boost_1_80_0 ./bootstrap.sh --with-python=python3 --prefix=/opt/boost ./b2 -j 2 --with-python --with-graph --with-iostreams install - name: Install Graphviz @@ -52,8 +52,8 @@ jobs: run: cd build && ctest --output-on-failure -j 2 - name: Run simple test run: mod -e "smiles('O').print()" - macOS: - runs-on: macos-10.15 + macOS-11: + runs-on: macos-11 steps: - name: Checkout uses: actions/checkout@v2 @@ -74,12 +74,43 @@ jobs: cd build cmake ../ -DBUILD_DOC=on -DBUILD_TESTING=on - name: Build - run: cd build && make -j 2 + run: cd build && make -j 3 - name: Install run: cd build && sudo make install - name: Build tests - run: cd build && make tests -j 2 + run: cd build && make tests -j 3 - name: Run tests - run: cd build && ctest --output-on-failure -j 2 + run: cd build && ctest --output-on-failure -j 3 + - name: Run simple test + run: mod -e "smiles('O').print()" + macOS-12: + runs-on: macos-12 + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + submodules: 'recursive' + - name: Install Brew dependencies + run: | + brew bundle + /usr/libexec/path_helper | sed -e 's/^PATH="//' -e 's/"; export PATH;//' | tr ":" "\n" | tail -r >> $GITHUB_PATH + - name: Install pip dependencies + run: pip3 install -r requirements.txt + - name: Bootstrap + run: ./bootstrap.sh + - name: Configure + run: | + mkdir build + cd build + cmake ../ -DBUILD_DOC=on -DBUILD_TESTING=on + - name: Build + run: cd build && make -j 3 + - name: Install + run: cd build && sudo make install + - name: Build tests + run: cd build && make tests -j 3 + - name: Run tests + run: cd build && ctest --output-on-failure -j 3 - name: Run simple test run: mod -e "smiles('O').print()" From e06b1f3ccb5cd09637cd8cebe248196365a1c457 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 21 Nov 2022 14:48:35 +0100 Subject: [PATCH 03/12] mypy fixes --- libs/pymod/lib/mod/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/pymod/lib/mod/__init__.py b/libs/pymod/lib/mod/__init__.py index 18871e9..62fe050 100644 --- a/libs/pymod/lib/mod/__init__.py +++ b/libs/pymod/lib/mod/__init__.py @@ -521,7 +521,7 @@ def _DGStrat_makeRightPredicate(pred: Callable[[Derivation], bool], strat: DGStr # DG Strategy Prettification #---------------------------------------------------------- -_DGStratType = Union[DGStrat, Rule, "_DGStrat_sequenceProxy", Iterable['_DGStratType']] # type: ignore +_DGStratType = Union[DGStrat, Rule, "_DGStrat_sequenceProxy", Iterable['_DGStratType']] def dgStrat(s: _DGStratType) -> DGStrat: if isinstance(s, DGStrat): @@ -927,7 +927,7 @@ def _RCMatch_composeAll(self, *, maximum=False, verbose=False): # RCExp prettification #---------------------------------------------------------- -_rcExpType = Union[RCExpExp, RCExpBind, RCExpComposeCommon, RCExpComposeParallel, RCExpComposeSub, RCExpComposeSuper, Iterable["_rcExpType"]] # type: ignore +_rcExpType = Union[RCExpExp, RCExpBind, RCExpComposeCommon, RCExpComposeParallel, RCExpComposeSub, RCExpComposeSuper, Iterable["_rcExpType"]] def rcExp(e: _rcExpType) -> RCExpExp: if isinstance(e, RCExpExp) or isinstance(e, Rule) or isinstance(e, RCExpUnion): From c05064a2c6e71650ebdfa7bf9d4d7cf2387d939a Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 21 Nov 2022 21:54:36 +0100 Subject: [PATCH 04/12] CI, fix macOS PATH --- .github/workflows/Main.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/Main.yml b/.github/workflows/Main.yml index 871c17c..944ec72 100644 --- a/.github/workflows/Main.yml +++ b/.github/workflows/Main.yml @@ -61,11 +61,13 @@ jobs: fetch-depth: 0 submodules: 'recursive' - name: Install Brew dependencies - run: | - brew bundle - /usr/libexec/path_helper | sed -e 's/^PATH="//' -e 's/"; export PATH;//' | tr ":" "\n" | tail -r >> $GITHUB_PATH + run: brew bundle - name: Install pip dependencies run: pip3 install -r requirements.txt + - name: Set PATH + run: | + /usr/libexec/path_helper | sed -e 's/^PATH="//' -e 's/"; export PATH;//' | tr ":" "\n" | tail -r >> $GITHUB_PATH + python3 -c 'import os,sysconfig;print(sysconfig.get_path("scripts",f"{os.name}_prefix"))' >> $GITHUB_PATH - name: Bootstrap run: ./bootstrap.sh - name: Configure @@ -92,11 +94,13 @@ jobs: fetch-depth: 0 submodules: 'recursive' - name: Install Brew dependencies - run: | - brew bundle - /usr/libexec/path_helper | sed -e 's/^PATH="//' -e 's/"; export PATH;//' | tr ":" "\n" | tail -r >> $GITHUB_PATH + run: brew bundle - name: Install pip dependencies run: pip3 install -r requirements.txt + - name: Set PATH + run: | + /usr/libexec/path_helper | sed -e 's/^PATH="//' -e 's/"; export PATH;//' | tr ":" "\n" | tail -r >> $GITHUB_PATH + python3 -c 'import os,sysconfig;print(sysconfig.get_path("scripts",f"{os.name}_prefix"))' >> $GITHUB_PATH - name: Bootstrap run: ./bootstrap.sh - name: Configure From 86c48a9302a0b388bfdd474266f8f56a383977d1 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 22 Nov 2022 12:58:11 +0100 Subject: [PATCH 05/12] Conda, pin Python also in build --- conda/meta.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/conda/meta.yaml b/conda/meta.yaml index b5af349..dbe4cb1 100644 --- a/conda/meta.yaml +++ b/conda/meta.yaml @@ -21,6 +21,11 @@ requirements: - make - pkg-config - cmake + # lib packages, needed here to make sure we get the exact same version + # as in the host environment + - boost {{ boost }} + - openbabel + - python {{ python }} # Add both exe and py packages to the build environemnt # so configuration checks succeed and tests can be run. # exe packages @@ -38,12 +43,12 @@ requirements: # lib packages - boost {{ boost }} - openbabel - - python + - python {{ python }} run: # lib packages - boost - openbabel - - python + - python {{ python }} # exe packages - graphviz>=2.46.1 # to get the rsvg plugin - pdf2svg From a3f2452d7dee858d8b0ab068be8b92ac68f22f96 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 22 Nov 2022 13:03:10 +0100 Subject: [PATCH 06/12] CI, fix Ubuntu --- .github/workflows/Main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Main.yml b/.github/workflows/Main.yml index 944ec72..551ff8a 100644 --- a/.github/workflows/Main.yml +++ b/.github/workflows/Main.yml @@ -16,7 +16,7 @@ jobs: sudo apt install python3-setuptools python3-wheel pip3 install -r requirements.txt - name: Install apt dependencies - run: sudo apt install -y $(bindep -b | tr '\n' ' ') + run: sudo apt install -y $(bindep -b testing | tr '\n' ' ') - name: Install Boost run: | wget https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz From c2e8e17450ac3bfe98eaae8f0627371c49edc539 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 22 Nov 2022 20:42:55 +0100 Subject: [PATCH 07/12] Bump Boost to 1.76 Due to https://github.com/boostorg/python/pull/344 for Python 3.10 --- .drone.jsonnet | 6 +- .drone.yml | 2084 ++--------------------- CMakeLists.txt | 6 +- ChangeLog.rst | 2 + conda/conda_build_config.yaml | 1 - doc/source/compiling.rst | 8 +- docker/Ubuntu.Dockerfile | 2 +- examples/pymod_extension/CMakeLists.txt | 4 +- 8 files changed, 181 insertions(+), 1932 deletions(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index cbb6b47..8bf2a2a 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -178,7 +178,7 @@ local Pipeline(withCoverage, compiler, boost) = { name: "Docker", steps: [ Bootstrap(false), - Configure("g++", "1_75_0", false), + Configure("g++", "1_80_0", false), { name: "dist", image: image, @@ -230,7 +230,7 @@ local Pipeline(withCoverage, compiler, boost) = { ] }, ] + [ - Pipeline(boost == "1_74_0" && compiler == "g++-9", compiler, boost) + Pipeline(boost == "1_80_0" && compiler == "g++-11", compiler, boost) for compiler in [ "g++-8", "g++-9", "g++-10", "g++-11", "clang++-8", @@ -238,6 +238,6 @@ local Pipeline(withCoverage, compiler, boost) = { "clang++-10", "clang++-11", "clang++-12", ] for boost in [ - "1_73_0", "1_74_0", "1_75_0", "1_76_0", "1_77_0", "1_78_0", "1_79_0", "1_80_0", + "1_76_0", "1_77_0", "1_78_0", "1_79_0", "1_80_0", ] ] diff --git a/.drone.yml b/.drone.yml index 95cf984..468f285 100644 --- a/.drone.yml +++ b/.drone.yml @@ -50,7 +50,7 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: CXX: g++ CXXFLAGS: -Werror @@ -97,225 +97,6 @@ steps: tags: - arch-test ---- -kind: pipeline -name: g++-8, Boost 1_73_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - environment: - CXX: g++-8 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-8, Boost 1_74_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 - environment: - CXX: g++-8 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-8, Boost 1_75_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 - environment: - CXX: g++-8 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - --- kind: pipeline name: g++-8, Boost 1_76_0 @@ -391,1571 +172,7 @@ steps: --- kind: pipeline -name: g++-8, Boost 1_77_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - environment: - CXX: g++-8 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-8, Boost 1_78_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - environment: - CXX: g++-8 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-8, Boost 1_79_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - environment: - CXX: g++-8 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-8, Boost 1_80_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - environment: - CXX: g++-8 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-9, Boost 1_73_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - environment: - CXX: g++-9 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-9, Boost 1_74_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 - environment: - CXX: g++-9 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - -- name: coverage - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir covBuild - - cd covBuild - - cmake ../ -DENABLE_IPO=off -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_TESTING=on -DBUILD_COVERAGE=on -DBOOST_ROOT=/opt/boost/1_74_0 - - make - - make install - - make tests - - make coverage_collect - - make coverage_build - - /copyCoverage.sh - environment: - CTEST_OUTPUT_ON_FAILURE: 1 - CXX: g++-9 - CXXFLAGS: -Werror - volumes: - - name: www - path: /www - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - -volumes: -- name: www - host: - path: /www/results/mod - ---- -kind: pipeline -name: g++-9, Boost 1_75_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 - environment: - CXX: g++-9 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-9, Boost 1_76_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - environment: - CXX: g++-9 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-9, Boost 1_77_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - environment: - CXX: g++-9 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-9, Boost 1_78_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - environment: - CXX: g++-9 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-9, Boost 1_79_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - environment: - CXX: g++-9 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-9, Boost 1_80_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - environment: - CXX: g++-9 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-10, Boost 1_73_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - environment: - CXX: g++-10 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-10, Boost 1_74_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 - environment: - CXX: g++-10 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-10, Boost 1_75_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 - environment: - CXX: g++-10 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-10, Boost 1_76_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - environment: - CXX: g++-10 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-10, Boost 1_77_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - environment: - CXX: g++-10 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-10, Boost 1_78_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - environment: - CXX: g++-10 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-10, Boost 1_79_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - environment: - CXX: g++-10 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-10, Boost 1_80_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - environment: - CXX: g++-10 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-11, Boost 1_73_0 - -platform: - os: linux - arch: amd64 - -steps: -- name: bootstrap - image: localhost:5000/jla/mod - commands: - - git fetch --tags - - git submodule update --init --recursive - - ./bootstrap.sh - -- name: configure - image: localhost:5000/jla/mod - commands: - - bindep testing - - mkdir build - - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - environment: - CXX: g++-11 - CXXFLAGS: -Werror - -- name: build - image: localhost:5000/jla/mod - commands: - - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 - - make - -- name: install - image: localhost:5000/jla/mod - commands: - - cd build - - make install - -- name: build-test - image: localhost:5000/jla/mod - commands: - - cd build - - make tests - -- name: test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -E cmake_add_subdirectory_build - -- name: simple test - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - cd ../ - - mod -e "smiles('O').print()" - -- name: test subdirectory build - image: localhost:5000/jla/mod - commands: - - cd build - - make install - - ctest --output-on-failure -R cmake_add_subdirectory_build - when: - ref: - - refs/heads/develop - - refs/heads/master - - refs/tags/v* - ---- -kind: pipeline -name: g++-11, Boost 1_74_0 +name: g++-8, Boost 1_77_0 platform: os: linux @@ -1975,16 +192,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: g++-11 + CXX: g++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -2028,7 +245,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_75_0 +name: g++-8, Boost 1_78_0 platform: os: linux @@ -2048,16 +265,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: g++-11 + CXX: g++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -2101,7 +318,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_76_0 +name: g++-8, Boost 1_79_0 platform: os: linux @@ -2121,16 +338,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: g++-11 + CXX: g++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -2174,7 +391,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_77_0 +name: g++-8, Boost 1_80_0 platform: os: linux @@ -2194,16 +411,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: g++-11 + CXX: g++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -2247,7 +464,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_78_0 +name: g++-9, Boost 1_76_0 platform: os: linux @@ -2267,16 +484,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: g++-11 + CXX: g++-9 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - make - name: install @@ -2320,7 +537,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_79_0 +name: g++-9, Boost 1_77_0 platform: os: linux @@ -2340,16 +557,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: g++-11 + CXX: g++-9 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -2393,7 +610,7 @@ steps: --- kind: pipeline -name: g++-11, Boost 1_80_0 +name: g++-9, Boost 1_78_0 platform: os: linux @@ -2413,16 +630,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: g++-11 + CXX: g++-9 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -2466,7 +683,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_73_0 +name: g++-9, Boost 1_79_0 platform: os: linux @@ -2486,16 +703,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: clang++-8 + CXX: g++-9 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -2539,7 +756,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_74_0 +name: g++-9, Boost 1_80_0 platform: os: linux @@ -2559,16 +776,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: clang++-8 + CXX: g++-9 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -2612,7 +829,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_75_0 +name: g++-10, Boost 1_76_0 platform: os: linux @@ -2632,16 +849,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: clang++-8 + CXX: g++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - make - name: install @@ -2685,7 +902,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_76_0 +name: g++-10, Boost 1_77_0 platform: os: linux @@ -2705,16 +922,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: clang++-8 + CXX: g++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -2758,7 +975,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_77_0 +name: g++-10, Boost 1_78_0 platform: os: linux @@ -2778,16 +995,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: clang++-8 + CXX: g++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -2831,7 +1048,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_78_0 +name: g++-10, Boost 1_79_0 platform: os: linux @@ -2851,16 +1068,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: clang++-8 + CXX: g++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -2904,7 +1121,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_79_0 +name: g++-10, Boost 1_80_0 platform: os: linux @@ -2924,16 +1141,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: clang++-8 + CXX: g++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -2977,7 +1194,7 @@ steps: --- kind: pipeline -name: clang++-8, Boost 1_80_0 +name: g++-11, Boost 1_76_0 platform: os: linux @@ -2997,16 +1214,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: clang++-8 + CXX: g++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - make - name: install @@ -3050,7 +1267,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_73_0 +name: g++-11, Boost 1_77_0 platform: os: linux @@ -3070,16 +1287,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: clang++-10 + CXX: g++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -3123,7 +1340,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_74_0 +name: g++-11, Boost 1_78_0 platform: os: linux @@ -3143,16 +1360,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: clang++-10 + CXX: g++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -3196,7 +1413,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_75_0 +name: g++-11, Boost 1_79_0 platform: os: linux @@ -3216,16 +1433,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: clang++-10 + CXX: g++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -3269,7 +1486,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_76_0 +name: g++-11, Boost 1_80_0 platform: os: linux @@ -3289,16 +1506,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: clang++-10 + CXX: g++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -3340,9 +1557,40 @@ steps: - refs/heads/master - refs/tags/v* +- name: coverage + image: localhost:5000/jla/mod + commands: + - bindep testing + - mkdir covBuild + - cd covBuild + - cmake ../ -DENABLE_IPO=off -DCMAKE_BUILD_TYPE=OptDebug -DBUILD_TESTING=on -DBUILD_COVERAGE=on -DBOOST_ROOT=/opt/boost/1_80_0 + - make + - make install + - make tests + - make coverage_collect + - make coverage_build + - /copyCoverage.sh + environment: + CTEST_OUTPUT_ON_FAILURE: 1 + CXX: g++-11 + CXXFLAGS: -Werror + volumes: + - name: www + path: /www + when: + ref: + - refs/heads/develop + - refs/heads/master + - refs/tags/v* + +volumes: +- name: www + host: + path: /www/results/mod + --- kind: pipeline -name: clang++-10, Boost 1_77_0 +name: clang++-8, Boost 1_76_0 platform: os: linux @@ -3362,16 +1610,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: clang++-10 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - make - name: install @@ -3415,7 +1663,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_78_0 +name: clang++-8, Boost 1_77_0 platform: os: linux @@ -3435,16 +1683,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: clang++-10 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -3488,7 +1736,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_79_0 +name: clang++-8, Boost 1_78_0 platform: os: linux @@ -3508,16 +1756,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: clang++-10 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -3561,7 +1809,7 @@ steps: --- kind: pipeline -name: clang++-10, Boost 1_80_0 +name: clang++-8, Boost 1_79_0 platform: os: linux @@ -3581,16 +1829,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: clang++-10 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -3634,7 +1882,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_73_0 +name: clang++-8, Boost 1_80_0 platform: os: linux @@ -3654,16 +1902,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: clang++-11 + CXX: clang++-8 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -3707,7 +1955,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_74_0 +name: clang++-10, Boost 1_76_0 platform: os: linux @@ -3727,16 +1975,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: - CXX: clang++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - make - name: install @@ -3780,7 +2028,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_75_0 +name: clang++-10, Boost 1_77_0 platform: os: linux @@ -3800,16 +2048,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: - CXX: clang++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -3853,7 +2101,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_76_0 +name: clang++-10, Boost 1_78_0 platform: os: linux @@ -3873,16 +2121,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: clang++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -3926,7 +2174,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_77_0 +name: clang++-10, Boost 1_79_0 platform: os: linux @@ -3946,16 +2194,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: clang++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -3999,7 +2247,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_78_0 +name: clang++-10, Boost 1_80_0 platform: os: linux @@ -4019,16 +2267,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: clang++-11 + CXX: clang++-10 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install @@ -4072,7 +2320,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_79_0 +name: clang++-11, Boost 1_76_0 platform: os: linux @@ -4092,7 +2340,7 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 environment: CXX: clang++-11 CXXFLAGS: -Werror @@ -4101,7 +2349,7 @@ steps: image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_76_0 - make - name: install @@ -4145,7 +2393,7 @@ steps: --- kind: pipeline -name: clang++-11, Boost 1_80_0 +name: clang++-11, Boost 1_77_0 platform: os: linux @@ -4165,7 +2413,7 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 environment: CXX: clang++-11 CXXFLAGS: -Werror @@ -4174,7 +2422,7 @@ steps: image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_77_0 - make - name: install @@ -4218,7 +2466,7 @@ steps: --- kind: pipeline -name: clang++-12, Boost 1_73_0 +name: clang++-11, Boost 1_78_0 platform: os: linux @@ -4238,16 +2486,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 environment: - CXX: clang++-12 + CXX: clang++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_73_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_78_0 - make - name: install @@ -4291,7 +2539,7 @@ steps: --- kind: pipeline -name: clang++-12, Boost 1_74_0 +name: clang++-11, Boost 1_79_0 platform: os: linux @@ -4311,16 +2559,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 environment: - CXX: clang++-12 + CXX: clang++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_74_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_79_0 - make - name: install @@ -4364,7 +2612,7 @@ steps: --- kind: pipeline -name: clang++-12, Boost 1_75_0 +name: clang++-11, Boost 1_80_0 platform: os: linux @@ -4384,16 +2632,16 @@ steps: - bindep testing - mkdir build - cd build - - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DCMAKE_BUILD_TYPE=OptDebug -DENABLE_IPO=off -DBUILD_DOC=on -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 environment: - CXX: clang++-12 + CXX: clang++-11 CXXFLAGS: -Werror - name: build image: localhost:5000/jla/mod commands: - cd build - - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_75_0 + - cmake ../ -DBUILD_TESTING=on -DBUILD_TESTING_SANITIZERS=off -DBOOST_ROOT=/opt/boost/1_80_0 - make - name: install diff --git a/CMakeLists.txt b/CMakeLists.txt index 9095263..1b908e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,9 +89,9 @@ set(libmod_config_find_files "FindPackageHandleStandardArgs.cmake;FindPackageMes # Boost # ------------------------------------------------------------------------- -set(v 1.73.0) +set(v 1.76.0) if(BUILD_PY_MOD) - foreach(PY 3 34 35 36 37 38 39 310 311 312 313 314 315 316 317 318 319) + foreach(PY 3 37 38 39 310 311 312 313 314 315 316 317 318 319) set(lib "python${PY}") find_package(Boost ${v} QUIET COMPONENTS ${lib}) if(Boost_FOUND) @@ -102,7 +102,7 @@ if(BUILD_PY_MOD) endforeach() if(NOT Boost_FOUND) find_package(Boost ${v} COMPONENTS python3) - message(FATAL_ERROR "Could not find Boost.Python for Python 3. Tried 'python' wih suffixes 3, 34 to 39, and 310 to 319.") + message(FATAL_ERROR "Could not find Boost.Python for Python 3. Tried 'python' wih suffixes 3, 37, 38, 39, and 310 to 319.") endif() endif() find_package(Boost ${v} REQUIRED COMPONENTS graph iostreams) diff --git a/ChangeLog.rst b/ChangeLog.rst index e9d07a6..9b77b4c 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -27,6 +27,8 @@ Doc, several pages have changed URL: - Due to a change in escaping of ``#`` characters in DG vertex/hyperedge labels when printing, some custom labels with additional escaping may need adjustment. See also the bug fix entry regarding this. +- Bump Boost version required to 1.76 or higher to avoid an incompatibility with + Python 3.10 (https://github.com/boostorg/python/pull/344). New Features diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml index 156fdc3..295c90a 100644 --- a/conda/conda_build_config.yaml +++ b/conda/conda_build_config.yaml @@ -4,7 +4,6 @@ python: - 3.9 - 3.10 boost: - - 1.74 - 1.76 - 1.77 - 1.78 diff --git a/doc/source/compiling.rst b/doc/source/compiling.rst index 84ea677..a554135 100644 --- a/doc/source/compiling.rst +++ b/doc/source/compiling.rst @@ -205,7 +205,7 @@ related to them. - libMØD: - A C++ compiler with reasonable C++17 support is needed. - - `Boost `__ dev >= 1.73 + - `Boost `__ dev >= 1.76 (use ``-DBOOST_ROOT=`` for non-standard locations). - `GraphCanon `__ >= 0.5. This is fulfilled via a Git submodule (make sure to do @@ -277,13 +277,13 @@ Non-standard Python Installation """""""""""""""""""""""""""""""" Passing ``--with-python=python3`` to ``bootstrap.sh`` should work. -This adds a line similar to "``using python : 3.3 ;``" to +This adds a line similar to "``using python : 3.7 ;``" to ``project-config.jam``. After compilation (running ``b2``) the file ``stage/lib/libboost_python3.so`` should exist. If not, it did not detect Python 3 properly. If Python is installed in a non-standard location, add the a line similar to -"``using python : 3.3 : python3 : /path/to/python/3/installtion/include ;``" to +"``using python : 3.7 : python3 : /path/to/python/3/installtion/include ;``" to ``project-config.jam``, where the last path is the path to the ``include``-folder of the Python-installation. @@ -295,4 +295,4 @@ Before running ``b2`` create the file ``user-config.jam`` in the root of the home dir (see `here `__ for the full documentation). Put a line similar to -"``using gcc : : /path/to/g++-4.8``" in the file. +"``using gcc : : /path/to/g++-10``" in the file. diff --git a/docker/Ubuntu.Dockerfile b/docker/Ubuntu.Dockerfile index 25ded52..84fc6e0 100644 --- a/docker/Ubuntu.Dockerfile +++ b/docker/Ubuntu.Dockerfile @@ -47,7 +47,7 @@ RUN \ # the folder can apparently not be called just 'boost', therefore 'boostDir' WORKDIR /opt/boostDir RUN wget \ - https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.tar.gz \ + https://boostorg.jfrog.io/artifactory/main/release/1.76.0/source/boost_1_76_0.tar.gz \ -O boost.tar.gz RUN \ tar -xf boost.tar.gz --one-top-level=boostSrc --strip-components=1 \ diff --git a/examples/pymod_extension/CMakeLists.txt b/examples/pymod_extension/CMakeLists.txt index 5f207bd..a7175c1 100644 --- a/examples/pymod_extension/CMakeLists.txt +++ b/examples/pymod_extension/CMakeLists.txt @@ -14,7 +14,7 @@ find_package(mod REQUIRED) # Boost.Python # ------------------------------------------------------------------------- set(v 1.64.0) -foreach(PY 3 34 35 36 37 38 39 310 311 312 313 314 315 316 317 318 319) +foreach(PY 3 37 38 39 310 311 312 313 314 315 316 317 318 319) set(lib "python${PY}") find_package(Boost ${v} QUIET COMPONENTS ${lib}) if(Boost_FOUND) @@ -25,7 +25,7 @@ foreach(PY 3 34 35 36 37 38 39 310 311 312 313 314 315 316 317 318 319) endforeach() if(NOT Boost_FOUND) find_package(Boost ${v} REQUIRED COMPONENTS python3) - message(FATAL_ERROR "Could not find Boost.Python for Python 3. Tried 'python' wih suffixes 3, 34, 35, 36, 37, 38, and 39.") + message(FATAL_ERROR "Could not find Boost.Python for Python 3. Tried 'python' wih suffixes 3, 37, 38, 39, and 310 to 319.") endif() From 54c5cefdf0bd3de7889a79a7961a8c6b2ebf75d6 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 26 Nov 2022 13:57:55 +0100 Subject: [PATCH 08/12] DGBuilder, add isActive and dg --- ChangeLog.rst | 2 ++ libs/libmod/src/mod/dg/Builder.cpp | 4 +++ libs/libmod/src/mod/dg/Builder.hpp | 4 +++ libs/pymod/lib/mod/__init__.py | 15 +++++--- libs/pymod/lib/mod/libpymod.pyi | 4 +++ libs/pymod/src/mod/py/dg/Builder.cpp | 36 ++++++++++++------- test/py/dg/040_build_basic.py | 6 ++++ ...rivation.py => 050_build_addDerivation.py} | 0 ...HyperEdge.py => 051_build_addHyperEdge.py} | 0 ...{030_build_apply.py => 060_build_apply.py} | 0 ...Proper.py => 065_build_apply_nonProper.py} | 0 11 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 test/py/dg/040_build_basic.py rename test/py/dg/{020_build_addDerivation.py => 050_build_addDerivation.py} (100%) rename test/py/dg/{021_build_addHyperEdge.py => 051_build_addHyperEdge.py} (100%) rename test/py/dg/{030_build_apply.py => 060_build_apply.py} (100%) rename test/py/dg/{035_build_apply_nonProper.py => 065_build_apply_nonProper.py} (100%) diff --git a/ChangeLog.rst b/ChangeLog.rst index 9b77b4c..b6a83c3 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -71,6 +71,8 @@ New Features See :ref:`cpp-Post`/:ref:`py-Post`. - Added :cpp:func:`graph::Graph::enumerateMonomorphisms`/:py:meth:`Graph.enumerateMonomorphisms`. - Added :cpp:func:`dg::Printer::setImageOverwrite`/:py:meth:`DGPrinter.setImageOverwrite`. +- Added :cpp:func:`dg::Builder::getDG`/:py:attr:`DGBuilder.dg` and + :py:attr:`DGBuilder.isActive`. Bugs Fixed ---------- diff --git a/libs/libmod/src/mod/dg/Builder.cpp b/libs/libmod/src/mod/dg/Builder.cpp index a68dcb5..1072ca1 100644 --- a/libs/libmod/src/mod/dg/Builder.cpp +++ b/libs/libmod/src/mod/dg/Builder.cpp @@ -28,6 +28,10 @@ Builder::Builder(Builder &&other) = default; Builder &Builder::operator=(Builder &&other) = default; Builder::~Builder() = default; +std::shared_ptr Builder::getDG() const { + return p->dg_; +} + bool Builder::isActive() const { return p != nullptr; } diff --git a/libs/libmod/src/mod/dg/Builder.hpp b/libs/libmod/src/mod/dg/Builder.hpp index 3c75048..0db83a1 100644 --- a/libs/libmod/src/mod/dg/Builder.hpp +++ b/libs/libmod/src/mod/dg/Builder.hpp @@ -31,6 +31,10 @@ class MOD_DECL Builder { Builder(Builder &&other); Builder &operator=(Builder &&other); ~Builder(); + // rst: .. function:: std::shared_ptr getDG() const + // rst: + // rst: :returns: the derivation graph this builder can modify. + std::shared_ptr getDG() const; // rst: .. function:: bool isActive() const // rst: // rst: :returns: whether this object is associated with a :cpp:class:`DG`. diff --git a/libs/pymod/lib/mod/__init__.py b/libs/pymod/lib/mod/__init__.py index 62fe050..0d79a2a 100644 --- a/libs/pymod/lib/mod/__init__.py +++ b/libs/pymod/lib/mod/__init__.py @@ -328,22 +328,29 @@ def _DG__getattribute__(self: DG, name: str) -> Any: class DGBuildContextManager: - dg: Optional[DG] _builder: Optional[DGBuilder] def __init__(self, dg: DG) -> None: assert dg is not None - self.dg = dg - self._builder = _DG_build_orig(self.dg) + self._builder = _DG_build_orig(dg) def __enter__(self) -> "DGBuildContextManager": return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: del self._builder - self.dg = None self._builder = None + @property + def dg(self) -> DG: + assert self._builder + return self._builder.dg + + @property + def isActive(self) -> bool: + assert self._builder + return self._builder.isActive + def addDerivation(self, d: Derivations, graphPolicy: IsomorphismPolicy = IsomorphismPolicy.Check) -> DGHyperEdge: assert self._builder diff --git a/libs/pymod/lib/mod/libpymod.pyi b/libs/pymod/lib/mod/libpymod.pyi index 93c9906..36eae48 100644 --- a/libs/pymod/lib/mod/libpymod.pyi +++ b/libs/pymod/lib/mod/libpymod.pyi @@ -180,6 +180,10 @@ class DGBuilder: def __enter__(self) -> DGBuilder: ... def __exit__(self, exc_type, exc_val, exc_tb) -> None: ... + @property + def dg(self) -> DG: ... + @property + def isActive(self) -> bool: ... def addDerivation(self, d: Derivations, graphPolicy: IsomorphismPolicy = ...) -> DGHyperEdge: ... def addHyperEdge(self, e: DGHyperEdge, graphPolicy: IsomorphismPolicy = ...) -> DGHyperEdge: ... def execute(self, strategy: DGStrat, *, verbosity: int=..., ignoreRuleLabelTypes: bool=...) -> DGExecuteResult: ... diff --git a/libs/pymod/src/mod/py/dg/Builder.cpp b/libs/pymod/src/mod/py/dg/Builder.cpp index 6eea411..ac5e226 100644 --- a/libs/pymod/src/mod/py/dg/Builder.cpp +++ b/libs/pymod/src/mod/py/dg/Builder.cpp @@ -40,20 +40,32 @@ void Builder_doExport() { // rst: Otherwise one can manually use ``del`` on the obtained builder to trigger the destruction. // rst: py::class_, boost::noncopyable>("DGBuilder", py::no_init) - // rst: .. method:: addDerivation(d, graphPolicy=IsomorphismPolicy.Check) + // rst: .. attribute:: dg // rst: - // rst: Adds a hyperedge corresponding to the given derivation to the associated :class:`DG`. - // rst: If it already exists, only add the given rules to the edge. + // rst: The derivation graph this builder can modify. // rst: - // rst: :param Derivations d: a derivation to add a hyperedge for. - // rst: :param IsomorphismPolicy graphPolicy: the isomorphism policy for adding the given graphs. - // rst: :returns: the hyperedge corresponding to the given derivation. - // rst: :rtype: DGHyperEdge - // rst: :raises: :class:`LogicError` if ``d.left`` or ``d.right`` is empty. - // rst: :raises: :class:`LogicError` if a ``None``is in ``d.left``, ``d.right``, or ``d.rules``. - // rst: :raises: :class:`LogicError` if ``graphPolicy == IsomorphismPolicy.Check`` and a given graph object - // rst: is different but isomorphic to another given graph object or to a graph object already - // rst: in the internal graph database in the associated derivation graph. + // rst: :type: DG + .add_property("dg", &Builder::getDG) + // rst: .. attribute:: isActive + // rst: + // rst: Whether this object is associated with a :py:class:`DG`. + // rst: + // rst: :type: bool + .add_property("isActive", &Builder::isActive) + // rst: .. method:: addDerivation(d, graphPolicy=IsomorphismPolicy.Check) + // rst: + // rst: Adds a hyperedge corresponding to the given derivation to the associated :class:`DG`. + // rst: If it already exists, only add the given rules to the edge. + // rst: + // rst: :param Derivations d: a derivation to add a hyperedge for. + // rst: :param IsomorphismPolicy graphPolicy: the isomorphism policy for adding the given graphs. + // rst: :returns: the hyperedge corresponding to the given derivation. + // rst: :rtype: DGHyperEdge + // rst: :raises: :class:`LogicError` if ``d.left`` or ``d.right`` is empty. + // rst: :raises: :class:`LogicError` if a ``None``is in ``d.left``, ``d.right``, or ``d.rules``. + // rst: :raises: :class:`LogicError` if ``graphPolicy == IsomorphismPolicy.Check`` and a given graph object + // rst: is different but isomorphic to another given graph object or to a graph object already + // rst: in the internal graph database in the associated derivation graph. .def("addDerivation", static_cast(&Builder::addDerivation)) // rst: .. method:: addHyperEdge(e, graphPolicy=IsomorphismPolicy.Check) // rst: diff --git a/test/py/dg/040_build_basic.py b/test/py/dg/040_build_basic.py new file mode 100644 index 0000000..50deed2 --- /dev/null +++ b/test/py/dg/040_build_basic.py @@ -0,0 +1,6 @@ +include("xx0_helpers.py") + +dg = DG() +b = dg.build() +assert b.dg == dg +assert b.isActive diff --git a/test/py/dg/020_build_addDerivation.py b/test/py/dg/050_build_addDerivation.py similarity index 100% rename from test/py/dg/020_build_addDerivation.py rename to test/py/dg/050_build_addDerivation.py diff --git a/test/py/dg/021_build_addHyperEdge.py b/test/py/dg/051_build_addHyperEdge.py similarity index 100% rename from test/py/dg/021_build_addHyperEdge.py rename to test/py/dg/051_build_addHyperEdge.py diff --git a/test/py/dg/030_build_apply.py b/test/py/dg/060_build_apply.py similarity index 100% rename from test/py/dg/030_build_apply.py rename to test/py/dg/060_build_apply.py diff --git a/test/py/dg/035_build_apply_nonProper.py b/test/py/dg/065_build_apply_nonProper.py similarity index 100% rename from test/py/dg/035_build_apply_nonProper.py rename to test/py/dg/065_build_apply_nonProper.py From b42caecf3cfb92cd85e2ba239c0aac3bbaa661eb Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 26 Nov 2022 14:11:10 +0100 Subject: [PATCH 09/12] DGBuilder, make the actual type in the interface the right name --- libs/pymod/lib/mod/__init__.py | 10 +++++----- libs/pymod/lib/mod/libpymod.pyi | 7 ++----- libs/pymod/src/mod/py/dg/Builder.cpp | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/libs/pymod/lib/mod/__init__.py b/libs/pymod/lib/mod/__init__.py index 0d79a2a..1375006 100644 --- a/libs/pymod/lib/mod/__init__.py +++ b/libs/pymod/lib/mod/__init__.py @@ -227,7 +227,7 @@ def dgDerivations(ders: Iterable[Derivation]) -> DG: dg = DG() with dg.build() as b: for d in ders: - b.addDerivation(d) # type: ignore + b.addDerivation(d) return dg def dgRuleComp(graphs: Iterable[Graph], strat: DGStrat, @@ -327,14 +327,14 @@ def _DG__getattribute__(self: DG, name: str) -> Any: DG.__hash__ = lambda self: self.id # type: ignore -class DGBuildContextManager: - _builder: Optional[DGBuilder] +class DGBuilder: + _builder: Optional[libpymod._DGBuilder] def __init__(self, dg: DG) -> None: assert dg is not None self._builder = _DG_build_orig(dg) - def __enter__(self) -> "DGBuildContextManager": + def __enter__(self) -> "DGBuilder": return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: @@ -381,7 +381,7 @@ def load(self, ruleDatabase: List[Rule], f: str, verbosity: int = 2) -> None: prefixFilename(f), verbosity) _DG_build_orig = DG.build -DG.build = lambda self: DGBuildContextManager(self) # type: ignore +DG.build = lambda self: DGBuilder(self) # type: ignore #---------------------------------------------------------- diff --git a/libs/pymod/lib/mod/libpymod.pyi b/libs/pymod/lib/mod/libpymod.pyi index 36eae48..0d8e923 100644 --- a/libs/pymod/lib/mod/libpymod.pyi +++ b/libs/pymod/lib/mod/libpymod.pyi @@ -170,16 +170,13 @@ class DG: def findEdge(self, sources: List[DGVertex], targets: List[DGVertex]) -> DGHyperEdge: ... @overload def findEdge(self, sourcesGraphs: List[Graph], targetGraphs: List[Graph]) -> DGHyperEdge: ... - def build(self) -> DGBuilder: ... + def build(self): ... def print(self, printer: DGPrinter=..., data: Optional[DGPrintData]=...) -> Tuple[str, str]: ... @staticmethod def load(graphDatabase: List[Graph], ruleDatabase: List[Rule], file: str, graphPolicy: IsomorphismPolicy=..., verbosity: int=...) -> DG: ... -class DGBuilder: - def __enter__(self) -> DGBuilder: ... - def __exit__(self, exc_type, exc_val, exc_tb) -> None: ... - +class _DGBuilder: @property def dg(self) -> DG: ... @property diff --git a/libs/pymod/src/mod/py/dg/Builder.cpp b/libs/pymod/src/mod/py/dg/Builder.cpp index ac5e226..e6fe2e6 100644 --- a/libs/pymod/src/mod/py/dg/Builder.cpp +++ b/libs/pymod/src/mod/py/dg/Builder.cpp @@ -39,7 +39,7 @@ void Builder_doExport() { // rst: // rst: Otherwise one can manually use ``del`` on the obtained builder to trigger the destruction. // rst: - py::class_, boost::noncopyable>("DGBuilder", py::no_init) + py::class_, boost::noncopyable>("_DGBuilder", py::no_init) // rst: .. attribute:: dg // rst: // rst: The derivation graph this builder can modify. From b034ae428fa32b463585ef40c180f4f99a9f902c Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 26 Nov 2022 17:41:45 +0100 Subject: [PATCH 10/12] Conda, add Python 3.11, remove Boost < 1.78 --- conda/conda_build_config.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/conda/conda_build_config.yaml b/conda/conda_build_config.yaml index 295c90a..2bab2cc 100644 --- a/conda/conda_build_config.yaml +++ b/conda/conda_build_config.yaml @@ -1,11 +1,13 @@ +# Note: conda-forge has global pinning, e.g., of boost, so make sure we include +# variations for those verions. +# https://github.com/conda-forge/conda-forge-pinning-feedstock/tree/main/recipe python: - 3.7 - 3.8 - 3.9 - 3.10 + - 3.11 boost: - - 1.76 - - 1.77 - 1.78 - 1.80 pin_run_as_build: From 2c901d7bb48a0a24a49fe2af9a5cc4d85f55af8c Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 27 Nov 2022 17:12:55 +0100 Subject: [PATCH 11/12] pip, don't use --user as it automatically does that With --user and then sudo make install it would be installed in the home dir of root, which doesn't make sense. Newer versions of pip (since early 2020) falls back to user-install when the global site is not writeable. --- libs/pymod/CMakeLists.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/libs/pymod/CMakeLists.txt b/libs/pymod/CMakeLists.txt index 39ab30a..446f159 100644 --- a/libs/pymod/CMakeLists.txt +++ b/libs/pymod/CMakeLists.txt @@ -72,14 +72,7 @@ configure_file(redirector/pyproject.toml ${exportDir}/pyproject.toml @ONLY) configure_file(redirector/setup.cfg.in ${exportDir}/setup.cfg @ONLY) configure_file(redirector/mod/__init__.py.in ${exportDir}/mod/__init__.py @ONLY) if(${BUILD_PY_MOD_PIP}) - # https://www.scivision.dev/cmake-install-python-package/ - # detect virtualenv and set Pip args accordingly - if(DEFINED ENV{VIRTUAL_ENV} OR DEFINED ENV{CONDA_PREFIX}) - set(_pip_args) - else() - set(_pip_args "--user") - endif() - install(CODE "execute_process(COMMAND ${Python3_EXECUTABLE} -m pip install . ${_pip_args} WORKING_DIRECTORY ${exportDir})" + install(CODE "execute_process(COMMAND ${Python3_EXECUTABLE} -m pip install . WORKING_DIRECTORY ${exportDir})" COMPONENT pymod_run) endif() From d370b44997460f2f551b3561984662fd5d87f3f2 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 28 Nov 2022 18:09:55 +0100 Subject: [PATCH 12/12] Set release date --- ChangeLog.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChangeLog.rst b/ChangeLog.rst index b6a83c3..e9a7791 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -4,8 +4,8 @@ Changes ####### -develop -======= +v0.14.0 (2022-11-29) +==================== Incompatible Changes --------------------