Skip to content

Commit

Permalink
Merge branch 'main' of github.com:ml4ai/skema into decapodes-endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Justin committed Oct 31, 2023
2 parents c2d43f2 + df3e789 commit 3eefc91
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 11 deletions.
90 changes: 89 additions & 1 deletion skema/rest/utils.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,101 @@
from collections import defaultdict
from typing import Any, Dict
import itertools as it

from askem_extractions.data_model import AttributeCollection, AttributeType, Mention
from bs4 import BeautifulSoup, Comment

from skema.rest.schema import TextReadingEvaluationResults, AMRLinkingEvaluationResults


def fn_preprocessor(function_network: Dict[str, Any]):

fn_data = function_network.copy()

logs = []

'''
We will currently preprocess based on 2 different common bugs
1) wire tgt's being -1 -> which we will delete these wires
2) metadata being inline for bf entries instead of an index into the metadata_collection -> which we will replace with an index of 2
3) missing function_type field on a bf entry -> will replace with function_type: "OTHER"
4) If there is not a body field to a function -> replace "FUNCTION" with "ABSTRACT and set "name":"unknown"
5) NOT DONE YET: In the future we will preprocess about function calls being arguments, in order to simplify extracting the dataflow
'''

# first we check the top bf level of wires and inline metadata:
keys_to_check = ['bf', 'wff', 'wfopi', 'wfopo', 'wopio']
for key in keys_to_check:
if key == 'bf':
try:
for (i, entry) in enumerate(fn_data['modules'][0]['fn'][key]):
try:
metadata_obj = entry['metadata']
if not isinstance(metadata_obj, int):
entry['metadata'] = 2
logs.append(f"Inline metadata on {i+1}'th entry in top level bf")
except:
continue
try:
temp = entry['function_type']
except:
entry['function_type'] = "IMPORTED"
logs.append(f"Missing function_type on {i+1}'th entry in top level bf")
try:
if entry['function_type'] == "FUNCTION":
temp = entry['body']
except:
entry['function_type'] = "ABSTRACT"
entry['name'] = "Unknown"
logs.append(f"Missing Function body on {i+1}'th entry in top level bf")
except:
continue
else:
try:
for (i, entry) in enumerate(fn_data['modules'][0]['fn'][key]):
if entry['tgt'] == -1:
del fn_data['modules'][0]['fn'][key][i]
logs.append(f"The {i+1}'th {key} wire in the top level bf is targeting -1")
except:
continue

# now we iterate through the fn_array and do the same thing
for (j,fn_ent) in enumerate(fn_data['modules'][0]['fn_array']):
for key in keys_to_check:
if key == 'bf':
try:
for (i, entry) in enumerate(fn_ent[key]):
try:
metadata_obj = entry['metadata']
if not isinstance(metadata_obj, int):
entry['metadata'] = 2
logs.append(f"Inline metadata on {i+1}'th bf in the {j+1}'th fn_array")
except:
continue
try:
temp = entry['function_type']
except:
entry['function_type'] = "IMPORTED"
logs.append(f"Missing function_type on {i+1}'th bf in the {j+1}'th fn_array")
try:
if entry['function_type'] == "FUNCTION":
temp = entry['body']
except:
entry['function_type'] = "ABSTRACT"
entry['name'] = "Unknown"
logs.append(f"Missing Function body on {i+1}'th bf in the {j+1}'th fn_array")
except:
continue
else:
try:
for (i, entry) in enumerate(fn_ent[key]):
if entry['tgt'] == -1:
del fn_ent[key][i]
logs.append(f"The {i+1}'th {key} wire in the {j+1}'th fn_array is targeting -1")
except:
continue

return fn_data, logs

