## `level` -- evaluate an object with a specified substitution depth

#### Introduction

`level(`object, n`)` evaluates `object` with substitution depth `n`.

#### Call(s)

`level(object)`
`level(object, n)`

#### Parameters

`object` | - | any MuPAD object
`n` | - | a nonnegative integer less than 2^31

#### Returns

the evaluated object.

#### Further Documentation

Chapter 5 of the MuPAD Tutorial.

#### Related Functions

`context`, `eval`, `hold`, `indexval`, `LEVEL`, `MAXLEVEL`, `val`

#### Details

- When a MuPAD object is evaluated, identifiers occurring in it are replaced by their values. This happens recursively, i.e., if the values themselves contain identifiers, then these are replaced as well. `level` serves to evaluate an object with a specified recursion depth for this substitution process.
- With `level(`object, 0`)`, `object` is evaluated without replacing any identifier occurring in it by its value. In most cases, but not always, this equivalent to `hold(object)`, and `object` is returned unevaluated. See example 3.
- With `level(`object, 1`)`, all identifiers occurring in `object` are replaced by their values, but not recursively, and then all function calls in the result of the substitution are executed. This is how objects are evaluated within a procedure by default.
- The call `level(`object`)` is equivalent to `level(`object, MAXLEVEL`)`, i.e., identifiers occurring in `object` are recursively replaced by their values up to substitution depth `MAXLEVEL - 1`, and an error occurs if the substitution depth `MAXLEVEL` is reached. Usually, this leads to a complete evaluation of `object`. See example 1.
- You can use `level` without a second argument to request the complete evaluation of an object not containing local variables or formal parameters within a procedure. This may be necessary since by default, objects are evaluated with substitution depth `1` within procedures. See example 2.

  Otherwise, it should never be necessary to use `level`.

- `level` does not affect the evaluation of local variables and formal parameters, of type `DOM_VAR`, in procedures. When such a local variable occurs in `object`, then it is always replaced by its value, independent of the value of `n`, and the value is not further recursively evaluated. See example 2.

- `level` works by temporarily setting the value of `LEVEL` to `n`, or to 2^31 - 1 if `n` is not given. However, the value of `MAXLEVEL` remains unchanged. If the substitution depth `MAXLEVEL` is reached, then an error message is returned. See `LEVEL` and `MAXLEVEL` for more information on these environment variables.
- In contrast to most other functions, `level` does not flatten its first argument if it is an expression sequence. See example 5.
- `level` does not recursively descend into `arrays`, `tables`, `matrices` or `polynomials`. Use the call `map(object, eval)` to evaluate the entries of an array, a table, a matrix or `mapcoeffs(object, eval)` to evaluate the coefficients of a polynomial. Cf. example 4 and example 6.

  Further information concerning the evaluation of arrays, tables, matrices or polynomials can be found on the `eval` help page.

- The maximal substitution depth of `level` depends on the environment variable `MAXLEVEL`, while the maximum evaluation depth of the function `eval` depends on the environment variable `LEVEL`. See example 7.
- Because `eval` evaluates the result again there is a difference between evaluating an expression with depth n by `level` in comparison with `eval`. See example 7.
- As mentioned `level` does not affect the evaluation of local variables and formal parameters, of type `DOM_VAR`, in procedures. Here `eval` behaves different. See example 7 and the `eval` help page for more information.

- The result of `level(`hold(x)`)` is always `x`, because a full evaluation of `hold(x)` leads to `x`. The same does not hold for `eval(hold(x))`, because `eval` first evaluates its argument and then evaluates the result again.
- The evaluation of elements of a user-defined `domain` depends on the implementation of the domain. Usually domain elements remain unevaluated by `level`. If the domain has a slot `"evaluate"`, the corresponding slot routine is called with the domain element as argument at each evaluation, and hence it is called once when `level` is invoked. Cf. example 8.

- `level` is a function of the system kernel.

#### Example 1

