Carl Love

Carl Love

28050 Reputation

25 Badges

12 years, 336 days
Himself
Wayland, Massachusetts, United States
My name was formerly Carl Devore.

MaplePrimes Activity


These are answers submitted by Carl Love

The codegen package is quite convenient for dynamic procedure generation, but unfortunately it is much older than keyword parameters and also (I think) default-value parameters. Here are two other methods for dynamic procedure creation: the subs method and the FromInert method. The subs method is sufficient for the vast majority of practical cases. The FromInert method can be unwieldy, but it can handle any case at all.

Method 1: The (undocumented) subs method: Although this usage of subs is undocumented, it has been in use at least 20 years and is used in Library code, so I don't expect it to change. We start with a "template" procedure, and create the dynamic procedure by making substitutions into it by using the syntax of the ordinary subs command (although the semantics are substantially different in undocumented ways).

Here is a template for a procedure similar to the one you showed. For brevity, I've omitted your parameters a and b.

#Procedure template:
P:= proc(  
    _rp1:= _rp1d, 
    _op2::_op2t:= _op2d, 
    {_kp3:= _kp3d, _kp4::_kp4t:= _kp4d}
) 
    (_rp1, _op2, _kp3, _kp4) #return value
end proc
:

Everything that will be changed by subs I've given a name beginning with underscore. It's not necessary to use an underscore, but they must be implicitly global symbols without assigned values. By implicitly, I mean that you can't force them to be global by using a :- prefix. Nor can you use unevaluation quotes '...' to ignore values that they've possibly been assigned.

Now we assign the desired parameter names, types, default values, and keywords:

#Create dynamic run-time procedure:
P1:= subs(
    {
        _rp1= 'c', _rp1d= 7, 
        _op2= 'd', _op2t= ':-posint', _op2d= 3, 
        _kp3= 'e', _kp3d= 5,
        _kp4= 'f', _kp4t= ':-integer', _kp4d= 0.5
    },
    eval(P)
);

P1:= proc(c:= 7, d::posint:= 3, {e:= 5, f::integer:= .5})
    c, d, e, f 
end proc

P1();
                          7, 3, 5, 0.5

P1(':-e'= 3, ':-f'= 7);
                           7, 3, 3, 7

Notes:

  • The eval is necessary because of the last name evaluation property of procedures (see ?last_name_eval).
  • As always, keywords must be global symbols, but any issues related to competing local variables or assigned values can be resolved with ':-...(as is usual).
  • Substitutions are effective in both the header and body of the procedure; those in the body can involve names local to the procedure.
  • Just like ordinary subs, there can be multiple sets or lists of substitutions, which are done sequentially (see ?subs).
  • You can substitute procedures for the symbols. In particular, if you already have a partial solution to your problem constructed with codegen, you can use it.
  • You can use subs[eval] instead of subs. This can be particularly effective when combined with the point immediately above.

Method 2: The FromInert method: This method can be unwieldy due to every substructure needing to be expanded to an inert form down to its minutest detail, but on the other hand, it does handle every possible case. FromInert allows for the dynamic creation of any Maple structure including procedures and modules. You need to learn the "language" that FromInert uses, which is where its inverse command ToInert helps. For any structure (other than a "naked" expression sequence),

S1:= FromInert(ToInert(eval(S)))

will return a copy of S. So, to use this for dynamic procedure creation, you start with a template and expand it with ToInert. Here, I'm using the same template as with the subs method:

InertP:= ToInert(eval(P));
InertP:= _Inert_PROC(_Inert_PARAMSEQ(
  _Inert_ASSIGN(_Inert_NAME("_rp1"), _Inert_NAME("_rp1d")), 
  _Inert_ASSIGN(
  _Inert_DCOLON(_Inert_NAME("_op2"), _Inert_NAME("_op2t")), 
  _Inert_NAME("_op2d")), _Inert_SET(_Inert_EXPSEQ(
  _Inert_ASSIGN(_Inert_NAME("_kp3"), _Inert_NAME("_kp3d")), 
  _Inert_ASSIGN(
  _Inert_DCOLON(_Inert_NAME("_kp4"), _Inert_NAME("_kp4t")), 
  _Inert_NAME("_kp4d"))))), _Inert_LOCALSEQ(), 
  _Inert_OPTIONSEQ(), _Inert_EXPSEQ(), _Inert_STATSEQ(
  _Inert_EXPSEQ(_Inert_PARAM(1), _Inert_PARAM(2), 
  _Inert_PARAM(3), _Inert_PARAM(4))), _Inert_DESCRIPTIONSEQ(), 
  _Inert_GLOBALSEQ(), _Inert_LEXICALSEQ(), 
  _Inert_EOP(_Inert_EXPSEQ()))

