Carl Love

Carl Love

28035 Reputation

25 Badges

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

MaplePrimes Activity


These are answers submitted by Carl Love

The following could be made faster by pregenerating arrays of random numbers (like you did in your code), but this is a good exposition of the Maple animation technique.

2D animation:

U:= RandomTools:-Generate(float(method= uniform, range= 0..1), makeproc):
frames:= 2^5:
points:= 2^9:
plots:-display(
     ['plot(x-> abs(x)*U(), -5..5, adaptive= false, numpoints= points)' $ frames],
     insequence, thickness= 0
);

3D animation:

frames:= 2^4:
points:= 2^7:
plots:-display(
     ['plot3d((x,y)-> abs(x)*U()+abs(y)^2*U(), -5..5, -5..5, grid= [points$2])' $ frames],
     insequence, shading= zhue, thickness= 0, style= wireframe, transparency= 0.5,
     axes= frame
);

I think that the 3D one may be too bulky to animate here in the post, but you will see it animated in a worksheet. You should use the toolbar controls to slow the Frames Per Second (FPS) to 1 to give the rendering a chance to keep up.

Here is the complete and simplified code. This procedure will print the complete contents of a module---all locals and all exports---and apply itself recursively to all submodules. I was able to simplify from the earlier code by eliminating the need for unwith and parse.

ModulePrint:= proc(M::`module`)
uses ST= StringTools;
local
     N,
     SIV:= interface(verboseproc= 2),
     SKO:= kernelopts(opaquemodules= false),
     EVs:= eval~(op~([1,3], eval(M)))
;
     for N in ST:-Split(sprintf(" %q", op~([1,3], eval(M))[]), ",") =~ EVs do
          printf("%s", lhs(N));
          print(rhs(N));
          if rhs(N)::'`module`' then  thisproc(rhs(N))  end if;
     end do;
     kernelopts(opaquemodules= SKO);
     interface(verboseproc= SIV);
     return
end proc:

combinat:-permute([0$3,1$3], 3);

     [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]

I'm guessing that the above list is what you really want. If you actually want the output as you presented it, then add (as Kitonum did)

(cat@op)~(%)[];

     000, 001, 010, 011, 100, 101, 110, 111

Here's a procedure for it. It's a bit quick-and-dirty because I'm in a hurry right now. Ideally, it'd do exports as well as locals, but the specific module that you mentioned doesn't have any exports. Also, ideally, it'd apply itself recursively to submodules.

The printout of the Maplet code is unfortunately not indented. This would be difficult to achieve, but I'll think about it.

restart:

ModulePrint:= proc(M::`module`)
uses ST= StringTools;
local
     Ns:= ST:-Split(sprintf("%q", op(3, eval(M))), ","),
     N
;
     interface(verboseproc= 2);
     kernelopts(opaquemodules= false);
     for N in Ns do
          lprint(N);
          parse(sprintf("print(eval(%s))", N), 'statement')
     end do
end proc:

ModulePrint(Student:-Calculus1:-DiffTutor);

Edit: There is a bug in the above procedure. Please use the corrected code in the third Reply below.

If your evaluations of the parameters are rational numbers, then an exact solution can be obtained instantaneously, as in

Vars:= {A||(1..8)}:
Rand:= rand(1..9):
Params:= indets(sys1, name) minus Vars:
Vals:= zip(`/`, '['Rand()'$nops(Params)]'$2):
sys2:= eval(sys1, Params=~ Vals):
<solve(sys2, Vars)[]>;

Even exact rational solutions with thousands of digits can be obtained instantaneously. Maple/GMP is very fast with rational arithmetic (state of the art, perhaps?).

If you have any procedure, and you want to expand its domain to types that it wasn't designed for, or you want to change its behavior for certain types of input, then you can use overload. In this case, we want to extend the definition of numelems to include input of type name.

unprotect(numelems);
numelems:= overload([
     proc(C::name &under (C-> eval(C))) option overload; 0 end proc,
     numelems
]):

numelems(a);
     0

numelems([1,2,3]);

     3