We demonstrate the effect of `level` for various values of the second parameter:

    >> delete a0, a1, a2, a3, a4, b: b := b + 1:
       a0 := a1: a1 := a2 + 2: a2 := a3 + a4: a3 := a4^2: a4 := 5:

    >> hold(a0), hold(a0 + a2), hold(b);
       level(a0, 0), level(a0 + a2, 0), level(b, 0);
       level(a0, 1), level(a0 + a2, 1), level(b, 1);
       level(a0, 2), level(a0 + a2, 2), level(b, 2);
       level(a0, 3), level(a0 + a2, 3), level(b, 3);
       level(a0, 4), level(a0 + a2, 4), level(b, 4);
       level(a0, 5), level(a0 + a2, 5), level(b, 5);
       level(a0, 6), level(a0 + a2, 6), level(b, 6);

                                  a0, a0 + a2, b

                                  a0, a0 + a2, b

                              a1, a1 + a3 + a4, b + 1

                                           2
                            a2 + 2, a2 + a4  + 7, b + 2

                         a3 + a4 + 2, a3 + a4 + 32, b + 3

                               2        2
                             a4  + 7, a4  + 37, b + 4

                                   32, 62, b + 5

                                   32, 62, b + 6

Evaluating `object` by just typing `object` at the command prompt is equivalent to `level(`object, LEVEL`)`:

    >> LEVEL := 2: MAXLEVEL := 4: a0, a2, b;
       level(a0, LEVEL), level(a2, LEVEL), level(b, LEVEL)

                                        2
                              a2 + 2, a4  + 5, b + 2

                                        2
                              a2 + 2, a4  + 5, b + 2

If the second argument is omitted, then this corresponds to a complete evaluation up to substitution depth `MAXLEVEL - 1`:

    >> level(a0)

          Error: Recursive definition [See ?MAXLEVEL]

    >> level(a2)

                                        30

    >> level(b)

          Error: Recursive definition [See ?MAXLEVEL]

    >> delete LEVEL, MAXLEVEL:

#### Example 2

We demonstrate the behavior of `level` in procedures:

    >> delete a, b, c: a := b: b := c: c := 42:
       p := proc()
         local x;
       begin
         x := a:
         print(level(x, 0), x, level(x, 2), level(x)):
         print(level(a, 0), a, level(a, 2), level(a)):
       end_proc:
       p()

                                    b, b, b, b

                                    a, b, c, 42

Since `a` is evaluated with the default substitution depth `1`, the assignment `x:=a` sets the value of the local variable `x` to the unevaluated identifier `b`. You can see that any evaluation of `x`, whether `level` is used or not, simply replaces `x` by its value `b`, but no further recursive evaluation happens. In contrast, evaluation of the identifier `a` takes place with the default substitution depth `1`, and `level(`a, 2`)` evaluates it with substitution depth `2`.

Thus `level` without a second argument can be used to request the complete evaluation of an object not containing any local variables or formal parameters.

#### Example 3

There are some rare cases where `level(`object, 0`)` and `hold(object)` behaves different. This is the case if `object` is not an identifier, e.g., a nameless function, because `level` influences only the evaluation of identifiers:

    >> level((x -> x^2)(2),0), hold((x -> x^2)(2))

                                 4, (x -> x^2)(2)

For the same reason `level(`object, 0`)` and `hold(object)` behave differently if `object` is a local variable of a procedure:

    >> f:=proc() local x; begin
         x := 42;
         hold(x), level(x, 0);
       end_proc:
       f();
       delete f:

                                 DOM_VAR(0,2), 42

#### Example 4

In contrast to lists and sets, evaluation of an array does not evaluate its entries. Thus `level` has no effect for arrays either. The same holds for tables and matrices. Use `map` to evaluate all entries of an array. On the `eval` help page further examples can be found:

    >> delete a, b:
       L := [a, b]:  A := array(1..2, L):  a := 1:   b := 2:
       L, A, level(A), map(A, level), map(A, eval)

                          +-    -+  +-    -+  +-    -+  +-    -+
                  [1, 2], | a, b |, | a, b |, | a, b |, | 1, 2 |
                          +-    -+  +-    -+  +-    -+  +-    -+

