Carl Love

Carl Love

28015 Reputation

25 Badges

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

MaplePrimes Activity


These are answers submitted by Carl Love

The straightforward indexing seems very fast to me. If the slowness that you're experiencing is due to getting near your memory limit, I don't think that Threads will help. But otherwise, maybe it will. When I used the threaded procedure below, it was somewhat slower than straightforward indexing, but it was less than double the time. However, I only have 4 processors and you have far more than twice as many as I do. So for you this procedure may be faster:

N:= 2^23:
L:= [seq](rand(), n= 1..N):
ind:= combinat:-randperm(N):

L1:= CodeTools:-Usage(L[ind]):
memory used=64.00MiB, alloc change=64.00MiB, 
cpu time=266.00ms, real time=279.00ms, gc time=0ns

#Modification of Iterator:-SplitRanks so that the output is ranges.
SR:= (n::posint)->
    (curry(op,1)..add-1)~(Iterator:-SplitRanks(n, _rest))          
:
TM:= (L::list, J::list(posint))-> 
    (M-> op~(M(`?[]`, L, M([`?[]`], J, `[]`~(SR(nops(J)))))))(
        Threads:-Map[2]
    )       
:
L2:= CodeTools:-Usage(TM(L, ind)):
memory used=192.01MiB, alloc change=192.04MiB, 
cpu time=843.00ms, real time=431.00ms, gc time=0ns

evalb(L1=L2);
                              true

 

You didn't say your Maple version in your header. This Answer applies if you're using Maple 2021, which introduced a variant of seq that allows for the efficient application of any associative operation to an arbitrary sequence of operands:

seq[reduce= `.`](A[k], k= 1..9); 

If the operation can be performed "on the fly" (which would be the case if the As were actual numeric matrices), this is more efficient because no actual sequence is created. Here's an efficiency comparison (the multiplication of 8192 2x2 hardware-float matrices):

restart:
A:= ['LinearAlgebra:-RandomMatrix(
    2, datatype= hfloat, generator= rand(0. .. 1.)
    )' $ 2^13
]:
CodeTools:-Usage(`.`(seq(a, a= A)));
memory used=1.08GiB, alloc change=-2.00MiB, 
cpu time=3.08s, real time=3.83s, gc time=1.67s

             [                   -202                     -202]
             [4.26894100810890 10      4.41948838632668 10    ]
             [                                                ]
             [                   -202                     -202]
             [8.47485884034369 10      8.77373103763009 10    ]

restart:
A:= ['LinearAlgebra:-RandomMatrix(
    2, datatype= hfloat, generator= rand(0. .. 1.)
    )' $ 2^13
]:
CodeTools:-Usage(seq[reduce= `.`](a, a= A));
memory used=17.75MiB, alloc change=0 bytes, 
cpu time=172.00ms, real time=175.00ms, gc time=0ns

             [                   -202                     -202]
             [4.26894100810890 10      4.41948838632668 10    ]
             [                                                ]
             [                   -202                     -202]
             [8.47485884034369 10      8.77373103763009 10    ]

 

