Carl Love

Carl Love

27291 Reputation

25 Badges

11 years, 362 days
Himself
Wayland, Massachusetts, United States
My name was formerly Carl Devore.

MaplePrimes Activity


These are replies submitted by Carl Love

@segfault Many procedures in the Maple library (and there are many thousands) are short (say, 5 t0 20 lines) and written in fairly easy to understand Maple. As you read these, you will encounter a few unfamiliar commands, such as `tools/genglobal`. If you're reading a procedure that has many unfamiliar commands, you should probably move on to an easier procedure.

The command LibraryTools:-Browse gives you convenient access to all procedures in the library that are written in Maple, which is the vast majority.

The following is a learning exercise that I devised and practice very frequently. There is no better way to learn a computer language:

  1. Find a procedure or other code written by someone else that seems bloated, overwritten, or hard-to-read, etc. Your goal is to simplify this code (without changing its output). Numerous examples can of course be found here on MaplePrimes. The best examples are procedures that have repeated subexpressions. Your goal then is to remove as much repetition as possible. Also, remove unnecessary parentheses.
  2. It would be best if you only have a very vague idea of the purpose of the code. This forces you to only make small changes rather than doing a top-down rewrite. But, you may make a great many very small changes.
  3. If the code has comments or the author has said something about it, ignore it. Assume (for the time being) that they are likely mistaken. However, assume that the code does something useful (that you don't understand) for somebody and does it correctly. Obviously, this means that you can't practice this exercise on code that has syntax errors, doesn't compile, etc.
  4. For each small change that you consider making, ask yourself whether there's any possibility that the code change could change the output. If your answer is "No", make the change. If you can't answer it, run some small test cases of the same small change by itself in some other area than the main code (such as in a scratch worksheet). You don't need to prove mathematically that the code change won't change the output; empirical evidence is enough. Reading help pages is fine at this point.
  5. For every small change, check that the code is still syntactically correct. In Maple, you just need to press Return to do this. This makes Maple an excellent learning environemnt.
  6. Every few minutes, check that the code still produces the same output.

@segfault Just stop using the procedure and it will revert.

@nm Thanks, I understand now,

Please point  out which RootOf needed to be evaluated twice and repost the worksheet with that RootOf hightlighted.

@sursumCorda No, the bug is in solve, as my Answer clearly shows. RealDomain:-solve is just repeating what it got from solve. If you filed a report against RealDomain:-solve, you should amend it. And I hope you didn't include your whole problem in the report. The complete bug is shown by this simple example:

sys:= [p^2-4*q = 0, 2*p^5 - 15*p^3*q + 27*p*q^2 < 0]:
solve(sys, {p});
               /        (1/2)\    /       (1/2)\ 
              { p = -2 q      }, { p = 2 q      }
               \             /    \            / 

eval(sys, [q=1, p=2]);
                        [0 = 0, -2 < 0]

It could be that inequalities with high-degree polynomials are being ignored by solve.

There was no good reason to use RealDomain:-solve. If there are no complex solutions, then there are no real solutions either. It is IMO one of the most frequently misused commands in Maple.

When coulditbe returns true or is returns false, they don't provide witnesses. This alone makes them nearly impossible to trust. The much-more-sophicated command QuantifierElimination:-QuantifierEliminate provides witnesses.

Anytime that you have a combination of algebraic relations and logical operators, you should put it in DNF before procveeding.

@nm I'm glad that you know about `tools/genglobal`; vote up.

I don't intend to sound harsh or critical; I just want you to understand this difference between indets and evalindets or subsindets.

Although your Answer above works well, in light of the other "evalindets vs. indets" Reply that I just posted, I take this as further evidence that you've somehow switched the meanings of evalindets and indets in your mind. Specifically, the evidence in this case is that these 3 lines from your procedure above:

C_used:= indets(sol, And(symbol, suffixed(c__,  nonnegint))):
my_C := map(X-> X = `tools/genglobal`(c__), C_used):
eval(sol, my_C);

do exactly the same thing as 

evalindets[flat, 2](sol, suffixed(c__, nonnegint), c__, `tools/genglobal`);

The process requested in this Question is not simply to find the constants (for which indets works great), but to find and replace them, which is a task for evalindets or subsindets.

@nm I think that you don't understand the difference between indets and evalindets. The former, indets, is for simply finding subexpressions; whereas evalindets (or subsindets) are for simultaneously finding and replacing subexpressions.

It's only somewhat of a coincidence that your use of evalindets above works, the coincidence being that the DESol is the entire thing being searched, not merely a subexpression of the thing being searched. So, to extract the 1st argument(s) of any DEsol(s), do

#Your example:
ode:= diff(y(x),x)-y(x)*a-b*y(x)^2=f(x):
my_sol:= DESol( ode, y(x) ):


(op@op~@@2)(1, indets(my_sol, specfunc(DESol)));

To extract the 2nd argument(s), change 1 to 2.
 

@C_R Yes, I use those from the plot context menu, not from a toolbar. I wasn't even aware of the existence of a plot toolbar until you showed it a few Replies ago.

@C_R This is probably related to the plot toolbar dissappearance. 

I only use menus and toolbars for commands that can't be typed. So I never use the plot toolbar. But I do use the animation toolbar because there's no typed command for animation speed. I've notivced that the animation toolbar disappears when there strangely inaccessible submenus.

@sursumCorda Above, I wrote:

  • You only need to invoke the last parameter:
        d; true
    because if the last parameter is matched, then so are all the others,

I am working on essentially the same thing as you, and now I have doubt about the truth of that statement. It seems in some circumstances (that are still quite vague to me) that I need to invoke all the parameters in order to trigger the overload. I need to do more research on it.

@sursumCorda The procedures are not the same because the absence in the passed arguments (i.e., args) of a match for a declared parameter that isn't actually used is not an "invalid input" error (as has always been the case in Maple), which is the only type of error that triggers the overload mechanism. I don't see this as a limitation, because I don't see how else it could possibly work. 

You only need to invoke the last parameter:

    d; true

because if the last parameter is matched, then so are all the others. Invoking any unmatched parameter triggers the "invalid input".

@acer All the iterators in the Iterator package auto-compile by default unless option compile= false is given. For the small case that you tested (7-permutations), the time for this compilation is the vast majority of the measured time. Here is a modified version of your time tests that takes this into account. I needed to increase the size to 9-permutations because the times for smaller cases are too small to be accurately measured (cpu time < 0.16 ms).

# Parameters common to all tests:
#
restart:
n:= 9:
save n, "n.m"

# Kitonum's method: select via ListTools:-Search:
#
restart;
read "n.m":
CodeTools:-Usage(
    select(
        t->ListTools:-Search(2,t)<ListTools:-Search(3,t),
        combinat:-permute(n)
    )
):
nops(%);

memory used=364.54MiB, alloc change=257.04MiB, cpu time=391.00ms, real time=1.97s, gc time=359.38ms

181440

# TopologicalSorts, using precompiled Iterator:
#
restart;
read "n.m":
P:= Iterator:-TopologicalSorts(2, {1<2}): #Force Iterator to compile.
#set-of-vectors form:
CodeTools:-Usage(
    {seq}(p[], p= Iterator:-TopologicalSorts(n, {2<3}))
):
nops(%), type(%, set(rtable));
#set-of-lists form:
CodeTools:-Usage(
    [seq]~({seq}(p[], p= Iterator:-TopologicalSorts(n, {2<3})))
):
nops(%), type(%, set(list));

memory used=58.83MiB, alloc change=37.85MiB, cpu time=109.00ms, real time=296.00ms, gc time=0ns

181440, true

memory used=156.82MiB, alloc change=110.23MiB, cpu time=656.00ms, real time=843.00ms, gc time=234.38ms

181440, true

# TopologicalSorts:
# 1st Iterator is compiled on-the-fly; 2nd continues with compiled version:
#
restart;
read "n.m":
#set-of-vectors form:
CodeTools:-Usage(
    {seq}(p[], p= (P:= Iterator:-TopologicalSorts(n, {2<3})))
):
nops(%), type(%, set(rtable));
#set-of-lists form:
CodeTools:-Usage(
    [seq]~({seq}(p[], p= Iterator:-TopologicalSorts(n, {2<3})))
):
nops(%), type(%, set(list));

memory used=71.75MiB, alloc change=70.85MiB, cpu time=62.00ms, real time=836.00ms, gc time=0ns

181440, true

memory used=156.82MiB, alloc change=110.78MiB, cpu time=125.00ms, real time=906.00ms, gc time=109.38ms

181440, true

# TopologicalSorts, using uncompiled Iterators:
#
restart;
read "n.m":
#set-of-vectors form:
CodeTools:-Usage(
    {seq}(p[], p= Iterator:-TopologicalSorts(n, {2<3}, compile= false))
):
nops(%), type(%, set(rtable));
#set-of-lists form:
CodeTools:-Usage(
    [seq]~(
        {seq}(p[], p= Iterator:-TopologicalSorts(n, {2<3}, compile= false))
    )
):
nops(%), type(%, set(list));

memory used=196.77MiB, alloc change=102.85MiB, cpu time=656.00ms, real time=1.21s, gc time=93.75ms

181440, true

memory used=291.67MiB, alloc change=78.78MiB, cpu time=953.00ms, real time=1.49s, gc time=296.88ms

181440, true

# To answer @mmcdara's request, here is TopologicalSorts timing for various # n:
#
# The first includes the time for compilation:
for n from 3 to 10 do
    printf("\nn = %d:\n======\n", n);
    CodeTools:-Usage([seq](p[], p= Iterator:-TopologicalSorts(n, {2<3})))
od:


n = 3:
======
memory used=9.10MiB, alloc change=0 bytes, cpu time=63.00ms, real time=753.00ms, gc time=0ns

n = 4:
======
memory used=9.42KiB, alloc change=0 bytes, cpu time=0ns, real time=0ns, gc time=0ns

n = 5:
======
memory used=23.84KiB, alloc change=0 bytes, cpu time=0ns, real time=1000.00us, gc time=0ns

n = 6:
======
memory used=105.66KiB, alloc change=0 bytes, cpu time=15.00ms, real time=2.00ms, gc time=0ns

n = 7:
======
memory used=0.70MiB, alloc change=0 bytes, cpu time=0ns, real time=8.00ms, gc time=0ns

n = 8:
======
memory used=5.82MiB, alloc change=0 bytes, cpu time=47.00ms, real time=41.00ms, gc time=0ns

n = 9:
======
memory used=53.44MiB, alloc change=-28.17MiB, cpu time=266.00ms, real time=389.00ms, gc time=93.75ms

n = 10:
======
memory used=0.59GiB, alloc change=399.45MiB, cpu time=3.78s, real time=3.92s, gc time=3.12s

# nutty idea
# (extra operator invocation might cost too much.
restart;
read "n.m":
L:=combinat:-permute([$n]):
CodeTools:-Usage(
    select(LL->ListTools:-SelectFirst(p->p=2 or p=3, LL)=2, L)
):
nops(%);

memory used=0.60GiB, alloc change=4.16MiB, cpu time=438.00ms, real time=3.42s, gc time=203.12ms

181440

# nutty idea 2
# (seems to0 expensive to create the sublists.
#  but allowing offset/stride/num to Search might
#  be a reasonable idea for the future...)
restart;
read "n.m":
L:=combinat:-permute([$n]):
CodeTools:-Usage(select(proc(t) local f2,f3;
                         f2 := ListTools:-Search(2,t);
                         if f2>floor(nops(t)/2) then
                           f3 := ListTools:-Search(3,t[f2+1..]);
                           return evalb(f3=0);
                         else
                           f3 := ListTools:-Search(3,t[..f2-1]);
                           return evalb(f3>0);
                         end if;
                         end proc, L)):
nops(%);

memory used=219.63MiB, alloc change=4.16MiB, cpu time=265.00ms, real time=1.30s, gc time=78.12ms

181440

 

 

 

Download SelectedPermsTiming.mw

@mmcdara You wrote:

  • It feels like everybody took the same direction than @nm's, which is "construct the list of all permutations and select those where 2 appears before 3".
    But It looks more natural to "construct directly the list of permutations for which 2 appears before 3".

No, not everybody. As I explicitly wrote in my Answer above, 

  • This command does not generate all the permutations and then filter them to get the ones that match the pattern. Rather, it generates only the ones that match in the first place.

The TopologicalSorts command is much faster than @acer 's tests indicate. There's an oversight in his timing method (understandable, since it's due to hidden compilation) that I'm about to explain in another Reply (just posted). In that Reply, I've also included the timings for N = 3..10 that you asked for. 

Knuth's "topological sort" algorithm can be found in

  • Knuth, Donald Ervin. The Art of Computer Programming, volume 4, fascicle 2 [or, nowadays, simply "volume 4A: Combinatorial Algorithms, part 1" -- Carl]; generating all tuples and permutations, sec. 7.2.1.2, p. 63, generating all permutations, algorithm V, all topological sorts.
     

@sursumCorda Using overload with the seq parameter modifier is a brilliant idea, and I think a quite original idea also. I've expanded on it, in an Answer that I just posted, to get a Maple implementation of Mathematica's Cases (at least to the extent that the OP used in this Question).

@Ronan Disjunctively means that has(e, [x,y]) is equivalent to has(e, x) or has(e, y). If instead it were has(e, x) and has(e, y), then that would be conjunctively.

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