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$
2
  • $\begingroup$ Somewhat related: (8558). $\endgroup$
    – Mr.Wizard
    Jan 18, 2016 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, 2016 at 18:40

1 Answer 1

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$
7
  • $\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, 2016 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, 2016 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, 2016 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, 2016 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, 2016 at 1:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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