Introdução à Programação em Python

Aula prática 1 (Laboratório) - Utilização do IPython

Carlos Caleiro, Jaime Ramos

Dep. Matemática, IST - 2016

(versão de 18 de Setembro de 2020)

Introdução

O IPython (agora integrado no projecto Jupyter) é um ambiente interactivo para a utilização livre da linguagem Python como ferramenta de cálculo e visualização, bem como para o desenvolvimento de (pequenos) programas na linguagem Python e sua prototipagem rápida. No modo Notebook a interacção dá-se através de um interface muito simples suportado por um browser, que por sua vez comunica com um kernel que disponibiliza um interpretador de Python. É neste modo que iremos trabalhar.

Cada "Notebook" está organizado em células. Cada célula tem associado um tipo (texto, input, ouput, gráficos, etc.). As células de input podem avaliar-se premindo simultaneamente as teclas "shift" e "return", o que inicia a necessária comunicação com o "Kernel".

Na disciplina usa-se a plataforma Anaconda com Python 3.6. Pode ser facilmente instalada a partir de https://www.anaconda.com/download/ em qualquer sistema operativo.

Recomenda-se a leitura do primeiro notebook das aulas teóricas: IPython como ambiente de computação, cálculo e visualização disponível em https://www.math.tecnico.ulisboa.pt/~ccal/python/ como complemento à informação desta aula.

Cálculo numérico

O Python disponibiliza à partida, para além de algumas funcionalidades básicas, várias operações numéricas que podemos utilizar: + (adição), - (subtracção), * (multiplicação), / (divisão), (exponenciação), // (divisão inteira), % (resto da divisão inteira). Agrupamos expressões usando parênteses, como é usual. As expressões são avaliadas premindo SHIFT+RETURN**.

Por exemplo:

In [1]:
1+1-3
Out[1]:
-1
In [2]:
7/3
Out[2]:
2.3333333333333335
In [3]:
7//3
Out[3]:
2
In [4]:
10**3
Out[4]:
1000

Avalie agora as expressões:

  • $2^{1000}$
  • $45+2\times3$
  • $(45+2)\times3$
  • $72\div3+3$
  • resto da divisão inteira de 25 por 3
  • resto da divisão inteira de 25 por 5

Para podermos usar este ambiente como ambiente de cálculo e visualização, rico e apelativo, necessitamos de usar extensões à linguagem básica. A linguagem Python, como muitas outras linguagens de programação modernas, dispõe de mecanismos de modularização robustos. Um dos mais importantes é a existência de bibliotecas com extensões à linguagem básica, que servem os mais diversos fins. Estas extensões estão organizadas em pacotes de módulos, de que falaremos mais adiante.

Uma destas extensões é a extensão Pylab que fornece um contexto essencialmente equivalente ao ambiente comercial MATLAB, profusamente utilizado em aplicações em diversas áreas da engenharia.

Por exemplo, se tentarmos avaliar a expressão sin(pi/2) obtemos uma mensagem de erro.

In [5]:
sin(pi/2)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-5-7ec80c2057e2> in <module>
----> 1 sin(pi/2)

NameError: name 'sin' is not defined

Para se poder avaliar a expressão anterior é necessário usar, por exemplo, a extensão pylab que pode ser importada através do comando seguinte.

In [6]:
%pylab inline
Populating the interactive namespace from numpy and matplotlib

Agora já podemos avaliar a expressão sin(pi/2).

In [7]:
sin(pi/2)
Out[7]:
1.0

Podemos obter informação sobre a função sin através da função help.

In [9]:
help(sin)
Help on ufunc object:

sin = class ufunc(builtins.object)
 |  Functions that operate element by element on whole arrays.
 |  
 |  To see the documentation for a specific ufunc, use np.info().  For
 |  example, np.info(np.sin).  Because ufuncs are written in C
 |  (for speed) and linked into Python with NumPy's ufunc facility,
 |  Python's help() function finds this page whenever help() is called
 |  on a ufunc.
 |  
 |  A detailed explanation of ufuncs can be found in the "ufuncs.rst"
 |  file in the NumPy reference guide.
 |  
 |  Unary ufuncs:
 |  =============
 |  
 |  op(X, out=None)
 |  Apply op to X elementwise
 |  
 |  Parameters
 |  ----------
 |  X : array_like
 |      Input array.
 |  out : array_like
 |      An array to store the output. Must be the same shape as `X`.
 |  
 |  Returns
 |  -------
 |  r : array_like
 |      `r` will have the same shape as `X`; if out is provided, `r`
 |      will be equal to out.
 |  
 |  Binary ufuncs:
 |  ==============
 |  
 |  op(X, Y, out=None)
 |  Apply `op` to `X` and `Y` elementwise. May "broadcast" to make
 |  the shapes of `X` and `Y` congruent.
 |  
 |  The broadcasting rules are:
 |  
 |  * Dimensions of length 1 may be prepended to either array.
 |  * Arrays may be repeated along dimensions of length 1.
 |  
 |  Parameters
 |  ----------
 |  X : array_like
 |      First input array.
 |  Y : array_like
 |      Second input array.
 |  out : array_like
 |      An array to store the output. Must be the same shape as the
 |      output would have.
 |  
 |  Returns
 |  -------
 |  r : array_like
 |      The return value; if out is provided, `r` will be equal to out.
 |  
 |  Methods defined here:
 |  
 |  __call__(self, /, *args, **kwargs)
 |      Call self as a function.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __str__(self, /)
 |      Return str(self).
 |  
 |  accumulate(...)
 |      accumulate(array, axis=0, dtype=None, out=None)
 |      
 |      Accumulate the result of applying the operator to all elements.
 |      
 |      For a one-dimensional array, accumulate produces results equivalent to::
 |      
 |        r = np.empty(len(A))
 |        t = op.identity        # op = the ufunc being applied to A's  elements
 |        for i in range(len(A)):
 |            t = op(t, A[i])
 |            r[i] = t
 |        return r
 |      
 |      For example, add.accumulate() is equivalent to np.cumsum().
 |      
 |      For a multi-dimensional array, accumulate is applied along only one
 |      axis (axis zero by default; see Examples below) so repeated use is
 |      necessary if one wants to accumulate over multiple axes.
 |      
 |      Parameters
 |      ----------
 |      array : array_like
 |          The array to act on.
 |      axis : int, optional
 |          The axis along which to apply the accumulation; default is zero.
 |      dtype : data-type code, optional
 |          The data-type used to represent the intermediate results. Defaults
 |          to the data-type of the output array if such is provided, or the
 |          the data-type of the input array if no output array is provided.
 |      out : ndarray, optional
 |          A location into which the result is stored. If not provided a
 |          freshly-allocated array is returned.
 |      
 |      Returns
 |      -------
 |      r : ndarray
 |          The accumulated values. If `out` was supplied, `r` is a reference to
 |          `out`.
 |      
 |      Examples
 |      --------
 |      1-D array examples:
 |      
 |      >>> np.add.accumulate([2, 3, 5])
 |      array([ 2,  5, 10])
 |      >>> np.multiply.accumulate([2, 3, 5])
 |      array([ 2,  6, 30])
 |      
 |      2-D array examples:
 |      
 |      >>> I = np.eye(2)
 |      >>> I
 |      array([[ 1.,  0.],
 |             [ 0.,  1.]])
 |      
 |      Accumulate along axis 0 (rows), down columns:
 |      
 |      >>> np.add.accumulate(I, 0)
 |      array([[ 1.,  0.],
 |             [ 1.,  1.]])
 |      >>> np.add.accumulate(I) # no axis specified = axis zero
 |      array([[ 1.,  0.],
 |             [ 1.,  1.]])
 |      
 |      Accumulate along axis 1 (columns), through rows:
 |      
 |      >>> np.add.accumulate(I, 1)
 |      array([[ 1.,  1.],
 |             [ 0.,  1.]])
 |  
 |  at(...)
 |      at(a, indices, b=None)
 |      
 |      Performs unbuffered in place operation on operand 'a' for elements
 |      specified by 'indices'. For addition ufunc, this method is equivalent to
 |      `a[indices] += b`, except that results are accumulated for elements that
 |      are indexed more than once. For example, `a[[0,0]] += 1` will only
 |      increment the first element once because of buffering, whereas
 |      `add.at(a, [0,0], 1)` will increment the first element twice.
 |      
 |      .. versionadded:: 1.8.0
 |      
 |      Parameters
 |      ----------
 |      a : array_like
 |          The array to perform in place operation on.
 |      indices : array_like or tuple
 |          Array like index object or slice object for indexing into first
 |          operand. If first operand has multiple dimensions, indices can be a
 |          tuple of array like index objects or slice objects.
 |      b : array_like
 |          Second operand for ufuncs requiring two operands. Operand must be
 |          broadcastable over first operand after indexing or slicing.
 |      
 |      Examples
 |      --------
 |      Set items 0 and 1 to their negative values:
 |      
 |      >>> a = np.array([1, 2, 3, 4])
 |      >>> np.negative.at(a, [0, 1])
 |      >>> print(a)
 |      array([-1, -2, 3, 4])
 |      
 |      ::
 |      
 |      Increment items 0 and 1, and increment item 2 twice:
 |      
 |      >>> a = np.array([1, 2, 3, 4])
 |      >>> np.add.at(a, [0, 1, 2, 2], 1)
 |      >>> print(a)
 |      array([2, 3, 5, 4])
 |      
 |      ::
 |      
 |      Add items 0 and 1 in first array to second array,
 |      and store results in first array:
 |      
 |      >>> a = np.array([1, 2, 3, 4])
 |      >>> b = np.array([1, 2])
 |      >>> np.add.at(a, [0, 1], b)
 |      >>> print(a)
 |      array([2, 4, 3, 4])
 |  
 |  outer(...)
 |      outer(A, B)
 |      
 |      Apply the ufunc `op` to all pairs (a, b) with a in `A` and b in `B`.
 |      
 |      Let ``M = A.ndim``, ``N = B.ndim``. Then the result, `C`, of
 |      ``op.outer(A, B)`` is an array of dimension M + N such that:
 |      
 |      .. math:: C[i_0, ..., i_{M-1}, j_0, ..., j_{N-1}] =
 |         op(A[i_0, ..., i_{M-1}], B[j_0, ..., j_{N-1}])
 |      
 |      For `A` and `B` one-dimensional, this is equivalent to::
 |      
 |        r = empty(len(A),len(B))
 |        for i in range(len(A)):
 |            for j in range(len(B)):
 |                r[i,j] = op(A[i], B[j]) # op = ufunc in question
 |      
 |      Parameters
 |      ----------
 |      A : array_like
 |          First array
 |      B : array_like
 |          Second array
 |      
 |      Returns
 |      -------
 |      r : ndarray
 |          Output array
 |      
 |      See Also
 |      --------
 |      numpy.outer
 |      
 |      Examples
 |      --------
 |      >>> np.multiply.outer([1, 2, 3], [4, 5, 6])
 |      array([[ 4,  5,  6],
 |             [ 8, 10, 12],
 |             [12, 15, 18]])
 |      
 |      A multi-dimensional example:
 |      
 |      >>> A = np.array([[1, 2, 3], [4, 5, 6]])
 |      >>> A.shape
 |      (2, 3)
 |      >>> B = np.array([[1, 2, 3, 4]])
 |      >>> B.shape
 |      (1, 4)
 |      >>> C = np.multiply.outer(A, B)
 |      >>> C.shape; C
 |      (2, 3, 1, 4)
 |      array([[[[ 1,  2,  3,  4]],
 |              [[ 2,  4,  6,  8]],
 |              [[ 3,  6,  9, 12]]],
 |             [[[ 4,  8, 12, 16]],
 |              [[ 5, 10, 15, 20]],
 |              [[ 6, 12, 18, 24]]]])
 |  
 |  reduce(...)
 |      reduce(a, axis=0, dtype=None, out=None, keepdims=False)
 |      
 |      Reduces `a`'s dimension by one, by applying ufunc along one axis.
 |      
 |      Let :math:`a.shape = (N_0, ..., N_i, ..., N_{M-1})`.  Then
 |      :math:`ufunc.reduce(a, axis=i)[k_0, ..,k_{i-1}, k_{i+1}, .., k_{M-1}]` =
 |      the result of iterating `j` over :math:`range(N_i)`, cumulatively applying
 |      ufunc to each :math:`a[k_0, ..,k_{i-1}, j, k_{i+1}, .., k_{M-1}]`.
 |      For a one-dimensional array, reduce produces results equivalent to:
 |      ::
 |      
 |       r = op.identity # op = ufunc
 |       for i in range(len(A)):
 |         r = op(r, A[i])
 |       return r
 |      
 |      For example, add.reduce() is equivalent to sum().
 |      
 |      Parameters
 |      ----------
 |      a : array_like
 |          The array to act on.
 |      axis : None or int or tuple of ints, optional
 |          Axis or axes along which a reduction is performed.
 |          The default (`axis` = 0) is perform a reduction over the first
 |          dimension of the input array. `axis` may be negative, in
 |          which case it counts from the last to the first axis.
 |      
 |          .. versionadded:: 1.7.0
 |      
 |          If this is `None`, a reduction is performed over all the axes.
 |          If this is a tuple of ints, a reduction is performed on multiple
 |          axes, instead of a single axis or all the axes as before.
 |      
 |          For operations which are either not commutative or not associative,
 |          doing a reduction over multiple axes is not well-defined. The
 |          ufuncs do not currently raise an exception in this case, but will
 |          likely do so in the future.
 |      dtype : data-type code, optional
 |          The type used to represent the intermediate results. Defaults
 |          to the data-type of the output array if this is provided, or
 |          the data-type of the input array if no output array is provided.
 |      out : ndarray, optional
 |          A location into which the result is stored. If not provided, a
 |          freshly-allocated array is returned.
 |      keepdims : bool, optional
 |          If this is set to True, the axes which are reduced are left
 |          in the result as dimensions with size one. With this option,
 |          the result will broadcast correctly against the original `arr`.
 |      
 |          .. versionadded:: 1.7.0
 |      
 |      Returns
 |      -------
 |      r : ndarray
 |          The reduced array. If `out` was supplied, `r` is a reference to it.
 |      
 |      Examples
 |      --------
 |      >>> np.multiply.reduce([2,3,5])
 |      30
 |      
 |      A multi-dimensional array example:
 |      
 |      >>> X = np.arange(8).reshape((2,2,2))
 |      >>> X
 |      array([[[0, 1],
 |              [2, 3]],
 |             [[4, 5],
 |              [6, 7]]])
 |      >>> np.add.reduce(X, 0)
 |      array([[ 4,  6],
 |             [ 8, 10]])
 |      >>> np.add.reduce(X) # confirm: default axis value is 0
 |      array([[ 4,  6],
 |             [ 8, 10]])
 |      >>> np.add.reduce(X, 1)
 |      array([[ 2,  4],
 |             [10, 12]])
 |      >>> np.add.reduce(X, 2)
 |      array([[ 1,  5],
 |             [ 9, 13]])
 |  
 |  reduceat(...)
 |      reduceat(a, indices, axis=0, dtype=None, out=None)
 |      
 |      Performs a (local) reduce with specified slices over a single axis.
 |      
 |      For i in ``range(len(indices))``, `reduceat` computes
 |      ``ufunc.reduce(a[indices[i]:indices[i+1]])``, which becomes the i-th
 |      generalized "row" parallel to `axis` in the final result (i.e., in a
 |      2-D array, for example, if `axis = 0`, it becomes the i-th row, but if
 |      `axis = 1`, it becomes the i-th column).  There are three exceptions to this:
 |      
 |      * when ``i = len(indices) - 1`` (so for the last index),
 |        ``indices[i+1] = a.shape[axis]``.
 |      * if ``indices[i] >= indices[i + 1]``, the i-th generalized "row" is
 |        simply ``a[indices[i]]``.
 |      * if ``indices[i] >= len(a)`` or ``indices[i] < 0``, an error is raised.
 |      
 |      The shape of the output depends on the size of `indices`, and may be
 |      larger than `a` (this happens if ``len(indices) > a.shape[axis]``).
 |      
 |      Parameters
 |      ----------
 |      a : array_like
 |          The array to act on.
 |      indices : array_like
 |          Paired indices, comma separated (not colon), specifying slices to
 |          reduce.
 |      axis : int, optional
 |          The axis along which to apply the reduceat.
 |      dtype : data-type code, optional
 |          The type used to represent the intermediate results. Defaults
 |          to the data type of the output array if this is provided, or
 |          the data type of the input array if no output array is provided.
 |      out : ndarray, optional
 |          A location into which the result is stored. If not provided a
 |          freshly-allocated array is returned.
 |      
 |      Returns
 |      -------
 |      r : ndarray
 |          The reduced values. If `out` was supplied, `r` is a reference to
 |          `out`.
 |      
 |      Notes
 |      -----
 |      A descriptive example:
 |      
 |      If `a` is 1-D, the function `ufunc.accumulate(a)` is the same as
 |      ``ufunc.reduceat(a, indices)[::2]`` where `indices` is
 |      ``range(len(array) - 1)`` with a zero placed
 |      in every other element:
 |      ``indices = zeros(2 * len(a) - 1)``, ``indices[1::2] = range(1, len(a))``.
 |      
 |      Don't be fooled by this attribute's name: `reduceat(a)` is not
 |      necessarily smaller than `a`.
 |      
 |      Examples
 |      --------
 |      To take the running sum of four successive values:
 |      
 |      >>> np.add.reduceat(np.arange(8),[0,4, 1,5, 2,6, 3,7])[::2]
 |      array([ 6, 10, 14, 18])
 |      
 |      A 2-D example:
 |      
 |      >>> x = np.linspace(0, 15, 16).reshape(4,4)
 |      >>> x
 |      array([[  0.,   1.,   2.,   3.],
 |             [  4.,   5.,   6.,   7.],
 |             [  8.,   9.,  10.,  11.],
 |             [ 12.,  13.,  14.,  15.]])
 |      
 |      ::
 |      
 |       # reduce such that the result has the following five rows:
 |       # [row1 + row2 + row3]
 |       # [row4]
 |       # [row2]
 |       # [row3]
 |       # [row1 + row2 + row3 + row4]
 |      
 |      >>> np.add.reduceat(x, [0, 3, 1, 2, 0])
 |      array([[ 12.,  15.,  18.,  21.],
 |             [ 12.,  13.,  14.,  15.],
 |             [  4.,   5.,   6.,   7.],
 |             [  8.,   9.,  10.,  11.],
 |             [ 24.,  28.,  32.,  36.]])
 |      
 |      ::
 |      
 |       # reduce such that result has the following two columns:
 |       # [col1 * col2 * col3, col4]
 |      
 |      >>> np.multiply.reduceat(x, [0, 3], 1)
 |      array([[    0.,     3.],
 |             [  120.,     7.],
 |             [  720.,    11.],
 |             [ 2184.,    15.]])
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  identity
 |      The identity value.
 |      
 |      Data attribute containing the identity element for the ufunc, if it has one.
 |      If it does not, the attribute value is None.
 |      
 |      Examples
 |      --------
 |      >>> np.add.identity
 |      0
 |      >>> np.multiply.identity
 |      1
 |      >>> np.power.identity
 |      1
 |      >>> print(np.exp.identity)
 |      None
 |  
 |  nargs
 |      The number of arguments.
 |      
 |      Data attribute containing the number of arguments the ufunc takes, including
 |      optional ones.
 |      
 |      Notes
 |      -----
 |      Typically this value will be one more than what you might expect because all
 |      ufuncs take  the optional "out" argument.
 |      
 |      Examples
 |      --------
 |      >>> np.add.nargs
 |      3
 |      >>> np.multiply.nargs
 |      3
 |      >>> np.power.nargs
 |      3
 |      >>> np.exp.nargs
 |      2
 |  
 |  nin
 |      The number of inputs.
 |      
 |      Data attribute containing the number of arguments the ufunc treats as input.
 |      
 |      Examples
 |      --------
 |      >>> np.add.nin
 |      2
 |      >>> np.multiply.nin
 |      2
 |      >>> np.power.nin
 |      2
 |      >>> np.exp.nin
 |      1
 |  
 |  nout
 |      The number of outputs.
 |      
 |      Data attribute containing the number of arguments the ufunc treats as output.
 |      
 |      Notes
 |      -----
 |      Since all ufuncs can take output arguments, this will always be (at least) 1.
 |      
 |      Examples
 |      --------
 |      >>> np.add.nout
 |      1
 |      >>> np.multiply.nout
 |      1
 |      >>> np.power.nout
 |      1
 |      >>> np.exp.nout
 |      1
 |  
 |  ntypes
 |      The number of types.
 |      
 |      The number of numerical NumPy types - of which there are 18 total - on which
 |      the ufunc can operate.
 |      
 |      See Also
 |      --------
 |      numpy.ufunc.types
 |      
 |      Examples
 |      --------
 |      >>> np.add.ntypes
 |      18
 |      >>> np.multiply.ntypes
 |      18
 |      >>> np.power.ntypes
 |      17
 |      >>> np.exp.ntypes
 |      7
 |      >>> np.remainder.ntypes
 |      14
 |  
 |  signature
 |  
 |  types
 |      Returns a list with types grouped input->output.
 |      
 |      Data attribute listing the data-type "Domain-Range" groupings the ufunc can
 |      deliver. The data-types are given using the character codes.
 |      
 |      See Also
 |      --------
 |      numpy.ufunc.ntypes
 |      
 |      Examples
 |      --------
 |      >>> np.add.types
 |      ['??->?', 'bb->b', 'BB->B', 'hh->h', 'HH->H', 'ii->i', 'II->I', 'll->l',
 |      'LL->L', 'qq->q', 'QQ->Q', 'ff->f', 'dd->d', 'gg->g', 'FF->F', 'DD->D',
 |      'GG->G', 'OO->O']
 |      
 |      >>> np.multiply.types
 |      ['??->?', 'bb->b', 'BB->B', 'hh->h', 'HH->H', 'ii->i', 'II->I', 'll->l',
 |      'LL->L', 'qq->q', 'QQ->Q', 'ff->f', 'dd->d', 'gg->g', 'FF->F', 'DD->D',
 |      'GG->G', 'OO->O']
 |      
 |      >>> np.power.types
 |      ['bb->b', 'BB->B', 'hh->h', 'HH->H', 'ii->i', 'II->I', 'll->l', 'LL->L',
 |      'qq->q', 'QQ->Q', 'ff->f', 'dd->d', 'gg->g', 'FF->F', 'DD->D', 'GG->G',
 |      'OO->O']
 |      
 |      >>> np.exp.types
 |      ['f->f', 'd->d', 'g->g', 'F->F', 'D->D', 'G->G', 'O->O']
 |      
 |      >>> np.remainder.types
 |      ['bb->b', 'BB->B', 'hh->h', 'HH->H', 'ii->i', 'II->I', 'll->l', 'LL->L',
 |      'qq->q', 'QQ->Q', 'ff->f', 'dd->d', 'gg->g', 'OO->O']

