From d85f33903d59d18a2966ff9e092bf4a644a7ac40 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Tue, 16 Apr 2024 16:06:53 +0200 Subject: [PATCH 1/6] tests for observables --- Testing/objects_testing.py | 2 ++ Testing/parsing/test_observables.py | 49 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 Testing/parsing/test_observables.py diff --git a/Testing/objects_testing.py b/Testing/objects_testing.py index a324d26..bd1b9dd 100644 --- a/Testing/objects_testing.py +++ b/Testing/objects_testing.py @@ -22,6 +22,8 @@ rule_parser = Parser("rule") rules_parser = Parser("rules") model_parser = Parser("model") +observables_parser = Parser("observables") +observable_parser = Parser("observable") # atomic a1 = AtomicAgent("T", "s") diff --git a/Testing/parsing/test_observables.py b/Testing/parsing/test_observables.py new file mode 100644 index 0000000..2a01224 --- /dev/null +++ b/Testing/parsing/test_observables.py @@ -0,0 +1,49 @@ +import pytest + +import Testing.objects_testing as objects + + +def test_parser(): + observable_expr1 = "abc: A()::cell" + assert objects.observable_parser.parse(observable_expr1) + + observable_expr2 = "efg: E()" + assert objects.observable_parser.parse(observable_expr2) + + observable_expr3 = "hij: $H()::cell" + assert objects.observable_parser.parse(observable_expr3) + + observable_expr4 = "klm: {matchOnce}K()" + assert objects.observable_parser.parse(observable_expr4) + + observable_expr5 = "nop: N()::cell > 2" + assert objects.observable_parser.parse(observable_expr5).success + + observable_expr6 = "qrs: Q().R().S()::cell > 2" + assert objects.observable_parser.parse(observable_expr6).success + + observable_expr7 = "tuv: T(U{v})" + assert objects.observable_parser.parse(observable_expr7).success + + observables_expr = ( + "#! observables\n" + + observable_expr1 + + "\n" + + observable_expr2 + + "\n" + + observable_expr3 + + "\n" + + observable_expr4 + + "\n" + + observable_expr5 + + "\n" + + observable_expr6 + ) + assert objects.observables_parser.parse(observables_expr).success + + + assert not objects.observable_parser.parse("A()::cell > 2").success + assert not objects.observable_parser.parse("a: A(::cell").success + assert not objects.observable_parser.parse("a: b: A():cell > 2").success + assert not objects.observable_parser.parse("a: 2 > A():cell").success + assert not objects.observable_parser.parse("a: A()::cell$").success From 8722fccc380f7a53e18ba57399951ccc6a608fe4 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Tue, 16 Apr 2024 16:07:20 +0200 Subject: [PATCH 2/6] observable grammar added --- eBCSgen/Parsing/ParseBCSL.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index 4bb2843..c7eff83 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -106,13 +106,14 @@ def to_side(self): GRAMMAR = r""" model: (sections)* rules (sections | rules)* - sections: inits | definitions | complexes | regulation + sections: inits | definitions | complexes | regulation | observables rules: RULES_START _NL+ (rule _NL+)* rule _NL* inits: INITS_START _NL+ (init _NL+)* init _NL* definitions: DEFNS_START _NL+ (definition _NL+)* definition _NL* complexes: COMPLEXES_START _NL+ (cmplx_dfn _NL+)* cmplx_dfn _NL* regulation: REGULATION_START _NL+ regulation_def _NL* + observables: OBSERVABLES_START _NL+ (observable _NL+)* observable _NL* init: const? rate_complex definition: def_param "=" number @@ -138,6 +139,7 @@ def to_side(self): DEFNS_START: "#! definitions" COMPLEXES_START: "#! complexes" REGULATION_START: "#! regulation" + OBSERVABLES_START: "#! observables" _NL: /(\r?\n[\t ]*)+/ !label: CNAME "~" @@ -239,6 +241,13 @@ def to_side(self): REGEX_CHAR: /[^\\^$().*+?{}\[\]|]/ """ +OBSERVABLES_GRAMMAR = """ + observable: CNAME ":" ("0" | basic_observable) + basic_observable: pattern_mod? (rate_complex | value) pattern_quantified? + !pattern_quantified: (">" | "<" | ">=" | "<=" | "==" | "!=") INT + !pattern_mod: "$" | "{matchOnce}" +""" + class TransformRegulations(Transformer): def regulation(self, matches): @@ -702,6 +711,15 @@ def inits(self, matches): else: result[init[0].children[0]] = 1 return {"inits": result} + + def observable(self, matches): + return {str(matches[0]): matches[1].children} + + def observables(self, matches): + result = dict() + for observable in matches[1:]: + result.update(observable) + return {"observables": result} def param(self, matches): self.params.add(str(matches[0])) @@ -712,6 +730,7 @@ def model(self, matches): definitions = dict() regulation = None inits = collections.Counter() + observables = dict() for match in matches: if type(match) == dict: key, value = list(match.items())[0] @@ -728,6 +747,8 @@ def model(self, matches): inits.update(value) elif key == "definitions": definitions.update(value) + elif key == "observables": + observables.update(value) elif key == "regulation": if regulation: raise UnspecifiedParsingError("Multiple regulations") @@ -749,6 +770,7 @@ def __init__(self, start): + EXTENDED_GRAMMAR + REGULATIONS_GRAMMAR + REGEX_GRAMMAR + + OBSERVABLES_GRAMMAR ) self.parser = Lark( grammar, parser="earley", propagate_positions=False, maybe_placeholders=False From 8b509d0846570a9995a340f6f8a89c409d6ca620 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Tue, 16 Apr 2024 16:25:48 +0200 Subject: [PATCH 3/6] observable tests extended --- Testing/parsing/test_observables.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Testing/parsing/test_observables.py b/Testing/parsing/test_observables.py index 2a01224..4b03232 100644 --- a/Testing/parsing/test_observables.py +++ b/Testing/parsing/test_observables.py @@ -7,7 +7,7 @@ def test_parser(): observable_expr1 = "abc: A()::cell" assert objects.observable_parser.parse(observable_expr1) - observable_expr2 = "efg: E()" + observable_expr2 = "efg: E(F{_})" assert objects.observable_parser.parse(observable_expr2) observable_expr3 = "hij: $H()::cell" @@ -47,3 +47,4 @@ def test_parser(): assert not objects.observable_parser.parse("a: b: A():cell > 2").success assert not objects.observable_parser.parse("a: 2 > A():cell").success assert not objects.observable_parser.parse("a: A()::cell$").success + assert not objects.observable_parser.parse("a: A{}::cell").success From 11aebbb1e7e4d6aaee16269b95753e5b24d35e35 Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Tue, 23 Apr 2024 10:41:39 +0200 Subject: [PATCH 4/6] observables extended and scaling implemented --- Testing/parsing/test_observables.py | 13 ++++++++++++- eBCSgen/Parsing/ParseBCSL.py | 6 ++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Testing/parsing/test_observables.py b/Testing/parsing/test_observables.py index 4b03232..8a71900 100644 --- a/Testing/parsing/test_observables.py +++ b/Testing/parsing/test_observables.py @@ -25,6 +25,18 @@ def test_parser(): observable_expr7 = "tuv: T(U{v})" assert objects.observable_parser.parse(observable_expr7).success + observable_expr8 = "wx: 2W{x}" + assert objects.observable_parser.parse(observable_expr8).success + + observable_expr9 = "z: Y{z} + Z{y}" + assert objects.observable_parser.parse(observable_expr9).success + + observable_expr10 = "z: 2 * Y{z} + Z{y}" + assert objects.observable_parser.parse(observable_expr10).success + + observable_expr10 = "z: Y{z} + Z{y} / 2.1 ** 10" + assert objects.observable_parser.parse(observable_expr10).success + observables_expr = ( "#! observables\n" + observable_expr1 @@ -41,7 +53,6 @@ def test_parser(): ) assert objects.observables_parser.parse(observables_expr).success - assert not objects.observable_parser.parse("A()::cell > 2").success assert not objects.observable_parser.parse("a: A(::cell").success assert not objects.observable_parser.parse("a: b: A():cell > 2").success diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index c7eff83..73ffb0f 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -242,10 +242,12 @@ def to_side(self): """ OBSERVABLES_GRAMMAR = """ - observable: CNAME ":" ("0" | basic_observable) - basic_observable: pattern_mod? (rate_complex | value) pattern_quantified? + observable: CNAME ":" pattern_mod? (basic_observable | scaled_observable) pattern_quantified? + basic_observable: side | (const? observable_complex "+")* (const? observable_complex)? + !scaled_observable: const | observable_complex | scaled_observable "+" scaled_observable | scaled_observable "-" scaled_observable | scaled_observable "*" scaled_observable | scaled_observable "/" scaled_observable | scaled_observable POW const | "(" scaled_observable ")" !pattern_quantified: (">" | "<" | ">=" | "<=" | "==" | "!=") INT !pattern_mod: "$" | "{matchOnce}" + observable_complex: (abstract_sequence|value|cmplx_name) """ From 6da78d948ae0d57a525081c424f50b25b652295c Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Tue, 23 Apr 2024 19:54:33 +0200 Subject: [PATCH 5/6] scaling complexes with compartments corrected --- Testing/parsing/test_observables.py | 10 ++++++-- eBCSgen/Parsing/ParseBCSL.py | 38 +++++++++++++++++------------ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/Testing/parsing/test_observables.py b/Testing/parsing/test_observables.py index 8a71900..1f5ac7e 100644 --- a/Testing/parsing/test_observables.py +++ b/Testing/parsing/test_observables.py @@ -25,7 +25,7 @@ def test_parser(): observable_expr7 = "tuv: T(U{v})" assert objects.observable_parser.parse(observable_expr7).success - observable_expr8 = "wx: 2W{x}" + observable_expr8 = "wx: 2 W{x}" assert objects.observable_parser.parse(observable_expr8).success observable_expr9 = "z: Y{z} + Z{y}" @@ -34,9 +34,15 @@ def test_parser(): observable_expr10 = "z: 2 * Y{z} + Z{y}" assert objects.observable_parser.parse(observable_expr10).success - observable_expr10 = "z: Y{z} + Z{y} / 2.1 ** 10" + observable_expr10 = "z: (Y{z} + Z{y}) / 2.1 ** 10" assert objects.observable_parser.parse(observable_expr10).success + observable_expr11 = "scaled_A: 1000 * A{i}::cell" + assert objects.observable_parser.parse(observable_expr11).success + + observable_expr12 = "obs_A_all: A{i}::cell + A{a}::cell" + assert objects.observable_parser.parse(observable_expr12).success + observables_expr = ( "#! observables\n" + observable_expr1 diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index 73ffb0f..3529772 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -243,11 +243,10 @@ def to_side(self): OBSERVABLES_GRAMMAR = """ observable: CNAME ":" pattern_mod? (basic_observable | scaled_observable) pattern_quantified? - basic_observable: side | (const? observable_complex "+")* (const? observable_complex)? - !scaled_observable: const | observable_complex | scaled_observable "+" scaled_observable | scaled_observable "-" scaled_observable | scaled_observable "*" scaled_observable | scaled_observable "/" scaled_observable | scaled_observable POW const | "(" scaled_observable ")" + basic_observable: const? (value | abstract_sequence) (DOUBLE_COLON compartment)? + !scaled_observable: const | basic_observable | scaled_observable "+" scaled_observable | scaled_observable "-" scaled_observable | scaled_observable "*" scaled_observable | scaled_observable "/" scaled_observable | scaled_observable POW const | "(" scaled_observable ")" !pattern_quantified: (">" | "<" | ">=" | "<=" | "==" | "!=") INT !pattern_mod: "$" | "{matchOnce}" - observable_complex: (abstract_sequence|value|cmplx_name) """ @@ -674,15 +673,19 @@ def rule(self, matches): reversible = False if arrow == "<=>": reversible = True - return reversible, Rule( - agents, - mid, - compartments, - complexes, - pairs, - Rate(rate1) if rate1 else None, - label, - ), Rate(rate2) if rate2 else None + return ( + reversible, + Rule( + agents, + mid, + compartments, + complexes, + pairs, + Rate(rate1) if rate1 else None, + label, + ), + Rate(rate2) if rate2 else None, + ) def rules(self, matches): rules = [] @@ -713,10 +716,10 @@ def inits(self, matches): else: result[init[0].children[0]] = 1 return {"inits": result} - + def observable(self, matches): return {str(matches[0]): matches[1].children} - + def observables(self, matches): result = dict() for observable in matches[1:]: @@ -775,7 +778,10 @@ def __init__(self, start): + OBSERVABLES_GRAMMAR ) self.parser = Lark( - grammar, parser="earley", propagate_positions=False, maybe_placeholders=False + grammar, + parser="earley", + propagate_positions=False, + maybe_placeholders=False, ) self.terminals = dict((v, k) for k, v in _TERMINAL_NAMES.items()) @@ -880,7 +886,7 @@ def syntax_check(self, expression: str) -> Result: return Result( False, { - "unexpected": str(u.token), + "unexpected": str(u.token), "expected": self.replace(u.expected), "line": u.line, "column": u.column, From fde3ff9f691a4f3b2b9d6be775517943b97589ee Mon Sep 17 00:00:00 2001 From: Marketa Opichalova <514349@mail.muni.cz> Date: Wed, 24 Apr 2024 13:19:29 +0200 Subject: [PATCH 6/6] observables - remove modifiers, quantifiers and stoichiometry --- Testing/parsing/test_observables.py | 20 ++++++++++---------- eBCSgen/Parsing/ParseBCSL.py | 7 ++----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Testing/parsing/test_observables.py b/Testing/parsing/test_observables.py index 1f5ac7e..75340a9 100644 --- a/Testing/parsing/test_observables.py +++ b/Testing/parsing/test_observables.py @@ -7,34 +7,34 @@ def test_parser(): observable_expr1 = "abc: A()::cell" assert objects.observable_parser.parse(observable_expr1) - observable_expr2 = "efg: E(F{_})" + observable_expr2 = "efg: E(F{_})::cell" assert objects.observable_parser.parse(observable_expr2) - observable_expr3 = "hij: $H()::cell" + observable_expr3 = "hij: H()::cell" assert objects.observable_parser.parse(observable_expr3) - observable_expr4 = "klm: {matchOnce}K()" + observable_expr4 = "klm: K()::cyt * L()::cell + M()::cell" assert objects.observable_parser.parse(observable_expr4) - observable_expr5 = "nop: N()::cell > 2" + observable_expr5 = "nop: N()::cell" assert objects.observable_parser.parse(observable_expr5).success - observable_expr6 = "qrs: Q().R().S()::cell > 2" + observable_expr6 = "qrs: Q().R().S()::cell" assert objects.observable_parser.parse(observable_expr6).success - observable_expr7 = "tuv: T(U{v})" + observable_expr7 = "tuv: T(U{v})::cell " assert objects.observable_parser.parse(observable_expr7).success - observable_expr8 = "wx: 2 W{x}" + observable_expr8 = "wx: 2 * W{x}::cell" assert objects.observable_parser.parse(observable_expr8).success - observable_expr9 = "z: Y{z} + Z{y}" + observable_expr9 = "z: Y{z}::cyt + Z{y}::ext" assert objects.observable_parser.parse(observable_expr9).success - observable_expr10 = "z: 2 * Y{z} + Z{y}" + observable_expr10 = "z: 2 * Y{z}::cyt + Z{y}::ext ** 2" assert objects.observable_parser.parse(observable_expr10).success - observable_expr10 = "z: (Y{z} + Z{y}) / 2.1 ** 10" + observable_expr10 = "z: (Y{z}::cell + Z{y}::cyt) / 2.1 ** 10" assert objects.observable_parser.parse(observable_expr10).success observable_expr11 = "scaled_A: 1000 * A{i}::cell" diff --git a/eBCSgen/Parsing/ParseBCSL.py b/eBCSgen/Parsing/ParseBCSL.py index 3529772..8a3973c 100644 --- a/eBCSgen/Parsing/ParseBCSL.py +++ b/eBCSgen/Parsing/ParseBCSL.py @@ -242,11 +242,8 @@ def to_side(self): """ OBSERVABLES_GRAMMAR = """ - observable: CNAME ":" pattern_mod? (basic_observable | scaled_observable) pattern_quantified? - basic_observable: const? (value | abstract_sequence) (DOUBLE_COLON compartment)? - !scaled_observable: const | basic_observable | scaled_observable "+" scaled_observable | scaled_observable "-" scaled_observable | scaled_observable "*" scaled_observable | scaled_observable "/" scaled_observable | scaled_observable POW const | "(" scaled_observable ")" - !pattern_quantified: (">" | "<" | ">=" | "<=" | "==" | "!=") INT - !pattern_mod: "$" | "{matchOnce}" + observable: CNAME ":" observable_pattern + !observable_pattern: const | complex | observable_pattern "+" observable_pattern | observable_pattern "-" observable_pattern | observable_pattern "*" observable_pattern | observable_pattern "/" observable_pattern | observable_pattern POW const | "(" observable_pattern ")" """