Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions vcell-math/src/main/java/cbit/vcell/parser/ASTAndNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ public String infixString(int lang)
buffer.append(jjtGetChild(i).infixString(lang));
}
buffer.append("))");
}else if (lang == LANGUAGE_NUM_EXPR) {
int numChildren = jjtGetNumChildren();
for (int i=0;i<numChildren;i++){
if (i>0) buffer.append(" & ");
buffer.append("(0.0!=");
buffer.append(jjtGetChild(i).infixString(lang));
buffer.append(")");
}
}else{
for (int i=0;i<jjtGetNumChildren();i++){
if (i>0) {
Expand Down
6 changes: 5 additions & 1 deletion vcell-math/src/main/java/cbit/vcell/parser/ASTFloatNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,11 @@ public String infixString(int lang)
if (value == Double.POSITIVE_INFINITY) return "float('inf')";
if (value == Double.NEGATIVE_INFINITY) return "float('-inf')";
return value.toString();
} else {
} else if (lang == LANGUAGE_NUM_EXPR) {
if (value == Double.POSITIVE_INFINITY) return "inf";
if (value == Double.NEGATIVE_INFINITY) return "-inf";
return value.toString();
} else {
return value.toString();
}
}
Expand Down
111 changes: 100 additions & 11 deletions vcell-math/src/main/java/cbit/vcell/parser/ASTFuncNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,29 @@ public final String getPythonTranslation() {
default -> "math." + getName();
};
}

public final String getNumExprTranslation() {
return switch (this){
case USERDEFINED -> throw new IllegalArgumentException("User defined functions have nothing to do with standard python names.");
case ABS -> "abs";
case MAX -> "maximum";
case MIN -> "minimum";
case CSC -> "1.0/" + SIN.getNumExprTranslation();
case COT -> "1.0/" + TAN.getNumExprTranslation();
case SEC -> "1.0/" + COS.getNumExprTranslation();
case ASIN -> "arcsin";
case ACOS -> "arccos";
case ATAN -> "arctan";
case ATAN2 -> "arctan2";
case CSCH -> "1.0/" + SINH.getNumExprTranslation();
case COTH -> "1.0/" + TANH.getNumExprTranslation();
case SECH -> "1.0/" + COSH.getNumExprTranslation();
case LOGBASE -> "1.0/" + LOG.getNumExprTranslation();
case ACSC, ACOT, ASEC, ACSCH, ACOTH, ASECH, FACTORIAL -> throw new UnsupportedOperationException("Python has no equivalent name.");
default -> getName();
};
}

public final String getHtmlDescription() {
return htmlDescription;
}
Expand Down Expand Up @@ -2389,12 +2412,28 @@ String getMathMLName() {
}

String getPythonName(){
if (funcType == FunctionType.USERDEFINED){
return getPythonName(funcType);
}

// This function is used when we need to "override" `funcType` to get the correct answer
private String getPythonName(ASTFuncNode.FunctionType functionType){
if (functionType == FunctionType.USERDEFINED){
return funcName;
}
return funcType.getPythonTranslation();
return functionType.getPythonTranslation();
}

String getNumExprName(){
return getNumExprName(funcType);
}

// This function is used when we need to "override" `funcType` to get the correct answer
private String getNumExprName(ASTFuncNode.FunctionType functionType){
if (functionType == FunctionType.USERDEFINED){
return funcName;
}
return functionType.getNumExprTranslation();
}

/**
* Insert the method's description here.
Expand Down Expand Up @@ -2455,7 +2494,7 @@ public String infixString(int lang) {
buffer.append(",");
buffer.append("((double)(" + jjtGetChild(1).infixString(lang) + "))");
buffer.append(")");
} else if (lang == LANGUAGE_PYTHON) {
} else if (lang == LANGUAGE_PYTHON || lang == LANGUAGE_NUM_EXPR) {
buffer.append("((");
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append(")**(");
Expand Down Expand Up @@ -2485,48 +2524,89 @@ public String infixString(int lang) {
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append("))");
break;
} else if (lang == LANGUAGE_NUM_EXPR){
// Need parenthesis to encapsulate the "1.0/x" operation
buffer.append("(");
buffer.append(getNumExprName());
buffer.append("(");
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append("))");
break;
}
// DO NOT PUT A "BREAK" HERE! We want to "fall through" to default!
}
case ACSC:
case ACOT:
case ASEC:
case ACSCH:
case ACOTH:
case ASECH:{
if (lang == LANGUAGE_PYTHON){ // Need parenthesis to encapsulate the "1.0/x" operation
if (lang == LANGUAGE_PYTHON){
// Need parenthesis to encapsulate the "1.0/x" operation
Map<FunctionType, FunctionType> inversionMap = Map.of(
FunctionType.ACSC, FunctionType.ASIN,
FunctionType.ACOT, FunctionType.ATAN,
FunctionType.ASEC, FunctionType.ACOS,
FunctionType.ACSCH, FunctionType.ASINH,
FunctionType.ACOTH, FunctionType.ATANH,
FunctionType.ASECH, FunctionType.ACOSH
);
FunctionType inversionType = inversionMap.get(funcType);
buffer.append("(");
buffer.append(getPythonName());
buffer.append(getPythonName(inversionType));
buffer.append("(1.0/(");
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append(")))");
break;
} else if (lang == LANGUAGE_NUM_EXPR){
// Need parenthesis to encapsulate the "1.0/x" operation
Map<FunctionType, FunctionType> inversionMap = Map.of(
FunctionType.ACSC, FunctionType.ASIN,
FunctionType.ASEC, FunctionType.ACOS,
FunctionType.ACSCH, FunctionType.ASINH,
FunctionType.ACOTH, FunctionType.ATANH,
FunctionType.ASECH, FunctionType.ACOSH
);
FunctionType inversionType = inversionMap.get(funcType);
buffer.append("(");
buffer.append(getNumExprName(inversionType));
buffer.append("(1.0/(");
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append(")))");
break;
}
// DO NOT PUT A "BREAK" HERE! We want to "fall through" to default!
}
case ACOT:{
if (lang == LANGUAGE_PYTHON){
buffer.append("(math.pi/2.0 - ");
buffer.append(getPythonName(FunctionType.ATAN));
buffer.append("(");
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append("))");
break;
} else if (lang == LANGUAGE_NUM_EXPR){
buffer.append(String.format("((%s/2.0) - ", Math.PI));
buffer.append(getNumExprName(FunctionType.ATAN));
buffer.append("(");
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append("))");
break;
}
}
case FACTORIAL:
if (lang == LANGUAGE_PYTHON){
String name = getPythonName();
// Since VCell now only accepts integer-values for factorial, integer casting should be safe
buffer.append(name).append("(int(");
// Since VCell now only accepts integer-values for factorial,
// Before we used casting, but there's a chance after math python can generate a different round off error.
// Since we know VCell only accepts integer, we're safe using `round` instead of just `int`
buffer.append(name).append("(round(");
if (1 < this.jjtGetNumChildren()) throw new UnsupportedOperationException("Cannot take factorial of multiple children");
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append("))");
break;
}
// DO NOT PUT A "BREAK" HERE! We want to "fall through" to default!
default:{
boolean needPythonicVersionOfName = lang == LANGUAGE_PYTHON && !funcType.equals(FunctionType.USERDEFINED);
String name = needPythonicVersionOfName ? getPythonName(): getName();
String name = getAppropriateName(lang);
buffer.append(name + "(");
for (int i=0;i<jjtGetNumChildren();i++){
if (i>0) buffer.append(", ");
Expand All @@ -2545,6 +2625,15 @@ public String infixString(int lang) {

}

private String getAppropriateName(int language){
if (FunctionType.USERDEFINED.equals(funcType)) return getName();
return switch (language){
case LANGUAGE_PYTHON -> getPythonName();
case LANGUAGE_NUM_EXPR -> getNumExprName();
default -> getName();
};
}

/**
* Insert the method's description here.
* Creation date: (6/20/01 11:04:41 AM)
Expand Down
18 changes: 14 additions & 4 deletions vcell-math/src/main/java/cbit/vcell/parser/ASTMultNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ public String infixString(int lang){

buffer.append("(");

if (bAllBoolean || bNoBoolean || (lang != SimpleNode.LANGUAGE_C && lang != SimpleNode.LANGUAGE_VISIT && lang != SimpleNode.LANGUAGE_PYTHON)) { // old way
if (bAllBoolean || bNoBoolean || (lang != SimpleNode.LANGUAGE_C && lang != SimpleNode.LANGUAGE_VISIT && lang != SimpleNode.LANGUAGE_PYTHON&& lang != SimpleNode.LANGUAGE_NUM_EXPR)) { // old way
for (int i=0;i<jjtGetNumChildren();i++){
if (jjtGetChild(i) instanceof ASTInvertTermNode){
// bAllBoolean must be false here!
Expand All @@ -430,9 +430,15 @@ public String infixString(int lang){
if (i>0){
if (lang == SimpleNode.LANGUAGE_MATLAB){
buffer.append(" .* ");
} else if(lang == SimpleNode.LANGUAGE_PYTHON && bAllBoolean){
buffer.append(" and ");
} else {
} else if (bAllBoolean){
if (lang == SimpleNode.LANGUAGE_PYTHON){
buffer.append(" and ");
} else if (lang == SimpleNode.LANGUAGE_NUM_EXPR) {
buffer.append(" & ");
} else {
buffer.append(" * ");
}
} else { // mixed or no-boolean, and not matlab
buffer.append(" * ");
}
}
Expand All @@ -447,6 +453,8 @@ public String infixString(int lang){
if (conditionBuffer.length() > 0) {
if (lang == SimpleNode.LANGUAGE_PYTHON){
conditionBuffer.append(" and ");
} else if (lang == SimpleNode.LANGUAGE_NUM_EXPR) {
conditionBuffer.append(" & ");
} else {
conditionBuffer.append(" && ");
}
Expand Down Expand Up @@ -476,6 +484,8 @@ public String infixString(int lang){
}
}else if (lang == SimpleNode.LANGUAGE_PYTHON) {
buffer.append("(").append(valueBuffer).append(") if (").append(conditionBuffer).append(") else 0.0");
}else if (lang == SimpleNode.LANGUAGE_NUM_EXPR) {
buffer.append("where(").append(conditionBuffer).append(", ").append(valueBuffer).append(", 0.0)");
}else{
buffer.append("((" + conditionBuffer + ") ? (" + valueBuffer + ") : 0.0)");
}
Expand Down
6 changes: 5 additions & 1 deletion vcell-math/src/main/java/cbit/vcell/parser/ASTNotNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,23 @@ public Node flatten(boolean substituteConstants) throws ExpressionException {
public String infixString(int language) {
StringBuffer buffer = new StringBuffer();
boolean parentIsLogical = parent != null && parent.isLogical();
boolean childIsLogical = jjtGetChild(0).isLogical();
if (language == LANGUAGE_VISIT){
buffer.append("not(");
}else if(language == LANGUAGE_PYTHON){
if (parentIsLogical) buffer.append("not(");
else buffer.append("float(not(");
}else if(language == LANGUAGE_NUM_EXPR){
if (childIsLogical) buffer.append("~(");
else buffer.append("(0.0==(");
}else if (language == LANGUAGE_ECLiPSe){
buffer.append("neg(");
}else{
buffer.append("!(");
}
buffer.append(jjtGetChild(0).infixString(language));
buffer.append(")");
if (language == LANGUAGE_PYTHON && !parentIsLogical) buffer.append(")");
if ((language == LANGUAGE_PYTHON && !parentIsLogical) || (language == LANGUAGE_NUM_EXPR && !childIsLogical)) buffer.append(")");

return buffer.toString();
}
Expand Down
8 changes: 8 additions & 0 deletions vcell-math/src/main/java/cbit/vcell/parser/ASTOrNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ public String infixString(int lang)
buffer.append(jjtGetChild(i).infixString(lang));
}
buffer.append("))");
}else if (lang == LANGUAGE_NUM_EXPR) {
int numChildren = jjtGetNumChildren();
for (int i=0;i<numChildren;i++){
if (i>0) buffer.append(" | ");
buffer.append("(0.0!=");
buffer.append(jjtGetChild(i).infixString(lang));
buffer.append(")");
}
}else{
for (int i=0;i<jjtGetNumChildren();i++){
if (i>0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ public String infixString(int lang){
buffer.append(" ^ ");
buffer.append(jjtGetChild(1).infixString(lang));
buffer.append(")");
} else if (lang == LANGUAGE_PYTHON) {
} else if (lang == LANGUAGE_PYTHON || lang == LANGUAGE_NUM_EXPR) {
buffer.append("((");
buffer.append(jjtGetChild(0).infixString(lang));
buffer.append(")**(");
Expand Down
4 changes: 4 additions & 0 deletions vcell-math/src/main/java/cbit/vcell/parser/Expression.java
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,10 @@ public String infix_Matlab()
public String infix_Python() {
return rootNode == null ? null : rootNode.infixString(SimpleNode.LANGUAGE_PYTHON);
}

public String infix_NumExpr() {
return rootNode == null ? null : rootNode.infixString(SimpleNode.LANGUAGE_NUM_EXPR);
}
/**
* This method was created by a SmartGuide.
* @return cbit.vcell.model.Expression
Expand Down
1 change: 1 addition & 0 deletions vcell-math/src/main/java/cbit/vcell/parser/SimpleNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public abstract class SimpleNode implements Node, java.io.Serializable {
final static int LANGUAGE_UNITS = 6;
final static int LANGUAGE_BNGL = 7;
final static int LANGUAGE_PYTHON = 8;
final static int LANGUAGE_NUM_EXPR = 9;

public SimpleNode(int i) {
id = i;
Expand Down
27 changes: 23 additions & 4 deletions vcell-math/src/test/java/cbit/vcell/parser/ExpressionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import cbit.vcell.parser.SymbolTableFunctionEntry.FunctionArgType;
import cbit.vcell.units.VCUnitSystem;
import net.sourceforge.interval.ia_math.RealInterval;
import org.apache.commons.math4.core.jdkmath.AccurateMath;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.BeforeAll;
Expand All @@ -29,7 +30,7 @@

@Tag("Fast")
public class ExpressionTest {
private static final boolean FAIL_ON_FIRST = true;
private static final boolean FAIL_ON_FIRST = false;
private static final boolean ENSURE_SAME_SEED = true; // change me
private static final long SAME_SEED = 1772035787937L;
private static File tempDir;
Expand All @@ -47,26 +48,29 @@ public class ExpressionTest {
// }

private static Set<String> skipInfixList = new HashSet<>(List.of(
"acoth(min(cot( - id_0), floor((0.8708964264563003 * 0.29826500971825554 * 0.6457243719944876))))",
"floor((pow(asinh(0.10118387985713251),cos(id_0)) * !(cot(id_0)) * min(csch(0.9837650351250335), coth(id_6))))",
" - (min(cot(id_0), csch(0.36737356253208997)) * atan2((0.7233117778683602 >= 0.9759958353333693), (0.1299551891086368 * id_7 * 0.7165699878023953)) / abs(id_1))",
"sech( - (coth(0.028177547796530367) ^ cot(id_1)))",
"coth(((sinh(0.6449666197606081) ^ (id_4 * id_8 * id_9)) + cot((id_3 < 0.2261011754280382)) + sech(abs(0.8120086721536708))))",
"(((cosh(id_3) * log(0.5598410965557573) * asin(0.015550241603964676)) * sin(floor(id_0)) * cosh(max(id_1, 0.585221823072032))) >= cot(floor((id_9 * id_2 * 0.30343841532874716))))",
"(cot(!( - id_6)) || ((tan(id_8) * atan2(id_5, id_7) * atan2(id_5, id_2)) * (pow(0.9897201897706811,id_1) * acosh(id_0) * (id_1 * 0.01088401412622797 * 0.8882413207692971)) * acot((0.3322552640871813 + id_9 + id_2))))",
"coth(floor(cot(abs(id_0))))",
" - factorial(( - id_2 / id_1 * ceil(id_5)))",
"csch(exp(csc((id_2 * id_2 * id_9))))",
"atan((tan(ceil(0.0)) || (cot(id_0) + asinh(0.5597772404451554) + sec(id_4))))",
"cot(factorial( - (0.20783348997951046 && id_5)))",
"(sqrt((atan2(0.7218065889622693, id_1) + asech(0.5381259927491784) + atanh(0.78855529690269))) >= asinh((atan2(id_0, id_9) <= cot(id_0))))",
" - cos((cos(id_1) ^ cot(id_0)))",
"pow(sinh(exp(acot(id_5))),tan(min(cot(id_0), (id_3 ^ id_8))))",
"atan(abs(cot(floor(id_5))))",
"coth(floor(cot((id_0 * 0.10280284481700341 * 0.3190467590050531))))",
" - (( - id_1 * acos(0.9408318720601397) * cot(id_0)) && asinh(min(id_6, 0.130136440506475)))",
"coth(factorial(floor(log(id_4))))",
"(cot(!(sech(id_3))) ^ min( - atan2(0.7870190039110462, id_2), ((id_7 * id_0 * 0.10143937822636417) * min(id_4, id_2) * sech(0.5569515469432404))))",
"cot(exp(coth((0.01722292952879423 ^ 0.7576553654648215))))",
"cosh(min(cot((id_7 * id_0 * 0.9871116728260001)), - !(id_6)))",
" - acos(((0.01949787088472099 * id_1 * id_9) ^ cot(id_0)))",
"factorial(min(tanh((id_9 ^ id_1)), floor( - 0.5153523376029631)))"
"(sec((asinh(0.0) && min(id_5, 0.648784608185188))) * (cot(floor(id_7)) <= (exp(id_7) * - 0.012384014519015274 * sqrt(0.7079441990714145))) * ceil(acot((0.8546428622959772 + id_0 + id_6))))",
"acsch(max(cot((id_0 * id_3 * 0.59541956426409)), (atan2(0.681661337447432, id_6) ^ atan2(0.2719307064055214, id_8))))"
));


Expand Down Expand Up @@ -413,6 +417,21 @@ public void testEval() {
}
}

@Test
public void testNumExprGeneration() throws ExpressionException {
Expression expr1 = new Expression("(2.0 && id_2) * 3.3 / acsc(1 + id_1) * pow(2, 3.2 ^ 2)");
String numExpr1 = expr1.infix_NumExpr();
Assertions.assertEquals("(where(((0.0!=2.0) & (0.0!=id_2)), 3.3 / (arcsin(1.0/((1.0 + id_1)))) * ((2.0)**(((3.2)**(2.0)))), 0.0))", numExpr1);

Expression expr2 = new Expression("(1.2 * 2.2) * logbase(3.3) * 2.0");
String numExpr2 = expr2.infix_NumExpr();
Assertions.assertEquals("((1.2 * 2.2) * (1.0/log(3.3)) * 2.0)", numExpr2);

Expression expr3 = new Expression("(1.2 || 2.2) * (3.3 && !(2.0 && 0.0))");
String numExpr3 = expr3.infix_NumExpr();
Assertions.assertEquals("(((0.0!=1.2) | (0.0!=2.2)) & ((0.0!=3.3) & (0.0!=~(((0.0!=2.0) & (0.0!=0.0))))))", numExpr3);
}


/**
* Insert the method's description here.
Expand Down
Loading
Loading