Skip to content

Cobweb

Cobweb

A class used to represent a Cobweb plot and a time series to study the convergence of a map.

Methods

  • plot : Creates two figures, one containing the Cobweb plot and other with the time series.
  • add_slider : Adds a slider which can change the value of a parameter in execution time.
  • initial_position_slider : Adds a slider for changing initial value on a cobweb plot.
Source code in phaseportrait/Cobweb.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
class Cobweb:
    """
    A class used to represent a Cobweb plot and a time series to study the convergence of a map.

    Methods
    -------
    * plot : Creates two figures, one containing the Cobweb plot and other with the time series.
    * add_slider : Adds a slider which can change the value of a parameter in execution time.
    * initial_position_slider : Adds a slider for changing initial value on a cobweb plot.
    """
    _name_ = 'Cobweb'
    def __init__(self, dF, initial_position, xrange, *, dF_args={}, yrange=[], max_steps=100, n_points=10000, **kargs):
        """Cobweb

        Args: 
            dF (function) : Map function, which returns value given a point and some parameters.
            initial_position (float) : Initial position for iterating the map.
            xrange (float or list) : Range of representation on x axis.
            dF_args (dict, optional) : Dictionary with parameters for `dF` function.
            yrange (float or list, optional) : Range of representation on y axis.
            max_steps (int, optional) : Number of iterations of the map.
            n_points (int, optional) : Number of points to plot the map.
            xlabel (str, optional) : x axis label in the plot. Default value is `r'$X_{n}$'`
            ylabel (str, optional) : y axis label in the plot. Default value is `r'$X_{n+1}$'`
            Title (str, optional) : title of the plot. Default value is `'Cobweb plot'`.
        """
        self.dF = dF
        self.dF_args = dF_args.copy()
        self.initial_position = initial_position 
        self.xrange = xrange

        self.yrange = yrange
        self.max_steps = max_steps
        self.n_points = n_points

        self.Title = kargs['Title'] if kargs.get('Title') else 'Cobweb plot'
        self.xlabel = kargs['xlabel'] if kargs.get('xlabel') else r'$X_n$'
        self.ylabel = kargs['ylabel'] if kargs.get('ylabel') else r'$X_{n+1}$'

        figCobweb, axCobweb = plt.subplots()
        figTimeSeries, axTimeSeries = plt.subplots()

        self.fig = {
            'Cobweb': figCobweb,
            'TimeSeries': figTimeSeries
        }
        self.ax = {
            'Cobweb': axCobweb,
            'TimeSeries': axTimeSeries
        }

        self.sliders = {}
        self.sliders_fig = False



    def _prepare_plot(self, min_value, max_value):
        """
        Internally used method. Sets titles and axis for Cobweb and Time Series plots.

        Args
            min_value (float): Minimum value of the function in the interval.
            max_value (float): Maximum value of the function in the interval.
        """
        self.ax['Cobweb'].set_title(self.Title)
        self.ax['Cobweb'].set_xlabel(self.xlabel)
        self.ax['Cobweb'].set_ylabel(self.ylabel)

        if self.yrange==[]:
            self.ax['Cobweb'].set_ylim(bottom= 1.10*min_value,top=1.10*max_value)
        else:
            self.ax['Cobweb'].set_ylim(self.yrange)

        self.ax['Cobweb'].grid()

        self.ax['TimeSeries'].set_title('Time Series')
        self.ax['TimeSeries'].set_ylabel(r'$x_t$')
        self.ax['TimeSeries'].set_xlabel('t')
        self.ax['TimeSeries'].set_ylim(self.xrange)
        self.ax['TimeSeries'].grid()


    def plot(self, *args, **kargs):
        """
        Prepares the plots, compute the values and plots them.

        Returns:
            (tuple[matplotlib Figure (Cobweb plot), matplotlib Axis (Cobweb plot), matplotlib Figure (Time series), matplotlib Axis (Time series)]):
        """
        bisector = np.linspace(self.xrange[0], self.xrange[1], self.n_points)
        func_result = self.dF(bisector, **self.dF_args)

        xTimeSeries = []
        yTimeSeries = []

        self._prepare_plot(np.min(func_result), np.max(func_result))

        self.ax['Cobweb'].plot(bisector, func_result, 'b')
        self.ax['Cobweb'].plot(bisector, bisector, ":", color='grey')

        x, y = self.initial_position, self.dF(self.initial_position, **self.dF_args)
        self.ax['Cobweb'].plot([x, x], [0, y], ':')
        self.ax['Cobweb'].scatter(x , 0, color='green')

        xTimeSeries.append(0)
        yTimeSeries.append(x)

        for i in range(self.max_steps):

            self.ax['Cobweb'].plot([x, y], [y, y], 'k:')
            self.ax['Cobweb'].plot([y, y], [y, self.dF(y, **self.dF_args)], 'k:')

            x, y = y, self.dF(y, **self.dF_args)

            xTimeSeries.append(i)
            yTimeSeries.append(x)

            if y>self.xrange[1] or y<self.xrange[0]:
                print(f'Warning: cobweb plot got out of range and could not compute {self.max_steps} steps.')
                break

        self.ax['TimeSeries'].scatter(xTimeSeries , yTimeSeries, color='black', s=10)
        self.ax['TimeSeries'].plot(xTimeSeries , yTimeSeries, ':', color='grey')

        self.fig['Cobweb'].canvas.draw_idle()
        self.fig['TimeSeries'].canvas.draw_idle()

        return self.fig['Cobweb'], self.ax['TimeSeries'], self.fig['TimeSeries'], self.ax['TimeSeries']

    def _create_sliders_plot(self):
        """
        Internally used method. Checks if there is already a sliders plot. If not, it creates it.
        """
        if not isinstance(self.sliders_fig, plt.Figure):
            self.sliders_fig, self.sliders_ax = plt.subplots() 
            self.sliders_ax.set_visible(False)


    def add_slider(self, param_name, *, valinit=None, valstep=0.1, valinterval=10):
        """
        Adds a slider on an existing plot.

        Args: 
            param_name (str) : The string key of the variable. Must be the same as the key in the `dF` function.
            valinit (float) : Initial value of the parameter.
            valinterval (Union[float, list]) : The range of values the slider of the parameter will cover.
            valstep (float) : Precision in the slider.
        """
        self._create_sliders_plot()

        self.sliders.update({param_name: sliders.Slider(self, param_name, valinit=valinit, valstep=valstep, valinterval=valinterval)})

        self.sliders[param_name].slider.on_changed(self.sliders[param_name])


    def update_dF_args(self):
        """
        Internally used method. It is used for setting the new values of dF_args and also for initial position.

        It is meant to be called on `call` method in Slider class.
        """
        for name, slider in self.sliders.items():
            if slider.value!= None and name!=r'$x_0$':
                self.dF_args[name] = slider.value 

        if self.sliders.get(r'$x_0$'):
            self.initial_position = self.sliders[r'$x_0$'].value

    def initial_position_slider(self, *, valinit=None, valstep=0.05, valinterval=None):
        """
        Adds a slider for changing initial value on a cobweb plot.

        Args: 
            valinit (float, optional) : Initial position. Default value is the same as initial position given when initializing Cobweb object.
            valinterval (Union[float, list]) : The range of values the slider of the parameter will cover.
            valstep (float) : Precision in the slider.
        """
        if valinit is None:
            valinit = self.initial_position

        if valinterval is None:
            valinterval = list(self.xrange)

        self.add_slider(r'$x_0$', valinit=valinit, valstep=valstep, valinterval=valinterval)


    @property
    def dF(self):
        return self._dF

    @dF.setter
    def dF(self, func):
        if not callable(func):
            raise exceptions.dFNotCallable(func)
        try:
            sig = signature(func)
        except ValueError:
            pass
        self._dF = func

    @property
    def xrange(self):
        return self._xrange

    @xrange.setter
    def xrange(self, value):
        if value == None:
            self._xrange = None
            return
        self._xrange = np.array(utils.construct_interval(value, dim=1))

    @property
    def yrange(self):
        return self._yrange

    @yrange.setter
    def yrange(self, value):
        if value == []:
            self._yrange = []
            return
        self._yrange = np.array(utils.construct_interval(value, dim=1))

    @property
    def dF_args(self):
        return self._dF_args

    @dF_args.setter
    def dF_args(self, value):
        if value:
            if not isinstance(value, dict):
                raise exceptions.dF_argsInvalid(value)
        self._dF_args = value