Indexing vs. subscriptingFirst, note the difference between indexing, as in alpha[2], and subscripting, as in alpha__2 (that's two underscores). (The 2 can be almost anything, not necessarily numeric.) These look almost the same in prettyprinted output: With subscripting, the 2 will be italicized[*1], and with indexing the will be upright. But these two variable types mean very different things: Subscripting produces completely independent variables, and indexing produces variables which'll become linked into a single data structure called a table if a value is assigned to any one of them. So, when you're assigning values to variables, you almost always want subscripting.

The types atomic, name, symbol, indexed, and suffixedIn much Maple documentation (especially in GUI menus), what I'm calling subscripted variables are often called atomic variables. However, this terminology is misleading because any variable is atomic in Maple's formal type system (see ?type). In that system, those without indices are called symbols. The union of the set of symbols and the set of indexed symbols are called names. The type system has no formal predefined nomenclature for subscripted symbols constructed with double-underscore, but, if need be, one could be constructed from suffixed. See the help pages ?type, ?name, ?type,name, ?type,symbol, ?type,indexed, and ?type,suffixed.

CatenationProducing a symbol from underlying characters (or groups of characters) arranged in a particular order is called catenation. This term is not specific to Maple; it's widely used in computer science and other fields. Maple has three primary commands for catenation: catnprintf, and the infix operator || (double-vertical-bar). The end result of all these are essentially the same: global symbols. There's no way to construct local symbols with catenation.

Examples: alpha__||2cat(alpha__, 2), and nprintf("alpha__%d", 2) all do the same thing.

The commands cat and || (but not nprintf) have two additional features that are especially applicable to your Question:

  1. They can produce sequences of symbols.
  2. They can be used on the left side of the assignment operator :=, and (unlike seq or $) that remains true even if a sequence is produced.

Multiple assignment: This works as you said. A sequence of n assignable entities (such as names) followed by := followed by a sequence of values (numeric or otherwise) will do all the assignments at once.

Putting it all together: Your goal can be achieved by

alpha__||(1..JointNum):= 0 $ JointNum;

or by

cat(alpha__, 1..JointNum):= 0 $ JointNum;

There's only a stylistic difference, not a practical one, between these two.


[*1] It's also possible to produce subscripted symbols whose subscripts are not italicized and thus prettyprint exactly the same as their indexed counterparts. Doing this is a little bit beyond the scope of this Answer, but ask if that's what you want.

The 2nd-to-last line contains the inequation b <> . The operator <> requires two operands. Your expression is missing the 2nd (or right-side) operand. So, that needs to be b <> 0.

I believe (not sure) that the variable-scoping problem that you originally reported (which is now at the bottom of your current Question---thanks for not deleting it) is not as general as you originally thought. I think that it's specific to using the name of a local object-submodule as an implied type, which is a quite special circumstance. Below, I show a technique (which is a bit too formal and builtin-by-design to be called a "workaround") that avoids this problem yet

  1. still allows you to use a type specifier on the declaration,
  2. keeps the object module as a local sibling to the exported submodules.

SInce your workaround doesn't address that point 1, and since my technique is builtin-by-design, and since I've thought about this on and off for 6 hours, this Answer is still worth posting.

First, some tips on using A:-B references: I think that it's usually a bad idea to use a reference to a module named A in the code of itself. If is local to A, then using A:-B is very likely to cause an error (because the syntax A:-B usually implies that is an export of A); whereas using without prefix is, of course, usually fine (except for this "type" issue---discussed at length below), just like the locals in most languages. If is a module local (or export), and there's a need to distinguish it from a that is a procedure local of one of the parent module's procedures, then the special syntax thismodule:-B can be used. However, unfortunately, there's no prefix such as `this_module's_parent`:-, which would apply in your case (where is the parent module of submodule A1).

Here's my technique. I don't have time to explain it fully at the moment, however feel free to ask any questions about it. The key point is that to be effectively useful, a type name must essentially be a global keyword. And look at the help pages ?ModuleType, ?ModuleLoad, and ?TypeTools. I didn't include anything equivalent to your submodule A2 because its presence added nothing of interest to your example.

restart:
interface(warnlevel= 4):
kernelopts(assertlevel= 2):

A:= module()
   export 
       module_A1:= module()
           export
               boo:= proc()
                   local X::':-person_type':= Object(person_type);
                   return 1
               end proc;
       end module
   ;
   local
       person_type:= module()
           option object;
           export 
               name::string, 
               age::string
          ; 
          local
              ModuleLoad::static:= ()->
                  TypeTools:-AddType(':-person_type', person_type)
          ;
          ModuleLoad()
      end module
   ;
end module:

 

It's a stochastic matrix, therefore its eigenvalue of largest magnitude is 1 and all other eigenvalues have magnitude <= 1. See this Wikipedia article: Stochastic matrix

It can be done like this:

sys:= {bcs||(1..2), ode||(3..4)}: 
SFvsNU:= Nu->
    eval(
        diff(f(eta), eta$2),
        dsolve(eval(sys, [N= Nu, a= 1, S= 1]), numeric)(0)
    )
:   
plot(SFvsNU, 1..10);

Both jobs can be done by the same command:

collect(numer(B(4)), x, factor);
collect(F(5), x, factor); #F(5) could also be numer(F(5));

If you want to keep the p factors in each term grouped together, then

collect(numer(B(4)), x, ``@factor);

In the future, please attach a worksheet (using the green uparrow) or include plaintext code of your expressions such that we can cut and paste it into Maple.

You can move mod to the end, like this:

solve(5*x=7, x) mod 16;

That'll give you simply 11.

You can modify mod itself so that it always returns an expression for "all solutions" like this:

restart:
modp__orig:= eval(modp):
unprotect(modp):
modp:= proc(e,p) 
local r, Z:= `tools/genglobal`(_Z);
    assume(Z, integer);
    r:= modp__orig(e,p);
    try r+p*Z catch: r end try
end proc:
protect(modp, modp__orig):

solve(5*x=7, x) mod 16;
                          11 + 16 _Z0

If you're using mods instead of the default modp, then replace modp with mods in all the above.

Like this:

deg:= n-> cat("", n, "&deg;"):
plots:-polarplot(
    t, t= 0..2*Pi, 
    axis[angular]= [  #or axis[2]
        tickmarks= 
            [seq](
                k*Pi/16= `if`(irem(k,4)=0, typeset(deg(k*45/4)), ""), 
                k= 0..31
            ), 
        gridlines= [32, majorlines= 4]
    ]
);

I don't think that there's any good reason why the angle axis is axis[2] rather than axis[1]. That's just the way it is. [Edit: Or you can use axis[angular] instead of axis[2], but that's limited to when axiscoordinates= polar. If using axis[2], then the options work for any 2D plot.]