def clean_mml(mml: str) -> str:
"""Cleans/sterilizes pMML for AMR generation service"""
# FIXME: revisit if JSON deserialization on MORAE side changes
Expand Down
2 changes: 2 additions & 0 deletions skema/rest/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ async def equations_to_amr(data: schema.MmlToAMR):
@router.post("/code/snippets-to-pn-amr", summary="Code snippets → PetriNet AMR")
async def code_snippets_to_pn_amr(system: code2fn.System):
gromet = await code2fn.fn_given_filepaths(system)
gromet, logs = utils.fn_preprocessor(gromet)
res = requests.put(f"{SKEMA_RS_ADDESS}/models/PN", json=gromet)
if res.status_code != 200:
return JSONResponse(
Expand Down Expand Up @@ -153,6 +154,7 @@ async def code_snippets_to_rn_amr(system: code2fn.System):
)
async def repo_to_pn_amr(zip_file: UploadFile = File()):
gromet = await code2fn.fn_given_filepaths_zip(zip_file)
gromet, logs = utils.fn_preprocessor(gromet)
res = requests.put(f"{SKEMA_RS_ADDESS}/models/PN", json=gromet)
if res.status_code != 200:
return JSONResponse(
Expand Down
28 changes: 25 additions & 3 deletions skema/skema-rs/mathml/src/parsers/decapodes_serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ pub fn to_decapodes_serialization(
Some(idx) => idx + 1,
None => {
let variable = Variable {
r#type: Type::infer,
r#type: Type::Literal,
name: number.to_string(),
};
tables.variables.push(variable.clone());
Expand Down Expand Up @@ -407,7 +407,7 @@ fn test_serialize_multiply_sup() {
let expression = input.parse::<MathExpressionTree>().unwrap();
let wiring_diagram = to_wiring_diagram(&expression);
let json = to_decapodes_json(wiring_diagram);
assert_eq!(json, "{\"Var\":[{\"type\":\"infer\",\"name\":\"mult_1\"},{\"type\":\"infer\",\"name\":\"Γ\"},{\"type\":\"infer\",\"name\":\"•1\"},{\"type\":\"infer\",\"name\":\"H\"},{\"type\":\"infer\",\"name\":\"sum_1\"},{\"type\":\"infer\",\"name\":\"n\"},{\"type\":\"infer\",\"name\":\"2\"}],\"Op1\":[],\"Op2\":[{\"proj1\":4,\"proj2\":5,\"res\":3,\"op2\":\"^\"},{\"proj1\":2,\"proj2\":3,\"res\":1,\"op2\":\"*\"}],\"Σ\":[{\"sum\":5}],\"Summand\":[{\"summand\":6,\"summation\":1},{\"summand\":7,\"summation\":1}]}");
assert_eq!(json, "{\"Var\":[{\"type\":\"infer\",\"name\":\"mult_1\"},{\"type\":\"infer\",\"name\":\"Γ\"},{\"type\":\"infer\",\"name\":\"•1\"},{\"type\":\"infer\",\"name\":\"H\"},{\"type\":\"infer\",\"name\":\"sum_1\"},{\"type\":\"infer\",\"name\":\"n\"},{\"type\":\"Literal\",\"name\":\"2\"}],\"Op1\":[],\"Op2\":[{\"proj1\":4,\"proj2\":5,\"res\":3,\"op2\":\"^\"},{\"proj1\":2,\"proj2\":3,\"res\":1,\"op2\":\"*\"}],\"Σ\":[{\"sum\":5}],\"Summand\":[{\"summand\":6,\"summation\":1},{\"summand\":7,\"summation\":1}]}");
}

#[test]
Expand Down Expand Up @@ -451,5 +451,27 @@ fn test_serialize_halfar_dome() {
let expression = input.parse::<MathExpressionTree>().unwrap();
let wiring_diagram = to_wiring_diagram(&expression);
let json = to_decapodes_json(wiring_diagram);
assert_eq!(json,"{\"Var\":[{\"type\":\"infer\",\"name\":\"•1\"},{\"type\":\"infer\",\"name\":\"mult_1\"},{\"type\":\"infer\",\"name\":\"mult_2\"},{\"type\":\"infer\",\"name\":\"mult_3\"},{\"type\":\"infer\",\"name\":\"Γ\"},{\"type\":\"infer\",\"name\":\"•2\"},{\"type\":\"infer\",\"name\":\"H\"},{\"type\":\"infer\",\"name\":\"sum_1\"},{\"type\":\"infer\",\"name\":\"n\"},{\"type\":\"infer\",\"name\":\"2\"},{\"type\":\"infer\",\"name\":\"•3\"},{\"type\":\"infer\",\"name\":\"•4\"},{\"type\":\"infer\",\"name\":\"•5\"},{\"type\":\"infer\",\"name\":\"•6\"},{\"type\":\"infer\",\"name\":\"1\"},{\"type\":\"infer\",\"name\":\"•7\"},{\"type\":\"infer\",\"name\":\"•8\"}],\"Op1\":[{\"src\":7,\"tgt\":13,\"op1\":\"Grad\"},{\"src\":13,\"tgt\":12,\"op1\":\"Abs\"},{\"src\":7,\"tgt\":16,\"op1\":\"Grad\"},{\"src\":2,\"tgt\":1,\"op1\":\"Div\"},{\"src\":7,\"tgt\":17,\"op1\":\"D(1,t)\"}],\"Op2\":[{\"proj1\":7,\"proj2\":8,\"res\":6,\"op2\":\"^\"},{\"proj1\":5,\"proj2\":6,\"res\":4,\"op2\":\"*\"},{\"proj1\":9,\"proj2\":15,\"res\":14,\"op2\":\"-\"},{\"proj1\":12,\"proj2\":14,\"res\":11,\"op2\":\"^\"},{\"proj1\":4,\"proj2\":11,\"res\":3,\"op2\":\"*\"},{\"proj1\":3,\"proj2\":16,\"res\":2,\"op2\":\"*\"}],\"Σ\":[{\"sum\":8}],\"Summand\":[{\"summand\":9,\"summation\":1},{\"summand\":10,\"summation\":1}]}");
assert_eq!(json,"{\"Var\":[{\"type\":\"infer\",\"name\":\"•1\"},{\"type\":\"infer\",\"name\":\"mult_1\"},{\"type\":\"infer\",\"name\":\"mult_2\"},{\"type\":\"infer\",\"name\":\"mult_3\"},{\"type\":\"infer\",\"name\":\"Γ\"},{\"type\":\"infer\",\"name\":\"•2\"},{\"type\":\"infer\",\"name\":\"H\"},{\"type\":\"infer\",\"name\":\"sum_1\"},{\"type\":\"infer\",\"name\":\"n\"},{\"type\":\"Literal\",\"name\":\"2\"},{\"type\":\"infer\",\"name\":\"•3\"},{\"type\":\"infer\",\"name\":\"•4\"},{\"type\":\"infer\",\"name\":\"•5\"},{\"type\":\"infer\",\"name\":\"•6\"},{\"type\":\"Literal\",\"name\":\"1\"},{\"type\":\"infer\",\"name\":\"•7\"},{\"type\":\"infer\",\"name\":\"•8\"}],\"Op1\":[{\"src\":7,\"tgt\":13,\"op1\":\"Grad\"},{\"src\":13,\"tgt\":12,\"op1\":\"Abs\"},{\"src\":7,\"tgt\":16,\"op1\":\"Grad\"},{\"src\":2,\"tgt\":1,\"op1\":\"Div\"},{\"src\":7,\"tgt\":17,\"op1\":\"D(1,t)\"}],\"Op2\":[{\"proj1\":7,\"proj2\":8,\"res\":6,\"op2\":\"^\"},{\"proj1\":5,\"proj2\":6,\"res\":4,\"op2\":\"*\"},{\"proj1\":9,\"proj2\":15,\"res\":14,\"op2\":\"-\"},{\"proj1\":12,\"proj2\":14,\"res\":11,\"op2\":\"^\"},{\"proj1\":4,\"proj2\":11,\"res\":3,\"op2\":\"*\"},{\"proj1\":3,\"proj2\":16,\"res\":2,\"op2\":\"*\"}],\"Σ\":[{\"sum\":8}],\"Summand\":[{\"summand\":9,\"summation\":1},{\"summand\":10,\"summation\":1}]}");
}

#[test]
fn test_serialize_halfar_dome_3_2() {
let input = "
<math>
<mi>Γ</mi>
<mo>=</mo>
<mfrac><mn>2</mn><mrow><mi>n</mi><mo>+</mo><mn>2</mn></mrow></mfrac>
<mi>A</mi>
<mo>(</mo>
<mi>ρ</mi>
<mi>g</mi>
<msup><mo>)</mo>
<mi>n</mi></msup>
</math>
";
let expression = input.parse::<MathExpressionTree>().unwrap();
let s_exp = expression.to_string();
let wiring_diagram = to_wiring_diagram(&expression);
let json = to_decapodes_json(wiring_diagram);
assert_eq!(json, "{\"Var\":[{\"type\":\"infer\",\"name\":\"mult_1\"},{\"type\":\"infer\",\"name\":\"mult_2\"},{\"type\":\"infer\",\"name\":\"•1\"},{\"type\":\"Literal\",\"name\":\"2\"},{\"type\":\"infer\",\"name\":\"sum_1\"},{\"type\":\"infer\",\"name\":\"n\"},{\"type\":\"infer\",\"name\":\"A\"},{\"type\":\"infer\",\"name\":\"•2\"},{\"type\":\"infer\",\"name\":\"mult_3\"},{\"type\":\"infer\",\"name\":\"ρ\"},{\"type\":\"infer\",\"name\":\"g\"},{\"type\":\"infer\",\"name\":\"Γ\"}],\"Op1\":[],\"Op2\":[{\"proj1\":4,\"proj2\":5,\"res\":3,\"op2\":\"/\"},{\"proj1\":3,\"proj2\":7,\"res\":2,\"op2\":\"*\"},{\"proj1\":10,\"proj2\":11,\"res\":9,\"op2\":\"*\"},{\"proj1\":9,\"proj2\":6,\"res\":8,\"op2\":\"^\"},{\"proj1\":2,\"proj2\":8,\"res\":1,\"op2\":\"*\"}],\"Σ\":[{\"sum\":5}],\"Summand\":[{\"summand\":6,\"summation\":1},{\"summand\":4,\"summation\":1}]}");
}
22 changes: 19 additions & 3 deletions skema/skema-rs/mathml/src/parsers/interpreted_mathml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use crate::{
},
parsers::generic_mathml::{
add, attribute, comma, dot, elem_many0, equals, etag, grad, lparen, mean, mi, mn, msqrt,
msub, msubsup, msup, multiply, rparen, stag, subtract, tag_parser, ws, xml_declaration,
IResult, ParseError, Span,
msub, msubsup, multiply, rparen, stag, subtract, tag_parser, ws, xml_declaration, IResult,
ParseError, Span,
},
};

Expand Down Expand Up @@ -441,12 +441,29 @@ pub fn absolute_with_msup(input: Span) -> IResult<MathExpression> {
Ok((s, sup))
}

///Parenthesis with Msup value
pub fn paren_as_msup(input: Span) -> IResult<MathExpression> {
let (s, sup) = ws(map(
ws(delimited(
tag("<mo>(</mo>"),
tuple((
map(many0(math_expression), |z| Mrow(z)),
preceded(tag("<msup><mo>)</mo>"), math_expression),
)),
tag("</msup>"),
)),
|(x, y)| MathExpression::Msup(Box::new(MathExpression::Mrow(x)), Box::new(y)),
))(input)?;
Ok((s, sup))
}

/// Parser for math expressions. This varies from the one in the generic_mathml module, since it
/// assumes that expressions such as S(t) are actually univariate functions.
pub fn math_expression(input: Span) -> IResult<MathExpression> {
ws(alt((
map(div, MathExpression::Mo),
absolute_with_msup,
paren_as_msup,
map(
first_order_derivative_leibniz_notation,
|(
Expand Down Expand Up @@ -553,7 +570,6 @@ pub fn math_expression(input: Span) -> IResult<MathExpression> {
map(operator, MathExpression::Mo),
mn,
superscript,
msup,
over_term,
msub,
msqrt,
Expand Down
18 changes: 17 additions & 1 deletion skema/skema-rs/mathml/src/parsers/math_expression_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,12 @@ impl MathExpression {
tokens.push(MathExpression::Mo(Operator::Rparen));
}
} else {
tokens.push(MathExpression::Mo(Operator::Lparen));
//tokens.push(MathExpression::Mo(Operator::Lparen));
base.flatten(tokens);
tokens.push(MathExpression::Mo(Operator::Power));
tokens.push(MathExpression::Mo(Operator::Lparen));
superscript.flatten(tokens);
tokens.push(MathExpression::Mo(Operator::Rparen));
}
}
MathExpression::AbsoluteSup(base, superscript) => {
Expand Down Expand Up @@ -384,6 +385,7 @@ fn insert_multiple_between_paren(lexer: &mut Lexer) {
if let Token::Op(Operator::Lparen) = token {
if let Token::Op(Operator::Rparen) = **next_token {
new_tokens.push(Token::Op(Operator::Multiply).clone());
} else {
}
}
}
Expand Down Expand Up @@ -1079,3 +1081,17 @@ fn test_combination() {
let s_exp = exp.to_string();
assert_eq!(s_exp, "(* (* (* S (+ n 4)) (- i 3)) (^ H (- m 2)))");
}

#[test]
fn test_mi_multiply() {
let input = "
<math>
<mi>A</mi>
<msup><mi>ρ</mi><mi>n</mi></msup>
<msup><mi>g</mi><mi>n</mi></msup>
</math>
";
let exp = input.parse::<MathExpressionTree>().unwrap();
let s_exp = exp.to_string();
println!("s_exp={:?}", s_exp);
}
28 changes: 25 additions & 3 deletions skema/skema-rs/skema/src/model_extraction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,42 @@ pub fn get_line_span(

#[allow(non_snake_case)]
pub fn module_id2mathml_MET_ast(module_id: i64, host: &str) -> Vec<FirstOrderODE> {
let mut core_dynamics_ast = Vec::<FirstOrderODE>::new();
let mut _metadata_map_ast = HashMap::new();
let graph = subgraph2petgraph(module_id, host); // makes petgraph of graph

let core_id = find_pn_dynamics(module_id, host); // gives back list of function nodes that might contain the dynamics
let _line_span = get_line_span(core_id[0], graph); // get's the line span of function id
//let _line_span = get_line_span(core_id[0], graph); // get's the line span of function id

//println!("\n{:?}", line_span);
if core_id.len() == 0 {
let deriv = Ci {
r#type: Some(Function),
content: Box::new(MathExpression::Mi(Mi("temp".to_string()))),
func_of: None,
};
let operate = Operator::Subtract;
let rhs_arg = MathExpressionTree::Atom(MathExpression::Mi(Mi("temp".to_string())));
let rhs = MathExpressionTree::Cons(operate, [rhs_arg].to_vec());
let fo_eq = FirstOrderODE {
lhs_var: deriv.clone(),
func_of: [deriv.clone()].to_vec(), // just place holders for construction
with_respect_to: deriv.clone(), // just place holders for construction
rhs: rhs,
};
core_dynamics_ast.push(fo_eq);
} else {
(core_dynamics_ast, _metadata_map_ast) =
subgrapg2_core_dyn_MET_ast(core_id[0], host).unwrap();
}

//println!("function_core_id: {:?}", core_id[0].clone());
//println!("module_id: {:?}\n", module_id.clone());
// 4.5 now to check if of those expressions, if they are arithmetric in nature

// 5. pass id to subgrapg2_core_dyn to get core dynamics
let (core_dynamics_ast, _metadata_map_ast) =
subgrapg2_core_dyn_MET_ast(core_id[0], host).unwrap();
//let (core_dynamics_ast, _metadata_map_ast) =
//subgrapg2_core_dyn_MET_ast(core_id[0], host).unwrap();

core_dynamics_ast
}
Expand Down

0 comments on commit 3eefc91

Please sign in to comment.