cellml / libcellml

Repository for libCellML development.
https://libcellml.org
Apache License 2.0
16 stars 21 forks source link

Logical operators assumed to always be binary #1139

Open nickerso opened 1 year ago

nickerso commented 1 year ago

The checks introduced in https://github.com/cellml/libcellml/pull/1083 (specifically this comment) highlighted that logical operators are assumed to always be used as MathML binary operators. In MathML eq, leq, lt, geq, gt are n-ary operators (followed by zero or more child elements) and neq is a binary operator (followed by exactly two child elements).

Experience suggests that this assumption is not going to be a problem, but adding this issue so we don't lose track of this and can revisit as needed in future.

luciansmith commented 1 year ago

SBML definitely does not make this assumption, and follows the MathML spec: only neq is binary, and the rest are n-ary.

It should be noted that 'plus' and 'times' are also n-ary, and have specific meaning when there are no children: plus() = 0, and times() = 1.

agarny commented 1 year ago

It should be noted that 'plus' and 'times' are also n-ary, and have specific meaning when there are no children: plus() = 0, and times() = 1.

Agreed about plus and times. This aside, where did you see those specific meanings? Right now, we support plus with one or more operands and times with two or more operands.

Going back to logical operators (and partly copying/pasting my comment at https://github.com/cellml/libcellml/pull/1083#discussion_r1151360289):

I am, however, confused as how we can interprete an n-ary operator. If we consider eq, it means that we could have:

luciansmith commented 1 year ago

It took me a while to find this back when I was updating the SBML spec, but I finally found the clearest definitions in Appendix C of the MathML 2 spec:

https://www.w3.org/TR/MathML2/appendixc.html

For plus, it says:

https://www.w3.org/TR/MathML2/appendixc.html#cedef.plus

"...If no operands are provided, the expression represents the additive identity. If one operand, a, is provided the expression evaluates to "a". If two or more operands are provided, the expression represents the (semi) group element corresponding to a left associative binary pairing of the operands."

Basically, the value of an n-ary function fn() is the value that would leave the expression identical if added to a list of arguments. So with plus, the value is 0, because plus(a, b) == plus(a, b, 0). With times, it's 1, because times(a, b) == times(a, b, 1).

By analogy, the value of and() would be TRUE, because and(a, b) == and(a, b, TRUE), and the value of or() would be FALSE because or(a, b) == or(a, b, FALSE). However, the MathML spec isn't as clear on this point, so the SBML editors voted to leave and() and or() be officially undefined in the SBML spec.

We did agree that fn(x) == x, for plus, times, and, and or.

For 'eq' and all the other comparison operators le, leq, gt, and geq, the spec says that they're n-ary, but that there must be at least two arguments: https://www.w3.org/TR/MathML2/appendixc.html#cedef.eq

so eq(a, b, c) == eq(a, b) && eq(b, c) && eq(a, c) The last can be derived from the other two, so you could implement this as either eq(n0, n1) && eq(n0, n2) && ... && eq(n0, nx) or as eq(n0, n1) && eq(n1, n2) && eq(n2, n3) && ... && eq(n(x-1), nx)

For le/leq/gt/geq, you'll need the second formulation, though, so it might be easiest to just use that for all five.

agarny commented 1 year ago

Thanks for the detailed clarification @luciansmith.

I can't help but wonder why they ever allowed plus, for instance, to have no operand. Where/when do you need that? Then, to say that eq, leq, lt, geq, and gt are n-ary operators, but that they must have at least two operands while other n-ary operators can have 0+ operands!?

Anyway, I feel like sticking to what we currently have. I don't see the point in making our life more difficult (by, for instance, supporting plus with no operand) when our current approach has been sufficient for the past 20+ years.