__init__(dF, initial_position, xrange, *, dF_args={}, yrange=[], max_steps=100, n_points=10000, **kargs)

Cobweb

Parameters:

Name Type Description Default
dF function)

Map function, which returns value given a point and some parameters.

required
initial_position float)

Initial position for iterating the map.

required
xrange float or list)

Range of representation on x axis.

required
dF_args dict, optional)

Dictionary with parameters for dF function.

{}
yrange float or list, optional)

Range of representation on y axis.

[]
max_steps int, optional)

Number of iterations of the map.

100
n_points int, optional)

Number of points to plot the map.

10000
xlabel str, optional)

x axis label in the plot. Default value is r'$X_{n}$'

required
ylabel str, optional)

y axis label in the plot. Default value is r'$X_{n+1}$'

required
Title str, optional)

title of the plot. Default value is 'Cobweb plot'.

required
Source code in phaseportrait/Cobweb.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def __init__(self, dF, initial_position, xrange, *, dF_args={}, yrange=[], max_steps=100, n_points=10000, **kargs):
    """Cobweb

    Args: 
        dF (function) : Map function, which returns value given a point and some parameters.
        initial_position (float) : Initial position for iterating the map.
        xrange (float or list) : Range of representation on x axis.
        dF_args (dict, optional) : Dictionary with parameters for `dF` function.
        yrange (float or list, optional) : Range of representation on y axis.
        max_steps (int, optional) : Number of iterations of the map.
        n_points (int, optional) : Number of points to plot the map.
        xlabel (str, optional) : x axis label in the plot. Default value is `r'$X_{n}$'`
        ylabel (str, optional) : y axis label in the plot. Default value is `r'$X_{n+1}$'`
        Title (str, optional) : title of the plot. Default value is `'Cobweb plot'`.
    """
    self.dF = dF
    self.dF_args = dF_args.copy()
    self.initial_position = initial_position 
    self.xrange = xrange

    self.yrange = yrange
    self.max_steps = max_steps
    self.n_points = n_points

    self.Title = kargs['Title'] if kargs.get('Title') else 'Cobweb plot'
    self.xlabel = kargs['xlabel'] if kargs.get('xlabel') else r'$X_n$'
    self.ylabel = kargs['ylabel'] if kargs.get('ylabel') else r'$X_{n+1}$'

    figCobweb, axCobweb = plt.subplots()
    figTimeSeries, axTimeSeries = plt.subplots()

    self.fig = {
        'Cobweb': figCobweb,
        'TimeSeries': figTimeSeries
    }
    self.ax = {
        'Cobweb': axCobweb,
        'TimeSeries': axTimeSeries
    }

    self.sliders = {}
    self.sliders_fig = False

