Carl Love

Carl Love

26488 Reputation

25 Badges

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

MaplePrimes Activity


These are answers submitted by Carl Love

Now that you've mentioned the large number of warning messages, I think that your excessive times are due to Maple's incredibly slow Standard GUI display. Do the same thing in command-line Maple for comparison. You may also be able to improve the time in the Standard GUI by setting interface(prettyprint= 0) (I'm not sure if that will help).

If you use .mla files, then you could do the code reading and .mla creation in command-line Maple while you kept GUI Maple open. Then you could test the code in the GUI immediately (after using restart).

I agree with Joe: Maple is usually able to process 25,000 lines of code in the blink of an eye.

A parse command cannot reference local variables. So, it'll work if res is global. Change print(res) to print(:-res), or simply remove res from the local declarations.

The option is lightmodel= none.

Hints for the second problem, the equilateral triangle:

  1. The slope of the line tangent to the curve y = f(x) at x = a is f '(a).
  2. The slope of any line in the x-y plane (be it a tangent line or not) equals the (trigonometric) tangent of the angle that it makes with the positive x-axis. (Note that tangent is being used with two different meanings here.) As always, by convention, angles are measured from the positive x-axis counterclockwise to the line. Failure to follow this convention might change the sign of a trigonometric function, but its absolute value will still be correct.

So, what are the required angles in this case?

I assume that your starting matrix contains only nonnegative integers. Here's a procedure for it, not particularly elegant:

A:= <1,1,2,2; 0,1,1,3; 2,0,1,2; 4,1,0,3>:

Tally:= proc(A::Matrix(nonnegint))
local 
    M:= max(A), K:= [$0..M], 
    r:= upperbound(A)[1], c:= upperbound(A)[2],
    R:= Array((1..M+1, 1..c+2, 1..r), fill= ``), i, j, T, n
;
    for i to r do
        T:= map([ListTools:-SearchAll], K, A[i]);
        n:= nops~(T);
        for j in K do R[j+1, ..n[j+1]+2, i]:= <j, n[j+1], T[j+1][]> od
    od;
    seq(R[..,.., i], i= 1..r);
end proc
:   
Tally(A);

 

Efficiency hardly matters for these short examples. But for longer examples, the efficiency of Categorize can be greatly improved (from quadratic to linear) if the two-argument matching function (Cq in your case) can be converted to a one-argument function that produces the canonical representative of the equivalence class (under Cq) of its argument, and then you use Classify instead of Categorize. This is not always possible, but in this case, it is:

Cq:= G-> e-> evalindets(e, specfunc(G), g-> G(sort([op](g))[])):
entries(op~(ListTools:-Classify(Cq(G), L)));

As in my Answer in the other thread, this works for functions G of any number of arguments. Attempting to apply the prior Categorize technique to functions of more than two arguments would be much more complex than it is for two arguments. In that case, it'd be worthwhile to use this Classify technique even for very small cases such as the current L.
 

@maxbanados A symmetric version (i.e., invariant under any permutation of its arguments) of any function can be made like this:

MakeSymm:= f-> subs(_f= f, ()-> _f(sort([args])[])):
f_symm:= MakeSymm(f):
f_symm(y,x);

                           
f(x, y)

This works for any number of arguments and regardless of whether f has any formal definition (as a procedure or anything else).

I thought that I had answered this exact same question from you 2 or so months ago.

Anyway, the solution is simple enough that it's easier for me to rewrite it than to search for an old Answer. All such Questions can be easily answered by starting from the matrix AllPairsDistance. Whether u-v is an edge doesn't change the result.

ShorterDistance:= proc(G::GRAPHLN, u, v)
local 
    D:= GraphTheory:-AllPairsDistance(G),
    V:= GraphTheory:-Vertices(G),
    x
;
    V:= table(V=~ [$1..nops(V)]);
    add(`if`(x<0, 1, 0), x= D[V[v]] - D[V[u]])
end proc
: 
G:= GraphTheory:-RandomGraphs:-RandomGraph(99);
 G := Graph 1: an undirected unweighted graph with 99 vertices 
    and 2716 edge(s)

ShorterDistance(G, 42, 57);
                               24

 

Unless your Maple version is more than few years old, all that you need to do is 

add(A)

Maple has a decent p-adic package, although its documentation is horrendously minimal. All of the elementary functions are implemented. However, 4 is not prime. So what then were you thinking regarding p-adic? And what's great about base-4 in particular?

Maple has numerous ways to proceed such that someone with a good knowledge of arithmetic and logic but only modest knowledge of Maple and programming can quickly make progress with this.

You mentioned convert procedures. Yes, they can be useful. However, note that there's no paradigmic difference between convert procedures and any other procedures: A procedure whose name is of the form `convert/...` can be used with the convert command. That's pretty much all that there is to it.

Maple has tools suited to many of the major programming paradigms. You can write code that looks kinda like Fortran, Algol, Lisp, C++, Python, etc. And the usage of those tools can be blended arbitrarily. Most familiar data structures (such as lists, sets, hash tables, arrays, stacks, queues, records (i.e., containers with labelled fields), etc.) are directly implemented--no need to "load" anything, no need to include any header files. Nearly all infix operators can be overloaded. Nearly all stock commands can be overloaded. (Overloading means changing their behavior for certain types of input while retaining the original behavior for the rest.) You can also define your own infix operators and set their associativity to either left or right (but, alas, not set their precedence). You can easily write code that modifies other code without needing to deconstruct it down to character strings; most code can be deconstructed and examined algebraically. Nearly any command or operator can be easily modified (often it's a one-character modification) so that it "maps" itself over basic containers. (The list of features that I could include in this paragraph goes on and on, but I'll stop there.)

Indeed, in my opinion, even if you were to remove all of Maple's higher-math functionality and all of its GUI, you'd still be left with the finest programming language ever created.

Do you have any familiarity with object-oriented programming (OOP)? I think that that's the way to go for your base-4 numbers. Start reading the Maple Programming Guide by entering ProgrammingGuide into Maple's help search. Let me know if you have any questions. Object-oriented programming begins in Chapter 9.

Once you get the basic ring or field arithmetic set up for any object, Maple can automatically extend it to matrix arithmetic via the LinearAlgebra:-Generic package.

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:

 

First 51 52 53 54 55 56 57 Last Page 53 of 382