|
| 1 | +#=============================================================================== |
| 2 | +# File: BoltzmannPowerFit.py |
| 3 | +# Language: Python 3 |
| 4 | +# Aims: |
| 5 | +# Create a control panel. |
| 6 | +# Plot the target Boltzmann curve. |
| 7 | +# Plot the fit. |
| 8 | +# Plot the first power Boltzmann curve. |
| 9 | + |
| 10 | +#=============================================================================== |
| 11 | +# Figure |
| 12 | + |
| 13 | +# Import modules |
| 14 | +from matplotlib import * |
| 15 | +from pylab import * # Required for figure |
| 16 | + |
| 17 | +# Create a figure |
| 18 | +fig = figure("Curves", figsize = (4.5, 3), dpi = 72) |
| 19 | + |
| 20 | +# Show the figure |
| 21 | +plt.show() |
| 22 | + |
| 23 | +#=============================================================================== |
| 24 | +# Define Fit class |
| 25 | + |
| 26 | +# Import modules |
| 27 | +import numpy as np |
| 28 | +from scipy.optimize import curve_fit |
| 29 | + |
| 30 | +# Start definition |
| 31 | +class Fit: |
| 32 | + |
| 33 | + # Constructor |
| 34 | + def __init__(self): |
| 35 | + self.x0 = 0.0 |
| 36 | + self.k = -10.0 |
| 37 | + self.power = 1 |
| 38 | + self.x = range(-100, 101) |
| 39 | + self.y = [] |
| 40 | + self.y2 = [] |
| 41 | + |
| 42 | + # Fit x, y to func with initial parameters in p |
| 43 | + def fit(self, x, y, p): |
| 44 | + # Fit curve |
| 45 | + popt, pcov = curve_fit(self.func, x, y, p0 = p) |
| 46 | + |
| 47 | + # Generate the fit curve |
| 48 | + self.x0 = popt[0] |
| 49 | + self.k = popt[1] |
| 50 | + self.y = self.func(self.x, self.x0, self.k) |
| 51 | + |
| 52 | + # Generate first power Boltzmann curve |
| 53 | + power = self.power |
| 54 | + self.power = 1 |
| 55 | + self.y2 = self.func(self.x, self.x0, self.k) |
| 56 | + self.power = power |
| 57 | + |
| 58 | + # Target function |
| 59 | + def func(self, x, x0, k): |
| 60 | + return (1 / (1 + np.exp((x - x0) / k)))**self.power |
| 61 | + |
| 62 | +# Instantiation |
| 63 | +fit = Fit() |
| 64 | + |
| 65 | +#=============================================================================== |
| 66 | +# Define Boltzmann class to hold the target curve |
| 67 | +class Boltzmann: |
| 68 | + # Constructor |
| 69 | + def __init__(self, x0 = 0.0, k = -10.0): |
| 70 | + self.x0 = x0 |
| 71 | + self.k = k |
| 72 | + self.x = range(-100, 101) |
| 73 | + self.y = [] |
| 74 | + |
| 75 | + # Set x and y coordinates |
| 76 | + def set(self): |
| 77 | + # self.y = [self.func(i, self.x0, self.k) for i in self.x] |
| 78 | + self.y = [self.func(i, self.x0, self.k) for i in self.x] |
| 79 | + |
| 80 | + # Boltzmann function |
| 81 | + def func(self, x, x0, k): |
| 82 | + return 1.0 / (1.0 + np.exp((x - x0) / k)) |
| 83 | + |
| 84 | +# Instantiation |
| 85 | +bm = Boltzmann() |
| 86 | + |
| 87 | +#=============================================================================== |
| 88 | +# Import tkinter |
| 89 | +from tkinter import * |
| 90 | + |
| 91 | +#=============================================================================== |
| 92 | +# Define the ControlPanel class |
| 93 | +class ControlPanel: |
| 94 | + # Constructor |
| 95 | + # master is the parent widget, which is the root widget here. |
| 96 | + def __init__(self, master): |
| 97 | + # Create a frame |
| 98 | + frameMain = Frame(master, width = 100, height = 100, padx = 5, pady = 5) |
| 99 | + frameMain.grid() |
| 100 | + rowNumber = 0 |
| 101 | + |
| 102 | + #=================== |
| 103 | + # Target Curve |
| 104 | + rowNumber += 1 |
| 105 | + # Create a label |
| 106 | + Label(frameMain, text = "Target Curve").grid(row = rowNumber, |
| 107 | + column = 1, |
| 108 | + columnspan = 2) |
| 109 | + |
| 110 | + #=================== |
| 111 | + # Vhalf |
| 112 | + rowNumber += 1 |
| 113 | + # Create a label |
| 114 | + Label(frameMain, text = "Vhalf").grid(row = rowNumber, column = 1) |
| 115 | + |
| 116 | + # Create a vhalf entry |
| 117 | + self.etyVhalf = Entry(frameMain) |
| 118 | + self.etyVhalf.insert(0, "0") |
| 119 | + self.etyVhalf.grid(row = rowNumber, column = 2) |
| 120 | + |
| 121 | + #=================== |
| 122 | + # k |
| 123 | + rowNumber += 1 |
| 124 | + # Create a label |
| 125 | + Label(frameMain, text = "k").grid(row = rowNumber, column = 1) |
| 126 | + |
| 127 | + # Create a k entry |
| 128 | + self.etyK = Entry(frameMain) |
| 129 | + self.etyK.insert(0, "-10") |
| 130 | + self.etyK.grid(row = rowNumber, column = 2) |
| 131 | + |
| 132 | + #=================== |
| 133 | + # Boltzmann Curve |
| 134 | + rowNumber += 1 |
| 135 | + # Create a label |
| 136 | + Label(frameMain, text = "Fit Curve: Boltzmann to the X power").grid( |
| 137 | + row = rowNumber, |
| 138 | + column = 1, |
| 139 | + columnspan = 2) |
| 140 | + |
| 141 | + #======================================================================= |
| 142 | + # Power |
| 143 | + rowNumber += 2 |
| 144 | + # Create a label |
| 145 | + Label(frameMain, text = "Power (X: 1~4)").grid( |
| 146 | + row = rowNumber, |
| 147 | + column = 1) |
| 148 | + |
| 149 | + # Create a power entry |
| 150 | + self.etyPower = Entry(frameMain) |
| 151 | + self.etyPower.insert(0, "1") |
| 152 | + self.etyPower.grid(row = rowNumber, column = 2) |
| 153 | + |
| 154 | + #=================== |
| 155 | + # Vhalf |
| 156 | + rowNumber += 1 |
| 157 | + # Create a label |
| 158 | + Label(frameMain, text = "Vhalf").grid(row = rowNumber, column = 1) |
| 159 | + |
| 160 | + # Create a vhalf entry |
| 161 | + self.etyVhalf2 = Entry(frameMain) |
| 162 | + self.etyVhalf2.grid(row = rowNumber, column = 2) |
| 163 | + |
| 164 | + #=================== |
| 165 | + # k |
| 166 | + rowNumber += 1 |
| 167 | + # Create a label |
| 168 | + Label(frameMain, text = "k").grid(row = rowNumber, column = 1) |
| 169 | + |
| 170 | + # Create a k entry |
| 171 | + self.etyK2 = Entry(frameMain) |
| 172 | + self.etyK2.grid(row = rowNumber, column = 2) |
| 173 | + |
| 174 | + #=================== |
| 175 | + # Plot |
| 176 | + rowNumber += 1 |
| 177 | + # Create a plot button |
| 178 | + btnPlot = Button(frameMain, |
| 179 | + text = "Plot", |
| 180 | + command = self.cbPlot, |
| 181 | + width = 5) |
| 182 | + btnPlot.grid(row = rowNumber, column = 1) |
| 183 | + |
| 184 | + #=================== |
| 185 | + # Quit |
| 186 | + rowNumber += 1 |
| 187 | + # Create a "Quit" button |
| 188 | + btnQuit = Button(frameMain, |
| 189 | + text = "Quit", |
| 190 | + command = frameMain.quit, |
| 191 | + width = 5) |
| 192 | + btnQuit.grid(row = rowNumber, column = 1) |
| 193 | + |
| 194 | + # Methods |
| 195 | + def cbPlot(self): |
| 196 | + fig.clear() |
| 197 | + |
| 198 | + # x0 |
| 199 | + x0 = float(self.etyVhalf.get()) |
| 200 | + bm.x0 = x0 |
| 201 | + |
| 202 | + # k |
| 203 | + k = float(self.etyK.get()) |
| 204 | + if(abs(k) < 1): |
| 205 | + if(k > 0): |
| 206 | + k = 1 |
| 207 | + else: |
| 208 | + k = -1 |
| 209 | + self.etyK.delete(0, END) |
| 210 | + self.etyK.insert(0, str(k)) |
| 211 | + bm.k = k |
| 212 | + |
| 213 | + # Power |
| 214 | + power = int(self.etyPower.get()) |
| 215 | + if(power < 1 or power > 4): |
| 216 | + power = 1 |
| 217 | + self.etyPower.delete(0, END) |
| 218 | + self.etyPower.insert(0, str(power)) |
| 219 | + print("Power is set to 1.") |
| 220 | + |
| 221 | + fit.power = power |
| 222 | + |
| 223 | + # Set x and y |
| 224 | + bm.set() |
| 225 | + |
| 226 | + # Fit |
| 227 | + fit.fit(bm.x, bm.y, [bm.x0, bm.k]) |
| 228 | + |
| 229 | + # Plot |
| 230 | + plot(bm.x, bm.y, "b-", linewidth = 1) |
| 231 | + plot(fit.x, fit.y, "r--", linewidth = 2) |
| 232 | + plot(fit.x, fit.y2, "g:", linewidth = 2) |
| 233 | + axis([-100, 100, -0.1, 1.1]) |
| 234 | + legend(["Target", "Fit", "Boltzmann"], loc = "best", frameon = False, |
| 235 | + labelspacing = 0) |
| 236 | + |
| 237 | + # Update GUI |
| 238 | + self.etyVhalf2.delete(0, END) |
| 239 | + self.etyVhalf2.insert(0, str(fit.x0)) |
| 240 | + self.etyK2.delete(0, END) |
| 241 | + self.etyK2.insert(0, str(fit.k)) |
| 242 | + |
| 243 | + # Print information |
| 244 | + print("") |
| 245 | + print("=== Result ===") |
| 246 | + print("Power = ", fit.power) |
| 247 | + print("Vhalf = ", fit.x0) |
| 248 | + print("k = ", fit.k) |
| 249 | + print("") |
| 250 | + |
| 251 | + |
| 252 | +#=============================================================================== |
| 253 | +# Create the root widget |
| 254 | +rootWidget = Tk() |
| 255 | +rootWidget.title("Curve Fitting") |
| 256 | +rootWidget.geometry("+500+10") |
| 257 | + |
| 258 | +#=============================================================================== |
| 259 | +# Instantiate App class |
| 260 | +controlPanel = ControlPanel(rootWidget) |
| 261 | + |
| 262 | +#=============================================================================== |
| 263 | +# Enter the event loop |
| 264 | +rootWidget.mainloop() |
| 265 | + |
| 266 | +#=============================================================================== |
0 commit comments