Skip to content

ci: add linter to GitHub Workflows #1

ci: add linter to GitHub Workflows

ci: add linter to GitHub Workflows #1

Triggered via push October 4, 2023 16:28
Status Success
Total duration 24s
Artifacts

linter.yml

on: push
Run linters
13s
Run linters
Fit to window
Zoom out
Zoom in

Annotations

16 errors and 1 warning
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/bifurcations.py#L7
import sys from lelib import Bifurcation, Map from utils import argparser, die + def parse_args(): - """This function parses and return arguments passed in """ - descr = 'Plot the Bifurcation Diagram of Logistic, Cubic, and Sine Maps' - examples = ''' + """This function parses and return arguments passed in""" + descr = "Plot the Bifurcation Diagram of Logistic, Cubic, and Sine Maps" + examples = """ %(prog)s -r 1:4 %(prog)s -r 4:6.5 --map=cubic %(prog)s --map=sine -s 200 -n 200 %(prog)s -r 3.:4. -s 500 -n 600 - %(prog)s -r 3.5:3.6 -y .3:.6 -s 800 -n 1000''' + %(prog)s -r 3.5:3.6 -y .3:.6 -s 800 -n 1000""" parser = argparser(descr, examples) # By default, make 300 iterations (n) and do no plot the first 200 ones (s) # By default select the Logistic Equation parser.add_argument( - "-r", "--rate", - action="store", dest="r", - help="range of the growth rate parameter (default: the entire range)") + "-r", + "--rate", + action="store", + dest="r", + help="range of the growth rate parameter (default: the entire range)", + ) parser.add_argument( - "-y", "--people", - action="store", dest="y", - help="normalized range of the population (default: the entire range)") + "-y", + "--people", + action="store", + dest="y", + help="normalized range of the population (default: the entire range)", + ) parser.add_argument( - "-s", "--skip", - action="store", dest="s", type=int, default=200, - help="skip plotting the first 's' iterations (default: %(default)s)") + "-s", + "--skip", + action="store", + dest="s", + type=int, + default=200, + help="skip plotting the first 's' iterations (default: %(default)s)", + ) parser.add_argument( - "-n", "--steps", - action="store", dest="n", type=int, default=100, - help="number of iterations (default: %(default)s)") + "-n", + "--steps", + action="store", + dest="n", + type=int, + default=100, + help="number of iterations (default: %(default)s)", + ) parser.add_argument( - "-m", "--map", - action="store", dest="map_name", default="logistic", + "-m", + "--map", + action="store", + dest="map_name", + default="logistic", choices=["logistic", "cubic", "sine"], - help="select the desired map (logistic, cubic, or sine)") + help="select the desired map (logistic, cubic, or sine)", + ) return parser.parse_args() def main(): args = parse_args() mapobj = Map(args.map_name) # range to vector: "1:4" --> [1., 4.] - r2v = (lambda a, minval, maxval : - [float(i) for i in a.split(':')] if a else - [minval, maxval]) + r2v = ( + lambda a, minval, maxval: [float(i) for i in a.split(":")] + if a + else [minval, maxval] + ) # Plot the entire diagram by default Bifurcation( r2v(args.r, mapobj.map_rmin, mapobj.map_rmax), r2v(args.y, mapobj.map_ymin, mapobj.map_ymax), args.n, args.s, - args.map_name + args.map_name, ).plot() -if __name__ == '__main__': + +if __name__ == "__main__": try: main() except KeyboardInterrupt: - die(3, 'Exiting on user request') + die(3, "Exiting on user request") sys.exit()
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/finalstate.py#L7
import sys from lelib import FinalState from utils import argparser, die + def parse_args(): - """This function parses and return arguments passed in """ - descr = 'Plot of the Final State Diagram' - examples = ''' + """This function parses and return arguments passed in""" + descr = "Plot of the Final State Diagram" + examples = """ %(prog)s -r 3.492 %(prog)s -r 3.614 -s 200 -n 300 %(prog)s -0 0.4 -r 3.2 -s 10 -n 50 - %(prog)s -0 0.8 -r 6.2 -n 20 --map=cubic''' + %(prog)s -0 0.8 -r 6.2 -n 20 --map=cubic""" parser = argparser(descr, examples) # By default, make 3000 iterations (n) and # do no plot the first 2000 ones (s) # By default select the Logistic Equation parser.add_argument( - "-0", "--x0", - action="store", dest="x0", type=float, default=.5, - help="initial condition (default: %(default)s)") + "-0", + "--x0", + action="store", + dest="x0", + type=float, + default=0.5, + help="initial condition (default: %(default)s)", + ) parser.add_argument( - "-r", "--rate", - action="store", dest="r", type=float, required=True, - help="growth rate parameter") + "-r", + "--rate", + action="store", + dest="r", + type=float, + required=True, + help="growth rate parameter", + ) parser.add_argument( - "-s", "--skip", - action="store", dest="s", type=int, default=2000, - help="skip plotting the first 's' iterations (default: %(default)s)") + "-s", + "--skip", + action="store", + dest="s", + type=int, + default=2000, + help="skip plotting the first 's' iterations (default: %(default)s)", + ) parser.add_argument( - "-n", "--steps", dest="n", type=int, default=1000, action="store", - help="number of iterations (default: %(default)s)") + "-n", + "--steps", + dest="n", + type=int, + default=1000, + action="store", + help="number of iterations (default: %(default)s)", + ) parser.add_argument( - "-m", "--map", - action="store", dest="map_name", default = "logistic", - choices = ["logistic", "cubic", "sine"], - help = "select the desired map (logistic, cubic, or sine)") + "-m", + "--map", + action="store", + dest="map_name", + default="logistic", + choices=["logistic", "cubic", "sine"], + help="select the desired map (logistic, cubic, or sine)", + ) return parser.parse_args() def main(): args = parse_args() - FinalState( - args.r, - args.n, - args.x0, - args.s, - args.map_name - ).plot() + FinalState(args.r, args.n, args.x0, args.s, args.map_name).plot() -if __name__ == '__main__': + +if __name__ == "__main__": try: main() except KeyboardInterrupt: - die(3, 'Exiting on user request') + die(3, "Exiting on user request") sys.exit()
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/legraph.py#L7
import sys from lelib import Logistic, LogisticDiff from utils import argparser, die + def parse_args(): - """This function parses and return arguments passed in """ - descr = 'Plot of Logistic Equation Time Series' - examples = ''' + """This function parses and return arguments passed in""" + descr = "Plot of Logistic Equation Time Series" + examples = """ # time series with a stable fixed point %(prog)s -0 0.4 -r 3.2 -n 50 %(prog)s -0 0.4 -1 0.45 -r 3.2 -n 50 # chaotic results (randon output) %(prog)s --x0 0.2 --x1 0.2000001 -r 4.0 -n 50 %(prog)s -0 0.2 -r 3.6 -n 5000 --dots-only %(prog)s -0 0.9 -r 4.5 -n 50 --map=cubic - %(prog)s -0 0.4 -r 0.8 -n 50 --map=sine''' + %(prog)s -0 0.4 -r 0.8 -n 50 --map=sine""" parser = argparser(descr, examples) # By default select the Logistic Equation parser.add_argument( - "-0", "--x0", - action="store", dest="x0", type=float, required=True, - help="1st initial condition") + "-0", + "--x0", + action="store", + dest="x0", + type=float, + required=True, + help="1st initial condition", + ) parser.add_argument( - "-1", "--x1", - action="store", dest="x1", type=float, - help="2nd initial condition (optional)") + "-1", + "--x1", + action="store", + dest="x1", + type=float, + help="2nd initial condition (optional)", + ) parser.add_argument( - "-d", "--dots-only", - action="store_true", dest="dotsonly", - help="do not connect the dots with lines (default: %(default)s)") + "-d", + "--dots-only", + action="store_true", + dest="dotsonly", + help="do not connect the dots with lines (default: %(default)s)", + ) parser.add_argument( - "-r", "--rate", - action="store", dest="r", type=float, required=True, - help="growth rate parameter") + "-r", + "--rate", + action="store", + dest="r", + type=float, + required=True, + help="growth rate parameter", + ) parser.add_argument( - "-s", "--skip", - action="store", dest="s", type=int, default=0, - help="skip plotting the first 's' iterations") + "-s", + "--skip", + action="store", + dest="s", + type=int, + default=0, + help="skip plotting the first 's' iterations", + ) parser.add_argument( - "-n", "--steps", - action="store", dest="n", type=int, required=True, - help="number of iterations") + "-n", + "--steps", + action="store", + dest="n", + type=int, + required=True, + help="number of iterations", + ) parser.add_argument( - "-m", "--map", - action="store", dest="map_name", default="logistic", - choices = ["logistic", "cubic", "sine"], - help = "select the desired map (logistic, cubic, or sine)") + "-m", + "--map", + action="store", + dest="map_name", + default="logistic", + choices=["logistic", "cubic", "sine"], + help="select the desired map (logistic, cubic, or sine)", + ) return parser.parse_args() + def main(): args = parse_args() lemap = ( - LogisticDiff( - args.r, args.n, args.x0, args.x1, args.s, args.map_name) - if args.x1 else Logistic( - args.r, args.n, args.x0, args.s, args.map_name)) + LogisticDiff(args.r, args.n, args.x0, args.x1, args.s, args.map_name) + if args.x1 + else Logistic(args.r, args.n, args.x0, args.s, args.map_name) + ) lemap.plotdots = not args.dotsonly lemap.plot() -if __name__ == '__main__': + +if __name__ == "__main__": try: main() except KeyboardInterrupt: - die(3, 'Exiting on user request') + die(3, "Exiting on user request") sys.exit()
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/lelib_test.py#L7
from __future__ import print_function import numpy as np from lelib import Map, Logistic, LogisticDiff + def test_class_map(): - """Test the class 'Map' """ + """Test the class 'Map'""" print("Running the tests for the class 'Map'...") m = Map() - m.ensure(m.map_name == 'logistic', "The default map should be 'logistic'") - m.ensure(m.map_longname == 'Logistic Equation', - "Logistic Map: bad long name") - m.ensure(m.map_rmin == 0 and m.map_rmax == 4, - "Logistic Map: bad range for r") - m.ensure(m.map_ymin == 0 and m.map_ymax == 1, - "Logistic Map: bad range for y") - i = m.map(4,.25) - m.ensure(i == .75, - "Logistic Map: bad value for r=4 and x=.25: should be %f" % i) + m.ensure(m.map_name == "logistic", "The default map should be 'logistic'") + m.ensure(m.map_longname == "Logistic Equation", "Logistic Map: bad long name") + m.ensure(m.map_rmin == 0 and m.map_rmax == 4, "Logistic Map: bad range for r") + m.ensure(m.map_ymin == 0 and m.map_ymax == 1, "Logistic Map: bad range for y") + i = m.map(4, 0.25) + m.ensure(i == 0.75, "Logistic Map: bad value for r=4 and x=.25: should be %f" % i) - m = Map('cubic') - m.ensure(m.map_name == 'cubic', "Cubic Map: bad map selection") - m.ensure(m.map_longname == 'Cubic Equation', "Cubic Map: bad long name") - m.ensure(m.map_rmin == 0 and m.map_rmax == 6.5, - "Cubic Map: bad range for r") - m.ensure(m.map_ymin == 0 and m.map_ymax == 1, - "Cubic Map: bad range for y") - i = m.map(2,.5) - m.ensure(i == .25, - "Cubic Map: bad value for r=2 and x=.5, should be %f" % i) + m = Map("cubic") + m.ensure(m.map_name == "cubic", "Cubic Map: bad map selection") + m.ensure(m.map_longname == "Cubic Equation", "Cubic Map: bad long name") + m.ensure(m.map_rmin == 0 and m.map_rmax == 6.5, "Cubic Map: bad range for r") + m.ensure(m.map_ymin == 0 and m.map_ymax == 1, "Cubic Map: bad range for y") + i = m.map(2, 0.5) + m.ensure(i == 0.25, "Cubic Map: bad value for r=2 and x=.5, should be %f" % i) - m = Map('sine') - m.ensure(m.map_name == 'sine', "Sine Map: bad map selection") - m.ensure(m.map_longname == 'Sine Equation', "Sine Map: bad long name") + m = Map("sine") + m.ensure(m.map_name == "sine", "Sine Map: bad map selection") + m.ensure(m.map_longname == "Sine Equation", "Sine Map: bad long name") m.ensure(m.map_rmin == 0 and m.map_rmax == 2, "Sine Map: bad range for r") m.ensure(m.map_ymin == 0 and m.map_ymax == 2, "Sine Map: bad range for y") - i = m.map(0.5,1) - m.ensure(i == .5, - "Sine Map: bad value for r=0.5 and x=1: should be %f" % i) + i = m.map(0.5, 1) + m.ensure(i == 0.5, "Sine Map: bad value for r=0.5 and x=1: should be %f" % i) + def test_class_logistic(): - """Test the class 'Logistic' """ + """Test the class 'Logistic'""" print("Running the tests for the class 'Logistic'...") r, n, x0 = 3.2, 100, 0.4 - le1 = Logistic(r, n, x0, False, 'logistic') + le1 = Logistic(r, n, x0, False, "logistic") x, y1 = le1.getxy() m = Map() - m.ensure(len(x) == n+1, "x should be a vector of size %d" % (n+1)) + m.ensure(len(x) == n + 1, "x should be a vector of size %d" % (n + 1)) m.ensure(x[0] == 0, "x[0] should be 0") m.ensure(x[n] == n, "the last element of x should be equal to %d" % n) - m.ensure(x.sum() == n*(n+1)/2, - "the sum of the elements of x is not correct") + m.ensure(x.sum() == n * (n + 1) / 2, "the sum of the elements of x is not correct") - m.ensure(len(y1) == n+1, "y1 should be a vector of size %d" % (n+1)) + m.ensure(len(y1) == n + 1, "y1 should be a vector of size %d" % (n + 1)) m.ensure(y1[0] == x0, "the first element of y1 should be equal to x0") - m.ensure(y1[n] == y1[n-2], "y1 is expected to be periodic with period 2") - m.ensure(y1[n-1] == y1[n-3], "y1 is expected to be periodic with period 2") + m.ensure(y1[n] == y1[n - 2], "y1 is expected to be periodic with period 2") + m.ensure(y1[n - 1] == y1[n - 3], "y1 is expected to be periodic with period 2") + def test_class_logisticdiff(): - """Test the class 'LogisticDiff' """ + """Test the class 'LogisticDiff'""" print("Running the tests for the class 'LogisticDiff'...") r, n, x0, x1 = 4.0, 50, 0.2, 0.2000001 - le2 = LogisticDiff(r, n, x0, x1, False, 'logistic') + le2 = LogisticDiff(r, n, x0, x1, False, "logistic") x, y1, _ = le2.getxy() m = Map() - m.ensure(len(x) == n+1, "x should be a vector of size %d" % (n+1)) + m.ensure(len(x) == n + 1, "x should be a vector of size %d" % (n + 1)) m.ensure(x[0] == 0, "x[0] should be 0") m.ensure(x[n] == n, "the last element of x should be equal to %d" % n) - m.ensure(x.sum() == n*(n+1)/2, - "the sum of the elements of x is not correct") + m.ensure(x.sum() == n * (n + 1) / 2, "the sum of the elements of x is not correct") - m.ensure(len(y1) == n+1, "y1 should be a vector of size %d" % (n+1)) + m.ensure(len(y1) == n + 1, "y1 should be a vector of size %d" % (n + 1)) m.ensure(y1[0] == x0, "the first element of y1 should be equal to x0") ydiff = le2.getdiffy() - m.ensure(len(ydiff) == n+1, - "the vector y2-y1 should have a size equal to %d" % (n+1)) - m.ensure(np.all(ydiff < 1e3) and np.all(ydiff > -1e3), - "the diff vector should show the Butterfly Effect") + m.ensure( + len(ydiff) == n + 1, "the vector y2-y1 should have a size equal to %d" % (n + 1) + ) + m.ensure( + np.all(ydiff < 1e3) and np.all(ydiff > -1e3), + "the diff vector should show the Butterfly Effect", + ) + def tests(): test_class_map() test_class_logistic() test_class_logisticdiff()
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/utils.py#L12
__status__ = "stable" import argparse import textwrap + def argparser(descr, examples): - """Return a new ArgumentParser object """ + """Return a new ArgumentParser object""" return argparse.ArgumentParser( - formatter_class = argparse.RawDescriptionHelpFormatter, - description = copyleft(descr), - epilog = "Examples:\n" + textwrap.dedent(examples)) + formatter_class=argparse.RawDescriptionHelpFormatter, + description=copyleft(descr), + epilog="Examples:\n" + textwrap.dedent(examples), + ) + def copyleft(descr): - """Print the Copyright message and License """ + """Print the Copyright message and License""" - return ("{0} v.{1} ({2})\n{3} <{4}>\nLicense: {5}" - .format( - descr, __version__, __status__, - __copyright__, __email__, - __license__)) + return "{0} v.{1} ({2})\n{3} <{4}>\nLicense: {5}".format( + descr, __version__, __status__, __copyright__, __email__, __license__ + ) + def die(exitcode, message): - """Print and error message and exit with 'exitcode' """ + """Print and error message and exit with 'exitcode'""" progname = sys.argv[0] - sys.stderr.write('%s: error: %s\n' % (progname, message)) + sys.stderr.write("%s: error: %s\n" % (progname, message)) sys.exit(exitcode)
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/setup.py#L12
Logistic, Cubic, or Sine Map with two different starting points (along with the plot of the differences between the two maps), and the Final State and Bifurcation Diagrams. """ -DOCLINES = DESCRIPTION.split('\n') +DOCLINES = DESCRIPTION.split("\n") CLASSIFIERS = """\ Development Status :: 5 - Production/Stable Intended Audience :: Education Intended Audience :: Science/Research
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/setup.py#L29
Operating System :: POSIX Operating System :: Unix Operating System :: MacOS """ -VERSION = '2' +VERSION = "2" setup( - name = 'dynamic-systems-and-chaos', - description = DOCLINES[0], - long_description = DESCRIPTION, - version = VERSION, - url = 'https://github.com/madrisan/dynamic-systems-and-chaos/', - - author = 'Davide Madrisan', - author_email = 'davide.madrisan@gmail.com', - - license = 'Apache License 2.0', - packages = ['dynamic-systems-and-chaos'], - - scripts = [ - 'dynamic-systems-and-chaos/bifurcations.py', - 'dynamic-systems-and-chaos/finalstate.py', - 'dynamic-systems-and-chaos/legraph.py', + name="dynamic-systems-and-chaos", + description=DOCLINES[0], + long_description=DESCRIPTION, + version=VERSION, + url="https://github.com/madrisan/dynamic-systems-and-chaos/", + author="Davide Madrisan", + author_email="davide.madrisan@gmail.com", + license="Apache License 2.0", + packages=["dynamic-systems-and-chaos"], + scripts=[ + "dynamic-systems-and-chaos/bifurcations.py", + "dynamic-systems-and-chaos/finalstate.py", + "dynamic-systems-and-chaos/legraph.py", ], - - classifiers = [ _f for _f in CLASSIFIERS.split('\n') if _f ], - - install_requires = [ - 'numpy', - 'matplotlib', + classifiers=[_f for _f in CLASSIFIERS.split("\n") if _f], + install_requires=[ + "numpy", + "matplotlib", ], - - platforms = ["Linux", "Mac OS-X", "Windows"], + platforms=["Linux", "Mac OS-X", "Windows"], )
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/lelib.py#L15
import numpy as np import matplotlib.pyplot as plt from math import pi, sin + class Map(object): - """Class that provides the map functions along with r and y ranges """ - - def __init__(self, mapname='logistic'): + """Class that provides the map functions along with r and y ranges""" + + def __init__(self, mapname="logistic"): params = { - # rmin rmax ymin ymax function - 'cubic' : [ 0, 6.5, 0, 1, lambda r, x: r * x**2 * (1.0 - x) ], - 'logistic': [ 0, 4.0, 0, 1, lambda r, x: r * x * (1.0 - x) ], - 'sine' : [ 0, 2.0, 0, 2, lambda r, x: r * sin(pi * x / 2.0) ] + # rmin rmax ymin ymax function + "cubic": [0, 6.5, 0, 1, lambda r, x: r * x**2 * (1.0 - x)], + "logistic": [0, 4.0, 0, 1, lambda r, x: r * x * (1.0 - x)], + "sine": [0, 2.0, 0, 2, lambda r, x: r * sin(pi * x / 2.0)], } self.map_name = mapname self.map_longname = "%s Equation" % mapname.capitalize() try: - self.map_rmin, self.map_rmax, \ - self.map_ymin, self.map_ymax, \ - self.map_function = params[mapname] + ( + self.map_rmin, + self.map_rmax, + self.map_ymin, + self.map_ymax, + self.map_function, + ) = params[mapname] self.map = self._mapper except Exception as e: - raise type(e)('Unknown map name ' + mapname) + raise type(e)("Unknown map name " + mapname) @staticmethod def ensure(expression, message, *argv): if not expression: raise AssertionError(message % (argv) if argv else message) def _mapper(self, r, x): self.ensure( (r >= self.map_rmin and r <= self.map_rmax), - 'The growth parameter r must be between %g and %g', - self.map_rmin, self.map_rmax) + "The growth parameter r must be between %g and %g", + self.map_rmin, + self.map_rmax, + ) return self.map_function(r, x) + class Logistic(Map): - """Class for plotting a Logistic/Cubic/Sine Map """ - - def __init__(self, r, n, x0, s=0, mapname='logistic'): + """Class for plotting a Logistic/Cubic/Sine Map""" + + def __init__(self, r, n, x0, s=0, mapname="logistic"): Map.__init__(self, mapname) - self.r = r # Growth rate parameter - self.n = n # Number of iterations - self.s = s # Number of iterations to skip in the plot + self.r = r # Growth rate parameter + self.n = n # Number of iterations + self.s = s # Number of iterations to skip in the plot self.x0 = x0 # The 1st initial condition self.x = self.y1 = [] self._dotsonly = False - self.ensure(n > 0, 'The number of iterations must be greater than zero.') - self.ensure(s >= 0, 'You cannot skip a negative number of iterations.') - self.ensure(x0 >= self.map_ymin and x0 <= self.map_ymax, - 'The initial condition x0 should be in [%g, %g].', - self.map_ymin, self.map_ymax) + self.ensure(n > 0, "The number of iterations must be greater than zero.") + self.ensure(s >= 0, "You cannot skip a negative number of iterations.") + self.ensure( + x0 >= self.map_ymin and x0 <= self.map_ymax, + "The initial condition x0 should be in [%g, %g].", + self.map_ymin, + self.map_ymax, + ) def _plotline(self, x, y, color): """Plot the dots (x, y) connected by straight lines - if the parameter 'dotsonly' if set to False """ - - self.ensure(x.any() and y.any(), '_plotline(): internal error') - plt.plot(x, y, color=color, linestyle='', - markerfacecolor=color, marker='o', markersize=5) + if the parameter 'dotsonly' if set to False""" + + self.ensure(x.any() and y.any(), "_plotline(): internal error") + plt.plot( + x, + y, + color=color, + linestyle="", + markerfacecolor=color, + marker="o", + markersize=5, + ) if self.plotdots: plt.plot(x, y, color=color, alpha=0.6) def getxy(self, fill_value=None): """Set the numpy vectors 'x' and 'y1' containing - the iterations (1..n) and the corresponding values - of the choosen Map """ + the iterations (1..n) and the corresponding values + of the choosen Map""" # do not initialize twice the x and y1 vectors - if len(self.x) > 0: return + if len(self.x) > 0: + return vectlen = self.n + self.s + 1 self.x = np.arange(vectlen) - self.y1 = np.arange(0, vectlen, 1.) + self.y1 = np.arange(0, vectlen, 1.0) self.y1[0] = self.x0 for t in self.x[1:]: - self.y1[t] = self.map(self.r, self.y1[t-1]) + self.y1[t] = self.map(self.r, self.y1[t - 1]) return self.x, self.y1 def plot(self): - """Plot a Logistic, Cubic or Sine map """ + """Plot a Logistic, Cubic or Sine map""" self.getxy() - plt.suptitle('Dynamic Systems and Chaos', fontsize=14, fontweight='bold') + plt.suptitle("Dynamic Systems and Chaos", fontsize=14, fontweight="bold") plt.title(self.map_longname) - plt.xlabel('time t') + plt.xlabel("time t") plt.ylim([self.map_ymin, self.map_ymax]) plt.grid(True) - self._plotline(self.x[self.s:], self.y1[self.s:], 'mediumseagreen') + self._plotline(self.x[self.s :], self.y1[self.s :], "mediumseagreen") plt.show() @Property def plotdots(self): return self._dotsonly @plotdots.setter def plotdots(self, value): - """Set whether to plot or not the dots in a logistic graph """ + """Set whether to plot or not the dots in a logistic graph""" self._dotsonly = value class FinalState(Logistic): - """Derived class for plotting a Final State Diagram """ + """Derived class for plotting a Final State Diagram""" # By default, set the initial state to .5 # make 3000 iterations and do no plot the first 2000 ones - def __init__(self, r, n=1000, x0=.5, s=2000, mapname='logistic'): + def __init__(self, r, n=1000, x0=0.5, s=2000, mapname="logistic"): Logistic.__init__(self, r, n, x0, s, mapname) - def getxy(self, fill_value=.5): + def getxy(self, fill_value=0.5): """Set the numpy vectors 'x' and 'y1' containing the values of the - choosen Map for the first n iterations """ + choosen Map for the first n iterations""" # do not initialize twice the x and y1 vectors - if len(self.x) > 0: return + if len(self.x) > 0: + return vectlen = self.n + self.s + 1 self.x = np.full(vectlen, self.x0, dtype=np.float64) for t in range(1, vectlen): - self.x[t] = self.map(self.r, self.x[t-1]) + self.x[t] = self.map(self.r, self.x[t - 1]) self.y1 = np.full(vectlen, fill_value, dtype=np.float) return self.x, self.y1 def plot(self): - """Plot a Final State Diagram """ + """Plot a Final State Diagram""" self.getxy() - plt.suptitle('Dynamic Systems and Chaos', fontsize=14, fontweight='bold') - plt.title('Final State Diagram for the ' + self.map_longname) + plt.suptitle("Dynamic Systems and Chaos", fontsize=14, fontweight="bold") + plt.title("Final State Diagram for the " + self.map_longname) plt.xlim([self.map_ymin, self.map_ymax]) - plt.ylim([0, 1.]) + plt.ylim([0, 1.0]) plt.yticks([]) plt.grid(True) - plt.plot([self.map_ymin, self.map_ymax], [.5, .5], - color='black', lw=1) - plt.plot(self.x[self.s:], self.y1[self.s:], color='black', linestyle='', - markerfacecolor='black', marker='o', markersize=8) - plt.text(.1 * self.map_ymax, .4, 'r = %g' % self.r, style='italic', - bbox={'facecolor':'red', 'alpha':0.5, 'pad':10}) + plt.plot([self.map_ymin, self.map_ymax], [0.5, 0.5], color="black", lw=1) + plt.plot( + self.x[self.s :], + self.y1[self.s :], + color="black", + linestyle="", + markerfacecolor="black", + marker="o", + markersize=8, + ) + plt.text( + 0.1 * self.map_ymax, + 0.4, + "r = %g" % self.r, + style="italic", + bbox={"facecolor": "red", "alpha": 0.5, "pad": 10}, + ) plt.show() class LogisticDiff(Logistic): """Derived class for plotting a Logistic/Cubic/Sine Map - with two different initial conditions, followed by a plot of - their differences (for a visualization of the Butterfly Effect) """ - - def __init__(self, r, n, x0, x1, s=0, mapname='logistic'): + with two different initial conditions, followed by a plot of + their differences (for a visualization of the Butterfly Effect)""" + + def __init__(self, r, n, x0, x1, s=0, mapname="logistic"): Logistic.__init__(self, r, n, x0, s, mapname) - self.ensure(x1 >= self.map_ymin and x1 <= self.map_ymax, - 'The initial condition x1 should be in [%g, %g].', - self.map_ymin, self.map_ymax) + self.ensure( + x1 >= self.map_ymin and x1 <= self.map_ymax, + "The initial condition x1 should be in [%g, %g].", + self.map_ymin, + self.map_ymax, + ) self.x1 = x1 # The 2st initial condition self.y2 = [] def getxy(self, fill_value=None): """Set the numpy vectors 'x', 'y1', and 'y2' containing - the iterations (1..n) and the corresponding values - of the choosen Map """ + the iterations (1..n) and the corresponding values + of the choosen Map""" x, y1 = super(LogisticDiff, self).getxy() # do not initialize twice the vector y2 - if len(self.y2) > 0: return - - self.y2 = np.arange(0, self.n + self.s + 1, 1.) + if len(self.y2) > 0: + return + + self.y2 = np.arange(0, self.n + self.s + 1, 1.0) self.y2[0] = self.x1 for t in self.x[1:]: - self.y2[t] = self.map(self.r, self.y2[t-1]) + self.y2[t] = self.map(self.r, self.y2[t - 1]) return x, y1, self.y2 def getdiffy(self): - """Return the difference between the two vectors y2 and y1 """ + """Return the difference between the two vectors y2 and y1""" return self.y2 - self.y1 def plot(self): """Plot a Logistic, Cubic or Sine map with two different seeds (two plots) - followed by their difference """ + followed by their difference""" self.getxy() plt.figure(1) - plt.suptitle('Dynamic Systems and Chaos', - fontsize=14, fontweight='bold') + plt.suptitle("Dynamic Systems and Chaos", fontsize=14, fontweight="bold") plt.subplot(211) - plt.title('Time series for a ' + self.map_longname + \ - ' with two different initial conditions') - plt.ylabel(r'$y_1(t),\ y_2(t)$', fontsize=14) + plt.title( + "Time series for a " + + self.map_longname + + " with two different initial conditions" + ) + plt.ylabel(r"$y_1(t),\ y_2(t)$", fontsize=14) plt.ylim([self.map_ymin, self.map_ymax]) plt.grid(True) - self._plotline(self.x[self.s:], self.y1[self.s:], 'indianred') - self._plotline(self.x[self.s:], self.y2[self.s:], 'mediumseagreen') + self._plotline(self.x[self.s :], self.y1[self.s :], "indianred") + self._plotline(self.x[self.s :], self.y2[self.s :], "mediumseagreen") ydiff = self.y2 - self.y1 plt.subplot(212) - plt.title('Difference between the two time series') - plt.xlabel('time t') - plt.ylabel(r'$y_2(t) - y_1(t)$', fontsize=14) + plt.title("Difference between the two time series") + plt.xlabel("time t") + plt.ylabel(r"$y_2(t) - y_1(t)$", fontsize=14) plt.grid(True) - self._plotline(self.x[self.s:], ydiff[self.s:], 'royalblue') + self._plotline(self.x[self.s :], ydiff[self.s :], "royalblue") plt.show() class Bifurcation(Map): - """Class for plotting a Logistic/Cubic/Sine Bifurcation Diagram """ - def __init__(self, r, y, n=100, s=200, mapname='logistic'): + """Class for plotting a Logistic/Cubic/Sine Bifurcation Diagram""" + + def __init__(self, r, y, n=100, s=200, mapname="logistic"): Map.__init__(self, mapname) - self.ensure(len(r) == 2, 'The growth rate vector should contains two elements') - self.ensure(r[0] >= self.map_rmin and r[0] < r[1] and r[1] <= self.map_rmax, - ('The parameters [r0, r1] must be between %g and %g, ' - 'and in ascending order.'), self.map_rmin, self.map_rmax) - self.ensure(len(y) == 2, 'The y range vector should contains two elements') - self.ensure(y[0] >= self.map_ymin and y[0] < y[1] and y[1] <= self.map_ymax, - ('The parameters [y0, y1] must be between %g and %g, ' - 'and in ascending order.'), self.map_ymin, self.map_ymax) - - self.rmin = r[0] # Range of the growth rate for plot() + self.ensure(len(r) == 2, "The growth rate vector should contains two elements") + self.ensure( + r[0] >= self.map_rmin and r[0] < r[1] and r[1] <= self.map_rmax, + ( + "The parameters [r0, r1] must be between %g and %g, " + "and in ascending order." + ), + self.map_rmin, + self.map_rmax, + ) + self.ensure(len(y) == 2, "The y range vector should contains two elements") + self.ensure( + y[0] >= self.map_ymin and y[0] < y[1] and y[1] <= self.map_ymax, + ( + "The parameters [y0, y1] must be between %g and %g, " + "and in ascending order." + ), + self.map_ymin, + self.map_ymax, + ) + + self.rmin = r[0] # Range of the growth rate for plot() self.rmax = r[1] - self.ymin = y[0] # Range of the population for plot() + self.ymin = y[0] # Range of the population for plot() self.ymax = y[1] - self.ensure(n > 0, 'The number of iterations must be greater than zero.') - self.n = n # Number of iterations - - self.ensure(s >= 0, 'You cannot skip a negative number of iterations.') - self.s = s # Number of iterations to skip in the plot + self.ensure(n > 0, "The number of iterations must be greater than zero.") + self.n = n # Number of iterations + + self.ensure(s >= 0, "You cannot skip a negative number of iterations.") + self.s = s # Number of iterations to skip in the plot def plot(self): - plt.suptitle('Dynamic Systems and Chaos', fontsize=14, fontweight='bold') - plt.title('Bifurcation Diagram for the ' + self.map_longname) + plt.suptitle("Dynamic Systems and Chaos", fontsize=14, fontweight="bold") + plt.title("Bifurcation Diagram for the " + self.map_longname) plt.xlim([self.rmin, self.rmax]) plt.xticks([round(i, 1) for i in np.linspace(self.rmin, self.rmax, 5)]) - plt.xlabel('r') + plt.xlabel("r") plt.ylim([self.ymin, self.ymax]) - plt.ylabel('final states') + plt.ylabel("final states") for r in np.linspace(self.rmin, self.rmax, 1000): - x, y = FinalState(r, self.n, .5, self.s, self.map_name).getxy(r) - plt.plot(y[self.s:], x[self.s:], color='black', linestyle='', - markerfacecolor='black', marker=',', markersize=1) + x, y = FinalState(r, self.n, 0.5, self.s, self.map_name).getxy(r) + plt.plot( + y[self.s :], + x[self.s :], + color="black", + linestyle="", + markerfacecolor="black", + marker=",", + markersize=1, + ) plt.show() -if __name__ == '__main__': +if __name__ == "__main__": from lelib_test import tests + tests() print("All tests successfully passed!")
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/finalstate.py#L7
import sys from lelib import FinalState from utils import argparser, die + def parse_args(): - """This function parses and return arguments passed in """ - descr = 'Plot of the Final State Diagram' - examples = ''' + """This function parses and return arguments passed in""" + descr = "Plot of the Final State Diagram" + examples = """ %(prog)s -r 3.492 %(prog)s -r 3.614 -s 200 -n 300 %(prog)s -0 0.4 -r 3.2 -s 10 -n 50 - %(prog)s -0 0.8 -r 6.2 -n 20 --map=cubic''' + %(prog)s -0 0.8 -r 6.2 -n 20 --map=cubic""" parser = argparser(descr, examples) # By default, make 3000 iterations (n) and # do no plot the first 2000 ones (s) # By default select the Logistic Equation parser.add_argument( - "-0", "--x0", - action="store", dest="x0", type=float, default=.5, - help="initial condition (default: %(default)s)") + "-0", + "--x0", + action="store", + dest="x0", + type=float, + default=0.5, + help="initial condition (default: %(default)s)", + ) parser.add_argument( - "-r", "--rate", - action="store", dest="r", type=float, required=True, - help="growth rate parameter") + "-r", + "--rate", + action="store", + dest="r", + type=float, + required=True, + help="growth rate parameter", + ) parser.add_argument( - "-s", "--skip", - action="store", dest="s", type=int, default=2000, - help="skip plotting the first 's' iterations (default: %(default)s)") + "-s", + "--skip", + action="store", + dest="s", + type=int, + default=2000, + help="skip plotting the first 's' iterations (default: %(default)s)", + ) parser.add_argument( - "-n", "--steps", dest="n", type=int, default=1000, action="store", - help="number of iterations (default: %(default)s)") + "-n", + "--steps", + dest="n", + type=int, + default=1000, + action="store", + help="number of iterations (default: %(default)s)", + ) parser.add_argument( - "-m", "--map", - action="store", dest="map_name", default = "logistic", - choices = ["logistic", "cubic", "sine"], - help = "select the desired map (logistic, cubic, or sine)") + "-m", + "--map", + action="store", + dest="map_name", + default="logistic", + choices=["logistic", "cubic", "sine"], + help="select the desired map (logistic, cubic, or sine)", + ) return parser.parse_args() def main(): args = parse_args() - FinalState( - args.r, - args.n, - args.x0, - args.s, - args.map_name - ).plot() + FinalState(args.r, args.n, args.x0, args.s, args.map_name).plot() -if __name__ == '__main__': + +if __name__ == "__main__": try: main() except KeyboardInterrupt: - die(3, 'Exiting on user request') + die(3, "Exiting on user request") sys.exit()
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/bifurcations.py#L7
import sys from lelib import Bifurcation, Map from utils import argparser, die + def parse_args(): - """This function parses and return arguments passed in """ - descr = 'Plot the Bifurcation Diagram of Logistic, Cubic, and Sine Maps' - examples = ''' + """This function parses and return arguments passed in""" + descr = "Plot the Bifurcation Diagram of Logistic, Cubic, and Sine Maps" + examples = """ %(prog)s -r 1:4 %(prog)s -r 4:6.5 --map=cubic %(prog)s --map=sine -s 200 -n 200 %(prog)s -r 3.:4. -s 500 -n 600 - %(prog)s -r 3.5:3.6 -y .3:.6 -s 800 -n 1000''' + %(prog)s -r 3.5:3.6 -y .3:.6 -s 800 -n 1000""" parser = argparser(descr, examples) # By default, make 300 iterations (n) and do no plot the first 200 ones (s) # By default select the Logistic Equation parser.add_argument( - "-r", "--rate", - action="store", dest="r", - help="range of the growth rate parameter (default: the entire range)") + "-r", + "--rate", + action="store", + dest="r", + help="range of the growth rate parameter (default: the entire range)", + ) parser.add_argument( - "-y", "--people", - action="store", dest="y", - help="normalized range of the population (default: the entire range)") + "-y", + "--people", + action="store", + dest="y", + help="normalized range of the population (default: the entire range)", + ) parser.add_argument( - "-s", "--skip", - action="store", dest="s", type=int, default=200, - help="skip plotting the first 's' iterations (default: %(default)s)") + "-s", + "--skip", + action="store", + dest="s", + type=int, + default=200, + help="skip plotting the first 's' iterations (default: %(default)s)", + ) parser.add_argument( - "-n", "--steps", - action="store", dest="n", type=int, default=100, - help="number of iterations (default: %(default)s)") + "-n", + "--steps", + action="store", + dest="n", + type=int, + default=100, + help="number of iterations (default: %(default)s)", + ) parser.add_argument( - "-m", "--map", - action="store", dest="map_name", default="logistic", + "-m", + "--map", + action="store", + dest="map_name", + default="logistic", choices=["logistic", "cubic", "sine"], - help="select the desired map (logistic, cubic, or sine)") + help="select the desired map (logistic, cubic, or sine)", + ) return parser.parse_args() def main(): args = parse_args() mapobj = Map(args.map_name) # range to vector: "1:4" --> [1., 4.] - r2v = (lambda a, minval, maxval : - [float(i) for i in a.split(':')] if a else - [minval, maxval]) + r2v = ( + lambda a, minval, maxval: [float(i) for i in a.split(":")] + if a + else [minval, maxval] + ) # Plot the entire diagram by default Bifurcation( r2v(args.r, mapobj.map_rmin, mapobj.map_rmax), r2v(args.y, mapobj.map_ymin, mapobj.map_ymax), args.n, args.s, - args.map_name + args.map_name, ).plot() -if __name__ == '__main__': + +if __name__ == "__main__": try: main() except KeyboardInterrupt: - die(3, 'Exiting on user request') + die(3, "Exiting on user request") sys.exit()
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/legraph.py#L7
import sys from lelib import Logistic, LogisticDiff from utils import argparser, die + def parse_args(): - """This function parses and return arguments passed in """ - descr = 'Plot of Logistic Equation Time Series' - examples = ''' + """This function parses and return arguments passed in""" + descr = "Plot of Logistic Equation Time Series" + examples = """ # time series with a stable fixed point %(prog)s -0 0.4 -r 3.2 -n 50 %(prog)s -0 0.4 -1 0.45 -r 3.2 -n 50 # chaotic results (randon output) %(prog)s --x0 0.2 --x1 0.2000001 -r 4.0 -n 50 %(prog)s -0 0.2 -r 3.6 -n 5000 --dots-only %(prog)s -0 0.9 -r 4.5 -n 50 --map=cubic - %(prog)s -0 0.4 -r 0.8 -n 50 --map=sine''' + %(prog)s -0 0.4 -r 0.8 -n 50 --map=sine""" parser = argparser(descr, examples) # By default select the Logistic Equation parser.add_argument( - "-0", "--x0", - action="store", dest="x0", type=float, required=True, - help="1st initial condition") + "-0", + "--x0", + action="store", + dest="x0", + type=float, + required=True, + help="1st initial condition", + ) parser.add_argument( - "-1", "--x1", - action="store", dest="x1", type=float, - help="2nd initial condition (optional)") + "-1", + "--x1", + action="store", + dest="x1", + type=float, + help="2nd initial condition (optional)", + ) parser.add_argument( - "-d", "--dots-only", - action="store_true", dest="dotsonly", - help="do not connect the dots with lines (default: %(default)s)") + "-d", + "--dots-only", + action="store_true", + dest="dotsonly", + help="do not connect the dots with lines (default: %(default)s)", + ) parser.add_argument( - "-r", "--rate", - action="store", dest="r", type=float, required=True, - help="growth rate parameter") + "-r", + "--rate", + action="store", + dest="r", + type=float, + required=True, + help="growth rate parameter", + ) parser.add_argument( - "-s", "--skip", - action="store", dest="s", type=int, default=0, - help="skip plotting the first 's' iterations") + "-s", + "--skip", + action="store", + dest="s", + type=int, + default=0, + help="skip plotting the first 's' iterations", + ) parser.add_argument( - "-n", "--steps", - action="store", dest="n", type=int, required=True, - help="number of iterations") + "-n", + "--steps", + action="store", + dest="n", + type=int, + required=True, + help="number of iterations", + ) parser.add_argument( - "-m", "--map", - action="store", dest="map_name", default="logistic", - choices = ["logistic", "cubic", "sine"], - help = "select the desired map (logistic, cubic, or sine)") + "-m", + "--map", + action="store", + dest="map_name", + default="logistic", + choices=["logistic", "cubic", "sine"], + help="select the desired map (logistic, cubic, or sine)", + ) return parser.parse_args() + def main(): args = parse_args() lemap = ( - LogisticDiff( - args.r, args.n, args.x0, args.x1, args.s, args.map_name) - if args.x1 else Logistic( - args.r, args.n, args.x0, args.s, args.map_name)) + LogisticDiff(args.r, args.n, args.x0, args.x1, args.s, args.map_name) + if args.x1 + else Logistic(args.r, args.n, args.x0, args.s, args.map_name) + ) lemap.plotdots = not args.dotsonly lemap.plot() -if __name__ == '__main__': + +if __name__ == "__main__": try: main() except KeyboardInterrupt: - die(3, 'Exiting on user request') + die(3, "Exiting on user request") sys.exit()
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/lelib_test.py#L7
from __future__ import print_function import numpy as np from lelib import Map, Logistic, LogisticDiff + def test_class_map(): - """Test the class 'Map' """ + """Test the class 'Map'""" print("Running the tests for the class 'Map'...") m = Map() - m.ensure(m.map_name == 'logistic', "The default map should be 'logistic'") - m.ensure(m.map_longname == 'Logistic Equation', - "Logistic Map: bad long name") - m.ensure(m.map_rmin == 0 and m.map_rmax == 4, - "Logistic Map: bad range for r") - m.ensure(m.map_ymin == 0 and m.map_ymax == 1, - "Logistic Map: bad range for y") - i = m.map(4,.25) - m.ensure(i == .75, - "Logistic Map: bad value for r=4 and x=.25: should be %f" % i) + m.ensure(m.map_name == "logistic", "The default map should be 'logistic'") + m.ensure(m.map_longname == "Logistic Equation", "Logistic Map: bad long name") + m.ensure(m.map_rmin == 0 and m.map_rmax == 4, "Logistic Map: bad range for r") + m.ensure(m.map_ymin == 0 and m.map_ymax == 1, "Logistic Map: bad range for y") + i = m.map(4, 0.25) + m.ensure(i == 0.75, "Logistic Map: bad value for r=4 and x=.25: should be %f" % i) - m = Map('cubic') - m.ensure(m.map_name == 'cubic', "Cubic Map: bad map selection") - m.ensure(m.map_longname == 'Cubic Equation', "Cubic Map: bad long name") - m.ensure(m.map_rmin == 0 and m.map_rmax == 6.5, - "Cubic Map: bad range for r") - m.ensure(m.map_ymin == 0 and m.map_ymax == 1, - "Cubic Map: bad range for y") - i = m.map(2,.5) - m.ensure(i == .25, - "Cubic Map: bad value for r=2 and x=.5, should be %f" % i) + m = Map("cubic") + m.ensure(m.map_name == "cubic", "Cubic Map: bad map selection") + m.ensure(m.map_longname == "Cubic Equation", "Cubic Map: bad long name") + m.ensure(m.map_rmin == 0 and m.map_rmax == 6.5, "Cubic Map: bad range for r") + m.ensure(m.map_ymin == 0 and m.map_ymax == 1, "Cubic Map: bad range for y") + i = m.map(2, 0.5) + m.ensure(i == 0.25, "Cubic Map: bad value for r=2 and x=.5, should be %f" % i) - m = Map('sine') - m.ensure(m.map_name == 'sine', "Sine Map: bad map selection") - m.ensure(m.map_longname == 'Sine Equation', "Sine Map: bad long name") + m = Map("sine") + m.ensure(m.map_name == "sine", "Sine Map: bad map selection") + m.ensure(m.map_longname == "Sine Equation", "Sine Map: bad long name") m.ensure(m.map_rmin == 0 and m.map_rmax == 2, "Sine Map: bad range for r") m.ensure(m.map_ymin == 0 and m.map_ymax == 2, "Sine Map: bad range for y") - i = m.map(0.5,1) - m.ensure(i == .5, - "Sine Map: bad value for r=0.5 and x=1: should be %f" % i) + i = m.map(0.5, 1) + m.ensure(i == 0.5, "Sine Map: bad value for r=0.5 and x=1: should be %f" % i) + def test_class_logistic(): - """Test the class 'Logistic' """ + """Test the class 'Logistic'""" print("Running the tests for the class 'Logistic'...") r, n, x0 = 3.2, 100, 0.4 - le1 = Logistic(r, n, x0, False, 'logistic') + le1 = Logistic(r, n, x0, False, "logistic") x, y1 = le1.getxy() m = Map() - m.ensure(len(x) == n+1, "x should be a vector of size %d" % (n+1)) + m.ensure(len(x) == n + 1, "x should be a vector of size %d" % (n + 1)) m.ensure(x[0] == 0, "x[0] should be 0") m.ensure(x[n] == n, "the last element of x should be equal to %d" % n) - m.ensure(x.sum() == n*(n+1)/2, - "the sum of the elements of x is not correct") + m.ensure(x.sum() == n * (n + 1) / 2, "the sum of the elements of x is not correct") - m.ensure(len(y1) == n+1, "y1 should be a vector of size %d" % (n+1)) + m.ensure(len(y1) == n + 1, "y1 should be a vector of size %d" % (n + 1)) m.ensure(y1[0] == x0, "the first element of y1 should be equal to x0") - m.ensure(y1[n] == y1[n-2], "y1 is expected to be periodic with period 2") - m.ensure(y1[n-1] == y1[n-3], "y1 is expected to be periodic with period 2") + m.ensure(y1[n] == y1[n - 2], "y1 is expected to be periodic with period 2") + m.ensure(y1[n - 1] == y1[n - 3], "y1 is expected to be periodic with period 2") + def test_class_logisticdiff(): - """Test the class 'LogisticDiff' """ + """Test the class 'LogisticDiff'""" print("Running the tests for the class 'LogisticDiff'...") r, n, x0, x1 = 4.0, 50, 0.2, 0.2000001 - le2 = LogisticDiff(r, n, x0, x1, False, 'logistic') + le2 = LogisticDiff(r, n, x0, x1, False, "logistic") x, y1, _ = le2.getxy() m = Map() - m.ensure(len(x) == n+1, "x should be a vector of size %d" % (n+1)) + m.ensure(len(x) == n + 1, "x should be a vector of size %d" % (n + 1)) m.ensure(x[0] == 0, "x[0] should be 0") m.ensure(x[n] == n, "the last element of x should be equal to %d" % n) - m.ensure(x.sum() == n*(n+1)/2, - "the sum of the elements of x is not correct") + m.ensure(x.sum() == n * (n + 1) / 2, "the sum of the elements of x is not correct") - m.ensure(len(y1) == n+1, "y1 should be a vector of size %d" % (n+1)) + m.ensure(len(y1) == n + 1, "y1 should be a vector of size %d" % (n + 1)) m.ensure(y1[0] == x0, "the first element of y1 should be equal to x0") ydiff = le2.getdiffy() - m.ensure(len(ydiff) == n+1, - "the vector y2-y1 should have a size equal to %d" % (n+1)) - m.ensure(np.all(ydiff < 1e3) and np.all(ydiff > -1e3), - "the diff vector should show the Butterfly Effect") + m.ensure( + len(ydiff) == n + 1, "the vector y2-y1 should have a size equal to %d" % (n + 1) + ) + m.ensure( + np.all(ydiff < 1e3) and np.all(ydiff > -1e3), + "the diff vector should show the Butterfly Effect", + ) + def tests(): test_class_map() test_class_logistic() test_class_logisticdiff()
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/utils.py#L12
__status__ = "stable" import argparse import textwrap + def argparser(descr, examples): - """Return a new ArgumentParser object """ + """Return a new ArgumentParser object""" return argparse.ArgumentParser( - formatter_class = argparse.RawDescriptionHelpFormatter, - description = copyleft(descr), - epilog = "Examples:\n" + textwrap.dedent(examples)) + formatter_class=argparse.RawDescriptionHelpFormatter, + description=copyleft(descr), + epilog="Examples:\n" + textwrap.dedent(examples), + ) + def copyleft(descr): - """Print the Copyright message and License """ + """Print the Copyright message and License""" - return ("{0} v.{1} ({2})\n{3} <{4}>\nLicense: {5}" - .format( - descr, __version__, __status__, - __copyright__, __email__, - __license__)) + return "{0} v.{1} ({2})\n{3} <{4}>\nLicense: {5}".format( + descr, __version__, __status__, __copyright__, __email__, __license__ + ) + def die(exitcode, message): - """Print and error message and exit with 'exitcode' """ + """Print and error message and exit with 'exitcode'""" progname = sys.argv[0] - sys.stderr.write('%s: error: %s\n' % (progname, message)) + sys.stderr.write("%s: error: %s\n" % (progname, message)) sys.exit(exitcode)
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/setup.py#L12
Logistic, Cubic, or Sine Map with two different starting points (along with the plot of the differences between the two maps), and the Final State and Bifurcation Diagrams. """ -DOCLINES = DESCRIPTION.split('\n') +DOCLINES = DESCRIPTION.split("\n") CLASSIFIERS = """\ Development Status :: 5 - Production/Stable Intended Audience :: Education Intended Audience :: Science/Research
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/setup.py#L29
Operating System :: POSIX Operating System :: Unix Operating System :: MacOS """ -VERSION = '2' +VERSION = "2" setup( - name = 'dynamic-systems-and-chaos', - description = DOCLINES[0], - long_description = DESCRIPTION, - version = VERSION, - url = 'https://github.com/madrisan/dynamic-systems-and-chaos/', - - author = 'Davide Madrisan', - author_email = 'davide.madrisan@gmail.com', - - license = 'Apache License 2.0', - packages = ['dynamic-systems-and-chaos'], - - scripts = [ - 'dynamic-systems-and-chaos/bifurcations.py', - 'dynamic-systems-and-chaos/finalstate.py', - 'dynamic-systems-and-chaos/legraph.py', + name="dynamic-systems-and-chaos", + description=DOCLINES[0], + long_description=DESCRIPTION, + version=VERSION, + url="https://github.com/madrisan/dynamic-systems-and-chaos/", + author="Davide Madrisan", + author_email="davide.madrisan@gmail.com", + license="Apache License 2.0", + packages=["dynamic-systems-and-chaos"], + scripts=[ + "dynamic-systems-and-chaos/bifurcations.py", + "dynamic-systems-and-chaos/finalstate.py", + "dynamic-systems-and-chaos/legraph.py", ], - - classifiers = [ _f for _f in CLASSIFIERS.split('\n') if _f ], - - install_requires = [ - 'numpy', - 'matplotlib', + classifiers=[_f for _f in CLASSIFIERS.split("\n") if _f], + install_requires=[ + "numpy", + "matplotlib", ], - - platforms = ["Linux", "Mac OS-X", "Windows"], + platforms=["Linux", "Mac OS-X", "Windows"], )
/home/runner/work/dynamic-systems-and-chaos/dynamic-systems-and-chaos/dynamic-systems-and-chaos/lelib.py#L15
import numpy as np import matplotlib.pyplot as plt from math import pi, sin + class Map(object): - """Class that provides the map functions along with r and y ranges """ - - def __init__(self, mapname='logistic'): + """Class that provides the map functions along with r and y ranges""" + + def __init__(self, mapname="logistic"): params = { - # rmin rmax ymin ymax function - 'cubic' : [ 0, 6.5, 0, 1, lambda r, x: r * x**2 * (1.0 - x) ], - 'logistic': [ 0, 4.0, 0, 1, lambda r, x: r * x * (1.0 - x) ], - 'sine' : [ 0, 2.0, 0, 2, lambda r, x: r * sin(pi * x / 2.0) ] + # rmin rmax ymin ymax function + "cubic": [0, 6.5, 0, 1, lambda r, x: r * x**2 * (1.0 - x)], + "logistic": [0, 4.0, 0, 1, lambda r, x: r * x * (1.0 - x)], + "sine": [0, 2.0, 0, 2, lambda r, x: r * sin(pi * x / 2.0)], } self.map_name = mapname self.map_longname = "%s Equation" % mapname.capitalize() try: - self.map_rmin, self.map_rmax, \ - self.map_ymin, self.map_ymax, \ - self.map_function = params[mapname] + ( + self.map_rmin, + self.map_rmax, + self.map_ymin, + self.map_ymax, + self.map_function, + ) = params[mapname] self.map = self._mapper except Exception as e: - raise type(e)('Unknown map name ' + mapname) + raise type(e)("Unknown map name " + mapname) @staticmethod def ensure(expression, message, *argv): if not expression: raise AssertionError(message % (argv) if argv else message) def _mapper(self, r, x): self.ensure( (r >= self.map_rmin and r <= self.map_rmax), - 'The growth parameter r must be between %g and %g', - self.map_rmin, self.map_rmax) + "The growth parameter r must be between %g and %g", + self.map_rmin, + self.map_rmax, + ) return self.map_function(r, x) + class Logistic(Map): - """Class for plotting a Logistic/Cubic/Sine Map """ - - def __init__(self, r, n, x0, s=0, mapname='logistic'): + """Class for plotting a Logistic/Cubic/Sine Map""" + + def __init__(self, r, n, x0, s=0, mapname="logistic"): Map.__init__(self, mapname) - self.r = r # Growth rate parameter - self.n = n # Number of iterations - self.s = s # Number of iterations to skip in the plot + self.r = r # Growth rate parameter + self.n = n # Number of iterations + self.s = s # Number of iterations to skip in the plot self.x0 = x0 # The 1st initial condition self.x = self.y1 = [] self._dotsonly = False - self.ensure(n > 0, 'The number of iterations must be greater than zero.') - self.ensure(s >= 0, 'You cannot skip a negative number of iterations.') - self.ensure(x0 >= self.map_ymin and x0 <= self.map_ymax, - 'The initial condition x0 should be in [%g, %g].', - self.map_ymin, self.map_ymax) + self.ensure(n > 0, "The number of iterations must be greater than zero.") + self.ensure(s >= 0, "You cannot skip a negative number of iterations.") + self.ensure( + x0 >= self.map_ymin and x0 <= self.map_ymax, + "The initial condition x0 should be in [%g, %g].", + self.map_ymin, + self.map_ymax, + ) def _plotline(self, x, y, color): """Plot the dots (x, y) connected by straight lines - if the parameter 'dotsonly' if set to False """ - - self.ensure(x.any() and y.any(), '_plotline(): internal error') - plt.plot(x, y, color=color, linestyle='', - markerfacecolor=color, marker='o', markersize=5) + if the parameter 'dotsonly' if set to False""" + + self.ensure(x.any() and y.any(), "_plotline(): internal error") + plt.plot( + x, + y, + color=color, + linestyle="", + markerfacecolor=color, + marker="o", + markersize=5, + ) if self.plotdots: plt.plot(x, y, color=color, alpha=0.6) def getxy(self, fill_value=None): """Set the numpy vectors 'x' and 'y1' containing - the iterations (1..n) and the corresponding values - of the choosen Map """ + the iterations (1..n) and the corresponding values + of the choosen Map""" # do not initialize twice the x and y1 vectors - if len(self.x) > 0: return + if len(self.x) > 0: + return vectlen = self.n + self.s + 1 self.x = np.arange(vectlen) - self.y1 = np.arange(0, vectlen, 1.) + self.y1 = np.arange(0, vectlen, 1.0) self.y1[0] = self.x0 for t in self.x[1:]: - self.y1[t] = self.map(self.r, self.y1[t-1]) + self.y1[t] = self.map(self.r, self.y1[t - 1]) return self.x, self.y1 def plot(self): - """Plot a Logistic, Cubic or Sine map """ + """Plot a Logistic, Cubic or Sine map""" self.getxy() - plt.suptitle('Dynamic Systems and Chaos', fontsize=14, fontweight='bold') + plt.suptitle("Dynamic Systems and Chaos", fontsize=14, fontweight="bold") plt.title(self.map_longname) - plt.xlabel('time t') + plt.xlabel("time t") plt.ylim([self.map_ymin, self.map_ymax]) plt.grid(True) - self._plotline(self.x[self.s:], self.y1[self.s:], 'mediumseagreen') + self._plotline(self.x[self.s :], self.y1[self.s :], "mediumseagreen") plt.show() @Property def plotdots(self): return self._dotsonly @plotdots.setter def plotdots(self, value): - """Set whether to plot or not the dots in a logistic graph """ + """Set whether to plot or not the dots in a logistic graph""" self._dotsonly = value class FinalState(Logistic): - """Derived class for plotting a Final State Diagram """ + """Derived class for plotting a Final State Diagram""" # By default, set the initial state to .5 # make 3000 iterations and do no plot the first 2000 ones - def __init__(self, r, n=1000, x0=.5, s=2000, mapname='logistic'): + def __init__(self, r, n=1000, x0=0.5, s=2000, mapname="logistic"): Logistic.__init__(self, r, n, x0, s, mapname) - def getxy(self, fill_value=.5): + def getxy(self, fill_value=0.5): """Set the numpy vectors 'x' and 'y1' containing the values of the - choosen Map for the first n iterations """ + choosen Map for the first n iterations""" # do not initialize twice the x and y1 vectors - if len(self.x) > 0: return + if len(self.x) > 0: + return vectlen = self.n + self.s + 1 self.x = np.full(vectlen, self.x0, dtype=np.float64) for t in range(1, vectlen): - self.x[t] = self.map(self.r, self.x[t-1]) + self.x[t] = self.map(self.r, self.x[t - 1]) self.y1 = np.full(vectlen, fill_value, dtype=np.float) return self.x, self.y1 def plot(self): - """Plot a Final State Diagram """ + """Plot a Final State Diagram""" self.getxy() - plt.suptitle('Dynamic Systems and Chaos', fontsize=14, fontweight='bold') - plt.title('Final State Diagram for the ' + self.map_longname) + plt.suptitle("Dynamic Systems and Chaos", fontsize=14, fontweight="bold") + plt.title("Final State Diagram for the " + self.map_longname) plt.xlim([self.map_ymin, self.map_ymax]) - plt.ylim([0, 1.]) + plt.ylim([0, 1.0]) plt.yticks([]) plt.grid(True) - plt.plot([self.map_ymin, self.map_ymax], [.5, .5], - color='black', lw=1) - plt.plot(self.x[self.s:], self.y1[self.s:], color='black', linestyle='', - markerfacecolor='black', marker='o', markersize=8) - plt.text(.1 * self.map_ymax, .4, 'r = %g' % self.r, style='italic', - bbox={'facecolor':'red', 'alpha':0.5, 'pad':10}) + plt.plot([self.map_ymin, self.map_ymax], [0.5, 0.5], color="black", lw=1) + plt.plot( + self.x[self.s :], + self.y1[self.s :], + color="black", + linestyle="", + markerfacecolor="black", + marker="o", + markersize=8, + ) + plt.text( + 0.1 * self.map_ymax, + 0.4, + "r = %g" % self.r, + style="italic", + bbox={"facecolor": "red", "alpha": 0.5, "pad": 10}, + ) plt.show() class LogisticDiff(Logistic): """Derived class for plotting a Logistic/Cubic/Sine Map - with two different initial conditions, followed by a plot of - their differences (for a visualization of the Butterfly Effect) """ - - def __init__(self, r, n, x0, x1, s=0, mapname='logistic'): + with two different initial conditions, followed by a plot of + their differences (for a visualization of the Butterfly Effect)""" + + def __init__(self, r, n, x0, x1, s=0, mapname="logistic"): Logistic.__init__(self, r, n, x0, s, mapname) - self.ensure(x1 >= self.map_ymin and x1 <= self.map_ymax, - 'The initial condition x1 should be in [%g, %g].', - self.map_ymin, self.map_ymax) + self.ensure( + x1 >= self.map_ymin and x1 <= self.map_ymax, + "The initial condition x1 should be in [%g, %g].", + self.map_ymin, + self.map_ymax, + ) self.x1 = x1 # The 2st initial condition self.y2 = [] def getxy(self, fill_value=None): """Set the numpy vectors 'x', 'y1', and 'y2' containing - the iterations (1..n) and the corresponding values - of the choosen Map """ + the iterations (1..n) and the corresponding values + of the choosen Map""" x, y1 = super(LogisticDiff, self).getxy() # do not initialize twice the vector y2 - if len(self.y2) > 0: return - - self.y2 = np.arange(0, self.n + self.s + 1, 1.) + if len(self.y2) > 0: + return + + self.y2 = np.arange(0, self.n + self.s + 1, 1.0) self.y2[0] = self.x1 for t in self.x[1:]: - self.y2[t] = self.map(self.r, self.y2[t-1]) + self.y2[t] = self.map(self.r, self.y2[t - 1]) return x, y1, self.y2 def getdiffy(self): - """Return the difference between the two vectors y2 and y1 """ + """Return the difference between the two vectors y2 and y1""" return self.y2 - self.y1 def plot(self): """Plot a Logistic, Cubic or Sine map with two different seeds (two plots) - followed by their difference """ + followed by their difference""" self.getxy() plt.figure(1) - plt.suptitle('Dynamic Systems and Chaos', - fontsize=14, fontweight='bold') + plt.suptitle("Dynamic Systems and Chaos", fontsize=14, fontweight="bold") plt.subplot(211) - plt.title('Time series for a ' + self.map_longname + \ - ' with two different initial conditions') - plt.ylabel(r'$y_1(t),\ y_2(t)$', fontsize=14) + plt.title( + "Time series for a " + + self.map_longname + + " with two different initial conditions" + ) + plt.ylabel(r"$y_1(t),\ y_2(t)$", fontsize=14) plt.ylim([self.map_ymin, self.map_ymax]) plt.grid(True) - self._plotline(self.x[self.s:], self.y1[self.s:], 'indianred') - self._plotline(self.x[self.s:], self.y2[self.s:], 'mediumseagreen') + self._plotline(self.x[self.s :], self.y1[self.s :], "indianred") + self._plotline(self.x[self.s :], self.y2[self.s :], "mediumseagreen") ydiff = self.y2 - self.y1 plt.subplot(212) - plt.title('Difference between the two time series') - plt.xlabel('time t') - plt.ylabel(r'$y_2(t) - y_1(t)$', fontsize=14) + plt.title("Difference between the two time series") + plt.xlabel("time t") + plt.ylabel(r"$y_2(t) - y_1(t)$", fontsize=14) plt.grid(True) - self._plotline(self.x[self.s:], ydiff[self.s:], 'royalblue') + self._plotline(self.x[self.s :], ydiff[self.s :], "royalblue") plt.show() class Bifurcation(Map): - """Class for plotting a Logistic/Cubic/Sine Bifurcation Diagram """ - def __init__(self, r, y, n=100, s=200, mapname='logistic'): + """Class for plotting a Logistic/Cubic/Sine Bifurcation Diagram""" + + def __init__(self, r, y, n=100, s=200, mapname="logistic"): Map.__init__(self, mapname) - self.ensure(len(r) == 2, 'The growth rate vector should contains two elements') - self.ensure(r[0] >= self.map_rmin and r[0] < r[1] and r[1] <= self.map_rmax, - ('The parameters [r0, r1] must be between %g and %g, ' - 'and in ascending order.'), self.map_rmin, self.map_rmax) - self.ensure(len(y) == 2, 'The y range vector should contains two elements') - self.ensure(y[0] >= self.map_ymin and y[0] < y[1] and y[1] <= self.map_ymax, - ('The parameters [y0, y1] must be between %g and %g, ' - 'and in ascending order.'), self.map_ymin, self.map_ymax) - - self.rmin = r[0] # Range of the growth rate for plot() + self.ensure(len(r) == 2, "The growth rate vector should contains two elements") + self.ensure( + r[0] >= self.map_rmin and r[0] < r[1] and r[1] <= self.map_rmax, + ( + "The parameters [r0, r1] must be between %g and %g, " + "and in ascending order." + ), + self.map_rmin, + self.map_rmax, + ) + self.ensure(len(y) == 2, "The y range vector should contains two elements") + self.ensure( + y[0] >= self.map_ymin and y[0] < y[1] and y[1] <= self.map_ymax, + ( + "The parameters [y0, y1] must be between %g and %g, " + "and in ascending order." + ), + self.map_ymin, + self.map_ymax, + ) + + self.rmin = r[0] # Range of the growth rate for plot() self.rmax = r[1] - self.ymin = y[0] # Range of the population for plot() + self.ymin = y[0] # Range of the population for plot() self.ymax = y[1] - self.ensure(n > 0, 'The number of iterations must be greater than zero.') - self.n = n # Number of iterations - - self.ensure(s >= 0, 'You cannot skip a negative number of iterations.') - self.s = s # Number of iterations to skip in the plot + self.ensure(n > 0, "The number of iterations must be greater than zero.") + self.n = n # Number of iterations + + self.ensure(s >= 0, "You cannot skip a negative number of iterations.") + self.s = s # Number of iterations to skip in the plot def plot(self): - plt.suptitle('Dynamic Systems and Chaos', fontsize=14, fontweight='bold') - plt.title('Bifurcation Diagram for the ' + self.map_longname) + plt.suptitle("Dynamic Systems and Chaos", fontsize=14, fontweight="bold") + plt.title("Bifurcation Diagram for the " + self.map_longname) plt.xlim([self.rmin, self.rmax]) plt.xticks([round(i, 1) for i in np.linspace(self.rmin, self.rmax, 5)]) - plt.xlabel('r') + plt.xlabel("r") plt.ylim([self.ymin, self.ymax]) - plt.ylabel('final states') + plt.ylabel("final states") for r in np.linspace(self.rmin, self.rmax, 1000): - x, y = FinalState(r, self.n, .5, self.s, self.map_name).getxy(r) - plt.plot(y[self.s:], x[self.s:], color='black', linestyle='', - markerfacecolor='black', marker=',', markersize=1) + x, y = FinalState(r, self.n, 0.5, self.s, self.map_name).getxy(r) + plt.plot( + y[self.s :], + x[self.s :], + color="black", + linestyle="", + markerfacecolor="black", + marker=",", + markersize=1, + ) plt.show() -if __name__ == '__main__': +if __name__ == "__main__": from lelib_test import tests + tests() print("All tests successfully passed!")
Run linters
The following actions uses node12 which is deprecated and will be forced to run on node16: actions/checkout@v2, actions/setup-python@v1. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/