acer

33395 Reputation

29 Badges

20 years, 316 days
Ontario, Canada

Social Networks and Content at Maplesoft.com

MaplePrimes Activity


These are replies submitted by acer

@Alfred_F 

I used inert Int because I don't want lowercase (active) int to waste time trying to compute the symbolic integral (and failing, only then reverting to numeric quadrature) for each different value used for the `a` parameter. Maple's plotting calls evalf on the Int calls, as a mechnism for directly inducing numeric integration.

I used epsilon=some_float as an error-estimate tolerance for the numeric integration, because at fast double precision evalf(Int(...)) might not always attain a converged numeric integration result (for all `a`). I wanted something quickly. (I suspect it did better than that tolerance, for some `a` ranges. For plotting I usually can't see finer than about 10^-5 discrepancies.)

I used those unapply calls to turn the integrands from expressions into operators. That preempts evalf(Int(...)) from spending time figuring out potential discontinuities or places it might break the integration range into pieces. (It sees the operators more like black-boxes, that it can't properly do symbolic analysis against.) Again, I wanted something quickly.

I used method=_Dexp because 1) I saw it seemed to work, and pretty quickly, and 2) if I'm doing numeric integration for the purpose of comparison I'd like to know method's being used without getting a flood of userinfo messages.

And I passed adaptive = false, numpoints = 200 to the plot command because I wanted an even spread of `a` values (but not too many).

ps. There are sometimes some ways to get GUI zoom to be able to recompute & redraw (here, at finer range of `a` values, say). But I didn't expand on that because I don't find it a programmatically useful approach. Also, it seems impractical for those slow plot calls in your code.

@C_R You're now written,

"Maple AI could not provide an explanation why the call to assign declares the passed names as local names although the arguments of the assign call assign(params) are exactly the same that lead to gobals in my attempts."

But Carl's Answer's method assigns to the names a,b,c,d from the outer scope, and not to any new locals a,b,c,d of the procedure my_assign.

It's clear from your quote that you continue to think the opposite of what is actually happening with respect to globals/locals/etc names a,b,c,d in Carl's Answer's example involving his procedures my_assign.

You've ascribed to special-evaluation technique something about variable-names (local/global/declaration/ect) that did not happen. The reason you didn't understand it is not because you don't understand the evaluation details; it's because something you think happened never actually happened.

That's why I then wrote, in my Reply in that Answer thread, "But that makes it confusing to me since you discussed Carl's Answer's  my_assign procedure, without mentioning that it assigns to the global names." Now I understand why you didn't respond to that sentence. It's because it's claim wasn't understood.

The only local in Carl's Answer's procedure my_assign is the name check (which is used merely to store a not-fully-evaluated  list containing equations involving the names from a higher scope).

Look, in your original Question it was not clear that you were trying to avoid assignment to the globals names. Instead, it looked as if you might be trying to figure out how to successfully pass in a list involving global a,b,c,d more than once, and assign to them as well as then update their assigned values. In that context Carl's code my_assign makes sense; it let's you do that. And the example in his Answer demonstrates exactly that.

Only after that did you explain (in a few Replies) that what you really wanted to do was assign to locals a,b,c,d inside in procedure. So Carl then wrote a second wrapping procedure Foo with its own explictly declared b,c,d and have that Foo pass in a list involving those explicitly declared locals to his my_assign. But note that his Foo never got passed in a list from the outer top-level, so it doesn't handle was appears (to me) to be your actual use-case involivng a passed list from elsewhere. The locals in Foo are explicitly declared there(!), and the list is constructed there. There's no hidden "rule" at work. and no magic construction of new locals on-the-fly, etc.

Note that the list of equations in Carl's Foo example there contains the locals b,c and the global d. And the list is created inside Foo, not passed in.

Only then, with new knowledge that you didn't want to affect globals, while still passing in the equations b=..,etc from the higher level, Carl wrote a third routine foo that accepted four separate keyword parameters for each of a,b,c,d.

@Ronan It's not clear to me with what you're agreeing, sorry.

Note that in my Answer I gave code that accepted a set of any number (including zero) or both 3-element lists and 3-element column Vectors.

But it seems now that you're saying that there must be exactly one (and only one) of each type. So here is code for that.

I use a list to contain the two. But see also the proc example.

restart;

TypeTools:-AddType(_L3DP,{[[algebraic$3],'Vector[column](3,algebraic)'],
                           ['Vector[column](3,algebraic)',[algebraic$3]]});


Strictly two elements. Either order is accepted. One of each: a 3-element list, and a 3-element Vector.

type([[7,8,9],<1,2,3>],_L3DP);