In [10]:
?sin

Avalie as expressões seguintes.

In [ ]:
sin(pi/2)-1
In [ ]:
sin(pi/4)-sqrt(2)/2

Números complexos

A unidade imaginária em Python é representada pelo símbolo j. Esta constante tem de surgir sempre prefixada por um número, por exemplo, 1j. Para além da constante j são disponibilizadas, pela extensão pylab, as funções usuais para manipular números complexos: conjugate, abs, entre outras. Seguem-se alguns exemplos.

In [12]:
(1+2j)+(3-5j)
Out[12]:
(4-3j)
In [13]:
conjugate(1+2j)
Out[13]:
(1-2j)
In [14]:
(1+2j)**2
Out[14]:
(-3+4j)
In [16]:
abs(1+2j)
Out[16]:
2.23606797749979

Atente-se no caso $i^2$.

In [15]:
1j**2
Out[15]:
(-1+0j)

Avalie as expressões seguintes:

  • $(2+3i)+2i$
  • $(5+2i)i$
  • $(5+2i)(2+3i)$

Cálculo simbólico

Cálculo simbólico compreende a manipulação algébrica de expressões que podem envolver símbolos. Para poder fazer manipulação simbólica de expressões, comece por importar a extensão sympy.

In [18]:
from sympy import *

