10

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

xyz
  • 605
  • 4
  • 38
  • 117

1 Answers1

3

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?

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 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:? – xyz Jan 18 '16 at 01:44
  • @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. – Mr.Wizard Jan 18 '16 at 01:47
  • 1
    @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. – Mr.Wizard Jan 18 '16 at 01:51
  • @Shutao Other than that, now fixed I hope, do you see any other problems in using this? – Mr.Wizard Jan 18 '16 at 01:54
  • @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. – Mr.Wizard Jan 18 '16 at 01:57
  • Another issue isD[func[3, 3, x], x] cannot work normally:) By your demo, I know that I just need to add If[] to deal with bad argument of UpValues – xyz Jan 18 '16 at 02:00
  • @Shutao Sorry, it seems I did not take my time with this problem. I'll try to give it a better go later. – Mr.Wizard Jan 18 '16 at 02:16