true

type([<1,2,3>,[7,8,9]],_L3DP);

true

type([[a,b,c],[7,8,9],<1,2,3>],_L3DP);

false

type([[a,b,c],[7,8,9]],_L3DP);

false


Usage example:

F := proc( L::_L3DP ) local S:={L[]};
    return S[1], S[2];
end proc:

 

F( [<1,2,3>,[7,8,9]] );

[7, 8, 9], Vector(3, {(1) = 1, (2) = 2, (3) = 3})

F( [[7,8,9],<1,2,3>] );

[7, 8, 9], Vector(3, {(1) = 1, (2) = 2, (3) = 3})

Download Ronan_TT2.mw

@C_R I mentioned two calls to assign in your original example because there you called your procedure foo twice (and the second failed, because it was assigning to the global names).

In your Question (and responses) you've mention that you want to assign to local names of the procedure, eg.

local b:=2,c:=3,d:=4;

But that makes it confusing to me since you discussed Carl's Answer's  my_assign procedure, without mentioning that it assigns to the global names. I suppose that you might be accepting his followup comment's mention of using a,b,c,d as keyword parameters (for which I'll give an Help-page link, parameter_classes).

As the original example was written (it's been edited since) this doesn't seem to really have anything to do with local vs global names. At least, the original explanation did not explain it that way, at all.

Since the original Question has been edited, my comments may not apply. But, so far, I'll stand by the idea that the body of the Question does not explain the OP's goal.

And you're missing the key detail of what you expect to happen in the second `assign` call.

Once you assign to name b, explicitly entering,

    [ b = 2 ]

is going to evaluate, using the current value of b.

So, does the list come from somewhere else, or did you really intend to type it in explicitly the very same way, or did you hope to update the value assigned (by typing it in differently, etc), or did you want to just ignore the case in which the assignment is invalid, or something else?

nb. If name b has been assigned, then re-entering [b] explicitly isn't going to let you deduce that name `b` is involved. If that list was created *before* the assignment was done then you could get at it through eval. But nobody can tell since it's not explained.

@Ronan Yes, using numer around SV1[1] and SV1[i] is one aspect.

But notice also that M may contain entries that are actually zero (but not yet simplified to identically 0). That's the case with your example A. And (since numer expands and finds the actual zero), numer(SV1[1]) produces the actual 0. So your line,
    convert(M, set) minus {0}
fails to reject that zero entry. So I also added calls to normal around M, for creating V1 and SV1., so that such a hidden zero is caught & rejected before being used for numer(SV1[1]) or numer(SV1[i]).

You could also use simplify(M) there instead of normal(M), for that early step.  (You wrote of algebraic values, but your examples are just rational polynomial -- which is not the same thing. The normal command will resolve the latter. If you really do have Matrices with algebraic quantities then you might need simplify or evala.) Fully representative examples of your problem space are better.

ps. Do you perhaps want to utilize a similar approach for both numerator vs denominator "common" factors?

@C_R I thought that dharr's code was clear and legible and easily understandable. Hence I upvoted.

Btw, the argument checking and evalb are not strictly necessary there (if it mattered).

 

L := H = 0.01*Unit('m'), R = 0.006*Unit('m'), r_sub = 0.003*Unit('m'), k = 0.540*Unit(('W')/('m'*'K')), rho = 1060.0*Unit(('kg')/'m'^3), Cp = 3745*Unit(('J')/('kg'*'K')), Q_flux = 5000*Unit(('W')/'m'^2), Nz = 20, Nr = 20, t_final = 30*Unit('s'), t_step = Unit('s'), test_1 = Unit('kW'), test_2 = 2*Unit('N');

H = 0.1e-1*Units:-Unit(m), R = 0.6e-2*Units:-Unit(m), r_sub = 0.3e-2*Units:-Unit(m), k = .540*Units:-Unit(W/(m*K)), rho = 1060.0*Units:-Unit(kg/m^3), Cp = 3745*Units:-Unit(J/(kg*K)), Q_flux = 5000*Units:-Unit(W/m^2), Nz = 20, Nr = 20, t_final = 30*Units:-Unit(s), t_step = Units:-Unit(s), test_1 = Units:-Unit(kW), test_2 = 2*Units:-Unit(N)
 

inbaseu1 := p -> convert(p,'unit_free')=convert(simplify(p),'unit_free'):

 

The remove command itself does an evalb test, so that's not needed in the predicate inbaseu1.

remove(inbaseu1@rhs, [L]);

[test_1 = Units:-Unit(kW)]


Download ub_acc.mw

@dharr That's the same reason why I was confused as to why the OP considers this necessary.

@emendes So you're saying that,

svars:={x,y,z}:
Grid:-Set('svars'):
convert(Grid:-Map(w->map(v->[op](v),indets(w,name) minus svars),models),set)

works, but even,

svars:={x,y,z}:
Grid:-Set('svars'):
convert(Grid:-Map(w->map([op],indets(w,name) minus svars),models),set)

does not? That would surprise me.

Also, I've mentioned up above, a few times, that for me Grid:-Map was slower for the 10^5, 10^7 examples, etc, on Linux. I'd asked you a few times whether you had seen that too, but not got a response.

Similarly for my queries about whether you'd seen stack-limit issues for any of the other example sets and variants.

Similarly for whether you can set the ulimit or ulimits -s to unlimited in launching shells, eg ulimit -s unlimited and ulimit unlimited on your Linux (I know thta on OSX it needs a finite value).

I also find it hard to follow, since even for the smaller example sets I gave you didn't show the same kind of (easy-to-do) timing set results (including both with & without Grid:-Map).

Sorry, but I think I must bow out, because I can't get a clear picture of how things perform for you all round. And if I make three or four queries that I think might be pertinent I seem to be able to get a partial answer at most.

@emendes Isn't the fastest non-parallelized variant (with just plain map, not Threads:-Map) faster than the equivalent with Grid:-Map?

Actually, are all the non-parallelized variants faster than their equivalent with Grid:-Map? For the 10^7 examples I tried, that seemed the case.

Could you complete the 10^7 example in code I gave above, with any method?

My OS shell on Linux has the limit set to "unlimited".

 

ps. It might not be related, but I think that I don't know what Maple version you're using on each platform.

@emendes That's interesting, about the stacklimit.

You might be able to increase it, using the kernelopts option stacklimit. (It might depend on the limits set in the shell in which Maple is launched. I don't know well how that works on OSX.)

Are you saying that Grid:-Map actually improved timing performance on any of the base unparallelized approaches? For me, it just seemed slower...

@emendes Below, A2 construction (mine, without indets use) seems to be done faster than everything else except A5.

The measurement that matters most is "real time".

And A5 construction uses Threads:-Map with indets. But it might be the case that the thread-unsafeness of indets relates only (maybe) to its use on expressions of different nature than yours. I mean, perhaps the thread-unsafeness of indets depends on some aspect of the expression type -- rather than only to the way indets itself it coded. Maybe(?) it is safe on your examples.

restart; randomize():

svars:={x,y,z}: f := rand(0..2): g := rand(1..20): p := rand(0..1):

models:=[seq([seq(x^f()*y^f()*z^f()*alpha[g(),g()]
                  +p()*x^f()*y^f()*z^f()*alpha[g(),g()],
                  i=1..3)],j=1..10^5)]:

H:=map(`=`,svars,1):

# mine
A1 := CodeTools:-Usage( map(w->map(v->ifelse(v::`+`,op(map([op],[op(v)])),
                                             [op(v/content(v))]),eval({w[]},H)),
                            models) ):

memory used=201.97MiB, alloc change=87.00MiB, cpu time=2.15s, real time=1.74s, gc time=605.40ms

# mine, Thr
A2 := CodeTools:-Usage( Threads:-Map(w->map(v->ifelse(v::`+`,op(map([op],[op(v)])),
                                             [op(v/content(v))]),eval({w[]},H)),models) ):

memory used=192.70MiB, alloc change=496.81MiB, cpu time=5.13s, real time=550.00ms, gc time=1.50s

# OP original
A3 := CodeTools:-Usage( map(w->map(v->[op](v),indets(w,name) minus svars),models) ):

memory used=69.26MiB, alloc change=0 bytes, cpu time=1.16s, real time=759.00ms, gc time=457.15ms

# my tweak of OP original
A4 := CodeTools:-Usage( map(w->map([op],indets(w,indexed)),models) ):

memory used=69.51MiB, alloc change=0 bytes, cpu time=946.00ms, real time=590.00ms, gc time=401.00ms

A5 := CodeTools:-Usage( Threads:-Map(w->map([op],indets(w,indexed)),models) ):

memory used=66.89MiB, alloc change=0 bytes, cpu time=1.01s, real time=109.00ms, gc time=0ns

A6 := CodeTools:-Usage( Grid:-Map(w->map([op],indets(w,indexed)),models) ):

memory used=61.70MiB, alloc change=136.95MiB, cpu time=17.68s, real time=3.30s, gc time=4.66m

add(A2 - A3), add(A4 - A5), add(A6 - A5), add(A1 - A2);

0, 0, 0, 0

 

Download emendes_exS3.mw

I note also that your original is itself much faster with map than it is with Grid:-Map. I'll just stop timing that approach, below.

For an even larger number of lists in models, it removes some overhead if the procedure,
    v->ifelse(v::`+`,op(map([op],[op(v)])),[op(v/content(v))])
is pulled out of the middle mapped operator. At a smaller number its effect is less obvious.   emendes_exS3b.mw

Here's 10^6 such entries in models.

restart; randomize():

svars:={x,y,z}: f := rand(0..2): g := rand(1..20): p := rand(0..1):

models:=[seq([seq(x^f()*y^f()*z^f()*alpha[g(),g()]
                  +p()*x^f()*y^f()*z^f()*alpha[g(),g()],
                  i=1..3)],j=1..10^6)]:

H:=map(`=`,svars,1):

P := v->ifelse(v::`+`,op(map([op],[op(v)])),[op(v/content(v))]):

# mine
time[real]( map(w->map(P,eval({w[]},H)),models) );

18.112

# mine, Thr
time[real]( Threads:-Map(w->map(P,eval({w[]},H)),models) );

3.372

# OP original
time[real]( map(w->map(v->[op](v),indets(w,name) minus svars),models) );

7.900

# my tweak of OP original
time[real]( map(w->map([op],indets(w,indexed)),models) );

6.752

time[real]( Threads:-Map(w->map([op],indets(w,indexed)),models) );

1.127


Download emendes_exS3_6.mw

And here's that, for 10^7.

restart; randomize():

svars:={x,y,z}: f := rand(0..2): g := rand(1..20): p := rand(0..1):

models:=[seq([seq(x^f()*y^f()*z^f()*alpha[g(),g()]
                  +p()*x^f()*y^f()*z^f()*alpha[g(),g()],
                  i=1..3)],j=1..10^7)]:

H:=map(`=`,svars,1):

P := v->ifelse(v::`+`,op(map([op],[op(v)])),[op(v/content(v))]):

# mine
time[real]( map(w->map(P,eval({w[]},H)),models) );

214.726

# mine, Thr
time[real]( Threads:-Map(w->map(P,eval({w[]},H)),models) );

52.764

# OP original
time[real]( map(w->map(v->[op](v),indets(w,name) minus svars),models) );

87.201

# my tweak of OP original
time[real]( map(w->map([op],indets(w,indexed)),models) );

70.308

time[real]( Threads:-Map(w->map([op],indets(w,indexed)),models) );

23.144

 

 

Download emendes_exS3_7.mw

The timings grow by more than a factor of ten, for ten times as many elements. I suspect that garbage-collection may be taking its toll. I don't see a clear way to reduce garbage production in these approaches.

ps. Can the same alpha[n,m] appear twice in one of the sums, eg,
       x*z^2*alpha[3, 19] + z^3*alpha[3, 19]]
? If not then a call to content might be removed.

@emendes That's great, thanks.

I suspect that something fast can be cooked up, and without using indets. (...a bit like my variant above that didn't use indets, but likely faster as there is more structural detail known now.)

Gosh, even that 11pt (MS-Windows?) looks too heavy, to me.

Here's how it looks on my Ubuntu 24.04.4 LTS, in Maple 2026.1, without any Style-set use. This is a screenshot.

The size in this screenshot is not accurate, compared to what I see in the Maple GUI (which is actually good, for the 11pt non-bold). The ratio of the size of the 2D Input to that of the 11pt text is accurate. 

The weighting shown in this image is an accurate comparison to what I see in the product (which is actually good, for the 11pt non-bold).

Text_Linux_20261.mw

For fun,

restart;

`value/F` := proc()
   return W(args);
end proc:

 

expr1 := sin(F(p,q)) + F(cos(t),tan(t)) + %F(a,b);

sin(F(p, q))+F(cos(t), tan(t))+%F(a, b)

value(expr1);

sin(W(p, q))+W(cos(t), tan(t))+F(a, b)

value(%);

sin(W(p, q))+W(cos(t), tan(t))+W(a, b)

 

`print/Q` := proc()
   return Z(args);
end proc:

 

expr2 := sin(Q(p,q)) + Q(cos(t),tan(t)) + %Q(a,b);

sin(Q(p, q))+Q(cos(t), tan(t))+%Q(a, b)

lprint(%); # what expr2 actually contains

sin(Q(p,q))+Q(cos(t),tan(t))+%Q(a,b)


Download old_externsion_mech_ex2.mw

1 2 3 4 5 6 7 Last Page 1 of 612