So, you manipulate this structure using ordinary Maple commands (such as subsindets, and subsindets) and then reassemble the modified structure with FromInert. Note that this allows you to change the number of parameters, which you can't do by the subs method.

Here's a procedure for it (warning: this procedure is fairly deep):

ShowSteps:= (q::`=`(algebraic), v::{name, function})->
    subsindets(
        (InertForm:-MakeInert@isolate)(
            (value@subsindets[2])(q, numeric, `_%a`, nprintf), v 
        ),
        suffixed(_, numeric),
        parse@substring, 2..-1
    )
:
#Your example. Note that all arithmetic operations (other than =) and function names
#must be entered with %.
q1:= 0.379948 = 2 %/ (1 %+ %exp(-0.004 %* cp)) %- 1;

ShowSteps(q1, cp);
#You'll need to view its output in Maple to appreciate it.

value(%);
                        cp = 199.9994377

 

You only need three changes to make your code run without error:

  1. Change eval to eval[recurse]. The recurse is needed because some of the evaluations use variables on their right sides that appear on the left sides of other evaluations in the same set.
  2. Change (x,y,t)-> test to unapply(test, [x,y,t]). The arrow -> cannot be used to make variables that appear indirectly on its right side match the parameters on its left side.
  3. Change plot to plot3d. The former command is strictly for 2D plots.

Use the try command to trap the "time expired" error and break the loop, like this:

for k do
    try timelimit(10, MyProc(k))
    catch "time expired": break
    end try
od:

 

There are no real roots, but fsolve will find a complex root if you ask it to:

fsolve(P1, x, complex);

             0.02825145398 - 3.236871625*I

The issue is not that the for loop doesn't recognize the alias but rather that the aliases, declared as a[1], ..., a[4], don't recognize a[i] even if equals 1, 2, 3, or 4.

The following two paragraphs from the help page ?alias, particularly the first paragraph, explain your situation:

  • Because aliases are resolved at the time of parsing the original input, they substitute literally, without regard to values of variables and expressions.  For example, alias(a[1]=exp(1)) followed by evalf(a[1]) will replace a[1] with exp(1) to give the result 2.718281828, but evalf(a[i]) will not substitute a[i] for its alias even when i=1.  This is because a[i] does not literally match a[1].
     
  • Aliases defined inside a procedure or other compound statement are not effective for resolving input matches in the body of that statement.  This is the case because the current statement is parsed in its entirety before the alias command is evaluated.

I think that the command PDEtools:-declare would be more appropriate than alias for this.

 


 

The command Grid:-Interrupt can be used for this. See ?Grid,Interrupt.

There's no predefined button for this. I suppose that you could make such a button, but it hardly seems worth the effort to me.

r:= fsolve(P, lambda= -1..3.2);
                        r := 1.376880264

d:= ilog10(op(1,r));
                             d := 9

evalf(eval~(P, lambda=~ [r, r-10.^(-d), r+10.^d]));
           [        -9        -10                 9]
           [-1.28 10  , 4.0 10   , -1.666081101 10 ]


We see that over the smallest possible interval containing the root at the given precision, the function switches signs.

@binbagsss You wrote:

  •  I will need to define an alias

One never needs to define an alias. You can use direct assignment instead, as long as you don't use the same names on both left and right sides of the assignment operator. The only potential benefit of an alias (in this situation) is the way that it can simplify the output. However, I think that that simplified output is causing you some confusion. So, I think that for the time being you shouldn't use alias.

An example of potentially confusing aliased output is D(V). You've aliased to V(h(x)). So does D(V) then mean D(V(h(x)))? An experienced user probably realizes that this latter expression is pretty much nonsense, and thus knows that that is not what was meant. Thus they realize that the in D(V) is just plain, original V

So what does D(V) mean? It considers to be a univariate functional operator and is expressing the operator form of V's derivative with respect to its unnamed variable. Some more-explicit examples should help: 

D(sin) returns cos.
D(sin(x)) is nonsense, because sin(x) is an expression, not an operator.
D(sin)(x) returns cos(x).
D(sin)(0) returns 1.
diff(sin, x) is nonsense, because sin is an operator, not an expression depending on x.
diff(sin(x), x) returns cos(x).
diff(sin(1), x) returns 0 because sin(1) is constant with respect to x.
diff(sin(1), 1) is an error, because 1 is not a variable.
D(sin)(1) returns cos(1).