Todos os símbolos que sejam utilizados numa expressão têm de ser previamente definidos. A expressão seguinte permite definir o símbolo a.

In [19]:
a=symbols("a")
In [22]:
a+2*a
Out[22]:
$\displaystyle 3 a$

Defina os símbolos b, c, d, x e y e avalie a expressão $(a/b-c/d)^2$.

A expressão anterior pode ser expandida.

In [25]:
expand((a/b-c/d)**2)
Out[25]:
$\displaystyle \frac{a^{2}}{b^{2}} - \frac{2 a c}{b d} + \frac{c^{2}}{d^{2}}$

Avaliemos agora a expressão sin(x)/cos(x).

In [26]:
sin(x)/cos(x)
Out[26]:
$\displaystyle \frac{\sin{\left(x \right)}}{\cos{\left(x \right)}}$

Podemos simplificar esta expressão através do comando simplify.

In [27]:
simplify(sin(x)/cos(x))
Out[27]:
$\displaystyle \tan{\left(x \right)}$

Avalie a expressão $(a+b)^3$. De seguida aplique-lhe os comandos expand e simplify.

Avalie a expressão $a^3+3a^2b+3ab^2+b^3$. De seguida aplique-lhe os comandos expand e simplify. Observe que o comando symplify não produziu o resultado esperado. Aplique agora o comando factor.

