@@ -134,6 +134,29 @@ public final String getPythonTranslation() {
134134 default -> "math." + getName ();
135135 };
136136 }
137+
138+ public final String getNumExprTranslation () {
139+ return switch (this ){
140+ case USERDEFINED -> throw new IllegalArgumentException ("User defined functions have nothing to do with standard python names." );
141+ case ABS -> "abs" ;
142+ case MAX -> "maximum" ;
143+ case MIN -> "minimum" ;
144+ case CSC -> "1.0/" + SIN .getNumExprTranslation ();
145+ case COT -> "1.0/" + TAN .getNumExprTranslation ();
146+ case SEC -> "1.0/" + COS .getNumExprTranslation ();
147+ case ASIN -> "arcsin" ;
148+ case ACOS -> "arccos" ;
149+ case ATAN -> "arctan" ;
150+ case ATAN2 -> "arctan2" ;
151+ case CSCH -> "1.0/" + SINH .getNumExprTranslation ();
152+ case COTH -> "1.0/" + TANH .getNumExprTranslation ();
153+ case SECH -> "1.0/" + COSH .getNumExprTranslation ();
154+ case LOGBASE -> "1.0/" + LOG .getNumExprTranslation ();
155+ case ACSC , ACOT , ASEC , ACSCH , ACOTH , ASECH , FACTORIAL -> throw new UnsupportedOperationException ("Python has no equivalent name." );
156+ default -> getName ();
157+ };
158+ }
159+
137160 public final String getHtmlDescription () {
138161 return htmlDescription ;
139162 }
@@ -2389,12 +2412,28 @@ String getMathMLName() {
23892412}
23902413
23912414String getPythonName (){
2392- if (funcType == FunctionType .USERDEFINED ){
2415+ return getPythonName (funcType );
2416+ }
2417+
2418+ // This function is used when we need to "override" `funcType` to get the correct answer
2419+ private String getPythonName (ASTFuncNode .FunctionType functionType ){
2420+ if (functionType == FunctionType .USERDEFINED ){
23932421 return funcName ;
23942422 }
2395- return funcType .getPythonTranslation ();
2423+ return functionType .getPythonTranslation ();
23962424}
23972425
2426+ String getNumExprName (){
2427+ return getNumExprName (funcType );
2428+ }
2429+
2430+ // This function is used when we need to "override" `funcType` to get the correct answer
2431+ private String getNumExprName (ASTFuncNode .FunctionType functionType ){
2432+ if (functionType == FunctionType .USERDEFINED ){
2433+ return funcName ;
2434+ }
2435+ return functionType .getNumExprTranslation ();
2436+ }
23982437
23992438/**
24002439 * Insert the method's description here.
@@ -2455,7 +2494,7 @@ public String infixString(int lang) {
24552494 buffer .append ("," );
24562495 buffer .append ("((double)(" + jjtGetChild (1 ).infixString (lang ) + "))" );
24572496 buffer .append (")" );
2458- } else if (lang == LANGUAGE_PYTHON ) {
2497+ } else if (lang == LANGUAGE_PYTHON || lang == LANGUAGE_NUM_EXPR ) {
24592498 buffer .append ("((" );
24602499 buffer .append (jjtGetChild (0 ).infixString (lang ));
24612500 buffer .append (")**(" );
@@ -2485,48 +2524,89 @@ public String infixString(int lang) {
24852524 buffer .append (jjtGetChild (0 ).infixString (lang ));
24862525 buffer .append ("))" );
24872526 break ;
2527+ } else if (lang == LANGUAGE_NUM_EXPR ){
2528+ // Need parenthesis to encapsulate the "1.0/x" operation
2529+ buffer .append ("(" );
2530+ buffer .append (getNumExprName ());
2531+ buffer .append ("(" );
2532+ buffer .append (jjtGetChild (0 ).infixString (lang ));
2533+ buffer .append ("))" );
2534+ break ;
24882535 }
24892536 // DO NOT PUT A "BREAK" HERE! We want to "fall through" to default!
24902537 }
24912538 case ACSC :
2492- case ACOT :
24932539 case ASEC :
24942540 case ACSCH :
24952541 case ACOTH :
24962542 case ASECH :{
2497- if (lang == LANGUAGE_PYTHON ){ // Need parenthesis to encapsulate the "1.0/x" operation
2543+ if (lang == LANGUAGE_PYTHON ){
2544+ // Need parenthesis to encapsulate the "1.0/x" operation
24982545 Map <FunctionType , FunctionType > inversionMap = Map .of (
24992546 FunctionType .ACSC , FunctionType .ASIN ,
2500- FunctionType .ACOT , FunctionType .ATAN ,
25012547 FunctionType .ASEC , FunctionType .ACOS ,
25022548 FunctionType .ACSCH , FunctionType .ASINH ,
25032549 FunctionType .ACOTH , FunctionType .ATANH ,
25042550 FunctionType .ASECH , FunctionType .ACOSH
25052551 );
25062552 FunctionType inversionType = inversionMap .get (funcType );
25072553 buffer .append ("(" );
2508- buffer .append (getPythonName ());
2554+ buffer .append (getPythonName (inversionType ));
2555+ buffer .append ("(1.0/(" );
2556+ buffer .append (jjtGetChild (0 ).infixString (lang ));
2557+ buffer .append (")))" );
2558+ break ;
2559+ } else if (lang == LANGUAGE_NUM_EXPR ){
2560+ // Need parenthesis to encapsulate the "1.0/x" operation
2561+ Map <FunctionType , FunctionType > inversionMap = Map .of (
2562+ FunctionType .ACSC , FunctionType .ASIN ,
2563+ FunctionType .ASEC , FunctionType .ACOS ,
2564+ FunctionType .ACSCH , FunctionType .ASINH ,
2565+ FunctionType .ACOTH , FunctionType .ATANH ,
2566+ FunctionType .ASECH , FunctionType .ACOSH
2567+ );
2568+ FunctionType inversionType = inversionMap .get (funcType );
2569+ buffer .append ("(" );
2570+ buffer .append (getNumExprName (inversionType ));
25092571 buffer .append ("(1.0/(" );
25102572 buffer .append (jjtGetChild (0 ).infixString (lang ));
25112573 buffer .append (")))" );
25122574 break ;
25132575 }
25142576 // DO NOT PUT A "BREAK" HERE! We want to "fall through" to default!
25152577 }
2578+ case ACOT :{
2579+ if (lang == LANGUAGE_PYTHON ){
2580+ buffer .append ("(math.pi/2.0 - " );
2581+ buffer .append (getPythonName (FunctionType .ATAN ));
2582+ buffer .append ("(" );
2583+ buffer .append (jjtGetChild (0 ).infixString (lang ));
2584+ buffer .append ("))" );
2585+ break ;
2586+ } else if (lang == LANGUAGE_NUM_EXPR ){
2587+ buffer .append (String .format ("((%s/2.0) - " , Math .PI ));
2588+ buffer .append (getNumExprName (FunctionType .ATAN ));
2589+ buffer .append ("(" );
2590+ buffer .append (jjtGetChild (0 ).infixString (lang ));
2591+ buffer .append ("))" );
2592+ break ;
2593+ }
2594+ }
25162595 case FACTORIAL :
25172596 if (lang == LANGUAGE_PYTHON ){
25182597 String name = getPythonName ();
2519- // Since VCell now only accepts integer-values for factorial, integer casting should be safe
2520- buffer .append (name ).append ("(int(" );
2598+ // Since VCell now only accepts integer-values for factorial,
2599+ // Before we used casting, but there's a chance after math python can generate a different round off error.
2600+ // Since we know VCell only accepts integer, we're safe using `round` instead of just `int`
2601+ buffer .append (name ).append ("(round(" );
25212602 if (1 < this .jjtGetNumChildren ()) throw new UnsupportedOperationException ("Cannot take factorial of multiple children" );
25222603 buffer .append (jjtGetChild (0 ).infixString (lang ));
25232604 buffer .append ("))" );
25242605 break ;
25252606 }
25262607 // DO NOT PUT A "BREAK" HERE! We want to "fall through" to default!
25272608 default :{
2528- boolean needPythonicVersionOfName = lang == LANGUAGE_PYTHON && !funcType .equals (FunctionType .USERDEFINED );
2529- String name = needPythonicVersionOfName ? getPythonName (): getName ();
2609+ String name = getAppropriateName (lang );
25302610 buffer .append (name + "(" );
25312611 for (int i =0 ;i <jjtGetNumChildren ();i ++){
25322612 if (i >0 ) buffer .append (", " );
@@ -2545,6 +2625,15 @@ public String infixString(int lang) {
25452625
25462626}
25472627
2628+ private String getAppropriateName (int language ){
2629+ if (FunctionType .USERDEFINED .equals (funcType )) return getName ();
2630+ return switch (language ){
2631+ case LANGUAGE_PYTHON -> getPythonName ();
2632+ case LANGUAGE_NUM_EXPR -> getNumExprName ();
2633+ default -> getName ();
2634+ };
2635+ }
2636+
25482637/**
25492638 * Insert the method's description here.
25502639 * Creation date: (6/20/01 11:04:41 AM)
0 commit comments