add_slider(param_name, *, valinit=None, valstep=0.1, valinterval=10)

Adds a slider on an existing plot.

Parameters:

Name Type Description Default
param_name str)

The string key of the variable. Must be the same as the key in the dF function.

required
valinit float)

Initial value of the parameter.

None
valinterval Union[float, list])

The range of values the slider of the parameter will cover.

10
valstep float)

Precision in the slider.

0.1
Source code in phaseportrait/Cobweb.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def add_slider(self, param_name, *, valinit=None, valstep=0.1, valinterval=10):
    """
    Adds a slider on an existing plot.

    Args: 
        param_name (str) : The string key of the variable. Must be the same as the key in the `dF` function.
        valinit (float) : Initial value of the parameter.
        valinterval (Union[float, list]) : The range of values the slider of the parameter will cover.
        valstep (float) : Precision in the slider.
    """
    self._create_sliders_plot()

    self.sliders.update({param_name: sliders.Slider(self, param_name, valinit=valinit, valstep=valstep, valinterval=valinterval)})

    self.sliders[param_name].slider.on_changed(self.sliders[param_name])

initial_position_slider(*, valinit=None, valstep=0.05, valinterval=None)

Adds a slider for changing initial value on a cobweb plot.

Parameters:

Name Type Description Default
valinit float, optional)