The error message that you show is particularly common with boundary-layer BVPs. Unlike most error messages, it is not a result of a programming error, but represents something inherent to the structure of the BVP. Often it means that there are multiple solutions. You have parameters brk1, and lambda. (You also have parameter blt as a "fake infinity", but, for the moment, I don't want to consider that to be a changeable parameter.) The way around the error is to use approxsoln, as mentioned by @dharr . The first step towards doing that is to experiment with varying the parameters (to "reasonable"[*1] values) until you find values for them for which you don't get an error. Then we can gradually and contuously shift them to the parameter values that you want with each step using the previous step's result as its approxsoln. If you can find the values for the first step, I'll help with the rest.

[*1] Setting all parameters to 0 would likely converge without error, but that's probably not reasonable.

The key to an efficient solution procedure is to solve the equation symbolically (with solve) just once, and then construct all subsequent numeric solutions from that. Like this:

eq:= %abs[i](%a*x + %b) + %abs[j](%c*x + %d) - %t*x^2 + %m*x - %n = 0;

X:= subsindets(
    {seq}(
        seq(solve(eval(eq, %abs= [x-> x, x-> -x]), x), i= 1..2), 
        j= 1..2
    ),
    anything^(1/2),
    s-> %sqrt(op(1,s))
);

soln2:= subs(
    _X= X, [%a, %b, %c, %d, %t, %m, %n]=~ [a, b, c, d, t, m, n],
    (
        a::integer, b::integer, c::integer, d::integer,
        t::integer, m::integer, n::integer
    )-> 
        (X-> if nops(X)=6 then [[_rest], X] else fi)(
            select(type, value(_X), integer), args
        )
):

Sols:= CodeTools:-Usage((soln2@op)~(convert(L, listlist)));
memory used=29.38MiB, alloc change=-4.00MiB, 
cpu time=360.00ms, real time=334.00ms, gc time=78.12ms

     [[[5, 4, 3, 7, 1, 1, 1], {-4, -3, -2, -1, 1, 10}], 
         ...40 other solutions elided... 
     ]

I'm curious about something: Your equation can obviously have up to 8 solutions. Why are you only interested in 6?

 

Here is an Answer to your explicit Question about map: The command map replaces only a single function argument (although not necessarily the first argument) with values from a container (vector, list, set, etc.). To do the equivalent thing with multiple arguments simultaneously, use the elementwise operator ~. In your particular case, you could do

(tgf@op)~(convert(L, listlist))

where is a Matrix or 2D Array. The following also do it:

(tgf@seq)~(convert(L, listlist))
map(tgf@op, convert(L, listlist))

and many other variations are also possible.

However, if your primary concern is reducing time, a change along these lines is insignificant. The time for operating the looping mechanism (whether it be forseqmap~, etc.) too small to even be measured; the vast, vast majority of the time is used by solve. But you don't need to use solve. I gather from your worksheet that you're interested in finding integer 7-tuples (a, b, c, d, t, m, n) for which the equation abs(a*x + b) + abs(c*x + d) - t*x^2 + m*x - n = 0 has 6 integer solutions for x and then listing those solutions. Is that correct? If so, it can be done much more easily than by using solvefsolve, or any other real- or complex-valued root-finding technique. I'll do it in a separate Answer.

First, a note on terminology: An integral equation is an equation that contains an unknown function inside an integral. What you have is just an integral, which is much simpler than an integral equation.

You have used Sum instead of sum, which expresses the sum in inert form. That is, the sum is shown in Sigma notation (with large uppercase Greek sigma), even though in this case it's trivial to expand the sum to its terms. There's no problem with using an inert form; indeed, it makes the displayed expression much more readable. The presence of inert forms is indicated by a gray (rather than blue) character (or characters) in the prettyprinted display. In this case, the Sigma is gray. Likewise, Int can be used as an inert form of int.[*1] That wouldn't make much difference in this case. 

Kitonum's Answer replaces Sum with add. This can be done (and often should be done) in any case where the sum has a specific finite number of terms. However, you lose the benefit of the prettyprinted display. You also lose the option of the sum being simplified symbolically via a summation rule (that doesn't apply in this specific case).

The command value can be used to convert inert forms to their non-inert counterparts. So, in this case, you'd use value(eq1).

[*1] Don't assume that every command has an inert form obtained by capitalizing it. The commands for which this is true are diffevalintlimit, product, and sum. An inert form of any command foo (including those already listed) is %foo.

Try this link: Maple Learn

The GUI (user interface) is completely different than any of those offered by regular Maple, but I think that the "kernel" (mathematical engine) is the same.

Like this:

restart:
f1:= (x,y,z)-> x*y*z:
f2:= (x,y)-> x+y:
(x,y,z):= (2,3,4):
Grid:-Run(0, f1, [x,y,z], set= {f1}, assignto= 'r1'):
Grid:-Run(1, f2, [x,y], set= {f2}, assignto= 'r2', wait):

r1, r2;
                             24, 5

As you probably realize, the wait is not necessary if you have other work that could be performed asynchronously before r2 becomes available.

First 74 75 76 77 78 79 80 Last Page 76 of 395