142 lines
4.6 KiB
Python
Executable File
142 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import plotly.express as px
|
|
from pandas import DataFrame
|
|
|
|
# df = pd.DataFrame(data=dict(x=[1, 2, 3, 4], y=[1, 4, 9, 16]))
|
|
# fig = px.line(df, x="x", y="y", title="test")
|
|
# fig.show()
|
|
|
|
class Line:
|
|
def __init__(self, from_, to):
|
|
self.from_ = from_
|
|
self.to = to
|
|
|
|
def slope(self) -> float:
|
|
return (self.to[1] - self.from_[1]) / (self.to[0] - self.from_[0])
|
|
|
|
def get(self, x: float) -> float:
|
|
from_x, from_y = self.from_
|
|
to_x, to_y = self.to
|
|
tween = (x - from_x) / (to_x - from_x)
|
|
return tween * to_y + (1-tween) * from_y
|
|
|
|
|
|
class Piecewise:
|
|
def __init__(self, xy: list):
|
|
""" xy is a list of (x, y) pairs """
|
|
self.xy = list(xy)
|
|
|
|
def normalized(self, prev_max: float) -> 'Piecewise':
|
|
""" map every coordinate from [-prev_max, prev_max] to [0, 1] """
|
|
p = prev_max
|
|
r = 2*prev_max
|
|
s = 1.0/r
|
|
return Piecewise([
|
|
(s*(x + p), s*(y + p)) for (x, y) in self.xy
|
|
])
|
|
|
|
def logically_inverted(self) -> 'Piecewise':
|
|
""" return a Piecewise that evaluates to 1-y """
|
|
return Piecewise([
|
|
(x, 1-y) for (x, y) in self.xy
|
|
])
|
|
|
|
def line_for(self, x: float) -> Line:
|
|
for first_lower in self.xy[:-1][::-1]:
|
|
if first_lower[0] < x: break
|
|
for first_upper in self.xy[1:]:
|
|
if first_upper[0] > x: break
|
|
|
|
return Line(first_lower, first_upper)
|
|
|
|
def get(self, x: float) -> float:
|
|
"""
|
|
evaluate the piecewise function at the provided x value.
|
|
OOB points are just extrapolated from the nearest piece.
|
|
"""
|
|
return self.line_for(x).get(x)
|
|
|
|
def get_slope(self, x: float) -> float:
|
|
return self.line_for(x).slope()
|
|
|
|
def get_repeated(self, x: float, n: int = 63) -> float:
|
|
for _ in range(n):
|
|
x = self.get(x)
|
|
return x
|
|
|
|
def df_for(self, from_: float, to: float, points: int, f) -> DataFrame:
|
|
x_step = (to - from_) / (points - 1)
|
|
x = [from_ + x_step*x for x in range(points)]
|
|
y = [f(xi) for xi in x]
|
|
return DataFrame(data=dict(x=x, y=y))
|
|
|
|
def df(self, from_: float = 0.0, to: float = 1.0, points: int = 101) -> DataFrame:
|
|
return self.df_for(from_, to, points, self.get)
|
|
|
|
def slope_df(self, from_: float = 0.0, to: float = 1.0, points: int = 101) -> DataFrame:
|
|
return self.df_for(from_, to, points, self.get_slope)
|
|
|
|
def plot_for(self, from_: float, to: float, title: str, f):
|
|
df = self.df_for(from_, to, points=101, f=f)
|
|
fig = px.line(df, x="x", y="y", title=title)
|
|
fig.show()
|
|
|
|
def plot(self, from_: float = 0.0, to: float = 1.0, title: str = "Piecewise"):
|
|
self.plot_for(from_, to, title, self.get)
|
|
|
|
def plot_slope(self, from_: float = 0.0, to: float = 1.0, title: str = "Piecewise"):
|
|
self.plot_for(from_, to, title, self.get_slope)
|
|
|
|
def plot_equilibrium(self, from_: float = 0.0, to: float = 1.0, title: str = "Piecewise"):
|
|
self.plot_for(from_, to, title, self.get_repeated)
|
|
|
|
|
|
fwd_26 = Piecewise(
|
|
[
|
|
[ -14687, -7326 ],
|
|
[ -13049, -6503 ],
|
|
[ -11785, -5833 ],
|
|
[ -4649, -1447 ],
|
|
[ 4961, 7059 ],
|
|
[ 11283, 11147 ],
|
|
]
|
|
).normalized(17000)
|
|
|
|
# fwd_26.plot(title = "26 forward")
|
|
# fwd_26.logically_inverted().plot(title = "26 inverted")
|
|
# fwd_26.plot_slope(title = "26 slope")
|
|
# fwd_26.logically_inverted().plot_equilibrium(title = "26 equilibrium")
|
|
|
|
|
|
fwd_38_2_0 = Piecewise(
|
|
[
|
|
[ (-13745 + -13012)/2, -6222 ],
|
|
[ (-4969 + -4744)/2, 2373 ],
|
|
[ (1772 + 2070)/2, 10467 ],
|
|
[ (4472 + 4114)/2, 12921 ],
|
|
[ (7221 + 6291)/2, 14530 ],
|
|
[ (11159 + 10397)/2, 15865 ],
|
|
[ (12430 + 15653)/2, 16202 ],
|
|
]
|
|
).normalized(17000)
|
|
fwd_38_2_0.plot(title = "38 2:0 forward")
|
|
fwd_38_2_0.logically_inverted().plot(title = "38 2:0 inverted")
|
|
fwd_38_2_0.plot_slope(title = "38 2:0 slope")
|
|
fwd_38_2_0.logically_inverted().plot_equilibrium(title = "38 2:0 equilibrium")
|
|
|
|
fwd_38_3_0 = Piecewise(
|
|
[
|
|
[ (-13956 + -13890 + -13077)/3, -5203],
|
|
[ (-4979 + -4885 + -4717)/3, 5051],
|
|
[ (1531 + 503 + 1006)/3, 12509],
|
|
[ (4180 + 1821 + 2239)/3, 14386],
|
|
[ (6986 + 3436 + 3701)/3, 15451],
|
|
[ (10482 + 6644 + 7735)/3, 16081],
|
|
[ (11436 + 13343 + 14411)/3, 16380],
|
|
]
|
|
).normalized(17000)
|
|
fwd_38_3_0.plot(title = "38 3:0 forward")
|
|
fwd_38_3_0.logically_inverted().plot(title = "38 3:0 inverted")
|
|
fwd_38_3_0.plot_slope(title = "38 3:0 slope")
|
|
fwd_38_3_0.logically_inverted().plot_equilibrium(title = "38 3:0 equilibrium")
|