acer

32385 Reputation

29 Badges

19 years, 334 days
Ontario, Canada

Social Networks and Content at Maplesoft.com

MaplePrimes Activity


These are answers submitted by acer

I usually find it more convenient to use assuming rather than assume. It might also help you avoid this particular difficulty.

pi_filter_osc_anal_ac.mw

expr := alpha[3, 5]*xi[1]*xi[8] + alpha[3, 5]*xi[4]*xi[5] + alpha[3, 3]:

indets(expr, 'specindex(alpha)');

           {alpha[3, 3], alpha[3, 5]}

indets(expr, 'specindex(posint,alpha)');

           {alpha[3, 3], alpha[3, 5]}

You can find this (and several other related or useful structured types) on the Help page with Topic type,structure .

It's not entirely clear to me, from the implicitplot's Help page's Options section, why this should be so. But passing the adaptranges=false option produces this constrained red curve.

restart;
kernelopts(version);

   Maple 2021.1, X86 64 LINUX, May 19 2021, Build ID 1539851

plots:-display(
  plots:-implicitplot(-x^2 + y, x = 0 .. 1, y = 0 .. 1-x^2,
                      adaptranges=false, color=red),
  plot(1-x^2, x=0..2, y=0..2, color=blue) );

As mmcdara mentioned, it worked as you expected in Maple 2015.2. But not in 2016.2 and onwards, it seems. I will submit a report; its maintainer will know whether its a behavioral bug, or needs clarification in the Help.

The type &* can check for a specific number of multiplicands, of specified types, in a specified order.

The type `*` checks merely that all multiplicands are of the given type, with no regard to number, no guarantee of presence, and no guarantee or order. But the given type can be specified as a structured type, and that includes the possibility of using {} or Or to denote alternative matching types. If no type is specified then it's just a check that the expression is a product of any two or more terms.

The first of the above, &*, might sound useful but in practice can present difficulties because the terms in a product may get re-ordered (on account of unification of expressions, as represented in the kernel/engine). I showed a way to check for a specific number of specified types, appearing in any order, in another very recent Question by you. There are a few other related queries possible, that can be programmed.

The second of the above, `*`, is handy for checking either that an expression is just a product, or that all its multiplicands can only be of some given set of types. If you supply it with a set of possible types then in general there's no guarantee that each of those types are actually represented at all by any multiplicand present in the expression, or represented by distinct multiplicands. And so in general it's not a mechanism, alone, for precise pattern matching. It's great for checking that multiplicands can only be from some specified class -- which is a very common need even if it may not be your current need.

I use type `*` a great deal. I'd be hard pressed to remember the last time I used type &*, other than to address some of your recent Questions on type checking.

[edit] I don't want to lead you to incorrect conclusions about type &*. But there are some kinds of terms whose order within a product is guaranteed. For the expression, say,  3*x*y*z the rational coefficient will appear first. There are a small number of such rules, but I am really not sure whether figuring out and relying on those in some complicated mechanism might be beneficial for you.

You've only provided a simple, restricted, single example and then asked for a generalization about which approach is more suitable. (Your example is restricted in at least the sense that it has exactly two multiplicands, with multiplicands of distinct types, with at least one multiplicand being special wrt ordering.) Your example is so restricted that it satisfies both type checks under discussion. If you want a general rule for programming then consider your widest class of possible example, not the narrowest.

You asked about a "rule of thumb". In various situations, one of the key things is to figure out whether you're trying to 1) restrict which types might be present, or 2) ascertain whether some target types are definitely present. Those are quite different kinds of goal. The type `*` is often very useful for the former. For 2) I find it often productive to augment type `*` with tight queries that delineate the relevent cases -- where useful helper tools can include type satisfies, membertype, etc.

Your example does not adequately illustrate the underlying considerations.

In particular your example fails to illustrate that expansion (of at least some sort) may be required in order to discern what trig cancellations and simplifications are possible.

Consider this conceptually minor tweak to your example, and see how lack of expansion of the polynomial terms messes up some possible cancellation of terms.

restart;

ee := (cos(x)^2+sin(x)^2)+5
      +(1+x+x^2+x^3)*(sin(x)^2)*exp(x)
      +(x + 1)*(x^2 + 1)*(cos(x)^2)*exp(x):

simplify(ee, trig);

                              / 2    \       
                  6 + (x + 1) \x  + 1/ exp(x)

