Choose Language Hide Translation Bar
Highlighted
JesperJohansen
Level IV

Defining a function that takes another function as argument

Hi

I am trying to define a function that calculates the error of assuming linearity in a region of another function.

My function must do the following:

  1. Accept 3 inputs, the nonlinear function (preferably just a function name defined elsewhere, f(x)), the center of the region (xi) and the half-width of the region being investigated (delta).
  2. Generate slope and intercept of the asymptote linear function in xi, Lin(x).
  3. Generate an error function err(x) = Lin(x) - f(x), err(xi) = 0 by definition.
  4. Integrate err(x), from xi-delta to xi+delta, and divide by 2*delta, to find the mean error in the region.
  5. Use the mean error to calculate the standard deviation of the error in the region (also involves integration)

In trying to do this I run into three problems:

  1. How do I get the Function-function in jsl to accept another function as input?
  2. How do i get the Derivative-function to differentiate a function argument, supplied by function name, e.g. Derivative(f(x),x)?
  3. How do I get jsl to integrate a function in certain region? I can't find a built-in function to do this.

I hope some can help.

BR
Jesper
1 ACCEPTED SOLUTION

Accepted Solutions
Highlighted
gzmorgan0
Super User

Re: Defining a function that takes another function as argument

Jesper,

You did not mention what version of JMP you are using. JMP 12 and higher have both integrate() and symbolic derivative() functions. 

 

Attached is an example . It does not create the asymptotic linear formual just a simple linear. But teh goal of this script is to demo syntax.  To reference symbolic formulas, they need to be expressions.  In tehscripting Index, you should look up:

  • Substitute(), NameExpr, EvalInsert(), Parse(), Simplify() , and
  • of course, Integrate(), Derivative(). Note for the integrate function use an empty (dot) to specify infinity. 

The results of the test cases are written to the Log window, and shown in the second code window.

 

You should post your final version. It looks like a cool function.

Names Default to Here(1);

//I think you meant you wanted a formula.
//The Jesper Function just finds the linear function, not the asymptotic linear function ... this code is a syntax example.

funList = {2*x+3, 2^x + 3, 2*sqrt(x)+1.2}; //test case for 3 formulas for x1=1 and x2=2, i.e. cntr=1.5, delta=0.5

Jesper = Function({form_xp, cntr, delta}, {x1, x2, y1, y2, m, b, err_xp, err, deriv },
	x1 = cntr - delta; 
	x2 = cntr + delta; 
	y1 = Eval( Substitute(NameExpr(form_xp), Expr(x), x1 ));
	y2 = Eval( Substitute(NameExpr(form_xp), Expr(x), x2 ));
	m  = (y2-y1)/(x2-x1) ;
	b  = y2 - m*x2;
	lin_xp = Parse( EvalInsert( "^m^*x + ^b^"));
	err_xp = Substitute(Expr((a)-(b)), Expr(a), NameExpr(form_xp), Expr(b), NameExpr(lin_xp) );
	err_xp = SimplifyExpr(NameExpr(err_xp));
	err    = 1/(2*delta) * Eval ( Substitute( NameExpr(Integrate(_xp, x, _lo, _hi)), Expr(_xp), NameExpr(err_xp), Expr(_lo), x1, Expr(_hi), x2)  );
	deriv  = Derivative(NameExpr(form_xp), x);
	Eval List({err, NameExpr(form_xp), NameExpr(lin_xp), NameExpr(err_xp),  NameExpr(deriv)});
);

for(i=1 ,i<=nitems(funlist), i++,
	{err, func_xp, line_xp, err_xp, deriv_xp} =Jesper(funlist[i], 1.5, .5);
	show(i, err, func_xp, line_xp, err_xp, deriv_xp);
	write("\!N" ||Repeat("-", 80) );
);

The results from these 3 formulas are