Derivação, primitivação e limites

Para calcular a derivada de uma expressão usa-se o comando diff. Por exemplo, para calcular a derivada (em ordem a $x$) da expressão $\log(x)+x$ avalia-se a expressão seguinte.

In [38]:
diff(log(x)+x,x)
Out[38]:
$\displaystyle 1 + \frac{1}{x}$

Para manipular funções simbolicamente há que definir os símbolos correpondentes.

In [70]:
f=Function("f")
In [44]:
g=Function("g")

Calcule a derivada (em ordem a $x$) das expressões $f(x)+g(x)$, $f(x)g(x)$ e $g(f(x))$.

Para calcular a primitiva de uma expressão usa-se o comando integrate. Por exemplo, a primitiva das expressões $x$, $x^2$.

In [49]:
integrate(x,x)
Out[49]:
$\displaystyle \frac{x^{2}}{2}$
In [50]:
integrate(x**2,x)
Out[50]:
$\displaystyle \frac{x^{3}}{3}$

Calcule as primitivas de $\cos(x)$ e $\arctan(x)$.

Para calcular o limite de uma expressão usa-se o comando limit. Por exemplo, o limite de $\frac{1}{x}$ quando $x$ tende para infinito calcula-se como se segue, onde infinito se representa por oo (duas vezes a letra o).