thaw(simplify(subsindets[flat](ee,satisfies(Z->type(Z,polynom(integer, x))),(freeze))));

        //        / 2    \    3    2        \       2    3    2    
    6 + \\(x + 1) \x  + 1/ - x  - x  - x - 1/ cos(x)  + x  + x  + x
    
      \       
       + 1/ exp(x)

Some other problematic examples may be readily found.

I believe that it's also possible to find examples for which some possible trig cancellations will not be found (due to other lack of expansion).

[edit] Of course there are lots of alternative ways to produce that target result for the given example. Eg,

expr:= (cos(x)^2+sin(x)^2)+5+(1+x+x^2+x^3)*(cos(x)^2+sin(x)^2)*exp(x):
collect(expr, exp, simplify);

                      / 3    2        \       
                  6 + \x  + x  + x + 1/ exp(x)

collect(expr, exp,
        u->simplify(u,trig,applysimplifysize=false));

                      / 3    2        \       
                  6 + \x  + x  + x + 1/ exp(x)

I think that is quite beside the main point, since there are many other examples for which the original question applies but which will not be handled by such any one such simple technique (including that given by Kitonum in his Answer).

I interpret the original question as being about how (arithmetic) expansion or factoring might be avoided while allowing for the trig-specific simplifications. My answer is summarized by saying that such expansion of the relevant subexpressions or coefficients is (at the very least temporarily) generally necessary in order to ascertain which trig subexpressions might cancel or simplify.

Speculative expansion of subexpressions (examining which might or might not help, say) is possible but can be quite expensive.

The amount of clean-up simplification is another consideration. I might mention this avenue of computation:

simplify(expr, trig, applysimplifysize=false);

                     3           2                    
         6 + exp(x) x  + exp(x) x  + exp(x) x + exp(x)

The values specified by the size option actually dictate the dimensions of that frame, ie. the inlined plotting window. They don't dictate the lengths of the axes, or the visible extent of the plot features.

But your goal seems clear: you want the visible entent of the plotted feature to agree, between plots, as much as possible. And currently you are seeing a mismatch in the horizontal extent at which some of your plots are rendered.

You won't be able to utilize the size option, alone, to fix your problem, because as mentioned that specifies the dimension of the plotting window/frame. Those dimensions can be made to agree, and that sees like a good start. But the visible extent within those similarly-size frames is the key issue, and for your examples the plots do not render to the same extent up to the common frame boundary.

The bad new is that there seems to be a quirk in the plot renderer, that occurs whenever certain POLYGONS' substructures with surface style have a boundary point too close the axis' end points (with default, axes=normal look). Specifically, if certain POLYGONS' substructure is too close then the GUI will not render that polygon right up to the axis end-point, but instead will clip it upon rendering. In that situation it will also no longer render the background or some other plot features up to the axis end point. That's a shame. Your filled inequal plot contains such POLYGONS, for the filled areas. Internally inequal uses the surface style to produce an agglomeration of polygons that are rendered without borders, giving the effect of a filled region. The occurrence of the problem also seems to depend on relative scale.

I attach a worksheet that hopefully illustratess what I mean in the description above.

plot_scratch.mw

I can tell you that the problem is not fixed by forcing a "default" view, or removing the VIEW substructure, or labels. The GUI simply will not render certain filled polygons right up to the extent of the some kinds of displayed axes (in axes=normal configuration).

The problem might be due to a reluctance of the GUI to render a style=surface POLYGONS structure too close to outermost major tickmarks. I am not sure.

Also, the problem is not apparent for axes=box. The problem occurs for axes=normal, which is how your examples looked by default. With axes=box then the axes-boxes all agree. But you won't be able to get all manner of 2D plots to look similarly rendered this way, because different plots can have very different value widths for the y-tickmarks and y-axis label. With axes=box you can only get very close extent in the rendered dimensions (ie. same closeness to plotting frame/window) if you forcibly remove all axes labels and tickmarks. That might be unacceptable for your work. plot_scratch2.mw

If I think of anything useful I'll follow up here, but so far all I'm aware of are kuldgy techniques that can work on a plot-by-plot basis. And you seem to need a general approach.

The problem is a conflict between the type specification of ::positive that you put on func's first procedural parameter, and the fact that you've called func(x) with symbolic, non-numeric name x.

You'd get the same error message if you just called func(x) at the top-level, rather than as first argument to the Secant command.

The easiest solution is to remove that ::positive parameter type-spec on the func procedure. That parameter type-spec doesn't help here -- it just gets in the way.

