pymodrev 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pymodrev/__init__.py +0 -0
- pymodrev/__main__.py +5 -0
- pymodrev/asp_rules/async.lp +28 -0
- pymodrev/asp_rules/base.lp +17 -0
- pymodrev/asp_rules/complete.lp +15 -0
- pymodrev/asp_rules/steady_state.lp +30 -0
- pymodrev/asp_rules/sync.lp +20 -0
- pymodrev/asp_rules/time_series.lp +33 -0
- pymodrev/cli.py +171 -0
- pymodrev/configuration.py +87 -0
- pymodrev/network/__init__.py +0 -0
- pymodrev/network/edge.py +70 -0
- pymodrev/network/exceptions.py +27 -0
- pymodrev/network/function.py +455 -0
- pymodrev/network/inconsistency_solution.py +420 -0
- pymodrev/network/inconsistent_node.py +168 -0
- pymodrev/network/network.py +182 -0
- pymodrev/network/node.py +49 -0
- pymodrev/network/observation.py +31 -0
- pymodrev/network/repair_set.py +146 -0
- pymodrev/parsers/__init__.py +0 -0
- pymodrev/parsers/boolean_expression.py +563 -0
- pymodrev/parsers/network_parser.py +32 -0
- pymodrev/parsers/parser_asp.py +236 -0
- pymodrev/parsers/parser_bnet.py +154 -0
- pymodrev/parsers/parser_factory.py +31 -0
- pymodrev/parsers/parser_ginml.py +348 -0
- pymodrev/repair/__init__.py +1 -0
- pymodrev/repair/consistency.py +181 -0
- pymodrev/repair/engine.py +118 -0
- pymodrev/repair/function_search.py +275 -0
- pymodrev/repair/repair.py +52 -0
- pymodrev/repair/topology.py +335 -0
- pymodrev/updaters/__init__.py +0 -0
- pymodrev/updaters/async_updater.py +154 -0
- pymodrev/updaters/complete_updater.py +152 -0
- pymodrev/updaters/steady_state_updater.py +113 -0
- pymodrev/updaters/sync_updater.py +140 -0
- pymodrev/updaters/time_series_updater.py +57 -0
- pymodrev/updaters/updater.py +173 -0
- pymodrev-0.1.0.dist-info/METADATA +156 -0
- pymodrev-0.1.0.dist-info/RECORD +45 -0
- pymodrev-0.1.0.dist-info/WHEEL +5 -0
- pymodrev-0.1.0.dist-info/entry_points.txt +2 -0
- pymodrev-0.1.0.dist-info/top_level.txt +1 -0
pymodrev/__init__.py
ADDED
|
File without changes
|
pymodrev/__main__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
%only one node updates at each time
|
|
2
|
+
1{update(P,T,V):vertex(V)}1 :- exp(P), time(P,T), time(P,T+1).
|
|
3
|
+
|
|
4
|
+
vlabel(P,T+1,V,1) :- update(P,T,V), 1{noneNegative(P,T,V,Id):functionOr(V,Id)}, vertex(V), exp(P), not r_part(V), not topologicalerror(V), time(P,T+1).
|
|
5
|
+
vlabel(P,T+1,V,0) :- update(P,T,V), {noneNegative(P,T,V,Id):functionOr(V,Id)}0, vertex(V), exp(P), functionOr(V,_), not r_gen(V), not topologicalerror(V), time(P,T+1).
|
|
6
|
+
|
|
7
|
+
%keep all others
|
|
8
|
+
vlabel(P,T+1,V,S) :- not update(P,T,V), vlabel(P,T,V,S), time(P,T+1).
|
|
9
|
+
|
|
10
|
+
%prevent updates that do not change the state of the network
|
|
11
|
+
:- update(P,T,V), vlabel(P,T,V,S), vlabel(P,T+1,V,S).
|
|
12
|
+
|
|
13
|
+
%there cannot exists 2 different times with the same update, which function's inputs are the same and the output is different
|
|
14
|
+
%any function applied in two time points with the same inputs cannot have different outputs
|
|
15
|
+
%ignore this to consider topological repairs
|
|
16
|
+
topologicalerror(V) :- time(P1,T1), time(P2,T2), T1 != T2, time(P1,T1+1), time(P2,T2+1), update(P1, T1, V), update(P2, T2, V), {vlabel(P1,T1,V1,S1) : vlabel(P2,T2,V1,S2), functionAnd(V,Id, V1), S1!=S2}0, vlabel(P1,T1+1,V,S3), vlabel(P2,T2+1,V,S4), S3 != S4, not input(V).
|
|
17
|
+
topologicalerror(V) :- time(P1,T), time(P2,T), time(P1,T+1), time(P2,T+1), update(P1, T, V), update(P2, T, V), P1 != P2, {vlabel(P1,T,V1,S1) : vlabel(P2,T,V1,S2), S1!=S2, functionAnd(V,Id, V1)}0, vlabel(P1,T+1,V,S3), vlabel(P2,T+1,V,S4), S3 != S4, not input(V).
|
|
18
|
+
|
|
19
|
+
repair(V) :- topologicalerror(V).
|
|
20
|
+
|
|
21
|
+
#minimize {1@2,top,V : topologicalerror(V)}.
|
|
22
|
+
|
|
23
|
+
#show update/3.
|
|
24
|
+
|
|
25
|
+
% show inconsistent nodes per exeperiment
|
|
26
|
+
inc(P,V) :- vlabel(P,T+1,V,0), 1{noneNegative(P,T,V,Id):functionOr(V,Id)}, vertex(V), exp(P), r_part(V), not topologicalerror(V), time(P,T), time(P,T+1), update(P,T,V).
|
|
27
|
+
inc(P,V) :- vlabel(P,T+1,V,1), {noneNegative (P,T,V,Id):functionOr(V,Id)}0, vertex(V), exp(P), functionOr(V,_), r_gen(V), not topologicalerror(V), time(P,T), time(P,T+1), update(P,T,V).
|
|
28
|
+
#show inc/2.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
sign(0;1).
|
|
2
|
+
complement(T,S) :- sign(S),sign(T),T!=S.
|
|
3
|
+
|
|
4
|
+
%inferring vertex from edges
|
|
5
|
+
vertex(V) :- edge(V,_,_).
|
|
6
|
+
vertex(V) :- edge(_,V,_).
|
|
7
|
+
|
|
8
|
+
% each vertex may or may not be in need of repair
|
|
9
|
+
{r_gen(V)} :- vertex(V), not fixed(V).
|
|
10
|
+
{r_part(V)} :- vertex(V), not fixed(V).
|
|
11
|
+
|
|
12
|
+
repair(V) :- r_gen(V).
|
|
13
|
+
repair(V) :- r_part(V).
|
|
14
|
+
|
|
15
|
+
#show repair/1.
|
|
16
|
+
#show r_gen/1.
|
|
17
|
+
#show r_part/1.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
%at least one node updates at each time
|
|
2
|
+
1{update(P,T,V):vertex(V)} :- exp(P), time(P,T), time(P,T+1).
|
|
3
|
+
|
|
4
|
+
vlabel(P,T+1,V,1) :- update(P,T,V), 1{noneNegative(P,T,V,Id):functionOr(V,Id)}, vertex(V), exp(P), not r_part(V), time(P,T+1).
|
|
5
|
+
vlabel(P,T+1,V,0) :- update(P,T,V), {noneNegative(P,T,V,Id):functionOr(V,Id)}0, vertex(V), exp(P), functionOr(V,_), not r_gen(V), time(P,T+1).
|
|
6
|
+
|
|
7
|
+
%keep all others
|
|
8
|
+
vlabel(P,T+1,V,S) :- not update(P,T,V), vlabel(P,T,V,S), time(P,T+1).
|
|
9
|
+
|
|
10
|
+
#show update/3.
|
|
11
|
+
|
|
12
|
+
% show inconsistent nodes per exeperiment
|
|
13
|
+
inc(P,V) :- vlabel(P,T+1,V,0), 1{noneNegative(P,T,V,Id):functionOr(V,Id)}, vertex(V), exp(P), r_part(V), time(P,T), time(P,T+1), update(P,T,V).
|
|
14
|
+
inc(P,V) :- vlabel(P,T+1,V,1), {noneNegative (P,T,V,Id):functionOr(V,Id)}0, vertex(V), exp(P), functionOr(V,_), r_gen(V), time(P,T), time(P,T+1), update(P,T,V).
|
|
15
|
+
#show inc/2.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
%guarantee of stable state observation
|
|
2
|
+
ss(P) :- exp(P), not time(P,_).
|
|
3
|
+
|
|
4
|
+
%generate
|
|
5
|
+
1{vlabel(P,V,S):sign(S)}1 :- vertex(V), ss(P).
|
|
6
|
+
|
|
7
|
+
:-vlabel(P,V,S1), obs_vlabel(P,V,S2),complement(S1,S2).
|
|
8
|
+
|
|
9
|
+
% functions
|
|
10
|
+
% one positive or negative contribution in a clause
|
|
11
|
+
onePositive(P,V,Id) :- functionAnd(V,Id, V2), edge(V2,V,S), vlabel(P,V2,S), ss(P).
|
|
12
|
+
oneNegative(P,V,Id) :- functionAnd(V,Id, V2), edge(V2,V,S), vlabel(P,V2,T), complement(S,T), ss(P).
|
|
13
|
+
|
|
14
|
+
% none positive or none negative on a clause
|
|
15
|
+
% nonePositive(V,Id) :- not onePositive(V,Id), oneNegative(V,Id).
|
|
16
|
+
noneNegative(P,V,Id) :- onePositive(P,V,Id), not oneNegative(P,V,Id).
|
|
17
|
+
|
|
18
|
+
vlabel(P,V,1) :- 1{noneNegative(P,V,Id):functionOr(V,Id)}, vertex(V), ss(P), not r_part(V).
|
|
19
|
+
vlabel(P,V,0) :- {noneNegative(P,V,Id):functionOr(V,Id)}0, vertex(V), ss(P), functionOr(V,_), not r_gen(V).
|
|
20
|
+
|
|
21
|
+
#minimize {1,V : repair(V)}.
|
|
22
|
+
#minimize {1,g,V : r_gen(V)}.
|
|
23
|
+
#minimize {1,p,V : r_part(V)}.
|
|
24
|
+
|
|
25
|
+
#show vlabel/3.
|
|
26
|
+
|
|
27
|
+
% show inconsistent nodes per experiments
|
|
28
|
+
inc(P,V) :- vlabel(P,V,0), 1{noneNegative(P,V,Id):functionOr(V,Id)}, vertex(V), ss(P), r_part(V).
|
|
29
|
+
inc(P,V) :- vlabel(P,V,1), {noneNegative(P,V ,Id):functionOr(V,Id)}0, vertex(V), ss(P), functionOr(V,_), r_gen(V).
|
|
30
|
+
#show inc/2.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
vlabel(P,T+1,V,1) :- 1{noneNegative(P,T,V,Id):functionOr(V,Id)}, vertex(V), exp(P), not r_part(V), not topologicalerror(V), time(P,T), time(P,T+1).
|
|
2
|
+
vlabel(P,T+1,V,0) :- {noneNegative(P,T,V,Id):functionOr(V,Id)}0, vertex(V), exp(P), functionOr(V,_), not r_gen(V), not topologicalerror(V), time(P,T), time(P,T+1).
|
|
3
|
+
|
|
4
|
+
%there cannot exists 2 different times with the same update, which function's inputs are the same and the output is different
|
|
5
|
+
%any function applied in two time points with the same inputs cannot have different outputs
|
|
6
|
+
%ignore this to consider topological repairs
|
|
7
|
+
topologicalerror(V) :- time(P1,T1), time(P2,T2), T1 != T2, time(P1,T1+1), time(P2,T2+1), vertex(V), {vlabel(P1,T1,V1,S1): vlabel(P2,T2,V1,S2), S1!=S2, functionAnd(V,Id, V1)}0, vlabel(P1,T1+1,V,S3), vlabel(P2,T2+1,V,S4), S3 != S4, not input(V).
|
|
8
|
+
topologicalerror(V) :- time(P1,T), time(P2,T), time(P1,T+1), time(P2,T+1), exp(P1), exp(P2), P1 != P2, vertex(V), {vlabel(P1,T,V1,S1): vlabel(P2,T,V1,S2), S1!=S2, functionAnd(V,Id, V1)}0, vlabel(P1,T+1,V,S3), vlabel(P2,T+1,V,S4), S3 != S4, not input(V).
|
|
9
|
+
|
|
10
|
+
repair(V) :- topologicalerror(V).
|
|
11
|
+
|
|
12
|
+
#minimize {1@2,top,V : topologicalerror(V)}.
|
|
13
|
+
#show topologicalerror/1.
|
|
14
|
+
|
|
15
|
+
% show inconsistent nodes per exeperiment
|
|
16
|
+
inc(P,V) :- vlabel(P,T+1,V,0), 1{noneNegative(P,T,V,Id):functionOr(V,Id)}, vertex(V), exp(P), r_part(V), not topologicalerror(V), time(P,T), time(P,T+1).
|
|
17
|
+
inc(P,V) :- vlabel(P,T+1,V,1), {noneNegative (P,T,V,Id):functionOr(V,Id)}0, vertex(V), exp(P), functionOr(V,_), r_gen(V), not topologicalerror(V), time(P,T), time(P,T+1).
|
|
18
|
+
incT(P1,P2,V) :- time(P1,T1), time(P2,T2), T1!= T2, time(P1,T1+1), time(P2,T2+1), vertex(V), {vlabel( P1,T1,V1,S1): vlabel(P2,T2,V1,S2), S1!=S2, functionAnd(V, Id, V1)}0, vlabel(P1,T1+1,V,S3), vlabel(P2,T2+1,V,S4), S3 != S4, not input(V), P1 <= P2.
|
|
19
|
+
#show inc/2.
|
|
20
|
+
#show incT/3.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
%time control per experiment
|
|
2
|
+
time(P,T) :- exp(P), obs_vlabel(P,T,_,_).
|
|
3
|
+
time(P,T) :- time(P,T+1), T+1 > 0.
|
|
4
|
+
|
|
5
|
+
%generate
|
|
6
|
+
1{vlabel(P,T,V,S):sign(S)}1:-vertex(V), exp(P), time(P,T).
|
|
7
|
+
|
|
8
|
+
:-vlabel(P,T,V,S1), obs_vlabel(P,T,V,S2), complement(S1,S2).
|
|
9
|
+
|
|
10
|
+
% functions
|
|
11
|
+
%one positive or negative contribution in a clause
|
|
12
|
+
onePositive(P,T,V,Id) :- functionAnd(V,Id, V2), edge(V2,V,S), vlabel(P,T,V2,S), exp(P), time(P,T).
|
|
13
|
+
oneNegative(P,T,V,Id) :- functionAnd(V,Id, V2), edge(V2,V,S1), vlabel(P,T,V2,S2), complement(S1,S2), exp(P), time(P,T).
|
|
14
|
+
|
|
15
|
+
% none negative on a clause
|
|
16
|
+
noneNegative(P,T,V,Id) :- onePositive(P,T,V,Id), not oneNegative(P,T,V,Id).
|
|
17
|
+
|
|
18
|
+
%input nodes
|
|
19
|
+
input(V) :- not functionOr(V,_), vertex(V).
|
|
20
|
+
|
|
21
|
+
vlabel(P,T+1,V,0) :- input(V), vlabel(P,T,V,0), exp(P), time(P,T+1), not r_gen(V).
|
|
22
|
+
vlabel(P,T+1,V,1) :- input(V), vlabel(P,T,V,1), exp(P), time(P,T+1), not r_part(V).
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
#minimize {1@1,V : repair(V)}.
|
|
26
|
+
#minimize {1@1,g,V : r_gen(V)}.
|
|
27
|
+
#minimize {1@1,p,V : r_part(V)}.
|
|
28
|
+
#show vlabel/4.
|
|
29
|
+
|
|
30
|
+
% show inconsistent nodes per exeperiment
|
|
31
|
+
inc(P,V) :- vlabel(P,T+1,V,1), input(V), vlabel(P,T,V,0), exp(P), time(P,T+1), r_gen(V).
|
|
32
|
+
inc(P,V) :- vlabel(P,T+1,V,0), input(V), vlabel(P,T,V,1), exp(P), time(P,T+1), r_part(V).
|
|
33
|
+
#show inc/2.
|
pymodrev/cli.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This script analyzes a given network model to determine its consistency.
|
|
3
|
+
If inconsistencies are found, it attempts to compute the minimal set of repair
|
|
4
|
+
operations needed to restore consistency.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import argparse
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
from importlib import util
|
|
13
|
+
from pymodrev.network.network import Network
|
|
14
|
+
from pymodrev.parsers.parser_factory import get_parser
|
|
15
|
+
from pymodrev.configuration import config
|
|
16
|
+
from pymodrev.repair.engine import model_revision
|
|
17
|
+
from pymodrev.repair.consistency import check_consistency
|
|
18
|
+
from pymodrev.repair.engine import print_consistency
|
|
19
|
+
from pymodrev.repair.repair import apply_repair
|
|
20
|
+
|
|
21
|
+
# Configure logger
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def process_arguments(network: Network) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Process command-line arguments and configure network accordingly.
|
|
28
|
+
"""
|
|
29
|
+
parser = argparse.ArgumentParser(
|
|
30
|
+
description="Model Revision program. Given a model and a set of observations, it determines if the model is consistent. If not, it computes all the minimum number of repair operations in order to render the model consistent.",
|
|
31
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
32
|
+
epilog=f"Version: {config.version}"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
parser.add_argument("-m", "--model",
|
|
36
|
+
required=True, help="Input model file.")
|
|
37
|
+
parser.add_argument("-obs", "--observations", nargs='+',
|
|
38
|
+
required=True, metavar=('OBS', 'UPDATER'),
|
|
39
|
+
help="""List of observation files and updater pairs.
|
|
40
|
+
Each observation must be followed by its updater type.
|
|
41
|
+
Example: -obs obs1.lp asyncupdater obs2.lp syncupdater""")
|
|
42
|
+
parser.add_argument('-t', '--task', choices=['c', 'r', 'm'], required=True,
|
|
43
|
+
help="""Specify the task to perform (default=r):
|
|
44
|
+
c - check for consistency
|
|
45
|
+
r - get repairs
|
|
46
|
+
m - get repaired models""")
|
|
47
|
+
parser.add_argument("--exhaustive-search", action="store_true",
|
|
48
|
+
help="Force exhaustive search of function repair operations (default=false).")
|
|
49
|
+
parser.add_argument("--sub-opt", action="store_true",
|
|
50
|
+
help="Show sub-optimal solutions found (default=false).")
|
|
51
|
+
parser.add_argument("--all-opt", action="store_true",
|
|
52
|
+
help="""Computes all optimal solutions (default=true).
|
|
53
|
+
Stops at first optimal solution if false.""")
|
|
54
|
+
parser.add_argument("-v", "--verbose", type=int, choices=[0, 1, 2], default=2,
|
|
55
|
+
help="""Specify output verbose level (default=2):
|
|
56
|
+
0 - compact format
|
|
57
|
+
1 - json format
|
|
58
|
+
2 - human-readable format""")
|
|
59
|
+
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug mode.")
|
|
60
|
+
|
|
61
|
+
args = parser.parse_args()
|
|
62
|
+
|
|
63
|
+
# Apply arguments to config and network
|
|
64
|
+
network.input_file_network = args.model
|
|
65
|
+
config.task = args.task
|
|
66
|
+
config.force_optimum = args.exhaustive_search
|
|
67
|
+
config.show_solution_for_each_inconsistency = args.sub_opt
|
|
68
|
+
config.verbose = args.verbose
|
|
69
|
+
config.debug = args.debug
|
|
70
|
+
|
|
71
|
+
# Activate debug mode
|
|
72
|
+
if args.debug:
|
|
73
|
+
logging.basicConfig(level=logging.DEBUG, format='%(name)s - %(levelname)s: %(message)s')
|
|
74
|
+
|
|
75
|
+
obs_args = args.observations
|
|
76
|
+
if len(obs_args) % 2 != 0:
|
|
77
|
+
parser.error("Expected an even number of arguments for -obs (pairs of obs_file and updater_name)")
|
|
78
|
+
|
|
79
|
+
# Load updaters dynamically from updaters/ directory
|
|
80
|
+
updaters = {}
|
|
81
|
+
updater_dir = os.path.join(os.path.dirname(__file__), "updaters")
|
|
82
|
+
for filename in os.listdir(updater_dir):
|
|
83
|
+
if filename.endswith(".py") and filename not in ("__init__.py", "updater.py", "time_series_updater.py"):
|
|
84
|
+
module_name = os.path.splitext(filename)[0]
|
|
85
|
+
class_name = "".join(word.capitalize() for word in module_name.split("_"))
|
|
86
|
+
file_path = os.path.join(updater_dir, filename)
|
|
87
|
+
# Load module dynamically
|
|
88
|
+
spec = util.spec_from_file_location(module_name, file_path)
|
|
89
|
+
module = util.module_from_spec(spec)
|
|
90
|
+
spec.loader.exec_module(module)
|
|
91
|
+
# Get class from module
|
|
92
|
+
updater_class = getattr(module, class_name)()
|
|
93
|
+
# Add class to updaters dictionary
|
|
94
|
+
updaters[module_name.replace('_','')] = updater_class
|
|
95
|
+
|
|
96
|
+
for i in range(0, len(obs_args), 2):
|
|
97
|
+
obs_path = obs_args[i]
|
|
98
|
+
updater_name = obs_args[i+1]
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
network.add_observation_file(obs_path)
|
|
102
|
+
if updater_name not in updaters:
|
|
103
|
+
raise Exception(f"Updater '{updater_name}' not found in updaters directory")
|
|
104
|
+
network.add_updater(updaters[updater_name])
|
|
105
|
+
if updater_name == 'steadystateupdater':
|
|
106
|
+
network.has_ss_obs = True
|
|
107
|
+
else:
|
|
108
|
+
network.has_ts_obs = True
|
|
109
|
+
except Exception as e:
|
|
110
|
+
parser.error(str(e))
|
|
111
|
+
|
|
112
|
+
def main():
|
|
113
|
+
network = Network()
|
|
114
|
+
process_arguments(network)
|
|
115
|
+
|
|
116
|
+
# Delegate parsing to the correct reader based on file extension
|
|
117
|
+
try:
|
|
118
|
+
parser = get_parser(network.input_file_network)
|
|
119
|
+
parse = parser.read(network, network.input_file_network)
|
|
120
|
+
except ValueError as e:
|
|
121
|
+
logger.error(str(e))
|
|
122
|
+
sys.exit(1)
|
|
123
|
+
|
|
124
|
+
if parse < 1 and not config.ignore_warnings:
|
|
125
|
+
logger.error('Model definition with errors. Check documentation for input definition details.')
|
|
126
|
+
sys.exit(1)
|
|
127
|
+
|
|
128
|
+
# Check consistency
|
|
129
|
+
f_inconsistencies, optimization = check_consistency(network)
|
|
130
|
+
if config.task == 'c' or optimization == 0:
|
|
131
|
+
print_consistency(f_inconsistencies, optimization)
|
|
132
|
+
sys.exit(0)
|
|
133
|
+
|
|
134
|
+
# Model revision
|
|
135
|
+
repairs2apply = model_revision(network, f_inconsistencies, optimization)
|
|
136
|
+
if config.task == 'm':
|
|
137
|
+
import copy
|
|
138
|
+
import itertools
|
|
139
|
+
|
|
140
|
+
# 1. Collect all possible model combinations (Cartesian product of repair sets per solution)
|
|
141
|
+
all_models_to_save = []
|
|
142
|
+
for repair_sol in repairs2apply:
|
|
143
|
+
nodes_with_repairs = []
|
|
144
|
+
# Sort node IDs for deterministic output order
|
|
145
|
+
for node_id in sorted(repair_sol.inconsistent_nodes.keys()):
|
|
146
|
+
i_node = repair_sol.inconsistent_nodes[node_id]
|
|
147
|
+
if i_node.repair_sets:
|
|
148
|
+
node_options = [(node_id, rs) for rs in i_node.repair_sets]
|
|
149
|
+
nodes_with_repairs.append(node_options)
|
|
150
|
+
|
|
151
|
+
# Cartesian product of options across all nodes for THIS solution
|
|
152
|
+
for combination in itertools.product(*nodes_with_repairs):
|
|
153
|
+
node_repair_map = dict(combination)
|
|
154
|
+
all_models_to_save.append((repair_sol, node_repair_map))
|
|
155
|
+
|
|
156
|
+
# 2. Apply repairs and write files
|
|
157
|
+
total_models = len(all_models_to_save)
|
|
158
|
+
if total_models == 0:
|
|
159
|
+
logger.info("No repaired models to generate.")
|
|
160
|
+
else:
|
|
161
|
+
padding = len(str(total_models))
|
|
162
|
+
prefix, ext = os.path.splitext(network.input_file_network)
|
|
163
|
+
for i, (repair_sol, node_repair_map) in enumerate(all_models_to_save):
|
|
164
|
+
newNetwork = copy.deepcopy(network)
|
|
165
|
+
apply_repair(newNetwork, repair_sol, node_repair_map)
|
|
166
|
+
filename = f"{prefix}_{str(i+1).zfill(padding)}{ext}"
|
|
167
|
+
parser.write(newNetwork, filename)
|
|
168
|
+
print(f"Generated repaired model: {filename}")
|
|
169
|
+
|
|
170
|
+
if __name__ == '__main__':
|
|
171
|
+
main()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines enumerations and configuration settings for handling
|
|
3
|
+
inconsistencies and update types in a network analysis system.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
class Inconsistencies(Enum):
|
|
10
|
+
"""
|
|
11
|
+
Enumeration representing different levels of inconsistencies in the system.
|
|
12
|
+
|
|
13
|
+
Attributes:
|
|
14
|
+
CONSISTENT (int): No inconsistency detected.
|
|
15
|
+
SINGLE_INC_GEN (int): A general single inconsistency.
|
|
16
|
+
SINGLE_INC_PART (int): A partial single inconsistency.
|
|
17
|
+
DOUBLE_INC (int): A double inconsistency.
|
|
18
|
+
"""
|
|
19
|
+
CONSISTENT = (0, "No inconsistency detected")
|
|
20
|
+
SINGLE_INC_GEN = (1, "General single inconsistency")
|
|
21
|
+
SINGLE_INC_PART = (2, "Partial single inconsistency")
|
|
22
|
+
DOUBLE_INC = (3, "Double inconsistency")
|
|
23
|
+
|
|
24
|
+
def __init__(self, int_val, description):
|
|
25
|
+
self._value_ = int_val
|
|
26
|
+
self.description = description
|
|
27
|
+
def __str__(self):
|
|
28
|
+
return f"{self.name} ({self.value}): {self.description}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class UpdateType(Enum):
|
|
32
|
+
"""
|
|
33
|
+
Enumeration representing the types of update strategies that can be used.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
ASYNC (int): Asynchronous update strategy.
|
|
37
|
+
SYNC (int): Synchronous update strategy.
|
|
38
|
+
MASYNC (int): Mixed asynchronous update strategy.
|
|
39
|
+
"""
|
|
40
|
+
ASYNC = (0, "Asynchronous update strategy")
|
|
41
|
+
SYNC = (1, "Synchronous update strategy")
|
|
42
|
+
MASYNC = (2, "Mixed asynchronous update strategy")
|
|
43
|
+
|
|
44
|
+
def __init__(self, int_val, description):
|
|
45
|
+
self._value_ = int_val
|
|
46
|
+
self.description = description
|
|
47
|
+
def __str__(self):
|
|
48
|
+
return f"{self.name} ({self.value}): {self.description}"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
import importlib.metadata
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
_package_version = importlib.metadata.version('pymodrev')
|
|
55
|
+
except Exception:
|
|
56
|
+
_package_version = '1.0.0-dev'
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class Configuration:
|
|
60
|
+
"""Class representing the configuration settings for the system"""
|
|
61
|
+
name: str = 'pyModRev'
|
|
62
|
+
version: str = _package_version
|
|
63
|
+
task: str = 'r' # default is show the repairs
|
|
64
|
+
verbose: int = 2 # default is human-readable format
|
|
65
|
+
update: UpdateType = UpdateType.ASYNC # Setting the update type to ASYNC
|
|
66
|
+
debug: bool = False
|
|
67
|
+
check_asp: bool = True # Use ASP consistency check program
|
|
68
|
+
function_asp: bool = True # Use ASP function program
|
|
69
|
+
all_opt: bool = True # Show one or more solutions
|
|
70
|
+
labelling: bool = False
|
|
71
|
+
multiple_profiles: bool = True
|
|
72
|
+
compare_level_function: bool = True
|
|
73
|
+
exact_middle_function_determination: bool = True
|
|
74
|
+
ignore_warnings: bool = False
|
|
75
|
+
force_optimum: bool = False
|
|
76
|
+
show_solution_for_each_inconsistency: bool = False # Show best solution for each consistency check even if it is not globally optimum
|
|
77
|
+
show_all_functions: bool = True # Show all function repairs for a given node
|
|
78
|
+
check_consistency: bool = False # Just check the consistency of the model and return
|
|
79
|
+
|
|
80
|
+
def __getitem__(self, key):
|
|
81
|
+
return getattr(self, key)
|
|
82
|
+
|
|
83
|
+
def __setitem__(self, key, value):
|
|
84
|
+
setattr(self, key, value)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
config = Configuration()
|
|
File without changes
|
pymodrev/network/edge.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines the Edge class, which represents a connection between two
|
|
3
|
+
nodes in a network.
|
|
4
|
+
The Edge class provides methods to manage and query the properties of the edge,
|
|
5
|
+
such as its start node, end node, sign, and whether it is fixed.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pymodrev.network.node import Node
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Edge:
|
|
12
|
+
"""
|
|
13
|
+
Represents an edge in a network, connecting two nodes with a specific sign.
|
|
14
|
+
Provides methods to manage and query the edge's properties.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, start_node: Node, end_node: Node, sign: int) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Initializes an edge with a start node, end node, and sign.
|
|
20
|
+
"""
|
|
21
|
+
self._start_node = start_node
|
|
22
|
+
self._end_node = end_node
|
|
23
|
+
self._sign = sign
|
|
24
|
+
self._fixed = False
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def start_node(self) -> Node:
|
|
28
|
+
"""Returns the start node of the edge."""
|
|
29
|
+
return self._start_node
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def end_node(self) -> Node:
|
|
33
|
+
"""Returns the end node of the edge."""
|
|
34
|
+
return self._end_node
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def sign(self) -> int:
|
|
38
|
+
"""Returns the sign of the edge."""
|
|
39
|
+
return self._sign
|
|
40
|
+
|
|
41
|
+
@sign.setter
|
|
42
|
+
def sign(self, value: int):
|
|
43
|
+
"""Sets the sign of the edge."""
|
|
44
|
+
self._sign = value
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def fixed(self) -> bool:
|
|
48
|
+
"""Returns whether the edge is fixed."""
|
|
49
|
+
return self._fixed
|
|
50
|
+
|
|
51
|
+
@fixed.setter
|
|
52
|
+
def fixed(self, value: bool):
|
|
53
|
+
"""Sets whether the edge is fixed."""
|
|
54
|
+
self._fixed = value
|
|
55
|
+
|
|
56
|
+
def flip_sign(self) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Flips the sign of the edge (from 0 to 1 or from 1 to 0).
|
|
59
|
+
"""
|
|
60
|
+
self.sign = 1 if self.sign == 0 else 0
|
|
61
|
+
|
|
62
|
+
def __eq__(self, other) -> bool:
|
|
63
|
+
if not isinstance(other, Edge):
|
|
64
|
+
return False
|
|
65
|
+
return self.start_node == other.start_node and \
|
|
66
|
+
self.end_node == other.end_node and \
|
|
67
|
+
self.sign == other.sign
|
|
68
|
+
|
|
69
|
+
def __hash__(self) -> int:
|
|
70
|
+
return hash((self.start_node, self.end_node, self.sign))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module defines custom exception classes for the pyModRev system.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
class PyModRevError(Exception):
|
|
6
|
+
"""Base class for exceptions in this module."""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class NetworkError(PyModRevError):
|
|
10
|
+
"""Exception raised for errors related to network structure."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
class EdgeNotFoundError(NetworkError):
|
|
14
|
+
"""Exception raised when an edge is not found in the network."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class ParseError(PyModRevError):
|
|
18
|
+
"""Exception raised for errors during network or observation file parsing."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
class SolverError(PyModRevError):
|
|
22
|
+
"""Exception raised when the ASP solver encounters an error or impossibility."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
class ConfigurationError(PyModRevError):
|
|
26
|
+
"""Exception raised for invalid configurations or command-line arguments."""
|
|
27
|
+
pass
|