/*:

i = 1;
err = 0;
func_xp = 2 * x + 3;
line_xp = 2 * x + 3;
err_xp = 0;
deriv_xp = 2;
--------------------------------------------------------------------------------
i = 2;
err = -0.114609918222073;
func_xp = 2 ^ x + 3;
line_xp = 2 * x + 3;
err_xp = -2 * x + 2 ^ x;
deriv_xp = 2 ^ x * Log(2);
--------------------------------------------------------------------------------
i = 3;
err = 0.0236892706214399;
func_xp = 2 * Sqrt(x) + 1.2;
line_xp = 0.82842712474619 * x + 2.37157287525381;
err_xp = (-1.17157287525381) + -0.82842712474619 * x + 2 * Sqrt(x);
deriv_xp = 2 * (0.5 / Sqrt(x));
--------------------------------------------------------------------------------

View solution in original post

7 REPLIES 7
Highlighted
gzmorgan0
Super User

Re: Defining a function that takes another function as argument

Jesper,

You did not mention what version of JMP you are using. JMP 12 and higher have both integrate() and symbolic derivative() functions. 

 

Attached is an example . It does not create the asymptotic linear formual just a simple linear. But teh goal of this script is to demo syntax.  To reference symbolic formulas, they need to be expressions.  In tehscripting Index, you should look up:

  • Substitute(), NameExpr, EvalInsert(), Parse(), Simplify() , and
  • of course, Integrate(), Derivative(). Note for the integrate function use an empty (dot) to specify infinity. 

The results of the test cases are written to the Log window, and shown in the second code window.

 

You should post your final version. It looks like a cool function.

Names Default to Here(1);

//I think you meant you wanted a formula.
//The Jesper Function just finds the linear function, not the asymptotic linear function ... this code is a syntax example.

funList = {2*x+3, 2^x + 3, 2*sqrt(x)+1.2}; //test case for 3 formulas for x1=1 and x2=2, i.e. cntr=1.5, delta=0.5

Jesper = Function({form_xp, cntr, delta}, {x1, x2, y1, y2, m, b, err_xp, err, deriv },
	x1 = cntr - delta; 
	x2 = cntr + delta; 
	y1 = Eval( Substitute(NameExpr(form_xp), Expr(x), x1 ));
	y2 = Eval( Substitute(NameExpr(form_xp), Expr(x), x2 ));
	m  = (y2-y1)/(x2-x1) ;
	b  = y2 - m*x2;
	lin_xp = Parse( EvalInsert( "^m^*x + ^b^"));
	err_xp = Substitute(Expr((a)-(b)), Expr(a), NameExpr(form_xp), Expr(b), NameExpr(lin_xp) );
	err_xp = SimplifyExpr(NameExpr(err_xp));
	err    = 1/(2*delta) * Eval ( Substitute( NameExpr(Integrate(_xp, x, _lo, _hi)), Expr(_xp), NameExpr(err_xp), Expr(_lo), x1, Expr(_hi), x2)  );
	deriv  = Derivative(NameExpr(form_xp), x);
	Eval List({err, NameExpr(form_xp), NameExpr(lin_xp), NameExpr(err_xp),  NameExpr(deriv)});
);

for(i=1 ,i<=nitems(funlist), i++,
	{err, func_xp, line_xp, err_xp, deriv_xp} =Jesper(funlist[i], 1.5, .5);
	show(i, err, func_xp, line_xp, err_xp, deriv_xp);
	write("\!N" ||Repeat("-", 80) );
);

The results from these 3 formulas are

/*:

i = 1;
err = 0;
func_xp = 2 * x + 3;
line_xp = 2 * x + 3;
err_xp = 0;
deriv_xp = 2;
--------------------------------------------------------------------------------
i = 2;
err = -0.114609918222073;
func_xp = 2 ^ x + 3;
line_xp = 2 * x + 3;
err_xp = -2 * x + 2 ^ x;
deriv_xp = 2 ^ x * Log(2);
--------------------------------------------------------------------------------
i = 3;
err = 0.0236892706214399;
func_xp = 2 * Sqrt(x) + 1.2;
line_xp = 0.82842712474619 * x + 2.37157287525381;
err_xp = (-1.17157287525381) + -0.82842712474619 * x + 2 * Sqrt(x);
deriv_xp = 2 * (0.5 / Sqrt(x));
--------------------------------------------------------------------------------

View solution in original post

Highlighted
JesperJohansen
Level IV

Re: Defining a function that takes another function as argument

Thank you for your reply.

The Integrate function solved one of my problems.

Maybe I am not understanding everything in your code, but I still cannot get jsl to give me the derivative of a named function. In other words, I want der1=der2 in the code:

func = Function({x},x^2);
der1 = Derivative(x^2,x);
der2 = Derivative(func(x),x);

There must be som way to achieve this.

Also, your code feeds the Jesper function an expression, not the function name to the the Jesper function. How do I do this? Ideally the Jesper function should accept both, i.e. Jesper(x^2)  = Jesper(func(x))

BR
Jesper
Highlighted
JesperJohansen
Level IV

Re: Defining a function that takes another function as argument

Oh, and I use JMP 14

BR
Jesper
Highlighted
gzmorgan0
Super User

Re: Defining a function that takes another function as argument

Jesper,

 

I know you would like to find  Derivative( f(x), x), however, the JMP symbolic Derivative function requires the first argument to be an expression.  It might be easier to help you, if you explain how you want this to work. I expect you want to build your own function or application that has 3 arguments and you mentioned f(x), but where and how is f(x) being read-in or defined?

JMP expressions and functions try to evaluate immediately.  Copy this next block of code into a script window, right click to show the embedded log, then highlight and run the segments of code and look at the results in the log. This is the way JMP functions and expressions work.

Names Default to Here(1);

func = Function({x}, x^2);
xp   = Expr(x^2);

show(func, func(x)); //will get an error func(x/*###*/) since there is no such symbol, x
//----run to here---