#### Example 5

The first argument of `level` may be an expression sequence, which is not flattened. However, it must be enclosed in parentheses:

    >> delete a, b: a := b: b := 3:
       level((a, b), 1);
       level(a, b, 1)

                                       b, 3
          Error: Wrong number of arguments [level]

#### Example 6

`Polynomials` are inert when evaluated, and so `level` has no effect:

    >> delete a, x: p := poly(a*x, [x]): a := 2: x := 3:
       p, level(p)

                          poly(a x, [x]), poly(a x, [x])

Use `mapcoeffs` and the function `eval` to evaluate all coefficients:

    >> mapcoeffs(p, eval)

                                  poly(2 x, [x])

If you want to substitute a value for the indeterminate `x`, use `evalp`:

    >> delete x: evalp(p, x = 3)

                                        3 a

As you can see, the result of an `evalp` call may contain unevaluated identifiers, and you can evaluate them by an application of `eval`. It is necessary to use `eval` instead of `level` because `level` does not evaluate its result:

    >> eval(evalp(p, x = 3))

                                         6

#### Example 7

The subtle difference between `level` and `eval` is shown. The evaluation depth of `eval` is limited by the environment variable `LEVEL`. `level` pays no attention to `LEVEL`, but rather continues evaluating its argument either as many times as the second argument implies or until it has been evaluated completely:

    >> delete a0, a1, a2, a3:
       a0 := a1 + a2: a1 := a2 + a3: a2 := a3^2 - 1: a3 := 5:
       LEVEL := 1:
       eval(a0), level(a0);

                                           2
                               a2 + a3 + a3  - 1, 53

If the evaluation depth exceeds the value of `MAXLEVEL`, an error is raised in both cases:

    >> delete LEVEL:
       MAXLEVEL := 3:
       level(a0);

          Error: Recursive definition [See ?MAXLEVEL]

    >> delete LEVEL:
       MAXLEVEL := 3:
       eval(a0);
       delete MAXLEVEL:

          Error: Recursive definition [See ?MAXLEVEL]

It is not the same evaluating an expression `ex` with `eval` and an evaluation depth n and by `level(`(ex, n)`)`, because `eval` evaluates its result:

    >> LEVEL := 2: eval(a0), level(a0, 2);
       delete LEVEL:

                                               2
                               53, a2 + a3 + a3  - 1

`level` does not affect the evaluation of local variables of type `DOM_VAR` while `eval` evaluates them with evaluation depth `LEVEL`, which is one in a procedure:

    >> p := proc()
         local x;
       begin
         x := a0:
         print(eval(x), level(x)):
       end_proc:
       p()

                                        2
                            a2 + a3 + a3  - 1, a1 + a2

#### Example 8

The evaluation of an element of a user-defined domain depends on the implementation of the domain. Usually it is not further evaluated:

    >> delete a: T := newDomain("T"):
       e := new(T, a): a := 1:
       e, level(e), map(e, level), val(e)

                    new(T, a), new(T, a), new(T, a), new(T, a)

If the slot `"evaluate"` exists, the corresponding slot routine is called for a domain element each time it is evaluated. We implement the routine `T::evaluate`, which simply evaluates all internal operands of its argument, for our domain `T`. The unevaluated domain element can still be accessed via `val`:

    >> T::evaluate := x -> new(T, eval(extop(x))):
       e, level(e), map(e, level), val(e);

                    new(T, 1), new(T, 1), new(T, 1), new(T, a)

    >> delete e, T:

#### Changes

- `level` no longer affects the evaluation of a local variable or a formal parameter, for which a new data type `DOM_VAR` was introduced. See sections ``The LEVEL-Problem'' and ``Symbols and Variables'' of the document ``From MuPAD 1.4 to MuPAD 2.0'' for details.

Do you have questions or comments?
Copyright © SciFace Software GmbH & Co. KG 2000