In [55]:
limit(1/x,x,oo)
Out[55]:
$\displaystyle 0$

Podemos também calcular os limites laterais. O limite à esquerda calcula-se acrescentando a opção - aos argumentos do comando e o limite à direita calcula-se acrescentando a opção +.

In [56]:
limit(1/x,x,0,"-")
Out[56]:
$\displaystyle -\infty$
In [57]:
limit(1/x,x,0,"+")
Out[57]:
$\displaystyle \infty$

Calcule os seguintes limites:

  • $\lim_{x\to0} \frac{\sin(x)}{x}$
  • $\lim_{x\to0} \frac{\cos(x)-1}{x}$
  • $\lim_{x\to\infty} \arctan(x)$

Resolução de equações

O comando solve(expr,x) resolve a equação expr=0 em ordem a x. Por exemplo, para resolver a equação $3x+2=0$ (em ordem a $x$) avalia-se a expressão seguinte.

In [60]:
solve(3*x+2,x)
Out[60]:
[-2/3]

Este comando também permite resolver sistemas de equações.

In [62]:
solve([x+y+1,x-y+2],[x,y])
Out[62]:
{x: -3/2, y: 1/2}

Resolva as equações:

  • $x^3+x^2+x+1=0$
  • $x^3+2x^2+4x=-3$