Initial position. Default value is the same as initial position given when initializing Cobweb object.

None
valinterval Union[float, list])

The range of values the slider of the parameter will cover.

None
valstep float)

Precision in the slider.

0.05
Source code in phaseportrait/Cobweb.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
def initial_position_slider(self, *, valinit=None, valstep=0.05, valinterval=None):
    """
    Adds a slider for changing initial value on a cobweb plot.

    Args: 
        valinit (float, optional) : Initial position. Default value is the same as initial position given when initializing Cobweb object.
        valinterval (Union[float, list]) : The range of values the slider of the parameter will cover.
        valstep (float) : Precision in the slider.
    """
    if valinit is None:
        valinit = self.initial_position

    if valinterval is None:
        valinterval = list(self.xrange)

    self.add_slider(r'$x_0$', valinit=valinit, valstep=valstep, valinterval=valinterval)

plot(*args, **kargs)

Prepares the plots, compute the values and plots them.

Returns:

Type Description
tuple[matplotlib Figure (Cobweb plot), matplotlib Axis (Cobweb plot), matplotlib Figure (Time series), matplotlib Axis (Time series)]
Source code in phaseportrait/Cobweb.py
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def plot(self, *args, **kargs):
    """
    Prepares the plots, compute the values and plots them.

    Returns:
        (tuple[matplotlib Figure (Cobweb plot), matplotlib Axis (Cobweb plot), matplotlib Figure (Time series), matplotlib Axis (Time series)]):
    """
    bisector = np.linspace(self.xrange[0], self.xrange[1], self.n_points)
    func_result = self.dF(bisector, **self.dF_args)

    xTimeSeries = []
    yTimeSeries = []

    self._prepare_plot(np.min(func_result), np.max(func_result))

    self.ax['Cobweb'].plot(bisector, func_result, 'b')
    self.ax['Cobweb'].plot(bisector, bisector, ":", color='grey')

    x, y = self.initial_position, self.dF(self.initial_position, **self.dF_args)
    self.ax['Cobweb'].plot([x, x], [0, y], ':')
    self.ax['Cobweb'].scatter(x , 0, color='green')

    xTimeSeries.append(0)
    yTimeSeries.append(x)

    for i in range(self.max_steps):

        self.ax['Cobweb'].plot([x, y], [y, y], 'k:')
        self.ax['Cobweb'].plot([y, y], [y, self.dF(y, **self.dF_args)], 'k:')

        x, y = y, self.dF(y, **self.dF_args)

        xTimeSeries.append(i)
        yTimeSeries.append(x)

        if y>self.xrange[1] or y<self.xrange[0]:
            print(f'Warning: cobweb plot got out of range and could not compute {self.max_steps} steps.')
            break

    self.ax['TimeSeries'].scatter(xTimeSeries , yTimeSeries, color='black', s=10)
    self.ax['TimeSeries'].plot(xTimeSeries , yTimeSeries, ':', color='grey')

    self.fig['Cobweb'].canvas.draw_idle()
    self.fig['TimeSeries'].canvas.draw_idle()

    return self.fig['Cobweb'], self.ax['TimeSeries'], self.fig['TimeSeries'], self.ax['TimeSeries']

update_dF_args()

Internally used method. It is used for setting the new values of dF_args and also for initial position.

It is meant to be called on call method in Slider class.

Source code in phaseportrait/Cobweb.py
166
167
168
169
170
171
172
173
174
175
176
177
def update_dF_args(self):
    """
    Internally used method. It is used for setting the new values of dF_args and also for initial position.

    It is meant to be called on `call` method in Slider class.
    """
    for name, slider in self.sliders.items():
        if slider.value!= None and name!=r'$x_0$':
            self.dF_args[name] = slider.value 

    if self.sliders.get(r'$x_0$'):
        self.initial_position = self.sliders[r'$x_0$'].value