cosmopharm 0.0.26__py3-none-any.whl → 0.0.27__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,246 +1,246 @@
1
- '''
2
- A selection of rate functions, i.e., speed curves for animations.
3
- (1) https://docs.manim.community/en/stable/reference/manim.utils.rate_functions.html
4
- (2) https://easings.net/
5
- '''
6
- # Indicate which objects should be imported with "from spacing import *"
7
- __all__ = ["spacing"]
8
-
9
- import numpy as np
10
-
11
- # =============================================================================
12
- # Define Spacing Functions
13
- # =============================================================================
14
- def easeOutExpo(x, base=2, power=-10):
15
- return 1 - base**(power * x)
16
-
17
- def easeOutPoly(x, n=1.5):
18
- return 1 - (1 - x)**n
19
-
20
- def sigmoid(x: float) -> float:
21
- r"""Returns the output of the logistic function.
22
-
23
- The logistic function, a common example of a sigmoid function, is defined
24
- as :math:`\frac{1}{1 + e^{-x}}`.
25
-
26
- References
27
- ----------
28
- - https://en.wikipedia.org/wiki/Sigmoid_function
29
- - https://en.wikipedia.org/wiki/Logistic_function
30
- """
31
- return 1.0 / (1 + np.exp(-x))
32
-
33
- def smooth(t: float, inflection: float = 10.0) -> float:
34
- error = sigmoid(-inflection / 2)
35
- err = (sigmoid(inflection * (t - 0.5)) - error) / (1 - 2 * error)
36
- return np.minimum(np.maximum(err, 0), 1)
37
-
38
- funcs = {
39
- 'circle' : lambda x, n=2: (1 - (x - 1)**n)**(1/n),
40
- 'easeOutExpo' : easeOutExpo,
41
- 'poly' : easeOutPoly,
42
- 'root' : lambda x, n=2: x**(1/n),
43
- 'linear' : lambda x: easeOutPoly(x, n=1),
44
- 'quadratic' : lambda x: easeOutPoly(x, n=2),
45
- 'cubic' : lambda x: easeOutPoly(x, n=3),
46
- # 'sigmoid' : smooth,
47
- }
48
-
49
-
50
- def spacing(mi, ma, num, func_name='linear', reverse=False, log_scale=False, **kwargs):
51
- if log_scale:
52
- # mi, ma = np.log10(mi), np.log10(ma)
53
- mi, ma = 10**mi, 10**ma
54
- x = np.linspace(0, 1, num)
55
- y = evaluate_function(x, func_name, **kwargs)
56
- y = (1 - y)[::-1] if reverse else y
57
- # result = mi + (ma - mi) * y
58
- # return 10**result if log_scale else result
59
- return mi + (ma - mi) * y
60
-
61
-
62
-
63
- # =============================================================================
64
- # MAIN
65
- # =============================================================================
66
- def main():
67
- trim = False
68
-
69
- # Choose what to plot
70
- plot_bar_chart(trim=trim)
71
- # plot_specific_function('all', trim=trim)
72
- pass
73
-
74
-
75
- # =============================================================================
76
- # OPTIONS
77
- # =============================================================================
78
- def plot_bar_chart(trim=True):
79
- x, values, diffs, colors = prepare_data(trim=trim)
80
- fig, ax = plt.subplots()
81
- ax.set_ylim(0, 1)
82
- for idx, (name, dy) in enumerate(diffs.items()):
83
- bottom = 0
84
- for i, segment in enumerate(dy):
85
- bar = ax.bar(name, segment, bottom=bottom, color=colors[i])
86
- bar_width = bar[0].get_width()
87
- # if i > 0:
88
- # # Plot a dotted line connecting the corresponding segments
89
- # ax.plot([idx - 1, idx], [bottom, bottom], linestyle=':', color='black')
90
- if idx > 0:
91
- # Get previous bar's corresponding segment
92
- prev_bottom = sum(list(diffs.values())[idx - 1][:i])
93
- # Plot a dotted line connecting the corresponding segments
94
- w = bar_width / 2
95
- x, y = [(idx-1)+w, idx-w], [prev_bottom, bottom]
96
- ax.plot(x,y, ls=':', c='black', lw=.8)
97
- bottom += segment
98
- # Set the tick positions and labels for the x-axis
99
- ax.set_xticks(range(len(diffs.keys())))
100
- # Set the rotation angle for the x-axis tick labels
101
- ax.set_xticklabels(ax.get_xticklabels(), rotation=60)
102
-
103
-
104
- def plot_specific_function(name, trim=True):
105
- x, values, diffs, colors = prepare_data(name, trim)
106
- for name, y in values.items():
107
- plot(x, y, name)
108
-
109
-
110
- # =============================================================================
111
- # Auxilliary functions
112
- # =============================================================================
113
- def evaluate_function(x, func_name, trim=True, **kwargs):
114
- func = funcs.get(func_name)
115
- if func is None:
116
- raise ValueError(f"Function with name '{func_name}' not found")
117
- # Call the selected function
118
- trim_funcs = ('root', 'circle')
119
- if (func_name in trim_funcs) and trim:
120
- x = np.linspace(0, 1, len(x) + 1)
121
- y = func(x, **kwargs)[1:]
122
- y = (y-min(y))/(max(y)-min(y))
123
- else:
124
- y = func(x, **kwargs)
125
- return y
126
-
127
-
128
- def prepare_data(specific_func=None, trim=True):
129
- # # Generate an array of equally spaced points between 0 and 1
130
- x = np.linspace(0, 1, 11)
131
-
132
- # Calculate function values for each function in the funcs dictionary
133
- if specific_func is None or specific_func == 'all':
134
- values = {name: evaluate_function(x, name, trim) for name in funcs.keys()}
135
- else:
136
- values = {specific_func: evaluate_function(x, specific_func, trim)}
137
-
138
- # Compute the differences between consecutive values for each function
139
- diffs = {k: np.diff(y) for k, y in values.items()}
140
-
141
- # # Get the first segment for each function
142
- # first_segments = {k: y[1] for k, y in values.items()}
143
- # # Sort the functions based on the first segment
144
- # sorted_list = sorted(diffs.items(), key=lambda item: first_segments[item[0]])
145
- # diffs = dict(sorted_list)
146
- # sorted_list = ['linear','poly','quadratic','cubic','easeOutExpo', 'circle','root']
147
- # diffs = {name: diffs[name] for name in sorted_list}
148
-
149
- # # First sort by y[1]
150
- # # sort1 = {k: y[1] for k, y in values.items()}
151
- # sort1 = {k: (y[1],y[2]) for k, y in values.items()}
152
- # sort1 = sorted(sort1.items(), key=lambda item: item[1])
153
- # # sort1 = sorted(values.items(), key=lambda item: sort1[item[0]])
154
-
155
- # for i,(k,v) in enumerate(sort1):
156
- # v_next = sort1[i+1][1][1]
157
- # v_curr = v[1]
158
- # if v_curr > v_next:
159
- # change_index = i+1
160
- # break
161
-
162
- # sort2 = sort1[change_index:]
163
- # sort2 = {k: y[2] for k, y in sort2}
164
- # sort2 = sorted(sort2.items(), key=lambda item: item[0])
165
- # sorted_list = sort1[:change_index]
166
- # # sort2 = sorted(values.items(), key=lambda item: sort2[item[0]])
167
-
168
- # Create sort1 dictionary
169
- sort1 = {k: (y[1], y[2]) for k, y in values.items()}
170
-
171
- # Sort sort1 by y[1] in ascending order
172
- sort1_keys_sorted = sorted(sort1, key=lambda k: sort1[k][0])
173
-
174
- # Find the change_index
175
- for i, k in enumerate(sort1_keys_sorted[:-1]):
176
- curr_y2 = sort1[k][1]
177
- next_y2 = sort1[sort1_keys_sorted[i + 1]][1]
178
- if curr_y2 > next_y2:
179
- change_index = i + 1
180
- break
181
-
182
- # Divide the list into two parts
183
- first_part = sort1_keys_sorted[:change_index]
184
- second_part = sort1_keys_sorted[change_index:]
185
-
186
- # Sort the second part by y[2] in descending order
187
- second_part_sorted = sorted(second_part, key=lambda k: sort1[k][1], reverse=True)
188
-
189
- # Combine the two parts to get the final order
190
- final_order = first_part + second_part_sorted
191
-
192
- # Create a new dictionary sorted according to the final order of keys
193
- sorted_values = {k: values[k] for k in final_order}
194
- diffs = {k: diffs[k] for k in sorted_values.keys()}
195
-
196
-
197
- # # Find the index where the order changes from ascending to descending
198
- # change_index = None
199
- # for i in range(1, len(sorted_list)):
200
- # if sort1[sorted_list[i][0]] < sort1[sorted_list[i - 1][0]]:
201
- # change_index = i
202
- # break
203
-
204
- # # Sort the remaining elements in descending order based on y[2]
205
- # if change_index is not None:
206
- # sort2 = {k: y[2] for k, y in values.items()}
207
- # sorted_list = (
208
- # sorted_list[:change_index]
209
- # + sorted(
210
- # sorted_list[change_index:],
211
- # key=lambda item: sort2[item[0]],
212
- # reverse=True
213
- # )
214
- # )
215
- # diffs = dict(sorted_list)
216
-
217
- # Get the default color cycle
218
- default_colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
219
- # Generate additional colors using the viridis colormap
220
- additional_colors = plt.cm.viridis(np.linspace(0, 1, len(x)))
221
- additional_colors = [matplotlib.colors.to_hex(color) for color in additional_colors]
222
- # Combine default and additional colors
223
- colors = default_colors + additional_colors
224
-
225
- # Return the prepared data
226
- return x, values, diffs, colors
227
-
228
-
229
- def plot(x,y,name):
230
- fig,ax = plt.subplots()
231
- ax.plot(x,y,'.-',label=name)
232
- for xx,yy in zip(x,y):
233
- ax.axhline(yy, xmin=0, xmax=xx, ls=':')
234
- ax.set_ylim(0,1)
235
- ax.set_xlim(0,1)
236
- ax.legend(loc='lower right')
237
- ax.set_aspect('equal', 'box')
238
-
239
-
240
- # =============================================================================
241
- # Call MAIN
242
- # =============================================================================
243
- if __name__=='__main__':
244
- import matplotlib.pyplot as plt
245
- import matplotlib.colors
246
- main()
1
+ '''
2
+ A selection of rate functions, i.e., speed curves for animations.
3
+ (1) https://docs.manim.community/en/stable/reference/manim.utils.rate_functions.html
4
+ (2) https://easings.net/
5
+ '''
6
+ # Indicate which objects should be imported with "from spacing import *"
7
+ __all__ = ["spacing"]
8
+
9
+ import numpy as np
10
+
11
+ # =============================================================================
12
+ # Define Spacing Functions
13
+ # =============================================================================
14
+ def easeOutExpo(x, base=2, power=-10):
15
+ return 1 - base**(power * x)
16
+
17
+ def easeOutPoly(x, n=1.5):
18
+ return 1 - (1 - x)**n
19
+
20
+ def sigmoid(x: float) -> float:
21
+ r"""Returns the output of the logistic function.
22
+
23
+ The logistic function, a common example of a sigmoid function, is defined
24
+ as :math:`\frac{1}{1 + e^{-x}}`.
25
+
26
+ References
27
+ ----------
28
+ - https://en.wikipedia.org/wiki/Sigmoid_function
29
+ - https://en.wikipedia.org/wiki/Logistic_function
30
+ """
31
+ return 1.0 / (1 + np.exp(-x))
32
+
33
+ def smooth(t: float, inflection: float = 10.0) -> float:
34
+ error = sigmoid(-inflection / 2)
35
+ err = (sigmoid(inflection * (t - 0.5)) - error) / (1 - 2 * error)
36
+ return np.minimum(np.maximum(err, 0), 1)
37
+
38
+ funcs = {
39
+ 'circle' : lambda x, n=2: (1 - (x - 1)**n)**(1/n),
40
+ 'easeOutExpo' : easeOutExpo,
41
+ 'poly' : easeOutPoly,
42
+ 'root' : lambda x, n=2: x**(1/n),
43
+ 'linear' : lambda x: easeOutPoly(x, n=1),
44
+ 'quadratic' : lambda x: easeOutPoly(x, n=2),
45
+ 'cubic' : lambda x: easeOutPoly(x, n=3),
46
+ # 'sigmoid' : smooth,
47
+ }
48
+
49
+
50
+ def spacing(mi, ma, num, func_name='linear', reverse=False, log_scale=False, **kwargs):
51
+ if log_scale:
52
+ # mi, ma = np.log10(mi), np.log10(ma)
53
+ mi, ma = 10**mi, 10**ma
54
+ x = np.linspace(0, 1, num)
55
+ y = evaluate_function(x, func_name, **kwargs)
56
+ y = (1 - y)[::-1] if reverse else y
57
+ # result = mi + (ma - mi) * y
58
+ # return 10**result if log_scale else result
59
+ return mi + (ma - mi) * y
60
+
61
+
62
+
63
+ # =============================================================================
64
+ # MAIN
65
+ # =============================================================================
66
+ def main():
67
+ trim = False
68
+
69
+ # Choose what to plot
70
+ plot_bar_chart(trim=trim)
71
+ # plot_specific_function('all', trim=trim)
72
+ pass
73
+
74
+
75
+ # =============================================================================
76
+ # OPTIONS
77
+ # =============================================================================
78
+ def plot_bar_chart(trim=True):
79
+ x, values, diffs, colors = prepare_data(trim=trim)
80
+ fig, ax = plt.subplots()
81
+ ax.set_ylim(0, 1)
82
+ for idx, (name, dy) in enumerate(diffs.items()):
83
+ bottom = 0
84
+ for i, segment in enumerate(dy):
85
+ bar = ax.bar(name, segment, bottom=bottom, color=colors[i])
86
+ bar_width = bar[0].get_width()
87
+ # if i > 0:
88
+ # # Plot a dotted line connecting the corresponding segments
89
+ # ax.plot([idx - 1, idx], [bottom, bottom], linestyle=':', color='black')
90
+ if idx > 0:
91
+ # Get previous bar's corresponding segment
92
+ prev_bottom = sum(list(diffs.values())[idx - 1][:i])
93
+ # Plot a dotted line connecting the corresponding segments
94
+ w = bar_width / 2
95
+ x, y = [(idx-1)+w, idx-w], [prev_bottom, bottom]
96
+ ax.plot(x,y, ls=':', c='black', lw=.8)
97
+ bottom += segment
98
+ # Set the tick positions and labels for the x-axis
99
+ ax.set_xticks(range(len(diffs.keys())))
100
+ # Set the rotation angle for the x-axis tick labels
101
+ ax.set_xticklabels(ax.get_xticklabels(), rotation=60)
102
+
103
+
104
+ def plot_specific_function(name, trim=True):
105
+ x, values, diffs, colors = prepare_data(name, trim)
106
+ for name, y in values.items():
107
+ plot(x, y, name)
108
+
109
+
110
+ # =============================================================================
111
+ # Auxilliary functions
112
+ # =============================================================================
113
+ def evaluate_function(x, func_name, trim=True, **kwargs):
114
+ func = funcs.get(func_name)
115
+ if func is None:
116
+ raise ValueError(f"Function with name '{func_name}' not found")
117
+ # Call the selected function
118
+ trim_funcs = ('root', 'circle')
119
+ if (func_name in trim_funcs) and trim:
120
+ x = np.linspace(0, 1, len(x) + 1)
121
+ y = func(x, **kwargs)[1:]
122
+ y = (y-min(y))/(max(y)-min(y))
123
+ else:
124
+ y = func(x, **kwargs)
125
+ return y
126
+
127
+
128
+ def prepare_data(specific_func=None, trim=True):
129
+ # # Generate an array of equally spaced points between 0 and 1
130
+ x = np.linspace(0, 1, 11)
131
+
132
+ # Calculate function values for each function in the funcs dictionary
133
+ if specific_func is None or specific_func == 'all':
134
+ values = {name: evaluate_function(x, name, trim) for name in funcs.keys()}
135
+ else:
136
+ values = {specific_func: evaluate_function(x, specific_func, trim)}
137
+
138
+ # Compute the differences between consecutive values for each function
139
+ diffs = {k: np.diff(y) for k, y in values.items()}
140
+
141
+ # # Get the first segment for each function
142
+ # first_segments = {k: y[1] for k, y in values.items()}
143
+ # # Sort the functions based on the first segment
144
+ # sorted_list = sorted(diffs.items(), key=lambda item: first_segments[item[0]])
145
+ # diffs = dict(sorted_list)
146
+ # sorted_list = ['linear','poly','quadratic','cubic','easeOutExpo', 'circle','root']
147
+ # diffs = {name: diffs[name] for name in sorted_list}
148
+
149
+ # # First sort by y[1]
150
+ # # sort1 = {k: y[1] for k, y in values.items()}
151
+ # sort1 = {k: (y[1],y[2]) for k, y in values.items()}
152
+ # sort1 = sorted(sort1.items(), key=lambda item: item[1])
153
+ # # sort1 = sorted(values.items(), key=lambda item: sort1[item[0]])
154
+
155
+ # for i,(k,v) in enumerate(sort1):
156
+ # v_next = sort1[i+1][1][1]
157
+ # v_curr = v[1]
158
+ # if v_curr > v_next:
159
+ # change_index = i+1
160
+ # break
161
+
162
+ # sort2 = sort1[change_index:]
163
+ # sort2 = {k: y[2] for k, y in sort2}
164
+ # sort2 = sorted(sort2.items(), key=lambda item: item[0])
165
+ # sorted_list = sort1[:change_index]
166
+ # # sort2 = sorted(values.items(), key=lambda item: sort2[item[0]])
167
+
168
+ # Create sort1 dictionary
169
+ sort1 = {k: (y[1], y[2]) for k, y in values.items()}
170
+
171
+ # Sort sort1 by y[1] in ascending order
172
+ sort1_keys_sorted = sorted(sort1, key=lambda k: sort1[k][0])
173
+
174
+ # Find the change_index
175
+ for i, k in enumerate(sort1_keys_sorted[:-1]):
176
+ curr_y2 = sort1[k][1]
177
+ next_y2 = sort1[sort1_keys_sorted[i + 1]][1]
178
+ if curr_y2 > next_y2:
179
+ change_index = i + 1
180
+ break
181
+
182
+ # Divide the list into two parts
183
+ first_part = sort1_keys_sorted[:change_index]
184
+ second_part = sort1_keys_sorted[change_index:]
185
+
186
+ # Sort the second part by y[2] in descending order
187
+ second_part_sorted = sorted(second_part, key=lambda k: sort1[k][1], reverse=True)
188
+
189
+ # Combine the two parts to get the final order
190
+ final_order = first_part + second_part_sorted
191
+
192
+ # Create a new dictionary sorted according to the final order of keys
193
+ sorted_values = {k: values[k] for k in final_order}
194
+ diffs = {k: diffs[k] for k in sorted_values.keys()}
195
+
196
+
197
+ # # Find the index where the order changes from ascending to descending
198
+ # change_index = None
199
+ # for i in range(1, len(sorted_list)):
200
+ # if sort1[sorted_list[i][0]] < sort1[sorted_list[i - 1][0]]:
201
+ # change_index = i
202
+ # break
203
+
204
+ # # Sort the remaining elements in descending order based on y[2]
205
+ # if change_index is not None:
206
+ # sort2 = {k: y[2] for k, y in values.items()}
207
+ # sorted_list = (
208
+ # sorted_list[:change_index]
209
+ # + sorted(
210
+ # sorted_list[change_index:],
211
+ # key=lambda item: sort2[item[0]],
212
+ # reverse=True
213
+ # )
214
+ # )
215
+ # diffs = dict(sorted_list)
216
+
217
+ # Get the default color cycle
218
+ default_colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
219
+ # Generate additional colors using the viridis colormap
220
+ additional_colors = plt.cm.viridis(np.linspace(0, 1, len(x)))
221
+ additional_colors = [matplotlib.colors.to_hex(color) for color in additional_colors]
222
+ # Combine default and additional colors
223
+ colors = default_colors + additional_colors
224
+
225
+ # Return the prepared data
226
+ return x, values, diffs, colors
227
+
228
+
229
+ def plot(x,y,name):
230
+ fig,ax = plt.subplots()
231
+ ax.plot(x,y,'.-',label=name)
232
+ for xx,yy in zip(x,y):
233
+ ax.axhline(yy, xmin=0, xmax=xx, ls=':')
234
+ ax.set_ylim(0,1)
235
+ ax.set_xlim(0,1)
236
+ ax.legend(loc='lower right')
237
+ ax.set_aspect('equal', 'box')
238
+
239
+
240
+ # =============================================================================
241
+ # Call MAIN
242
+ # =============================================================================
243
+ if __name__=='__main__':
244
+ import matplotlib.pyplot as plt
245
+ import matplotlib.colors
246
+ main()
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 Ivan Antolovic <Ivan.Antolovic@tu-berlin.de>, Martin Klajmon <Martin.Klajmon@vscht.cz>
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ivan Antolovic <Ivan.Antolovic@tu-berlin.de>, Martin Klajmon <Martin.Klajmon@vscht.cz>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.