An alternative is to use single-right-tick uneval quotes, to prevent func(x) from evaluating until such time that x is actually given a numeric value under the caller (here, the Secant command).

Also, there's no benefit to wrapping your operator f inside another operator func. (That's just a waste of time.) So I will use just f directly. But for your benefit I will show how the (unhelpful) ::positive parameter type-spec can be dealt with, because you asked.

By the way, the stock Maple command for numeric root-finding is fsolve. You can use that command with either its operator form or expression-form calling sequence.

restart;

f := (x::positive) -> x^3 - 7*x^2 + 14*x - 6:

Student:-NumericalAnalysis:-Secant( f(x), x=[2.7,3.2], tolerance=10^(-2) );
Error, invalid input: f expects its 1st argument, x, to be of type positive, but received x

Student:-NumericalAnalysis:-Secant( 'f'(x), x=[2.7,3.2], tolerance=10^(-2) );

               3.005775858

fsolve( f, 2.7 .. 3.2 );

               3.000000000

fsolve( 'f'(x), x=2.7 .. 3.2 );

               3.000000000

I understand that your homework assignment might have literally said that you should write a procedure. Note that the operator f above is actually a procedure. It just has some special options that make it print with that neat arrow.

I'm curious, are you sure that your homework assignment isn't asking you to write a procedure that implements some numeric root-finding algorithm (secant method, bisection, etc), rather than merely use a Maple command that has already implemented it?

Once expandoff is defined by the first invocation of,
   expand(expandoff());
then subsequent calls to expandoff() return NULL.

So your guess was not correct; it's not a scoping issue. Nor is it specific to the fact that your code lies within a procedure. Rather, it's due to the fact that the call expandoff() acts differently once the first call actually defines expandoff. Here we can see a similar effect occurring at the top-level:

restart;

expand(expandoff());

                 expandoff()

expand(expandoff());
Error, invalid input: expand expects 1 argument,
but received 0

So you need to prevent such a returned NULL value being passed along to the expand command.

One way is to first check whether expandoff() already returns NULL, and if it does then do not execute the statement,
   expand(expandoff());
That way worked for me, for repeated attempts on your example, but I don't show it here.

Another way is to put uneval quotes around it, ie.
   expand('expandoff'());

Below I show that way working on both a first and second call at dealing with the example expression. I also used a try..catch..finally so that the finally clause would provide a little more assurance that cleanup would get done. (I don't know whether you want cleanup done. Maybe you provided only a toy/partial example.)

restart;

foo:=proc(expr)
  local F;
  forget(expand);
  expand('expandoff'());
  expandoff(cos,sin,exp);
  try
    F:=expand(expr);
  catch:
    error lasterror;
  finally
    forget(expand);
    expandon(cos,sin,exp);
  end try;
  return F;
end proc:

expr:=(1+x)*sin(3*x+k);

          expr := (1 + x) sin(3 x + k)

foo(expr); # good

         sin(3 x + k) + sin(3 x + k) x

expand(expr):

    4*cos(k)*sin(x)*cos(x)^2-cos(k)*sin(x)+4*sin(k)*cos(x)^3-3*sin(k)*cos(x)+4*x*
    cos(k)*sin(x)*cos(x)^2-x*cos(k)*sin(x)+4*x*sin(k)*cos(x)^3-3*x*sin(k)*cos(x)

foo(expr);  # good

         sin(3 x + k) + sin(3 x + k) x

By the way, there are various alternative ways to prevent expand from expanding the trig calls. Here are two others:

restart;
expr:=(1+x)*sin(3*x+k):
frontend(expand, [expr]);

         sin(3 x + k) x + sin(3 x + k)

restart;
expr:=(1+x)*sin(3*x+k):
thaw(expand(subsindets(expr,specfunc({sin,cos,exp}),
                       freeze)));

         sin(3 x + k) x + sin(3 x + k)

If you are concerned with reducing verbocity perhaps you could consider removing the unnecessary subtypes and Or calls (which are all already within {...}),  and combining similarly structured types, etc. For example,

restart;

TypeTools:-AddType('my_type', '`*`'({rational, specfunc({sin,cos}),
                                     specfunc({sin,cos})^rational}));

type(3*sin(x)^2*cos(x)^(-3), my_type);

                true

type(sin(x)^2*99*cos(x), my_type);

                true

type(-4*sin(x)*cos(x), my_type);

                true

type(-cos(x)*sin(x)^(1/2), my_type);

                true

Explicitly, I think that,

'`*`'({rational, specfunc({sin,cos}),
       specfunc({sin,cos})^rational})

is considerably more legible, as well as easier to augment, than the following, because the words sin and cos appear few times.

''`*`'( { Or('specfunc'(sin),'specfunc'(sin)^Or(integer,rational)),
          Or('specfunc'(cos),'specfunc'(cos)^Or(integer,rational)),
          Or(integer,rational)})'