[Edit: Of course, Kitonum's angularunit= degrees is simpler, but if you want the degree symbol (superscript small o), then I think that you still need the tickmarks option. And since the tickmarks option changes the gridlines, to restore the normal gridlines, I think that you need the gridlines option also.]

I will answer your questions about the scope rules, but there's two preliminary issues to clear up first:

Packages vs modules: Packages and modules are not exactly the same thing. The rules below apply to all modules, regardless of whether they are also packages. The vast majority of modules are not packages, so it would be best if you didn't confuse the issue by using the word "package" for this.

local vs global: Here I am contrasting only the keywords local and global rather than contrasting the concepts of "local variable" and "global variable". It's natural to think that these keywords do parallel things, each "creating" a type of variable. However, that isn't true. In my opinion, global should not be used. The alternative is shown below. I'm not saying that global variables shouldn't be used, just that the keyword global shouldn't be used.

Scope rules:

You asked:

  • Is the local variable XM accessible to the procedures used in SUBPACKAGE1?

Your tentative answer for this one was wrong. Yes, it is accessible simply as XM, no matter how deeply nested the procedures and submodules are.

  • Is the local variable X1 accessible to the procedure used in MAINPACKAGE ?

You are correct that it isn't (ordinarily) accessible. (For debugging purposes it's possible to make it accessible by setting an option that I won't mention here.)

  • Is the global variable YM accessible to the procedures used in SUBPACKAGE1? 
    Is the global variable Y1 accessible to the procedure used in MAINPACKAGE ? 

Global variables are accessible anywhere, regardless of whether they're declared with global. If there's a need to distinguish between a local x and a global x, or if an assignment is being made to global x, use the syntax :-x.

  • Is the export variable ZM accessible to the procedures used in SUBPACKAGE1? yes
    Is the export variable Z1 accessible to the procedure used in MAINPACKAGE ? yes with the syntax SUBPACKAGE:-Z1

Your tentative answers to those two are correct; however, for the second question, it makes me cringe that you call it a "package" instead of a module. It would be best if you simply forgot about packages, at least until you've mastered modules.

Your matrix P isn't necessarily stochastic, i.e., its rows don't necessarily sum to 1. You can make it stochastic by redefining some variables, such as

pi[1,2]:= 1-pi[1,1]:
pi[2,2]:= 1-pi[2,1]:

That'll take care of the first two rows. Rows 3 & 4 have many more variables. Did you really intend to use both rho and as variables?

If you make the matrix stochastic in this manner, your procedure will take about 30 seconds.

Another issue: This is apparently not a problem in your procedure, but I'd recommend against using the use command in a procedure. Instead, use a uses clause, as in

steadyStateVector:= proc(P::Matrix(square))
uses LA= LinearAlgebra;
local n:= upperbound(P)[1];
    LA:-LeastSquares(<P - LA:-IdentityMatrix(n) | <1$n>>^+, <0$n, 1>)^+
end proc
:

or

steadyStateVector:= proc(P::Matrix(square))
uses LinearAlgebra;
local n:= upperbound(P)[1];
    LeastSquares(<P - IdentityMatrix(n) | <1$n>>^%T, <0$n, 1>)^%T
end proc
:

 

You say that you tried the unassign command and that it didn't work. I suspect that you tried

unassign(phi);

However, (and unfortunately) the correct syntax is

unassign('phi');

A more commonly used alternative to unassign has already been mentioned:

phi:= 'phi';

(Indeed, given that the unassign command requires the quotes, I don't even see the point for its existence.)

An efficient way to do this is to use a little-known Maple data structure called a heap. A heap is like a stack or a queue except that the total ordering of the elements is determined by a user-supplied function rather than by arrival time.

SeqA:= proc(n::posint)
uses NT= NumberTheory;
local
    k, p:= 1, #current and initial value 
    A:= Array(1..1, [p]), #storage for sequence
    #structure that tracks "least entry not previously used this way": 
    H:= heap[new]((i,j)-> `if`(A[i]=A[j], i>j, A[i]>A[j])),
    N:= table(sparse) #non-novel entries
;
    for k to n-1 do
        A(k+1):= `if`(N[p]=0, NT:-tau(p), p + A[heap[extract](H)]);
        N[p]:= (); p:= A[k+1]; heap[insert](k, H)
    od;
    seq(A)
end proc
:    
SeqA(99);
1, 1, 2, 2, 3, 2, 4, 3, 5, 2, 4, 6, 4, 7, 2, 5, 7, 11, 2, 6, 8, 
  4, 8, 12, 6, 11, 16, 5, 11, 16, 22, 4, 10, 4, 8, 12, 19, 2, 9, 
  3, 5, 8, 13, 2, 10, 12, 20, 6, 14, 4, 10, 14, 22, 31, 2, 12, 
  14, 24, 8, 18, 6, 14, 20, 31, 42, 8, 19, 27, 4, 16, 20, 32, 6, 
  18, 24, 36, 9, 22, 31, 45, 6, 20, 26, 4, 18, 22, 36, 52, 6, 22, 
  28, 6, 22, 28, 46, 4, 22, 26, 44

 

Another way to do it is to set A4(0.):= 1. before doing the seq command. This is called setting a remember table value.

First 64 65 66 67 68 69 70 Last Page 66 of 394