Skip to content

Commit

Permalink
[Equations] Fixes/rewrite to some operators, additional comments and …
Browse files Browse the repository at this point in the history
…additional parsers (#828)

## Summary of Changes
Fixes/rewrite to some operators — integral, summation, gradients
Parser can handle:
- Derivative has new enum component to identify various different
notation. e.g. Leibniz, Newton, Euler, DNotation, Lagrange (Note that
previous operator `PartialDerivative` is removed and partial derivative
operator notation is now in `DerivativeNotation` enum. )
- Vector e.g. \vec{x}

---------

Co-authored-by: Deepsana Shahi <[email protected]>
Co-authored-by: Justin <[email protected]>
  • Loading branch information
3 people authored Feb 27, 2024
1 parent 2004566 commit 68e7d31
Show file tree
Hide file tree
Showing 16 changed files with 1,319 additions and 498 deletions.
2 changes: 1 addition & 1 deletion skema/isa/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@
</math>
"""

expected = 'digraph G {\n0 [color=blue, label="Div(Γ*(H^(n+2))*(Abs(Grad(H))^(n-1))*Grad(H))"];\n1 [color=blue, label="D(1, t)(H)"];\n2 [color=blue, label="Γ*(H^(n+2))*(Abs(Grad(H))^(n-1))*Grad(H)"];\n3 [color=blue, label="Γ"];\n4 [color=blue, label="H^(n+2)"];\n5 [color=blue, label="H"];\n6 [color=blue, label="n+2"];\n7 [color=blue, label="n"];\n8 [color=blue, label="2"];\n9 [color=blue, label="Abs(Grad(H))^(n-1)"];\n10 [color=blue, label="Abs(Grad(H))"];\n11 [color=blue, label="Grad(H)"];\n12 [color=blue, label="n-1"];\n13 [color=blue, label="1"];\n1 -> 0 [color=blue, label="="];\n2 -> 0 [color=blue, label="Div"];\n3 -> 2 [color=blue, label="*"];\n4 -> 2 [color=blue, label="*"];\n5 -> 4 [color=blue, label="^"];\n6 -> 4 [color=blue, label="^"];\n7 -> 6 [color=blue, label="+"];\n8 -> 6 [color=blue, label="+"];\n9 -> 2 [color=blue, label="*"];\n10 -> 9 [color=blue, label="^"];\n11 -> 10 [color=blue, label="Abs"];\n5 -> 11 [color=blue, label="Grad"];\n12 -> 9 [color=blue, label="^"];\n7 -> 12 [color=blue, label="+"];\n13 -> 12 [color=blue, label="-"];\n11 -> 2 [color=blue, label="*"];\n}\n'
expected = 'digraph G {\n0 [color=blue, label="Div(Γ*(H^(n+2))*(Abs(Grad(H))^(n-1))*Grad(H))"];\n1 [color=blue, label="PD(1, t)(H)"];\n2 [color=blue, label="Γ*(H^(n+2))*(Abs(Grad(H))^(n-1))*Grad(H)"];\n3 [color=blue, label="Γ"];\n4 [color=blue, label="H^(n+2)"];\n5 [color=blue, label="H"];\n6 [color=blue, label="n+2"];\n7 [color=blue, label="n"];\n8 [color=blue, label="2"];\n9 [color=blue, label="Abs(Grad(H))^(n-1)"];\n10 [color=blue, label="Abs(Grad(H))"];\n11 [color=blue, label="Grad(H)"];\n12 [color=blue, label="n-1"];\n13 [color=blue, label="1"];\n1 -> 0 [color=blue, label="="];\n2 -> 0 [color=blue, label="Div"];\n3 -> 2 [color=blue, label="*"];\n4 -> 2 [color=blue, label="*"];\n5 -> 4 [color=blue, label="^"];\n6 -> 4 [color=blue, label="^"];\n7 -> 6 [color=blue, label="+"];\n8 -> 6 [color=blue, label="+"];\n9 -> 2 [color=blue, label="*"];\n10 -> 9 [color=blue, label="^"];\n11 -> 10 [color=blue, label="Abs"];\n5 -> 11 [color=blue, label="Grad"];\n12 -> 9 [color=blue, label="^"];\n7 -> 12 [color=blue, label="+"];\n13 -> 12 [color=blue, label="-"];\n11 -> 2 [color=blue, label="*"];\n}\n'
4 changes: 2 additions & 2 deletions skema/rest/tests/test_eqn_to_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ async def test_post_image_to_latex():
# see https://fastapi.tiangolo.com/advanced/async-tests/#async-tests
async with AsyncClient(app=app, base_url="http://eqn-to-latex-test") as ac:
response = await ac.post(endpoint, files=files)
expected = "\\frac{d H}{dt}=\\nabla \\cdot {(\\Gamma*H^{n+2}*\\left|\\nabla{H}\\right|^{n-1}*\\nabla{H})}"
expected = "\\frac{\\partial H}{\\partial t}=\\nabla \\cdot {(\\Gamma*H^{n+2}*\\left|\\nabla{H}\\right|^{n-1}*\\nabla{H})}"
# check for route's existence
assert (
any(route.path == endpoint for route in app.routes) == True
Expand Down Expand Up @@ -50,7 +50,7 @@ async def test_post_image_to_latex_base64():
# see https://fastapi.tiangolo.com/advanced/async-tests/#async-tests
async with AsyncClient(app=app, base_url="http://eqn-to-latex-base64-test") as ac:
response = await ac.post(endpoint, data=img_b64)
expected = "\\frac{d H}{dt}=\\nabla \\cdot {(\\Gamma*H^{n+2}*\\left|\\nabla{H}\\right|^{n-1}*\\nabla{H})}"
expected = "\\frac{\\partial H}{\\partial t}=\\nabla \\cdot {(\\Gamma*H^{n+2}*\\left|\\nabla{H}\\right|^{n-1}*\\nabla{H})}"
# check for route's existence
assert (
any(route.path == endpoint for route in app.routes) == True
Expand Down
2 changes: 1 addition & 1 deletion skema/rest/tests/test_isa.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def test_align_eqns():
), f"Response should be 1.0, but instead received {response.text}"
# check response of union_graph
assert (
json.loads(response.text)["union_graph"] == expected
str(json.loads(response.text)["union_graph"]) == expected
), f"Response should be {expected}, but instead received {response.text}"


Expand Down
2 changes: 1 addition & 1 deletion skema/skema-rs/mathml/src/acset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,4 +991,4 @@ impl From<Vec<FirstOrderODE>> for RegNet {
metadata: None,
}
}
}
}
87 changes: 72 additions & 15 deletions skema/skema-rs/mathml/src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
use derive_new::new;
use schemars::JsonSchema;
use std::fmt;
use utoipa::ToSchema;
use schemars::JsonSchema;
pub mod operator;
use operator::Operator;
use serde::{Deserialize, Serialize};
//use crate::ast::MathExpression::SummationOp;

/// Represents identifiers such as variables, function names, constants.
/// E.g. x , sin, n
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub struct Mi(pub String);

/// Represents groups of any subexpressions
/// E.g. For a given expression: 2x+4 --> 2 and x are grouped together
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub struct Mrow(pub Vec<MathExpression>);

#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub enum Type {
Integer,
Rational,
Expand All @@ -35,7 +41,9 @@ pub enum Type {

/// Represents content identifiers such that variables can be have type attribute
/// to specify what it represents. E.g. function, vector, real,...
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub struct Ci {
pub r#type: Option<Type>,
pub content: Box<MathExpression>,
Expand All @@ -44,22 +52,28 @@ pub struct Ci {

/// Represents the differentiation operator `diff` for functions or expresions `func`
/// E.g. df/dx
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub struct Differential {
pub diff: Box<MathExpression>,
pub func: Box<MathExpression>,
}

/// Represents sum operator with terms that are summed over
/// E.g. sum_{i=1}^{K} f(x_i)
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub struct SummationMath {
pub op: Box<MathExpression>,
pub func: Box<MathExpression>,
}

/// Integral operation represents integral over math expressions
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub struct Integral {
pub op: Box<MathExpression>,
pub integrand: Box<MathExpression>,
Expand All @@ -68,24 +82,61 @@ pub struct Integral {

/// Represents Hat operator and the contents the hat operator is being applied to.
/// E.g. f \hat{x}, where `op` will be \hat{x}, `comp` with be the contents
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub struct HatComp {
pub op: Box<MathExpression>,
pub comp: Box<MathExpression>,
}

/// Laplacian operator of vector calculus which takes in argument `comp`.
/// E.g. ∇^2 f
#[derive(Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema)]
#[derive(
Debug, Ord, PartialOrd, PartialEq, Eq, Clone, Hash, new, Deserialize, Serialize, JsonSchema,
)]
pub struct LaplacianComp {
pub op: Box<MathExpression>,
pub comp: Ci,
}

/// Handles ↓ such that {comp}↓_{sub}^{sup} can parse
#[derive(
Debug,
Ord,
PartialOrd,
PartialEq,
Eq,
Clone,
Hash,
new,
Deserialize,
Serialize,
ToSchema,
JsonSchema,
)]
pub struct DownArrow {
pub sub: Option<Box<MathExpression>>,
pub sup: Option<Box<MathExpression>>,
pub comp: Box<MathExpression>,
}

/// The MathExpression enum is not faithful to the corresponding element type in MathML 3
/// (https://www.w3.org/TR/MathML3/appendixa.html#parsing_MathExpression)
#[derive(
Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Hash, Default, new, Deserialize, Serialize, ToSchema, JsonSchema
Debug,
PartialOrd,
Ord,
PartialEq,
Eq,
Clone,
Hash,
Default,
new,
Deserialize,
Serialize,
ToSchema,
JsonSchema,
)]
pub enum MathExpression {
Mi(Mi),
Expand Down Expand Up @@ -128,9 +179,9 @@ pub enum MathExpression {
Integral(Integral),
LaplacianComp(LaplacianComp),
/// Represents closed surface integral over contents. E.g. \\oiint_S ∇ \cdot T dS
SurfaceClosedIntegral(Box<MathExpression>),
/// Represents closed surface integral over contents where integration E.g. \\oiint_S ∇ \cdot dS
SurfaceClosedIntegralNoIntVar(Box<MathExpression>),
SurfaceIntegral(Box<MathExpression>),
/// ↓ as an operator e.g. I↓ indicates downward diffuse radiative fluxes per unit indcident flux
DownArrow(DownArrow),
#[default]
None,
}
Expand Down Expand Up @@ -193,9 +244,15 @@ impl fmt::Display for MathExpression {
write!(f, "{op}(")?;
write!(f, "{comp})")
}
MathExpression::SurfaceClosedIntegral(row) => {
MathExpression::SurfaceIntegral(row) => {
write!(f, "{row})")
}
MathExpression::DownArrow(DownArrow { sub, sup, comp }) => match (sub, sup) {
(Some(low), Some(up)) => write!(f, "{comp}↓_{{{low}}}^{{{up}}}"),
(Some(low), None) => write!(f, "{comp}↓_{{{low}}}"),
(None, Some(up)) => write!(f, "{comp}↓^{{{up}}}"),
(None, None) => write!(f, "{comp}↓"),
},
expression => write!(f, "{expression:?}"),
}
}
Expand Down
Loading

0 comments on commit 68e7d31

Please sign in to comment.