numelems(a+b);

Error, invalid input: numelems expects its 1st argument, t, to be of type indexable, but received a+b

(A curiosity about the above code: I was forced to use C-> eval(C) rather than the simpler eval. The latter tries to use two-argument eval, as seen in the following:

type(a, name &under eval);

Error, (in type/&under) invalid input: eval received NULL, which is not valid for its 2nd argument, eqns

This is a very curious error message because I thought that a procedure parameter could never be passed NULL! But what I'm more curious about is why it chooses to use two-argument eval. Here's a simpler example of the same thing:

eval(a, NULL);

Error, invalid input: eval received NULL, which is not valid for its 2nd argument, eqns)

Your entire loop needs to be in one execution group. To achieve this, use the editing command Join Execution Groups. You can find this by going to the Edit menu, then Split or Join, then Join Execution Groups. The shortcut key for this is F4. So, the entire operation can be done by placing your cursor at the top of the worksheet and repeatedly pressing F4. The cursor won't move, but if you look closely at the execution group boundaries at the left edge of the worksheet, you'll see them change.

After having merged the execution groups, add the loop header to the top and end do; to the bottom. If any of your original execution groups contained text rather than code, it doesn't matter: The parser will treat that text as comments. So, this process also gives you a way to include formatted text comments in your code.

This is the same error message as you got with your bubble sort procedure a few days ago. Like I told you, you can't ordinarily assign to a formal parameter, such as n. In other words, n shouldn't appear on the left side of :=. If you need to do something like that, first copy n to a local variable, as Markiyan has copied it to k. Also, I told you not to use the print statement as a substitute for a procedure's return value.

Here's my Collatz procedure:

Collatz:= proc(N::posint)
local n:= N, count;
     for count while n <> 1 do
          n:= `if`(n::even, iquo(n,2), 3*n+1)
     end do;
     count
end proc:

I find binary infix operators very useful. In this case, I agree with the use of the notation of that "other CAS", although I usually abhor it. The following code allows for the use of notation very similar to what you posted. You just need to change the angle symbol to &<.

phasor:= module()
option package;
export
     `+`:= proc(Z1::`&<`(realcons,realcons), Z::seq(`&<`(realcons,realcons)))
     option overload;
     local z, r:= add(op(1,z)*exp(op(2,z)*Pi/180*I), z= args);
          evalf(abs(r)) &< evalf(argument(r)*180/Pi)
     end proc,

     `-`:= proc(Z::`&<`(realcons,realcons))
     option overload;
          (-1*op(1,Z)) &< op(2,Z)
     end proc
;
end module:

with(phasor):

4&<45 + 5&<30;

This allows for sums and differences with an arbitrary number of phasors. With a little more work, I could get the output to print without the & and the extra parentheses.

After you fix the error pointed out by Robert Lopez, you should get an error about pi. In Maple, this must be capitalized (Pi) if you mean the famous mathematical constant.

Here's an example of what you want. It involves using the keyword unevaluated function typeset in the plot command (see ?plot,typesetting). I tested with 99 frames of triplets of random numbers, and there's no jitter. All that you need from this are the procedures P3 and T3; the rest is just code I wrote to test them.

P3:= (x::realcons)-> sprintf("= %5.3f   ", x):
frames:= 99:
L||(1..3):= 'RandomTools:-Generate(list(float(method= uniform), frames))' $ 3:
T3:= (f::seq(realcons))-> typeset(seq([theta[_k], P3(f[_k])][], _k= 1..nargs)):
plots:-display(
     [seq(
          plot(
               0,
               title= T3(seq(L||_k[_j], _k= 1..3)),
               titlefont= [TIMES,ROMAN,14]
          ), _j= 1..frames
     )],
     insequence
);


It can be done with a single command:

a2_||(pos,neg):= selectremove(t-> sign(t) = 1, a2);

Clearly you want a matrix whose entries are matrices rather than a block matrix because your sizes won't fit into a block matrix. Here's how to do it:

a1 := Matrix(3, [1, 2, 3, 7, 8, 9, 13, 14, 15]);
a2 := Matrix(3, 2, [5, 6, 11, 12, 17, 18]);
a3 := Matrix(2, [19, 20, 25, 26]);
a4 := Matrix(3, 2, [5, 6, 11, 12, 17, 18]);
A := Matrix(2, 2, (i,j)-> a||(2*(i-1)+j) );

Here's a complete example showing the method that I outlined in my Replies. I compute, by purely numeric computation, the surface area of the isosurface MF(x,y,z) = 11 for MF(x,y,z) = sin(y*z)+x+11. Then, for accuracy comparison, I compute the surface area by trivial numero-symbolic computation.

My worksheet could be made more efficient; there's some redundant calculation. I used the way that I did for clarity of exposition.

 

restart:

Digits:= 15:  #Usually the best value for numerics.


(* The MF below is kind of trivial, but it's a numeric black box to
simulate what you have. It only returns numeric information---none of
the root-finding, derivative, or integration routines can see inside it
to analyze the symbolics. *)

MF:= proc(x,y,z)
option remember;
     `if`(andmap(type, [args], realcons), sin(y*z)+x+11, 'procname'(args))
end proc:

(* F is the procedure that expresses the isosurface MF(x,y,z) = 11 as a
function x = f(y,z) *)

F:= proc(y,z) option remember; fsolve(x-> MF(x,y,z)-11, -1..1) end proc:

(* Fy and Fz are procedures for the numeric computation of the partial
derivatives of F. *)

Fy:= proc(y,z)
local x;
     if andmap(type, [args], realcons) then
          x:= F(y,z);
          -fdiff(MF, [2], [x,y,z])/fdiff(MF, [1], [x,y,z])
     else
          'procname'(args)
     end if
end proc:

Fz:= subs([2]= [3], eval(Fy)):
 


#Now do a pure numeric computation of the surface area of the isosurface.
evalf(Int((y,z)-> sqrt(1+Fy(y,z)^2+Fz(y,z)^2), [0..1, 0..1], epsilon= 1e-7));

1.24007661357377

(1)


(* For accuracy comparison, use numero-symbolic computation to get
the same surface area. Simply use the method from elementary
multivariable calculus. *)
G:= (y,z)-> sin(y*z):

evalf(Int(sqrt(1+D[1](G)(y,z)^2+D[2](G)(y,z)^2), [y= 0..1, z= 0..1]));

1.24007661358042

(2)

#So the first integration had 11 digits accuracy even though we only asked for 7!
#Amazing!!

``

 

Download numeric_isosurface_surface_area.mw

Your immediate problem is that X is a formal parameter, and you can't assign to a formal parameter. It may appear to you that you are only assigning to the elements of X, but since it's a list, any assignment to it reassigns the whole list. This makes assigning to the elements of a list a very bad thing to do, even though Maple will sometimes allow you to get away with it. The solution is to convert the list to a Vector on input, do your sorting, and then convert back to a list on output.

Some other points:

  1. Sorting the empty list should return the empty list; it shouldn't be an error. In the code below, the empty list just "falls through" because the outer for loop will just be skipped if n is 0 or 1.
  2. You can switch two elements in a Vector in a single assignment without using an intermediate variable. Look carefully at the central assignment in my code below.
  3. Don't use the print statement to return values. Either use a return statement, or just put the expression to be returned immediately before the end proc.

Here is your code with all of these recommendations implemented:

Bubble := proc(L::list(realcons))
local n := nops(L), X := Vector(L), i, j;
     for i to n-1 do
          for j to n-i do
               if X[j+1] < X[j] then
                    X[j..j+1]:= X[[j+1, j]]
               end if
          end do
     end do;
     convert(X, list)
end proc:

#Test it.
R:= rand(-99..99):
L:= ['R()'$99];

     output omitted

Bubble(L);

     output omitted

All in all, you did a good job! Your logic was perfect, and your only real problem was due to an idiosyncracy in the way that Maple implements lists.

 

 

First 247 248 249 250 251 252 253 Last Page 249 of 395