text: # Ordinary Differential Equations Exercise 1
  
  ## Imports
  
  
  
  In [1]:
  
  %matplotlib inline
  import matplotlib.pyplot as plt
  import numpy as np
  from scipy.integrate import odeint
  from IPython.html.widgets import interact, fixed
  
  
  
  
  :0: FutureWarning: IPython widgets are experimental and may change in the future.
  
  
  
  ## Euler's method
  
  Euler's method is the simplest numerical approach for solving a first order ODE numerically. Given the differential equation
  
  $$\frac{dy}{dx} = f(y(x), x)$$
  
  with the initial condition:
  
  $$y(x_0)=y_0$$
  
  Euler's method performs updates using the equations:
  
  $$y_{n+1} = y_n + h f(y_n,x_n)$$$$h = x_{n+1} - x_n$$
  
  Write a function solve_euler that implements the Euler method for a 1d ODE and follows the specification described in the docstring:
  
  
  
  In [2]:
  
  def solve_euler(derivs, y0, x):
  """Solve a 1d ODE using Euler's method.
  
  Parameters
  ----------
  derivs : function
  The derivative of the diff-eq with the signature deriv(y,x) where
  y and x are floats.
  y0 : float
  The initial condition y[0] = y(x[0]).
  x : np.ndarray, list, tuple
  The array of times at which of solve the diff-eq.
  
  Returns
  -------
  y : np.ndarray
  Array of solutions y[i] = y(x[i])
  """
  h=x[-1]-x[-2]
  data = [y0]
  for t in x[1:]:
  data.append(data[-1]+h*derivs(data[-1],t))
  return data
  
  
  
  
  In [3]:
  
  assert np.allclose(solve_euler(lambda y, x: 1, 0, [0,1,2]), [0,1,2])
  
  
  
  The midpoint method is another numerical method for solving the above differential equation. In general it is more accurate than the Euler method. It uses the update equation:
  
  $$y_{n+1} = y_n + h f\left(y_n+\frac{h}{2}f(y_n,x_n),x_n+\frac{h}{2}\right)$$
  
  Write a function solve_midpoint that implements the midpoint method for a 1d ODE and follows the specification described in the docstring:
  
  
  
  In [4]:
  
  def solve_midpoint(derivs, y0, x):
  """Solve a 1d ODE using the Midpoint method.
  
  Parameters
  ----------
  derivs : function
  The derivative of the diff-eq with the signature deriv(y,x) where y
  and x are floats.
  y0 : float
  The initial condition y[0] = y(x[0]).
  x : np.ndarray, list, tuple
  The array of times at which of solve the diff-eq.
  
  Returns
  -------
  y : np.ndarray
  Array of solutions y[i] = y(x[i])
  """
  h=x[-1]-x[-2]
  data = [y0]
  for t in x[1:]:
  data.append(data[-1]+h*derivs(data[-1]+h/2*derivs(data[-1],t),t+h/2))
  return data
  
  
  
  
  In [5]:
  
  assert np.allclose(solve_euler(lambda y, x: 1, 0, [0,1,2]), [0,1,2])
  
  
  
  You are now going to solve the following differential equation:
  
  $$\frac{dy}{dx} = x + 2y$$
  
  which has the analytical solution:
  
  $$y(x) = 0.25 e^{2x} - 0.5 x - 0.25$$
  
  First, write a solve_exact function that compute the exact solution and follows the specification described in the docstring:
  
  
  
  In [6]:
  
  def solve_exact(x):
  """compute the exact solution to dy/dx = x + 2y.
  
  Parameters
  ----------
  x : np.ndarray
  Array of x values to compute the solution at.
  
  Returns
  -------
  y : np.ndarray
  Array of solutions at y[i] = y(x[i]).
  """
  data = np.array(.25*np.exp(2*x)-.5*x-.25)
  return data
  
  
  
  
  In [7]:
  
  assert np.allclose(solve_exact(np.array([0,1,2])),np.array([0., 1.09726402, 12.39953751]))
  
  
  
  In the following cell you are going to solve the above ODE using four different algorithms:
  
  1. Euler's method
  2. Midpoint method
  3. odeint
  4. Exact
  
  Here are the details:
  
  • Generate an array of x values with $N=11$ points over the interval $[0,1]$ ($h=0.1$).
  • Define the derivs function for the above differential equation.
  • Using the solve_euler, solve_midpoint, odeint and solve_exact functions to compute the solutions using the 4 approaches.
  
  Visualize the solutions on a sigle figure with two subplots:
  
  1. Plot the $y(x)$ versus $x$ for each of the 4 approaches.
  2. Plot $\left|y(x)-y_{exact}(x)\right|$ versus $x$ for each of the 3 numerical approaches.
  
  Your visualization should have legends, labeled axes, titles and be customized for beauty and effectiveness.
  
  While your final plot will use $N=10$ points, first try making $N$ larger and smaller to see how that affects the errors of the different approaches.
  
  
  
  In [46]:
  
  x = np.linspace(0,1,11)
  def derivs(y, x):
  dy = x+2*y
  return dy
  euler_error = np.array(solve_euler(derivs, 0, x))-solve_exact(x)
  midpoint_error = np.array(solve_midpoint(derivs, 0, x))-solve_exact(x)
  odeint_error = np.array(odeint(derivs, 0, x)).flatten()-solve_exact(x)
  
  f = plt.figure(figsize = (9,6))
  ax = plt.subplot(211)
  ax.spines['top'].set_visible(False)
  ax.spines['right'].set_visible(False)
  ax.yaxis.set_ticks_position('left')
  ax.xaxis.set_ticks_position('bottom')
  plt.plot(x,solve_euler(derivs, 0, x), label="Euler")
  
  plt.plot(x,solve_midpoint(derivs, 0, x), label="Midpoint")
  
  plt.plot(x,solve_exact(x), label="Exact")
  
  plt.plot(x,odeint(derivs, 0, x), label="ODEInt")
  plt.ylabel("y(x)")
  plt.xlabel("x")
  plt.title(r"Numerical Solutions to $\frac{dy}{dx}=x+2y$")
  plt.legend(loc = "best")
  
  ax = plt.subplot(212)
  ax.spines['top'].set_visible(False)
  ax.spines['right'].set_visible(False)
  ax.yaxis.set_ticks_position('left')
  ax.xaxis.set_ticks_position('bottom')
  plt.plot(x,abs(euler_error), label = "Euler Error")
  
  plt.plot(x,abs(midpoint_error), label = "Midpoint Error")
  
  plt.plot(x,abs(odeint_error), label = "ODEInt Error")
  plt.ylabel("Errors")
  plt.xlabel("x")
  plt.title(r"Errors of numerical solutions to $\frac{dy}{dx}=x+2y$")
  plt.legend(loc = "best")
  plt.tight_layout()
  
  
  
  
  
  
  
  In [9]:
  
  assert True # leave this for grading the plots
  
  
  
  
  In [ ]:
explanation: The provided text extract is a Jupyter notebook that contains a series of exercises and code implementations for solving ordinary differential equations (ODEs) using different numerical methods. The notebook includes:
  
  1. **Imports**: Necessary libraries and modules are imported.
  2. **Euler's Method**: A function `solve_euler` is defined to solve a 1D ODE using Euler's method.
  3. **Midpoint Method**: A function `solve_midpoint` is defined to solve a 1D ODE using the midpoint method.
  4. **Exact Solution**: A function `solve_exact` is defined to compute the exact solution to a specific ODE.
  5. **Solving and Plotting**: The ODE \(\frac{dy}{dx} = x + 2y\) is solved using Euler's method, the midpoint method, `odeint`, and the exact solution. The solutions and their errors are plotted for comparison.
  
  The text extract does not contain a specific question or a question and answer pair. It is a complete set of instructions and code for solving and visualizing the solutions to an ODE.
distilabel_metadata.content: Evaluate the user given text extract and determine if it contains a math question and optionally and answer to this question.
  
  Output rules:
  
  1. Briefly describe the document and mention if it contains a question or a question and answer.
  2. Finalize the output with: Label: no-question,question,question-and-answer,multiple-question,multiple-question-and-answer
distilabel_metadata.role: system
distilabel_metadata.content: # Ordinary Differential Equations Exercise 1
  
  ## Imports
  
  
  
  In [1]:
  
  %matplotlib inline
  import matplotlib.pyplot as plt
  import numpy as np
  from scipy.integrate import odeint
  from IPython.html.widgets import interact, fixed
  
  
  
  
  :0: FutureWarning: IPython widgets are experimental and may change in the future.
  
  
  
  ## Euler's method
  
  Euler's method is the simplest numerical approach for solving a first order ODE numerically. Given the differential equation
  
  $$\frac{dy}{dx} = f(y(x), x)$$
  
  with the initial condition:
  
  $$y(x_0)=y_0$$
  
  Euler's method performs updates using the equations:
  
  $$y_{n+1} = y_n + h f(y_n,x_n)$$$$h = x_{n+1} - x_n$$
  
  Write a function solve_euler that implements the Euler method for a 1d ODE and follows the specification described in the docstring:
  
  
  
  In [2]:
  
  def solve_euler(derivs, y0, x):
  """Solve a 1d ODE using Euler's method.
  
  Parameters
  ----------
  derivs : function
  The derivative of the diff-eq with the signature deriv(y,x) where
  y and x are floats.
  y0 : float
  The initial condition y[0] = y(x[0]).
  x : np.ndarray, list, tuple
  The array of times at which of solve the diff-eq.
  
  Returns
  -------
  y : np.ndarray
  Array of solutions y[i] = y(x[i])
  """
  h=x[-1]-x[-2]
  data = [y0]
  for t in x[1:]:
  data.append(data[-1]+h*derivs(data[-1],t))
  return data
  
  
  
  
  In [3]:
  
  assert np.allclose(solve_euler(lambda y, x: 1, 0, [0,1,2]), [0,1,2])
  
  
  
  The midpoint method is another numerical method for solving the above differential equation. In general it is more accurate than the Euler method. It uses the update equation:
  
  $$y_{n+1} = y_n + h f\left(y_n+\frac{h}{2}f(y_n,x_n),x_n+\frac{h}{2}\right)$$
  
  Write a function solve_midpoint that implements the midpoint method for a 1d ODE and follows the specification described in the docstring:
  
  
  
  In [4]:
  
  def solve_midpoint(derivs, y0, x):
  """Solve a 1d ODE using the Midpoint method.
  
  Parameters
  ----------
  derivs : function
  The derivative of the diff-eq with the signature deriv(y,x) where y
  and x are floats.
  y0 : float
  The initial condition y[0] = y(x[0]).
  x : np.ndarray, list, tuple
  The array of times at which of solve the diff-eq.
  
  Returns
  -------
  y : np.ndarray
  Array of solutions y[i] = y(x[i])
  """
  h=x[-1]-x[-2]
  data = [y0]
  for t in x[1:]:
  data.append(data[-1]+h*derivs(data[-1]+h/2*derivs(data[-1],t),t+h/2))
  return data
  
  
  
  
  In [5]:
  
  assert np.allclose(solve_euler(lambda y, x: 1, 0, [0,1,2]), [0,1,2])
  
  
  
  You are now going to solve the following differential equation:
  
  $$\frac{dy}{dx} = x + 2y$$
  
  which has the analytical solution:
  
  $$y(x) = 0.25 e^{2x} - 0.5 x - 0.25$$
  
  First, write a solve_exact function that compute the exact solution and follows the specification described in the docstring:
  
  
  
  In [6]:
  
  def solve_exact(x):
  """compute the exact solution to dy/dx = x + 2y.
  
  Parameters
  ----------
  x : np.ndarray
  Array of x values to compute the solution at.
  
  Returns
  -------
  y : np.ndarray
  Array of solutions at y[i] = y(x[i]).
  """
  data = np.array(.25*np.exp(2*x)-.5*x-.25)
  return data
  
  
  
  
  In [7]:
  
  assert np.allclose(solve_exact(np.array([0,1,2])),np.array([0., 1.09726402, 12.39953751]))
  
  
  
  In the following cell you are going to solve the above ODE using four different algorithms:
  
  1. Euler's method
  2. Midpoint method
  3. odeint
  4. Exact
  
  Here are the details:
  
  • Generate an array of x values with $N=11$ points over the interval $[0,1]$ ($h=0.1$).
  • Define the derivs function for the above differential equation.
  • Using the solve_euler, solve_midpoint, odeint and solve_exact functions to compute the solutions using the 4 approaches.
  
  Visualize the solutions on a sigle figure with two subplots:
  
  1. Plot the $y(x)$ versus $x$ for each of the 4 approaches.
  2. Plot $\left|y(x)-y_{exact}(x)\right|$ versus $x$ for each of the 3 numerical approaches.
  
  Your visualization should have legends, labeled axes, titles and be customized for beauty and effectiveness.
  
  While your final plot will use $N=10$ points, first try making $N$ larger and smaller to see how that affects the errors of the different approaches.
  
  
  
  In [46]:
  
  x = np.linspace(0,1,11)
  def derivs(y, x):
  dy = x+2*y
  return dy
  euler_error = np.array(solve_euler(derivs, 0, x))-solve_exact(x)
  midpoint_error = np.array(solve_midpoint(derivs, 0, x))-solve_exact(x)
  odeint_error = np.array(odeint(derivs, 0, x)).flatten()-solve_exact(x)
  
  f = plt.figure(figsize = (9,6))
  ax = plt.subplot(211)
  ax.spines['top'].set_visible(False)
  ax.spines['right'].set_visible(False)
  ax.yaxis.set_ticks_position('left')
  ax.xaxis.set_ticks_position('bottom')
  plt.plot(x,solve_euler(derivs, 0, x), label="Euler")
  
  plt.plot(x,solve_midpoint(derivs, 0, x), label="Midpoint")
  
  plt.plot(x,solve_exact(x), label="Exact")
  
  plt.plot(x,odeint(derivs, 0, x), label="ODEInt")
  plt.ylabel("y(x)")
  plt.xlabel("x")
  plt.title(r"Numerical Solutions to $\frac{dy}{dx}=x+2y$")
  plt.legend(loc = "best")
  
  ax = plt.subplot(212)
  ax.spines['top'].set_visible(False)
  ax.spines['right'].set_visible(False)
  ax.yaxis.set_ticks_position('left')
  ax.xaxis.set_ticks_position('bottom')
  plt.plot(x,abs(euler_error), label = "Euler Error")
  
  plt.plot(x,abs(midpoint_error), label = "Midpoint Error")
  
  plt.plot(x,abs(odeint_error), label = "ODEInt Error")
  plt.ylabel("Errors")
  plt.xlabel("x")
  plt.title(r"Errors of numerical solutions to $\frac{dy}{dx}=x+2y$")
  plt.legend(loc = "best")
  plt.tight_layout()
  
  
  
  
  
  
  
  In [9]:
  
  assert True # leave this for grading the plots
  
  
  
  
  In [ ]:
distilabel_metadata.role: user
distilabel_metadata.raw_output_q_a_detector_0: The provided text extract is a Jupyter notebook that contains a series of exercises and code implementations for solving ordinary differential equations (ODEs) using different numerical methods. The notebook includes:
  
  1. **Imports**: Necessary libraries and modules are imported.
  2. **Euler's Method**: A function `solve_euler` is defined to solve a 1D ODE using Euler's method.
  3. **Midpoint Method**: A function `solve_midpoint` is defined to solve a 1D ODE using the midpoint method.
  4. **Exact Solution**: A function `solve_exact` is defined to compute the exact solution to a specific ODE.
  5. **Solving and Plotting**: The ODE \(\frac{dy}{dx} = x + 2y\) is solved using Euler's method, the midpoint method, `odeint`, and the exact solution. The solutions and their errors are plotted for comparison.
  
  The text extract does not contain a specific question or a question and answer pair. It is a complete set of instructions and code for solving and visualizing the solutions to an ODE.
  
  Label: no-question