A solução geral de uma equação de primeiro grau $ax+b=0$ é dada por:

Obtenha a fórmula resolvente para equações do tipo

  • $ax^2+bx+c=0$
  • $ax^3+bx^2+cx+d=0$
  • $ax^4+bx^3+cx^2+dx+e=0$

Embora não existam fórmulas resolventes para equações de grau superior a 4, é possível encontrar soluções para equações particulares. Tente obter a fórmula resolvente para equações do tipo $ax^5+bx^4+cx^3+dx^2+ex+k=0$. Resolva a equação $x^5+2x^2+4=0$.

Gráficos

Existem inúmeros comandos para desenhar gráficos. Ilustramos os comandos plot e plot3d, disponibilizados pela extensão sympy, que permitem desenhar gráficos de funções de uma ou duas variáveis num subconjunto do seu domínio.

O gráfico da função com expressão analítica $3x-1$ no intervalo $[-2,2]$ obtém-se como ilustra a seguir.

In [75]:
plot(3*x-1,(x,-2,2))
Out[75]:
<sympy.plotting.plot.Plot at 0x11c107b00>

Desenhe o gráfico da função com expressão analítica $5x^2-x+2$ no intervalo [−10,20].

O comando plot3d precisa de ser importado explicitamente:

In [76]:
from sympy.plotting import plot3d

O gráfico da função com expressão analítica $\cos(x^2+y^2)$ nos intervalos $[-1,1]\times [-1,1]$ e $[-2,2]\times[-2,2]$.

In [77]:
plot3d(cos(x**2+y**2),(x,-1,1),(y,-1,1))
Out[77]:
<sympy.plotting.plot.Plot at 0x128c62240>
In [78]:
plot3d(cos(x**2+y**2),(x,-2,2),(y,-2,2))
Out[78]:
<sympy.plotting.plot.Plot at 0x11c0ee908>

Desenhe o gráfico da função $f(x,y)=\frac{\sin(\sqrt{x^2+y^2})}{\sqrt{x^2+y^2}}$ no intervalo $[-10,10]\times [-10,10]$.

Exercício complementar: estudo de uma função

Considere a função real de variável real definida por $f(x)=\cos(x)\sin(3x/2)/x$.

Desenhe o gráfico de $f$ no intervalo $[-8\pi,8\pi]$.

Calcule os limites de $f$ quando x tende para $+\infty$ e $-\infty$.

Verifique que os limites de $f$ à esquerda e à direita do ponto 0 são ambos 3/2.

Calcule o valor da expressão da função derivada de $f$. Desenhe o gráfico da expressão no mesmo intervalo.