Carl Love

Carl Love

28065 Reputation

25 Badges

13 years, 20 days
Himself
Wayland, Massachusetts, United States
My name was formerly Carl Devore.

MaplePrimes Activity


These are answers submitted by Carl Love

@radaar wrote:

  • restart;
    i:=2:
    ff:=proc()
          i:=i+2:      #### implicit local 
          return i
    end proc:
    ff()

    output: i+2

    if we assign the output to another variable and try to output the variable it shows too many level of recursion

It's an interesting phenomenon. I wouldn't call it a bug, although I'd accuse a programmer who---through sloppy programming---relied on this to be creating their own bugs.

To analyze this, let's first cut away the bloat that has nothing to do with it:

  • That i has been globally assigned has nothing to do with this.
  • That the in the proc is implicitly local rather than declared local has nothing to do with this. Indeed, there's no runtime difference between the two, and a clean programmer should never rely on implicit locals.
  • Explicit return statements aren't needed.
  • Procedures don't need names; they can be anonymous.
  • Whether the output is assigned to another variable is irrelevant.
  • It's totally legitimate for a procedure to return an unevaluated local or an expression containing such. Such a local is called an escaped local. That local can happily live in the same lexical space as the global whose name is spelled the same. Indeed, there's a command specifically for creating such a local: convert(..., `local`). It'd be very instructive for you to study the very brief code of this command: showstat(`convert/local`). I recall that deconstructing that really opened my mind when I was first learning Maple.

Okay, let's look at some examples that all generate related error messages:

restart;
i:= i+2;

Are you satisfied that that causes a "recursive assignment" error? I am. If you truly want to use recursive assigments (at the top level), this works:

i:= ' 'i' + 2';
seq(eval(i, k), k= 1..9);

I find that to be a useful way to experiment with complicated recurrence relations because it gives you the ability to "single step" through them---no debugger needed.

Your example:

restart: #This is identical to your example, without the bloat:
proc() local i; i:= i+2 end proc();
%;

The exact same thing can be done with a global:

restart;
proc() :-i:= :-i + 2 end proc();
%;

Both give the "too many levels of recursion" error.  Are you satisfied with that?
 

Your examples are straying far enough away from your original Question that I think that they deserve their own thread.

You will learn a lot by trying to remove from your examples any factors that are irrelevant to the observed phenomena---those factors that I called "bloat" above.

It's a very good Question. Vote up. I can Answer it mostly, but there are some small details that I can't explain with certainty because they are not documented.

In the expressions seq(k, k= 1..5) and int(k, k= 1..5), the k is called a bound variable (see the Wikipedia article "Free variables and bound variables"). Note that bound, as used in this context, is the past-participle adjective form of the verb to bind; it is not the noun bound as used in "lower bound" and "upper bound", nor is it the past participle of to bound (which is bounded) as in "The functions have bounded derivatives"; nor is it directly related to binding as that verb is used in the context of the with command binding exports to globals. This classification of variables comes from formal logic, and it's used in a variety of fields such as mathematics, computer science, and even linguistics (where, for example, pronouns are the variables); it's not anything specific to Maple. Anyone who programs in a symbolic language such as Maple must be at least implicitly aware of this classification to be successful.

There are a huge number of stock Maple commands that can or must use bound variables in their calling sequence (such as diffplotlimitsum), and you can easily write your own procedures that use them. It is not a special declaration that makes them bound; it's a property of the meta-logic of the procedure.

There are three Maple commands where the bound variable has a special status, and that status is the same in all three: seqadd, and mul. There is no formal name that I'm aware of for this special status; this is very poorly documented. For the sake of this article, I'll call it PLBV (partially local bound variable), although that's not a precise description. It's only those three commands that use a PLBV. There's no formal mechanism whereby you can create your own procedure that uses a PLBV, although I wouldn't be surprised if it were informally possible with the "hackware" commands (addressofdisassembleassemble, and pointto).

In many ways (but definitely not all ways), PLBVs act like local variables. This is the reason for the anomaly that you reported. You can think of the ii in ii:= 50 and the ii in the seq command as different variables. It is not necessary to use a bound variable in a seq command. If you want to repeat ii five times, you can use seq(ii, 1..5).

Use add instead of sum.