show(xp); // xp=x^2 JMP does not evaluate an expression within when it is a Show() function argument
//----run to here---

x= Empty();
show(func, func(x), xp, Eval(xp) ); //func(x) = .; since x is empty, then any calculation involving x is empty
//----run to here---

x=2;
show(func, func(x), xp, Eval(xp)); //func(x) = 4; since x = 2.  
//----run to here---

Show(Derivative(xp,x), Derivative(NameExpr(xp),x) )
//----run to here---


//: JMP tries to evaluate func(x), and Derivative(xp,x) evaluates xp unless you specify NameExpr(xp)

So the only method that I know of to use the built-in Derivative() function is to make the first argument an expression.

 

Your question did bring up an interesting question, how can JSL extract the expression argument (the last argument) of an already defined user function.  Maybe other expert bloggers have a better solution. Or maybe this is a wish list item, that a future version of JMP provides a function or message that extracts the expression from a user defined function. 

 

The next block of code shows a method to extract the expression from a user defined function. 

 

So, let the blog know how the function is being read-in (created) and maybe you will get a more palatable solution. For example if your application is prompting the user for a function in a text box, working with text can be simpler.

 

func = Function({x},x^2);
//assumes a function has been defined tfxp=Parse(Word( 2, Trim(LogCapture(show(func))), "=") );
//show(func) writes the function definition to the Log
//Log Capture() captures that as a string
//the Word() function above gets the right hand side of the equal sign
//Parse turns it into an expression
fxp = argExpr(tfxp, nargExpr(tfxp) ); //this extracts the last argument of Function()
show(Derivative(nameExpr(fxp),x)); //______________________________________________________________ //instead of creating a function and doing all the steps to extract the expression //create the function from an expression //---create expression myxp = Expr(3*x^3 -17*x + 12 ); //---this is a template, an expression that will assign func to the expression myfunc = Expr(func= Function({x}, _xp)); //---create the function //this substitutes the expression and evaluates //once it is run, func=Function({x}, 3*x^3 -17*x +12 ) has been defined Eval(Substitute(NameExpr(myfunc), Expr(_xp), NameExpr(myxp)) ); //this verifies show(func, myxp, func(2), Derivative(NameExpr(myxp), x));

 

 

 

Highlighted
gzmorgan0
Super User

Re: Defining a function that takes another function as argument

FYI  blogger's Note

 

JMP 14 has a new object called Custom Function, with useful messages. I have requested a Get Expr message as an enhancement. I don't think it applies to your request, however, it would be easier to get the function: func << Get Function, instead of the 2 steps with LogCapture(show(func)) etc....

Highlighted
JesperJohansen
Level IV

Re: Defining a function that takes another function as argument

I think your suggestions helped me on the way.

In the meantime I changed my mind about what I want the function to do.

But here is what I got so far:

expression = Expr( x ^ 2 );
LinearityError = Function( {expression, location, sigma, sigmaRange = 3}, 
	
	locationY = Eval( Substitute( Name Expr( expression ), Expr( x ), location ) );
	
	SlopeFunc = Derivative( Name Expr( expression ), x );
	Slope = Eval( Substitute( Name Expr( SlopeFunc ), Expr( x ), location ) );
	Intercept = locationY - Slope * location;
	
	LinExpr = Parse( Eval Insert( "^Slope^*x + ^Intercept^" ) );
	
	errExpr = Substitute( Expr( (a) - (b) ), Expr( a ), Name Expr( LinExpr ), Expr( b ), Name Expr( expression ) );
	errExpr = Simplify Expr( Name Expr( errExpr ) );
	
	lowerLimit = location - sigma * sigmaRange;
	upperLimit = location + sigma * sigmaRange;
	
	errMean = Integrate( errExpr, x, lowerLimit, upperLimit ) / (2 * sigma * sigmaRange);
	
	errErrSquaredExpr = Substitute( Expr( ((a) - (b))^2 ), Expr( a ), Name Expr( errExpr ), Expr( b ), errMean );
	errErrSquaredExpr = Simplify Expr( Name Expr( errErrSquaredExpr ) );
	
	errStdDev = Sqrt(Integrate( errErrSquaredExpr, x, lowerLimit, upperLimit ) / (2 * sigma * sigmaRange));
	
	Return(errStdDev);
);

I takes an expression as argument as opposed to a function, but you provided a solution for that. It is just not implemented yet.

 

Tank you!

BR
Jesper
Highlighted
gzmorgan0
Super User

Re: Defining a function that takes another function as argument

Just an FYI to whomever was following this post. JMP Support supplied an alternative methods to get the expression from an already defined function. It is an alternative to Capture Log( Show(func) );

funcExpr = Parse( "Expr(" || Char( Name Expr( func ) ) || ")" );
innerf = Arg( Arg( funcExpr, 1 ), 2 ); 

 

Article Labels

    There are no labels assigned to this post.