Also, having to nest uneval quotes is often a sign that some aspect could be improved. In your way of merely assigning the whole structured type to a name -- with extra quotes -- the ephemeral nature of uneval quotes is more apparent. If that assigned name accidentally got any unwanted extra evaluation then it could break. But by instead using AddType the minimal (and understandable) quoting is enough, and once the type is registered in the system some further inadvertant evaluation risk is avoided.

[edit] Another possibility is to create your own auxiliary type, whose handler procedure incorporates the combined test that you wanted to avoid when defining the structured type that you will later utilize. The auxiliary type is created just once, and it allows for multiple structured types to be created that might access it. The definition of the latter, structured type can is now even less verbose. And the words sin and cos appear just once, in a centralized way, making augmentation even easier. Eg,

restart;

TypeTools:-AddType('ratpow',(e,etype)->type(e,{etype,etype^rational}));
 
TypeTools:-AddType('my_type','`*`'({rational, 'ratpow'(specfunc({sin,cos}))}));

type(3*sin(x)^2*cos(x)^(-3), my_type);

                true

type(sin(x)^2*99*cos(x), my_type);

                true

type(-4*sin(x)*cos(x), my_type);

                true

type(-cos(x)*sin(x)^(1/2), my_type);

                true

If you wanted you could also make it more flexible, having the auxiliary type accept the type specification of the exponent (above, hard-coded as rational) as an additional argument. In that case perhaps the auxiliary could be named something like specpow instead of ratpow. For example,

TypeTools:-AddType('specpow',(e,etype,ptype)->type(e,{etype,etype^ptype}));
 
TypeTools:-AddType('my_type',
                   '`*`'({rational, 'specpow'(specfunc({sin,cos}),rational)}));

That second definition -- for my_type -- is now still short and quite understandable, but more flexible.

You wrote, "where (f@g)(..) cannot be written f(g(..))".

But your problematic case,
    ((max-min)/abs(Mean))(S);
is not actually of that stated, latter form.

Rather, your problematic case involves abs(Mean) applied to something, ie, like,
    abs(Mean)(..)
and so is more of the form
    f(g)(..)
rather than (as you stated) of the form f(g(..)).

The problem, then, is that your example involves f(g) where you apply f to g. That is not a syntax for composition of operators, and its result may not itself act like an operator. So the result of abs(Mean) may not act like an operator which could be applied to something in the way you want.

And nor is f(g)(..) a nested application of f and g to something.

What happens if you instead do it as either of these,

  plot(ta, 2000.0 .. 2030.0);

  plot('ta'(x), x=2000.0 .. 2030.0);

?

You are passing ta(x) to the `plot` command. That means that ta(x) will be evaluated before the plot command receives it as an argument, with `x` being an as-yet-unassigned name.

Your procedure ta might work fine if passed an actual numeric argument. But it can happen it goes wrong if ta is evaluted at a *symbolic* argument (name), followed by substitution of the numeric value for that name into whatever results.

This is a common difficulty, sometimes known as premature evaluation.

This is not specific to the name Digits.

That particular abbreviated syntax for entering floats (with the "e") requires a literal number for the exponent, not a name (whose value might be an integer). And that is all.

I realize that you are aware of several alternatives.

Your previous Question was about acting on a "whole expression" that was a sum of terms.

If this new Question thread relates to that (a sum of terms) as the another kind of relevant "whole expression" then that fact may dictate a course of action.