Here's what's happening: sum does symbolic summation. The symbolic summation formula for sum(c, k= a..b) (where c doesn't depend on k) is c*(b+1-a). In constrast, add just does brute-force naive computation.

The integral can be done completely by

value(IntegrationTools:-Expand(Int(test, r)));

The imaginary unit in Maple is I (uppercase), not (lowercase).

There are some commands that were commonly used when your textbook was written that have been superceded by newer commands. Three of those are arrayvector, and matrix (all begin with a lowercase letter). These have been superceded by ArrayVectorMatrix, and rtable. While the older forms continue to work, you shouldn't use them; formally we refer to them as deprecated.

A simpler alternative to Array(1..3, 1..4, (i,j)-> i^j) is Array(1..3, 1..4, `^`). I prefer to group the indices with parentheses for clarity:

Array((1..3, 1..4), `^`)

The extra parentheses are a purely syntactic addition; they have no effect on performance.

The computational problem (as opposed to the mathematical-display problem) can be addressed by iterating mul over a set or list of indices:

  • mul(lambda[i] - lambda[k], k= {$1..n} minus {i}) (set of indices)
  • mul(lambda[i] - lambda[k], k= [$1..i-1, $i+1..n]) (list of indices)

 

There is a formal paradigm that applies here called "Scales (or Levels) of Measurement". The scale determines which arithmetic and statistical operations can be performed on the measurements.

The basic (Stevens's four)[*1] scales of measurement are (this is now often taught on day 1 in "Statistics 101"):

  1. Nominal (aka Categorical): Objects being measured can be categorized into a finite number of categories, but there's no numerical relationship between the categories. Examples: {female, male}, {usable, repairable, discardable}, {too sick to be worth treating---going to die anyway; should be medically treated; not sick enough to be worth treating---going to get well anyway}. Allowed operations are set membership and set cardinality.
  2. Ordinal: Objects can be placed in order on some scale, but other arithmetic operations are not appropriate. Examples: hardness scales of materials, such as MOHS; Beaufort scale of hurricane strength. Allowed operations now include <, <=, and the like.
  3. Interval: Objects are measured in such a way that the differences of measurements make sense and can be compared by ratio, but the ratios of individual measurements make no sense. Examples: calendar year, Fahrenheit temperation, Celsius temperature.
  4. Ratio: Measurements can be compared by ratio. Examples: the vast majority of standard measurements with units, such as years or Kelvin temperature.

Although there is much debate over the details of this paradigm, the vast majority of it is that there need to be more levels[*1]; there's wide acceptance of the ideas that there are Levels of Measurement and that some classification of them is useful. Personally, I would always include a logarithmic level for scales such as pH, dB, Richter, etc.

When you do a units conversion in Maple, it is operating on the ratio scale. Suppose I said "The temperature of the solution increased by 18 Fahrenheit degrees. What's that in Celsius?" The answer is 10 Celsius degrees. That's not the same computation as a conversion of absolute temperatures on scales that don't have equal 0 points.

[*1] See Wikipedia article "Level of measurement".

Your method is not even close to something that would work. You need to make 2 implicitplots, one of each color.

restart:
S1:= (x,y,z)-> x^2 + y^2 + z^2 - 16:  
S2:= (x,y,z)-> (x+4)^2 + y^2 + (z-2)^2 - 49:
S1S2intersect:= solve([S1,S2](x,y,z), [x,y,z], explicit):
IntPlaneX:= unapply(rhs(S1S2intersect[1][1]), (y,z));
p1:= (x,y,z)-> `if`(x <= IntPlaneX(y,z), S1(x,y,z), undefined): 
p2:= (x,y,z)-> `if`(x > IntPlaneX(y,z), S2(x,y,z), undefined):
plots:-display(
    plots:-implicitplot3d~([p1,p2], -12..5, (-7..7)$2, grid= [50$3], color=~ [red, blue]),
    style= surface
);

If the scaling (or unit of measure) of the axes is different, then lines may not appear perpendicular even though mathematically they are. This can be adjusted by including the option scaling= constrained in the plotting command.

Please put Questions in the Questions section, not the Posts section.

Expectation (under null hypothesis of "true randomness") is that 1/2 of runs will be length 1, 1/4 length 2, 1/8 length 3, etc.

RunLengths:= proc(L::list)
local R:= Array(1..0), r:= 1, k;
    for k from 2 to nops(L) do
        if L[k-1]=L[k] then r++ else R,= r; r:= 1 fi
    od;
    Statistics:-Tally([seq((R,= r))]);
end
:
Chi2:= proc(T::list(`=`)) #return p-value (probability of randomness)
local chi2:= 0, T0:= table(sparse, T), S:= rhs(add(T)), E:= S, s:= 0, k;
    for k while E > 10 do
        chi2+= (T0[k]-(E/= 2))^2/E;
        s+= T0[k]
    od;
    #Last category is "all remaining": 
    1-Statistics:-CDF(ChiSquare(k), chi2 + (S-s-E)^2/E)
end proc
:
#Your data:
A:= [0,1,0,1,0,1,1,0,1,1,1,0,0,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,
0,0,1,0,1,0,1,0,1,1,1,1,1,0,0,1,0,1,0,1,0,1,0,1,1,1,0,1,0,1,0,0,0,1,1,1,
1,0,0,0,1,0,1,0,0,0,0,0,1,1,1,0,1,0,1,0,1,0,1,0,0,0,1,1,0,1,0]
:
B:=[0,1,1,0,1,1,0,1,1,1,0,1,0,0,1,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,0,1,0,0,0,1,0,1,0,1,1,0,1,1,0,0,0,0,0,0,1,0,1,1,1,0,0,0,1,0,1,0,
0,0,0,1,0,0,0,0,1,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,1,0]
:
evalf(Chi2(RunLengths(A)));
                        0.0005236818491

evalf(Chi2(RunLengths(B)));
                          0.6136983916

 

Another way is like below. I prefer this slightly over using listprocedure for two closely related reasons:

  1. listprocedure gives the naive user the false impression that it's possible to compute one of these functions without computing all 4. In reality, it's simply obscuring that it's always computing all 4 together.
  2. Managing your own remember table gives you finer control over the recomputation.

 

restart: 
ddesys := {diff(S(t),t) = -beta*S(t)*Ix(t)/N, 
diff(Ex(t),t) = beta*S(t)*Ix(t)/N - sigma*Ex(t-tau__1),
diff(Ix(t), t) = sigma*Ex(t - tau__1)- gamma*Ix(t-tau__2), 
diff(R(t),t) = gamma*Ix(t-tau__2), 
diff(Dx(t),t) = delta*Ix(t), 
S(0) = 80900, Ex(0) = 1, Ix(0) = 1, R(0) = 0, Dx(0) = 0 }:
params:= {
    beta = 4, gamma = 0.0478, sigma = 0.10, delta = 0.0005,
    N=80900, tau__1 = 1.1, tau__2 = 8.7, tau__3 = 0
}:
dsn := dsolve(eval(ddesys, params), numeric):

Dsn:= proc(t) option remember; dsn(t) end proc:

F:= t-> 
    if t::numeric then eval(op(procname)(':-t'), Dsn(t)) 
    else 'procname'(args)
    fi
:

The parts that I added are in red.

Now, you can use these numeric procedures to do things like this:

plot([F[Ex], F[Ix]], 0..9);

fsolve(t-> F[Ex](t) - 10, 0..infinity);

The definitive difference between Threads and Grid (regardless of the specific command (such as Seq) used from those packages) is that Threads uses a shared-memory environment and Grid does not. A group of processes running in parallel under Threads can all "see" the same variables at higher lexical levels. Those processes can just as well overwrite those variables, which is where potential trouble arises. Two processes overwriting the same variable at the same time will usually cause erroneous results. Those commands and procedures that are guaranteed to not do that are called "threadsafe". Many commands are actually threadsafe even though they're not formally declared as such. An example is ListTools:-Classify. Most of the high-powered symbolic commands such as solve are not threadsafe.

If it's possible to do your problem with Threads, then you should: It's faster and uses much less memory. If you cannot make your code threadsafe, but it's still parallelizable, then you must use Grid.

To upload worksheets to this forum, use the green arrow on the toolbar.
 

(f, g, a, b):= (x^cos(x), x^sin(x), 1, 15);

x^cos(x), x^sin(x), 1, 15

plots:-display(
    plots:-shadebetween(
        f, g, x= a..b,
        changefill= [color= [magenta, yellow]]
    ),
    plot(
        [f,g], x= a..b,
        color= ["DarkGreen", red], thickness= 3, legend= [f,g]
    )
);

We need the 5 intersection points. Command solve needs a bit of help with this, which I provide by taking the ln of both sides of the equation. The options explicit and allsolutions are documented at ?solve,details, but it's not documented that they can be used together when solving a transcendental equation like below. This is how we get only the solutions between the left and right boundaries:

X:= solve({ln(f)=ln(g), x >= a, x <= b}, x, explicit, allsolutions);

{x = (5/4)*Pi}, {x = (13/4)*Pi}, {x = (9/4)*Pi}, {x = (17/4)*Pi}, {x = 1}

X:= sort(eval~(x, [X]));

[1, (5/4)*Pi, (9/4)*Pi, (13/4)*Pi, (17/4)*Pi]

It helps to add the right endpoint to that. The left endpoint is already included.

X:= [X[], b];

[1, (5/4)*Pi, (9/4)*Pi, (13/4)*Pi, (17/4)*Pi, 15]

 

add(int((f-g)*(-1)^k, x= X[k]..X[k+1]), k= 1..nops(X)-1);

int(-x^cos(x)+x^sin(x), x = 1 .. (5/4)*Pi)+int(x^cos(x)-x^sin(x), x = (5/4)*Pi .. (9/4)*Pi)+int(-x^cos(x)+x^sin(x), x = (9/4)*Pi .. (13/4)*Pi)+int(x^cos(x)-x^sin(x), x = (13/4)*Pi .. (17/4)*Pi)+int(-x^cos(x)+x^sin(x), x = (17/4)*Pi .. 15)

That Maple returned unevaluated integrals means that it can't do these symbolically. So, use evalf:

evalf(%);

50.57916030

Or, the whole thing can be done without the intersection points, as I said before.

evalf(Int(abs(f-g), x= a..b));

50.57916030

 


 

Download betounes2.mw

In your example 2, Maple can't solve it because the input equations do not contain _xx[2]!

In your example 3: The explicit complex number is not the same thing as the RootOf because the RootOf represents all of the roots of the polynomial.

First 103 104 105 106 107 108 109 Last Page 105 of 395