10
$\begingroup$

In practice, I needs to define the UpValues of a user-defined function. For instance, the operation of function like differential formula , expansion and so on.

Here, I will give a example that came from my answer. Please see here

Bernstein::invidx = 
 "Index `1` should be a non-negative machine-sized integer betwwen `2` and `3`.";

SyntaxInformation[Bernstein] = {"ArgumentsPattern" -> {_,_,_}};

SetAttributes[Bernstein, NumericFunction]
(*special cases*)
Bernstein[0, 0, u_?NumericQ] := 1
Bernstein[0, 0, u_Symbol] := 1

(*normal cases*)
Bernstein[deg_Integer?NonNegative, idx_Integer?NonNegative, u_?NumericQ] /;
  idx <= deg && 0 <= u <= 1 :=
 Binomial[deg, idx] u^idx (1 - u)^(deg - idx)

Bernstein[deg_Integer?NonNegative, idx_Integer?NonNegative, u_?NumericQ] /;
  idx <= deg && (u > 1 || u < 0) := 0

Throw the error-informations

Bernstein[deg_Integer?NonNegative, idx_Integer?NonNegative, u_] /;
  idx > deg && (Message[Bernstein::invidx, idx, 0, deg - 1]; False) := $Failed;

expr : Bernstein[deg_ /; ! (IntegerQ[deg] && NonNegative[deg]), idx_, u_] /;
 (Message[Bernstein::intnm, Unevaluated[expr], 1]; False) := $Failed;

expr : Bernstein[deg_, idx_ /; ! (IntegerQ[idx] && NonNegative[idx]), u_] /;
 (Message[Bernstein::intnm, Unevaluated[expr], 2]; False) := $Failed;

Bernstein[args___] /;
 ! ArgumentCountQ[Bernstein, Length[{args}], 3, 3] && False := $Failed;    

The derivatives of Bernstein basis

Bernstein /: Derivative[0, 0, k_Integer?Positive][Bernstein] :=
 Function[{deg, idx, u},
  D[
   deg (Bernstein[deg - 1, idx - 1, u] - Bernstein[deg - 1, idx, u]),
   {u, k - 1}]
 ]

TEST

D[Bernstein[3, -2, x], x]
D[Bernstein[3, -2, x], {x, 2}]

enter image description here

Question

  • How to deal with bad arguments when a function's UpValues is a pure-function? Namely, throw the error information and then return the symbol $Failed.

Although Mr.Wizard given me a solution that using If[]

func /: Derivative[0, 0, 1][func] :=
 Function[{n, i, x},
  If[MatchQ[n, _Integer?NonNegative] && MatchQ[i, _Integer?NonNegative] && i <= n,
   n (func[n - 1, i - 1, x] - func[n - 1, i, x]),
   Defer@func[n, i, x]
 ]
]

However, which leads to another issue.

enter image description here

In fact, the built-in BSplineBasis[] also ingnore this problem.

knots = {0, 0, 0, 0, 1/3, 2/3, 1, 1, 1, 1};
D[BSplineBasis[{3, knots}, 7, x], {x, 2}]

enter image description here

$\endgroup$
  • $\begingroup$ Somewhat related: (8558). $\endgroup$ – Mr.Wizard Jan 18 '16 at 1:14
  • $\begingroup$ I updated my answer with two minor changes that appear to address your new example. Please let me know if and where this fails; I did not try to be exhaustive. $\endgroup$ – Mr.Wizard Jan 21 '16 at 18:40
3
+100
$\begingroup$

This references a past dialog on the subject of argument testing. There may be a good reasons for the side-effect method you are using, but in this case a check function may simplify things.

func::invidx = 
  "Index `1` should be a non-negative machine-sized integer betwwen `2` and `3`.";
func::intnm = "Number `1` should be a non-negative machine-sized integer.";

ClearAll[nni, less, check]
SetAttributes[check, HoldFirst]

nni[n_Integer?NonNegative] := True
nni[else_] := Message[func::intnm, else]

less[i_, n_] /; i <= n || Message[func::invidx, i, 0, n] := True

check[_[n_?nni, i_?nni, x_]] /; less[i, n] := NumericQ[x]
check[_[args___]] /; ! ArgumentCountQ[func, Length[{args}], 3, 3] && False := $Failed;
check[else_] := False

And now the main definitions:

func[a___]?check := #2/#1*#3 &[a]

func /: Derivative[0, 0, 1][func] := {n, i, x} \[Function] 
  If[Quiet @ check @ Hold[n, i, x],
    n (func[n - 1, i - 1, x] - func[n - 1, i, x]), 
    Quiet @ func[n, i, x]
  ]

Now messages are as shown in your example but:

D[func[-4, 2, x], x]

func::intnm: Number -4 should be a non-negative machine-sized integer. >>

func[-4, 2, x]
D[func[-4, 2, x], {x, 2}]

func::intnm: Number -4 should be a non-negative machine-sized integer. >>

func[-4, 2, x]

I think this is the result you want?

$\endgroup$
  • $\begingroup$ Thanks for your answer. I have a question: why D[func[-4, 2, x], x] throws many error infos func::intnm:, rather than just a func::intnm:? $\endgroup$ – xyz Jan 18 '16 at 1:44
  • $\begingroup$ @Shutao One of them comes from the last term in If, which could be avoided with Defer. A Trace should show where the others originate; I think I'll do that now. $\endgroup$ – Mr.Wizard Jan 18 '16 at 1:47
  • 1
    $\begingroup$ @Shutao The other leak came from check @ Hold[n, i, x] which I somehow forgot about. I'll update this answer to give only a single message in that call. $\endgroup$ – Mr.Wizard Jan 18 '16 at 1:51
  • $\begingroup$ @Shutao Other than that, now fixed I hope, do you see any other problems in using this? $\endgroup$ – Mr.Wizard Jan 18 '16 at 1:54
  • $\begingroup$ @Shutao Oh, I just realized that the check[_[args___]] /; ! ArgumentCountQ line isn't doing anything as check is only applied to the three-argument form. I'll fix that too. $\endgroup$ – Mr.Wizard Jan 18 '16 at 1:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.