Here is a common approach to a related and somewhat common situation. (I cannot tell if it truly represents your situation, because you haven't fully described it.)

if type(expr, `+`) then
  select(type, expr, mytype);
elif type(expr, mytype) then
  expr;
else
  # Choose some appropriate
  # and meaningful fallback. Eg,
  0;
end if

You might wish to interchange positions of those first two conditions/actions.

But in general it may be quite innappropriate to make that `select` call (as Joe did,  following your cue) without knowing or checking that `expr` is something over which you'd want that action taken.

You might validly want `select` to act over the operands of `expr`, if it is a sum of terms. But otherwise that action might be inappropriate.

Ie. if `expr` were some other kind of multi-argument function-call then calling `select` on it might well produce a nonsensical result.

Just because expr is not of type mytype doesn't mean that it must be something (eg. a sum of terms) to which `select` can/should be appropriately applied. It could be a radical, or a special-function or trig call, etc. What's the sense in calling `select` on those beasts here, as fallback clause?

One problem here is that you are suppressing the output of the lines which assign to expr_1..expr_6. That prevents you from visualizing the ordering of multiplicands in the resulting product terms, which is part of the explanation.

The product subterms like, say, sin(x)*(1+x+x^2) are uniquified in memory. For efficiency Maple only stores one copy of this product (DAG). Its first creation specifies the ordering with which later instances are uniquified. If you subsequently enter (1+x+x^2)*sin(x) then that will be a reference to the (earlier) stored instance.

restart;

3+sin(x)*(1+x+x^2);

                       / 2        \
            3 + sin(x) \x  + x + 1/

3+(1+x+x^2)*sin(x);

                       / 2        \
            3 + sin(x) \x  + x + 1/

Another part of the problem is your particular choice of type. Compare with the following, which is not affected by the multiplicative ordering. Below, I force both orderings, and both succeed here.

restart;

3+sin(x)*(1+x+x^2);

                       / 2        \
            3 + sin(x) \x  + x + 1/

expr:=3+(1+x+x^2)*sin(x);

                       / 2        \
            3 + sin(x) \x  + x + 1/

select(type, expr,
       '`*`'({polynom(And(algebraic,
                          satisfies(u -> not has(u, I))),x),
              specfunc('sin')}));

                     / 2        \
              sin(x) \x  + x + 1/

restart;

expr:=3+(1+x+x^2)*sin(x);

                / 2        \       
            3 + \x  + x + 1/ sin(x)

select(type, expr,
       '`*`'({polynom(And(algebraic,
                          satisfies(u -> not has(u, I))),x),
              specfunc('sin')}));

              / 2        \       
              \x  + x + 1/ sin(x)

note: I didn't change the quoting within your inner types. It's not necessary to uneval-quote sin, as it's a protected name. But quoting specfunc may be prudent.

Perhaps another way to phrase it might be: it's not appropriate to use a noncommutative product type specification (in which ordering of multiplicands is specified) for a test on products in which commuting of multiplicands may occur.

[edit] There are related examples which may be of interest. The alternative I gave above does not guarantee that all the given types will appear in the products that are accepted, for more general examples.

Here's another example, and two other alternatives (which also allow for different orderings). The predicate in the last call to select allows only those products which contain exactly one multiplicand for each of the given types. And the middle select call requires that all the given types are represented.

restart;

3+sin(x)*x^2+x^4*sin(s)*cos(s)+x^6*cos(t):
expr_1:=3+x^2*sin(x)+sin(s)*cos(s)*x^4+cos(t)*x^6;

              2    4                  6       
  3 + sin(x) x  + x  sin(s) cos(s) + x  cos(t)

T := {polynom(And(algebraic,satisfies(u->not has(u, I))),x),
      specfunc('sin')}:

select(type, expr_1,'`*`'(T));

            2    4                  6       
    sin(x) x  + x  sin(s) cos(s) + x  cos(t)

select(u -> u::`*`
            and andmap(membertype,T,[op(u)]),
       expr_1);

                  2    4              
          sin(x) x  + x  sin(s) cos(s)

select(u -> u::`*` and nops(u)=nops(T)
            and andmap(membertype,T,[op(u)]),
       expr_1);

                           2
                   sin(x) x 

It's not clear from your very simple example whether in general your other examples of this task would each have their specified types cover disjoint classes of expression, or whether you are hoping to establish a 1-1 correspondence between the expressions (ie. multiplicands in a product) and the list/set of specified types. You should clarify that, though perhaps you consider that mention of pattern-matching in other related recent Questions by you might make it obvious(?).

As the error message indicates, the conditional check (of the `while` clause of the do-loop) cannot be tested if fs and de have not been assigned numeric values.

And, indeed, the very first iteration has that problem.

You could assign f(x0) to fs, and any concrete value greater than tol to de, just before beginning that while-do-loop.

Alternatively, you could use `until` to check at the end of the iteration, instead of using `while` to check at the start of each iteration.

I'll let you give it a shot, since it's likely homework. Let us know if you have further trouble.

First 85 86 87 88 89 90 91 Last Page 87 of 336