"...ssh:/git@developer.sourcefind.cn:2222/tsoc/openmm.git" did not exist on "b5812780ed91f59a25d701cea86a9921a975a6ed"
Commit 00e2c6a3 authored by peastman's avatar peastman
Browse files

Support atan2() in custom expressions

parent 6e3c2142
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013-2016 Stanford University and the Authors. * * Portions copyright (c) 2013-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -103,6 +103,7 @@ private: ...@@ -103,6 +103,7 @@ private:
#ifdef LEPTON_USE_JIT #ifdef LEPTON_USE_JIT
void generateJitCode(); void generateJitCode();
void generateSingleArgCall(asmjit::X86Compiler& c, asmjit::X86Xmm& dest, asmjit::X86Xmm& arg, double (*function)(double)); void generateSingleArgCall(asmjit::X86Compiler& c, asmjit::X86Xmm& dest, asmjit::X86Xmm& arg, double (*function)(double));
void generateTwoArgCall(asmjit::X86Compiler& c, asmjit::X86Xmm& dest, asmjit::X86Xmm& arg1, asmjit::X86Xmm& arg2, double (*function)(double, double));
std::vector<double> constants; std::vector<double> constants;
asmjit::JitRuntime runtime; asmjit::JitRuntime runtime;
#endif #endif
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2009-2018 Stanford University and the Authors. * * Portions copyright (c) 2009-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -63,7 +63,7 @@ public: ...@@ -63,7 +63,7 @@ public:
* can be used when processing or analyzing parsed expressions. * can be used when processing or analyzing parsed expressions.
*/ */
enum Id {CONSTANT, VARIABLE, CUSTOM, ADD, SUBTRACT, MULTIPLY, DIVIDE, POWER, NEGATE, SQRT, EXP, LOG, enum Id {CONSTANT, VARIABLE, CUSTOM, ADD, SUBTRACT, MULTIPLY, DIVIDE, POWER, NEGATE, SQRT, EXP, LOG,
SIN, COS, SEC, CSC, TAN, COT, ASIN, ACOS, ATAN, SINH, COSH, TANH, ERF, ERFC, STEP, DELTA, SQUARE, CUBE, RECIPROCAL, SIN, COS, SEC, CSC, TAN, COT, ASIN, ACOS, ATAN, ATAN2, SINH, COSH, TANH, ERF, ERFC, STEP, DELTA, SQUARE, CUBE, RECIPROCAL,
ADD_CONSTANT, MULTIPLY_CONSTANT, POWER_CONSTANT, MIN, MAX, ABS, FLOOR, CEIL, SELECT}; ADD_CONSTANT, MULTIPLY_CONSTANT, POWER_CONSTANT, MIN, MAX, ABS, FLOOR, CEIL, SELECT};
/** /**
* Get the name of this Operation. * Get the name of this Operation.
...@@ -137,6 +137,7 @@ public: ...@@ -137,6 +137,7 @@ public:
class Asin; class Asin;
class Acos; class Acos;
class Atan; class Atan;
class Atan2;
class Sinh; class Sinh;
class Cosh; class Cosh;
class Tanh; class Tanh;
...@@ -689,6 +690,28 @@ public: ...@@ -689,6 +690,28 @@ public:
ExpressionTreeNode differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs, const std::string& variable) const; ExpressionTreeNode differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs, const std::string& variable) const;
}; };
class LEPTON_EXPORT Operation::Atan2 : public Operation {
public:
Atan2() {
}
std::string getName() const {
return "atan2";
}
Id getId() const {
return ATAN2;
}
int getNumArguments() const {
return 2;
}
Operation* clone() const {
return new Atan2();
}
double evaluate(double* args, const std::map<std::string, double>& variables) const {
return std::atan2(args[0], args[1]);
}
ExpressionTreeNode differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs, const std::string& variable) const;
};
class LEPTON_EXPORT Operation::Sinh : public Operation { class LEPTON_EXPORT Operation::Sinh : public Operation {
public: public:
Sinh() { Sinh() {
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2013-2016 Stanford University and the Authors. * * Portions copyright (c) 2013-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -294,6 +294,9 @@ void CompiledExpression::generateJitCode() { ...@@ -294,6 +294,9 @@ void CompiledExpression::generateJitCode() {
c.movsd(workspaceVar[target[step]], workspaceVar[args[0]]); c.movsd(workspaceVar[target[step]], workspaceVar[args[0]]);
c.divsd(workspaceVar[target[step]], workspaceVar[args[1]]); c.divsd(workspaceVar[target[step]], workspaceVar[args[1]]);
break; break;
case Operation::POWER:
generateTwoArgCall(c, workspaceVar[target[step]], workspaceVar[args[0]], workspaceVar[args[1]], pow);
break;
case Operation::NEGATE: case Operation::NEGATE:
c.xorps(workspaceVar[target[step]], workspaceVar[target[step]]); c.xorps(workspaceVar[target[step]], workspaceVar[target[step]]);
c.subsd(workspaceVar[target[step]], workspaceVar[args[0]]); c.subsd(workspaceVar[target[step]], workspaceVar[args[0]]);
...@@ -325,6 +328,9 @@ void CompiledExpression::generateJitCode() { ...@@ -325,6 +328,9 @@ void CompiledExpression::generateJitCode() {
case Operation::ATAN: case Operation::ATAN:
generateSingleArgCall(c, workspaceVar[target[step]], workspaceVar[args[0]], atan); generateSingleArgCall(c, workspaceVar[target[step]], workspaceVar[args[0]], atan);
break; break;
case Operation::ATAN2:
generateTwoArgCall(c, workspaceVar[target[step]], workspaceVar[args[0]], workspaceVar[args[1]], atan2);
break;
case Operation::SINH: case Operation::SINH:
generateSingleArgCall(c, workspaceVar[target[step]], workspaceVar[args[0]], sinh); generateSingleArgCall(c, workspaceVar[target[step]], workspaceVar[args[0]], sinh);
break; break;
...@@ -400,4 +406,13 @@ void CompiledExpression::generateSingleArgCall(X86Compiler& c, X86Xmm& dest, X86 ...@@ -400,4 +406,13 @@ void CompiledExpression::generateSingleArgCall(X86Compiler& c, X86Xmm& dest, X86
call->setArg(0, arg); call->setArg(0, arg);
call->setRet(0, dest); call->setRet(0, dest);
} }
void CompiledExpression::generateTwoArgCall(X86Compiler& c, X86Xmm& dest, X86Xmm& arg1, X86Xmm& arg2, double (*function)(double, double)) {
X86Gp fn = c.newIntPtr();
c.mov(fn, imm_ptr((void*) function));
CCFuncCall* call = c.call(fn, FuncSignature2<double, double, double>());
call->setArg(0, arg1);
call->setArg(1, arg2);
call->setRet(0, dest);
}
#endif #endif
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2009-2015 Stanford University and the Authors. * * Portions copyright (c) 2009-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -202,6 +202,16 @@ ExpressionTreeNode Operation::Atan::differentiate(const std::vector<ExpressionTr ...@@ -202,6 +202,16 @@ ExpressionTreeNode Operation::Atan::differentiate(const std::vector<ExpressionTr
childDerivs[0]); childDerivs[0]);
} }
ExpressionTreeNode Operation::Atan2::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs, const std::string& variable) const {
return ExpressionTreeNode(new Operation::Divide(),
ExpressionTreeNode(new Operation::Subtract(),
ExpressionTreeNode(new Operation::Multiply(), children[1], childDerivs[0]),
ExpressionTreeNode(new Operation::Multiply(), children[0], childDerivs[1])),
ExpressionTreeNode(new Operation::Add(),
ExpressionTreeNode(new Operation::Square(), children[0]),
ExpressionTreeNode(new Operation::Square(), children[1])));
}
ExpressionTreeNode Operation::Sinh::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs, const std::string& variable) const { ExpressionTreeNode Operation::Sinh::differentiate(const std::vector<ExpressionTreeNode>& children, const std::vector<ExpressionTreeNode>& childDerivs, const std::string& variable) const {
return ExpressionTreeNode(new Operation::Multiply(), return ExpressionTreeNode(new Operation::Multiply(),
ExpressionTreeNode(new Operation::Cosh(), ExpressionTreeNode(new Operation::Cosh(),
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2009-2015 Stanford University and the Authors. * * Portions copyright (c) 2009-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -313,6 +313,7 @@ Operation* Parser::getFunctionOperation(const std::string& name, const map<strin ...@@ -313,6 +313,7 @@ Operation* Parser::getFunctionOperation(const std::string& name, const map<strin
opMap["asin"] = Operation::ASIN; opMap["asin"] = Operation::ASIN;
opMap["acos"] = Operation::ACOS; opMap["acos"] = Operation::ACOS;
opMap["atan"] = Operation::ATAN; opMap["atan"] = Operation::ATAN;
opMap["atan2"] = Operation::ATAN2;
opMap["sinh"] = Operation::SINH; opMap["sinh"] = Operation::SINH;
opMap["cosh"] = Operation::COSH; opMap["cosh"] = Operation::COSH;
opMap["tanh"] = Operation::TANH; opMap["tanh"] = Operation::TANH;
...@@ -368,6 +369,8 @@ Operation* Parser::getFunctionOperation(const std::string& name, const map<strin ...@@ -368,6 +369,8 @@ Operation* Parser::getFunctionOperation(const std::string& name, const map<strin
return new Operation::Acos(); return new Operation::Acos();
case Operation::ATAN: case Operation::ATAN:
return new Operation::Atan(); return new Operation::Atan();
case Operation::ATAN2:
return new Operation::Atan2();
case Operation::SINH: case Operation::SINH:
return new Operation::Sinh(); return new Operation::Sinh();
case Operation::COSH: case Operation::COSH:
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2009-2015 Stanford University and the Authors. * * Portions copyright (c) 2009-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -123,7 +123,7 @@ private: ...@@ -123,7 +123,7 @@ private:
void findRelatedPowers(const Lepton::ExpressionTreeNode& node, const Lepton::ExpressionTreeNode& searchNode, void findRelatedPowers(const Lepton::ExpressionTreeNode& node, const Lepton::ExpressionTreeNode& searchNode,
std::map<int, const Lepton::ExpressionTreeNode*>& powers); std::map<int, const Lepton::ExpressionTreeNode*>& powers);
void callFunction(std::stringstream& out, std::string singleFn, std::string doubleFn, const std::string& arg, const std::string& tempType); void callFunction(std::stringstream& out, std::string singleFn, std::string doubleFn, const std::string& arg, const std::string& tempType);
void callFunction2(std::stringstream& out, std::string fn, const std::string& arg1, const std::string& arg2, const std::string& tempType); void callFunction2(std::stringstream& out, std::string singleFn, std::string doubleFn, const std::string& arg1, const std::string& arg2, const std::string& tempType);
std::vector<std::vector<double> > computeFunctionParameters(const std::vector<const TabulatedFunction*>& functions); std::vector<std::vector<double> > computeFunctionParameters(const std::vector<const TabulatedFunction*>& functions);
CudaContext& context; CudaContext& context;
FunctionPlaceholder fp1, fp2, fp3, periodicDistance; FunctionPlaceholder fp1, fp2, fp3, periodicDistance;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2009-2018 Stanford University and the Authors. * * Portions copyright (c) 2009-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -484,6 +484,9 @@ void CudaExpressionUtilities::processExpression(stringstream& out, const Express ...@@ -484,6 +484,9 @@ void CudaExpressionUtilities::processExpression(stringstream& out, const Express
case Operation::ATAN: case Operation::ATAN:
callFunction(out, "atanf", "atan", getTempName(node.getChildren()[0], temps), tempType); callFunction(out, "atanf", "atan", getTempName(node.getChildren()[0], temps), tempType);
break; break;
case Operation::ATAN2:
callFunction2(out, "atan2f", "atan2", getTempName(node.getChildren()[0], temps), getTempName(node.getChildren()[1], tempType);
break;
case Operation::SINH: case Operation::SINH:
callFunction(out, "sinh", "sinh", getTempName(node.getChildren()[0], temps), tempType); callFunction(out, "sinh", "sinh", getTempName(node.getChildren()[0], temps), tempType);
break; break;
...@@ -619,10 +622,10 @@ void CudaExpressionUtilities::processExpression(stringstream& out, const Express ...@@ -619,10 +622,10 @@ void CudaExpressionUtilities::processExpression(stringstream& out, const Express
break; break;
} }
case Operation::MIN: case Operation::MIN:
callFunction2(out, "min", getTempName(node.getChildren()[0], temps), getTempName(node.getChildren()[1], temps), tempType); callFunction2(out, "min", "min", getTempName(node.getChildren()[0], temps), getTempName(node.getChildren()[1], temps), tempType);
break; break;
case Operation::MAX: case Operation::MAX:
callFunction2(out, "max", getTempName(node.getChildren()[0], temps), getTempName(node.getChildren()[1], temps), tempType); callFunction2(out, "max", "max", getTempName(node.getChildren()[0], temps), getTempName(node.getChildren()[1], temps), tempType);
break; break;
case Operation::ABS: case Operation::ABS:
callFunction(out, "fabs", "fabs", getTempName(node.getChildren()[0], temps), tempType); callFunction(out, "fabs", "fabs", getTempName(node.getChildren()[0], temps), tempType);
...@@ -923,22 +926,17 @@ Lepton::CustomFunction* CudaExpressionUtilities::getPeriodicDistancePlaceholder( ...@@ -923,22 +926,17 @@ Lepton::CustomFunction* CudaExpressionUtilities::getPeriodicDistancePlaceholder(
void CudaExpressionUtilities::callFunction(stringstream& out, string singleFn, string doubleFn, const string& arg, const string& tempType) { void CudaExpressionUtilities::callFunction(stringstream& out, string singleFn, string doubleFn, const string& arg, const string& tempType) {
bool isDouble = (tempType[0] == 'd'); bool isDouble = (tempType[0] == 'd');
bool isVector = (tempType[tempType.size()-1] == '3'); bool isVector = (tempType[tempType.size()-1] == '3');
if (isVector) { string fn = (isDouble ? doubleFn : singleFn);
if (isDouble) if (isVector)
out<<"make_double3("<<doubleFn<<"("<<arg<<".x), "<<doubleFn<<"("<<arg<<".y), "<<doubleFn<<"("<<arg<<".z))"; out<<"make_"<<tempType<<"("<<fn<<"("<<arg<<".x), "<<fn<<"("<<arg<<".y), "<<fn<<"("<<arg<<".z))";
else else
out<<"make_float3("<<singleFn<<"("<<arg<<".x), "<<singleFn<<"("<<arg<<".y), "<<singleFn<<"("<<arg<<".z))"; out<<fn<<"("<<arg<<")";
}
else {
if (isDouble)
out<<doubleFn<<"("<<arg<<")";
else
out<<singleFn<<"("<<arg<<")";
}
} }
void CudaExpressionUtilities::callFunction2(stringstream& out, string fn, const string& arg1, const string& arg2, const string& tempType) { void CudaExpressionUtilities::callFunction2(stringstream& out, string singleFn, string doubleFn, const string& arg1, const string& arg2, const string& tempType) {
bool isDouble = (tempType[0] == 'd');
bool isVector = (tempType[tempType.size()-1] == '3'); bool isVector = (tempType[tempType.size()-1] == '3');
string fn = (isDouble ? doubleFn : singleFn);
if (isVector) { if (isVector) {
out<<"make_"<<tempType<<"("; out<<"make_"<<tempType<<"(";
out<<fn<<"("<<arg1<<".x, "<<arg2<<".x), "; out<<fn<<"("<<arg1<<".x, "<<arg2<<".x), ";
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2009-2018 Stanford University and the Authors. * * Portions copyright (c) 2009-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -464,6 +464,9 @@ void OpenCLExpressionUtilities::processExpression(stringstream& out, const Expre ...@@ -464,6 +464,9 @@ void OpenCLExpressionUtilities::processExpression(stringstream& out, const Expre
case Operation::ATAN: case Operation::ATAN:
out << "atan(" << getTempName(node.getChildren()[0], temps) << ")"; out << "atan(" << getTempName(node.getChildren()[0], temps) << ")";
break; break;
case Operation::ATAN2:
out << "atan2(" << getTempName(node.getChildren()[0], temps) << ", " << getTempName(node.getChildren()[1], temps) << ")";
break;
case Operation::SINH: case Operation::SINH:
out << "sinh(" << getTempName(node.getChildren()[0], temps) << ")"; out << "sinh(" << getTempName(node.getChildren()[0], temps) << ")";
break; break;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Biological Structures at Stanford, funded under the NIH Roadmap for * * Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. * * Medical Research, grant U54 GM072970. See https://simtk.org. *
* * * *
* Portions copyright (c) 2008-2015 Stanford University and the Authors. * * Portions copyright (c) 2008-2019 Stanford University and the Authors. *
* Authors: Peter Eastman * * Authors: Peter Eastman *
* Contributors: * * Contributors: *
* * * *
...@@ -216,6 +216,21 @@ void testIllegalVariable() { ...@@ -216,6 +216,21 @@ void testIllegalVariable() {
ASSERT(threwException); ASSERT(threwException);
} }
void testAtan2() {
System system;
system.addParticle(1.0);
CustomExternalForce* force = new CustomExternalForce("atan2(x, y)");
force->addParticle(0);
system.addForce(force);
VerletIntegrator integrator(0.01);
Context context(system, integrator, platform);
vector<Vec3> positions(1);
positions[0] = Vec3(1.5, -2.1, 1.2);
context.setPositions(positions);
State state = context.getState(State::Energy);
ASSERT_EQUAL_TOL(atan2(positions[0][0], positions[0][1]), state.getPotentialEnergy(), 1e-5);
}
void runPlatformTests(); void runPlatformTests();
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
...@@ -226,6 +241,7 @@ int main(int argc, char* argv[]) { ...@@ -226,6 +241,7 @@ int main(int argc, char* argv[]) {
testPeriodic(); testPeriodic();
testZeroPeriodicDistance(); testZeroPeriodicDistance();
testIllegalVariable(); testIllegalVariable();
testAtan2();
runPlatformTests(); runPlatformTests();
} }
catch(const exception& e) { catch(const exception& e) {
......
...@@ -265,6 +265,7 @@ int main() { ...@@ -265,6 +265,7 @@ int main() {
verifyEvaluation("ceil(x)", -2.1, 3.0, -2.0); verifyEvaluation("ceil(x)", -2.1, 3.0, -2.0);
verifyEvaluation("select(x, 1.0, y)", 0.3, 2.0, 1.0); verifyEvaluation("select(x, 1.0, y)", 0.3, 2.0, 1.0);
verifyEvaluation("select(x, 1.0, y)", 0.0, 2.0, 2.0); verifyEvaluation("select(x, 1.0, y)", 0.0, 2.0, 2.0);
verifyEvaluation("atan2(x, y)", 3.0, 1.5, std::atan(2.0));
verifyInvalidExpression("1..2"); verifyInvalidExpression("1..2");
verifyInvalidExpression("1*(2+3"); verifyInvalidExpression("1*(2+3");
verifyInvalidExpression("5++4"); verifyInvalidExpression("5++4");
...@@ -285,6 +286,7 @@ int main() { ...@@ -285,6 +286,7 @@ int main() {
verifyDerivative("asin(x)", "1/sqrt(1-x^2)"); verifyDerivative("asin(x)", "1/sqrt(1-x^2)");
verifyDerivative("acos(x)", "-1/sqrt(1-x^2)"); verifyDerivative("acos(x)", "-1/sqrt(1-x^2)");
verifyDerivative("atan(x)", "1/(1+x^2)"); verifyDerivative("atan(x)", "1/(1+x^2)");
verifyDerivative("atan2(2*x,y)", "2*y/(4*x^2+y^2)");
verifyDerivative("sinh(x)", "cosh(x)"); verifyDerivative("sinh(x)", "cosh(x)");
verifyDerivative("cosh(x)", "sinh(x)"); verifyDerivative("cosh(x)", "sinh(x)");
verifyDerivative("tanh(x)", "1/(cosh(x)^2)"); verifyDerivative("tanh(x)", "1/(cosh(x)^2)");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment