SageMath Script to create Animation and Image of Exponential function

In order to explain the relationship between an exponential function whose base is a positive real number and its derivative, I have created graph animations and still images using SageMath. The SageMath script is published in this page.

(There is the Japanese(日本語) page.)

(Last updated date: March 24, 2021)

Preface

The previous page explains the relationship between an exponential function \(a^x\), whose base is a positive real number \(a\), and its derivative. This explanation uses animations and still images of graphs drawing these equations for visual understanding. In addition, animations and still images of the graphs of logarithmic functions are used for supporting explanations.

The animations and images are created with SageMath. The SageMath script is published in the next chapter.

Script to create animations and images

The following is the SageMath script for creating the animations and the still images shown in the previous page. This script creates the still images first, and then the animations.

# This SageMath script have been tested with SageMath 9.1 and FFmpeg 3.4.8
# on Ubuntu 18.04.

# This script outputs the graph image files (SVG format) of
# the following functions:
#
# * The exponential function y=a^x and its derivative y',
#   where "a" is a positive real number.
#   2, e, and 4 are substituted for the "a" respectively.
#   In the script below, this graph is named "exp graph".
#
# * The logarithm function y=log(x).
#   The point (a, log(a)) is added to the graph.
#   This graph is named "log graph".
#
# * Another graph that arranges the above two graphs side by side
#   is also output.
#   This graph is named "parallel graph".
#
# This script outputs the graph animation files (MP4 and WebM formats).
# The animations illustrate the variations in the above graphs
# when the "a" is continuously varied from 1.1 to 5.
# These animations are named "exp animation", "log animation", and
# "parallel animation" respectively.
#
# This script also outputs another animation that plays both
# the "exp animation" and the "log animation" serially.
# This animation is named "serial animation".
#
# The "exp animation" and the "log animation" are grouped together and
# named "single animations".
# Similarly, the "parallel animation" and the "serial animation"
# are grouped together and named "double animations"

# The "a" in the following script represents the above "a".
# It is not an English "indefinite article".

a_min, a_max = 1.1, 5

def create_a_list():
    l=srange(a_min, a_max, 0.02, include_endpoint=True) + [e]
    l.sort()
    return l

def format_a(a):
    return r'\mathrm{e}' if a == e else format(float(a), '.2f')

def format_log_text(a, log_a):
    t= r'$\log(a=' + format_a(a) + ')'

    if a == e:
        t+= '=1'
    else:
        t+= r'\approx' + format(float(round(log_a, 3)), '.3f')
        if log_a < 1:
            t+= '<1'
        else:
            t+= '>1'

    t+= '$'
    return t

def format_graph(g):
    g.axes_labels(['$x$', '$y$'])
    g.fontsize(14)
    g.axes_labels_size(1.3)
    g.set_legend_options(font_size='xx-large', labelspacing=0.5)
    g.set_aspect_ratio(1)

tick_formats={'ticks_integer':True, 'tick_formatter':'latex'}

text_formats={'axis_coords':True, 'horizontal_alignment':'left', \
    'fontsize':'xx-large', 'rgbcolor':'black', \
    'bounding_box':{'boxstyle':'square', 'fc':'w', 'ec':'darkgray'}}

def create_exp_graph(a):
    x_min, x_max = -1, 3

    g=plot([a^x, (a^x)*log(a)], (x, x_min, x_max), \
        legend_label=['$a^x$', "$(a^x)'$"], **tick_formats)

    g+=text('$a=' + format_a(a) + '$', (0.785, 0.64), **text_formats)

    format_graph(g)
    g.set_legend_options(loc='upper right')
    g.set_axes_range(x_min, x_max, 0, 3.5)

    return g

def create_log_graph(a):
    x_min, x_max = 0.5, (a_max +0.5)
    x_axes_min, x_axes_max = (x_min -0.1), (x_max +0.1)
    log_a = log(a)

    g=plot(log(x), (x, x_min, x_max), \
        legend_label=r'$\log(x)$', **tick_formats)

    g+=text(format_log_text(a, log_a), (0.38, 0.92), **text_formats)

    g+=line([(x_axes_min, 1), (x_axes_max, 1)], \
        rgbcolor='black', thickness=0.2)

    g+=point((a, log_a), rgbcolor='red', pointsize=80, \
        legend_label=r'$\left(a, \log(a) \right)$')

    g+=line([(a, log_a), (a, 0)], \
        rgbcolor='red', thickness=0.3)

    g+=arrow((a, log_a), (x_axes_min, log_a), \
        rgbcolor='red', width=0.3, arrowsize=4)

    format_graph(g)
    g.set_legend_options(loc='upper left')
    g.set_axes_range(x_min, x_max, -0.7, 3.0)

    return g

def make_parallel_graph(exp_graph, log_graph):
    return multi_graphics([ \
        (exp_graph, (0   , 0, 0.45, 0.45)), \
        (log_graph, (0.43, 0, 0.5 , 0.45))])

parallel_graph_formats={'figsize':[13, 8.7]}

# Write the graph image files of "exp", "log", and "parallel"
def write_images():
    for a in [2, e, 4]:
        exp_graph = create_exp_graph(a)
        log_graph = create_log_graph(a)
        parallel_graph = make_parallel_graph(exp_graph, log_graph)

        exp_graph.save('exp-' + str(a) + '.svg')
        log_graph.save('log-' + str(a) + '.svg')
        parallel_graph.save('parallel-' + str(a) + '.svg', \
            **parallel_graph_formats)

# Create the lists of "exp graph" and "log graph" respectively
def create_exp_log_graph_lists():
    a_list = create_a_list()
    exp_graph_list = [create_exp_graph(a) for a in a_list]
    log_graph_list = [create_log_graph(a) for a in a_list]
    return exp_graph_list, log_graph_list

animation_ext_list = ['.mp4', '.webm']

def write_single_animations():
    exp_graph_list, log_graph_list = create_exp_log_graph_lists()

    for file_ext in animation_ext_list:
        animate(exp_graph_list).save('exp' + file_ext)
        animate(log_graph_list).save('log' + file_ext)

def write_parallel_animation(exp_graph_list, log_graph_list):
    parallel_graph_list = [make_parallel_graph(exp_graph, log_graph) \
        for exp_graph, log_graph in zip(exp_graph_list, log_graph_list)]

    for file_ext in animation_ext_list:
        animate(parallel_graph_list, **parallel_graph_formats).save( \
            'parallel' + file_ext)

def write_serial_animation(exp_graph_list, log_graph_list):
    for log_graph in log_graph_list:
        log_graph.set_legend_options(font_size='x-large')

    serial_graph_list = exp_graph_list + log_graph_list

    serial_graph_formats={'figsize':[5.0, 5.0], 'fig_tight':False, \
        'aspect_ratio':'automatic'}

    for file_ext in animation_ext_list:
        animate(serial_graph_list, **serial_graph_formats).save( \
            'serial' + file_ext)

def write_double_animations():
    exp_graph_list, log_graph_list = create_exp_log_graph_lists()

    write_parallel_animation(exp_graph_list, log_graph_list)
    write_serial_animation(exp_graph_list, log_graph_list)

write_images()
write_single_animations()
write_double_animations()

This script uses FFmpeg to create the animations.

The still images are output in SVG format. The animations are output in two formats, MP4 and WebM.


The profile of the MP4 files output by the above script is “High 4:4:4 Predictive”. Some applications may not be able to display MP4 files with this profile properly.

Accordingly, here is a shell script using FFmpeg to convert MP4 files into the “Main” profile.

#!/bin/sh

# This shell script have been tested with FFmpeg 3.4.8 on Ubuntu 18.04.

# This script converts "High 4:4:4 Predictive profile" of 
# the MP4 files (H.264 codec) into "Main profile".
#
# The resolution (width and height) after the conversion
# must both be an even number.
# Therefore, if the width or height of the frame in the input file
# is an odd number, the corresponding edge of the frame is deleted
# by one pixel.
# Since the input videos have margins, the above deletion is not
# a problem.

for target_basename in exp log parallel serial
do
  input_filename="${target_basename}.mp4"    # e.g. exp.mp4
  output_filename="${target_basename}-m.mp4" # e.g. exp-m.mp4

  ffmpeg -y -i "${input_filename}" -codec:v libx264 -profile:v main \
    -pix_fmt yuv420p -vf "crop=trunc(iw/2)*2:trunc(ih/2)*2" \
    "${output_filename}"
done

Most of the still images and the animations created by these two scripts are shown in the previous page. What is not shown is an animation that plays both the exponential function animation and the logarithmic function animation serially. In the above script, the animation is named “Serial animation”. The links to the animation are shown below.

This “serial animation” has been created with the intention of displaying it as a video when the contents of the previous page are introduced on another website.


Previous page: Animation of Exponential function and Derivative (for Students)

Comments

Popular posts from this blog