lab5
This commit is contained in:
2
Л5-В6/Программа/.gitignore
vendored
Normal file
2
Л5-В6/Программа/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.venv
|
||||
*/__pycache__
|
||||
5
Л5-В6/Программа/requirements.txt
Normal file
5
Л5-В6/Программа/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
sympy
|
||||
numpy
|
||||
scipy
|
||||
pyqt6
|
||||
matplotlib
|
||||
45
Л5-В6/Программа/src/algorithm.py
Normal file
45
Л5-В6/Программа/src/algorithm.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import numpy as np
|
||||
import typing
|
||||
|
||||
|
||||
def runge_kutta_4(
|
||||
x_0: float,
|
||||
y_0: typing.List[float],
|
||||
x_n: float,
|
||||
h: float,
|
||||
f: typing.List[typing.Callable[[float, typing.List[float]], float]],
|
||||
) -> typing.Dict[float, np.ndarray]:
|
||||
count = len(y_0)
|
||||
|
||||
if count != len(f):
|
||||
raise ValueError("len(y_0) != len(f)")
|
||||
|
||||
x_i = x_0
|
||||
y_i = np.array(y_0, dtype=np.float64)
|
||||
result = {x_i: y_i}
|
||||
|
||||
while x_i < x_n + h / 2:
|
||||
y_i = calculate_y(x_i, y_i, h, f)
|
||||
x_i += h
|
||||
|
||||
result[x_i] = y_i
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def calculate_y(x_i, y_i, h, f):
|
||||
k_1 = np.array([f[i](x_i, y_i) for i in range(len(y_i))], dtype=np.float64)
|
||||
k_2 = np.array(
|
||||
[f[i](x_i + h / 2, y_i + h * k_1 / 2) for i in range(len(y_i))],
|
||||
dtype=np.float64,
|
||||
)
|
||||
k_3 = np.array(
|
||||
[f[i](x_i + h / 2, y_i + h * k_2 / 2) for i in range(len(y_i))],
|
||||
dtype=np.float64,
|
||||
)
|
||||
k_4 = np.array(
|
||||
[f[i](x_i + h, y_i + h * k_3) for i in range(len(y_i))], dtype=np.float64
|
||||
)
|
||||
|
||||
y_i = y_i + h / 6 * (k_1 + 2 * k_2 + 2 * k_3 + k_4)
|
||||
return y_i
|
||||
17
Л5-В6/Программа/src/main.py
Normal file
17
Л5-В6/Программа/src/main.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import sys
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from PyQt6.QtGui import QFont
|
||||
|
||||
import window
|
||||
import widgets
|
||||
|
||||
if __name__ == "__main__":
|
||||
widgets.setup_dark_theme()
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
font = QFont("Cascadia Code", 14)
|
||||
app.setFont(font)
|
||||
window = window.MainWindow()
|
||||
window.show()
|
||||
|
||||
sys.exit(app.exec())
|
||||
68
Л5-В6/Программа/src/task.py
Normal file
68
Л5-В6/Программа/src/task.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import sympy
|
||||
import typing
|
||||
|
||||
f_str = "y * ln(y) / sin(x)"
|
||||
f_expr = sympy.sympify(f_str)
|
||||
f_l = sympy.lambdify("x, y", f_expr, cse=True)
|
||||
|
||||
|
||||
def task1_f(x: float, y: typing.List[float]) -> float:
|
||||
return f_l(x, *y)
|
||||
|
||||
|
||||
def task1_f_display() -> str:
|
||||
return f_str
|
||||
|
||||
|
||||
f_1_str = "sin(x**2 + y_2**2)"
|
||||
f_1_expr = sympy.sympify(f_1_str)
|
||||
f_1_l = sympy.lambdify("x, y_1, y_2", f_1_expr, cse=True)
|
||||
|
||||
f_2_str = "cos(x * y_1)"
|
||||
f_2_expr = sympy.sympify(f_2_str)
|
||||
f_2_l = sympy.lambdify("x, y_1, y_2", f_2_expr, cse=True)
|
||||
|
||||
|
||||
def task2_f_1(x: float, y: typing.List[float]) -> float:
|
||||
return f_1_l(x, *y)
|
||||
|
||||
|
||||
def task2_f_2(x: float, y: typing.List[float]) -> float:
|
||||
return f_2_l(x, *y)
|
||||
|
||||
|
||||
def task2_f_1_display() -> str:
|
||||
return f_1_str
|
||||
|
||||
|
||||
def task2_f_2_display() -> str:
|
||||
return f_2_str
|
||||
|
||||
|
||||
def task3_f_display() -> str:
|
||||
return "y'' + a1 * y' + a2 * y"
|
||||
|
||||
|
||||
x, a1, a2 = sympy.symbols("x a1 a2", real=True)
|
||||
y = sympy.symbols("y", cls=sympy.Function)(x)
|
||||
yp = y.diff(x)
|
||||
ypp = y.diff(x, 2)
|
||||
|
||||
f_xy = ypp + a1 * yp + a2 * y
|
||||
|
||||
task3_generic_solution = sympy.dsolve(f_xy, y).rhs
|
||||
|
||||
|
||||
def task3_solution_display() -> str:
|
||||
return str(task3_generic_solution)
|
||||
|
||||
|
||||
def task3_make_f_sympy(a1_, a2_, x_0_, y_0_, y_d_0_):
|
||||
solution = task3_generic_solution.subs({a1: a1_, a2: a2_})
|
||||
cnd1 = sympy.Eq(solution.subs(x, x_0_), y_0_)
|
||||
cnd2 = sympy.Eq(solution.diff(x).subs(x, x_0_), y_d_0_)
|
||||
|
||||
C1, C2 = sympy.symbols("C1 C2")
|
||||
C1C2_sl = sympy.solve([cnd1, cnd2], (C1, C2))
|
||||
|
||||
return sympy.simplify(solution.subs(C1C2_sl))
|
||||
330
Л5-В6/Программа/src/widgets.py
Normal file
330
Л5-В6/Программа/src/widgets.py
Normal file
@@ -0,0 +1,330 @@
|
||||
from io import BytesIO
|
||||
from PyQt6.QtWidgets import (
|
||||
QLabel,
|
||||
QWidget,
|
||||
QLineEdit,
|
||||
QHBoxLayout,
|
||||
QSlider,
|
||||
QVBoxLayout,
|
||||
)
|
||||
from PyQt6.QtCore import Qt
|
||||
from PyQt6.QtSvgWidgets import QSvgWidget
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.figure
|
||||
import matplotlib.axes
|
||||
from matplotlib.backends.backend_qtagg import (
|
||||
FigureCanvasQTAgg as FigureCanvas,
|
||||
NavigationToolbar2QT as NavigationToolbar,
|
||||
)
|
||||
|
||||
import sympy
|
||||
|
||||
import abc
|
||||
import typing
|
||||
|
||||
import matplotlib.pyplot
|
||||
|
||||
|
||||
def setup_dark_theme():
|
||||
matplotlib.pyplot.style.use(
|
||||
{
|
||||
"figure.facecolor": "#1e1e1e",
|
||||
"savefig.facecolor": "#1e1e1e",
|
||||
"text.color": "white",
|
||||
"xtick.color": "white",
|
||||
"ytick.color": "white",
|
||||
"legend.facecolor": "#1e1e1e",
|
||||
"legend.edgecolor": "white",
|
||||
"legend.labelcolor": "white",
|
||||
"grid.color": "white",
|
||||
"figure.edgecolor": "white",
|
||||
"axes.facecolor": "#1e1e1e",
|
||||
"axes.axisbelow": "true",
|
||||
"axes.edgecolor": "white",
|
||||
"axes.labelcolor": "white",
|
||||
"axes.grid": True,
|
||||
"axes.spines.left": False,
|
||||
"axes.spines.right": False,
|
||||
"axes.spines.top": False,
|
||||
"axes.spines.bottom": False,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class DisplayField(QWidget):
|
||||
label: str
|
||||
|
||||
def __init__(self, parent, label="", text="", wrap=False):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.label = label
|
||||
self.text = QLabel(f"{self.label}{text}")
|
||||
self.text.setWordWrap(wrap)
|
||||
self.text.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||||
|
||||
self._layout.addWidget(self.text)
|
||||
|
||||
def set_visible(self, visible):
|
||||
self.text.setVisible(visible)
|
||||
|
||||
def set_text(self, text):
|
||||
self.text.setText(f"{self.label}{text}")
|
||||
|
||||
|
||||
class InputWidget(QWidget):
|
||||
value: typing.Any
|
||||
previous_value: typing.Any
|
||||
callback: typing.Callable[[typing.Self], bool] | None = None
|
||||
|
||||
def init(self):
|
||||
self.value = self._get_value_internal()
|
||||
self.previous_value = self.value
|
||||
self._set_callback_internal(self._on_change_internal)
|
||||
|
||||
def get_value(self) -> typing.Any:
|
||||
return self.value
|
||||
|
||||
def get_previous_value(self) -> typing.Any:
|
||||
return self.previous_value
|
||||
|
||||
def set_value(self, value: typing.Any):
|
||||
self._set_value_internal(value)
|
||||
self._set_value_no_sync(value)
|
||||
|
||||
def set_callback(self, callback: typing.Callable[[typing.Self], bool]):
|
||||
self.callback = callback
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_value_internal(self) -> typing.Any:
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _set_value_internal(self, value: typing.Any):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _set_callback_internal(self, callback: typing.Callable[[], None]):
|
||||
pass
|
||||
|
||||
def _set_value_no_sync(self, value: typing.Any):
|
||||
self.previous_value = self.value
|
||||
self.value = value
|
||||
|
||||
def _on_change_internal(self):
|
||||
self._set_value_no_sync(self._get_value_internal())
|
||||
|
||||
if self.callback:
|
||||
self.callback(self)
|
||||
|
||||
|
||||
def make_callback_chain(
|
||||
callbacks: typing.List[typing.Callable[[InputWidget], bool]]
|
||||
) -> typing.Callable[[InputWidget], bool]:
|
||||
def callback(widget: InputWidget):
|
||||
for callback in callbacks:
|
||||
if not callback(widget):
|
||||
break
|
||||
return True
|
||||
|
||||
return callback
|
||||
|
||||
|
||||
def make_min_max_callback(
|
||||
min: typing.Any, max: typing.Any
|
||||
) -> typing.Callable[[InputWidget], bool]:
|
||||
def callback(widget: InputWidget):
|
||||
value = widget.get_value()
|
||||
|
||||
if type(min)(value) < min:
|
||||
widget.set_value(min)
|
||||
if type(max)(value) > max:
|
||||
widget.set_value(max)
|
||||
|
||||
return True
|
||||
|
||||
return callback
|
||||
|
||||
|
||||
def float_check_callback(widget: InputWidget):
|
||||
try:
|
||||
internal_value = widget._get_value_internal()
|
||||
if not (internal_value == "" or internal_value == "-" or internal_value == "."):
|
||||
_ = float(widget.get_value())
|
||||
return True
|
||||
else:
|
||||
widget.value = widget.previous_value
|
||||
return False
|
||||
except:
|
||||
widget.set_value(widget.previous_value)
|
||||
return False
|
||||
|
||||
|
||||
def int_check_callback(widget: InputWidget):
|
||||
try:
|
||||
internal_value = widget._get_value_internal()
|
||||
if not (internal_value == "" or internal_value == "-"):
|
||||
_ = int(widget.get_value())
|
||||
return True
|
||||
else:
|
||||
widget.value = widget.previous_value
|
||||
return False
|
||||
except:
|
||||
widget.set_value(widget.previous_value)
|
||||
return False
|
||||
|
||||
|
||||
class InputField(InputWidget):
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
label_text="",
|
||||
placeholder_text="",
|
||||
lineedit_text="",
|
||||
label_position="left",
|
||||
):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.label = QLabel(label_text)
|
||||
self.lineedit = QLineEdit(lineedit_text)
|
||||
self.lineedit.setPlaceholderText(placeholder_text)
|
||||
self.lineedit.setMinimumWidth(50)
|
||||
|
||||
if label_position == "left":
|
||||
self._layout.addWidget(self.label)
|
||||
self._layout.addWidget(self.lineedit)
|
||||
elif label_position == "right":
|
||||
self._layout.addWidget(self.lineedit)
|
||||
self._layout.addWidget(self.label)
|
||||
elif label_position == "none":
|
||||
self._layout.addWidget(self.lineedit)
|
||||
|
||||
super().init()
|
||||
super().set_value(lineedit_text)
|
||||
|
||||
def _get_value_internal(self):
|
||||
return self.lineedit.text()
|
||||
|
||||
def _set_value_internal(self, value):
|
||||
self.lineedit.setText(str(value))
|
||||
|
||||
def _set_callback_internal(self, callback):
|
||||
self.lineedit.textChanged.connect(callback)
|
||||
|
||||
|
||||
class MathInputField(InputField):
|
||||
internal_value: typing.Any
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
label_text="",
|
||||
placeholder_text="",
|
||||
lineedit_value="0.0",
|
||||
label_position="left",
|
||||
):
|
||||
super().__init__(
|
||||
parent, label_text, placeholder_text, lineedit_value, label_position
|
||||
)
|
||||
|
||||
def _get_value_internal(self):
|
||||
try:
|
||||
self.internal_value = str(
|
||||
float(
|
||||
sympy.sympify(self.lineedit.text()).evalf(
|
||||
subs={
|
||||
"e": sympy.E,
|
||||
"Pi": sympy.pi,
|
||||
"PI": sympy.pi,
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
return self.internal_value
|
||||
|
||||
|
||||
class InputSlider(InputWidget):
|
||||
slider: QSlider
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent,
|
||||
label_text="",
|
||||
value=0,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
step=1,
|
||||
ticks=False,
|
||||
):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QHBoxLayout(self)
|
||||
self._layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.label = QLabel(label_text)
|
||||
self.slider = QSlider(Qt.Orientation.Horizontal)
|
||||
if ticks:
|
||||
self.slider.setTickPosition(QSlider.TickPosition.TicksBelow)
|
||||
self.slider.setTickInterval(step)
|
||||
|
||||
self.slider.setMinimum(min_value)
|
||||
self.slider.setMaximum(max_value)
|
||||
self.slider.setValue(value)
|
||||
self.slider.setSingleStep(step)
|
||||
|
||||
self._layout.addWidget(self.label)
|
||||
self._layout.addWidget(self.slider)
|
||||
|
||||
super().init()
|
||||
super().set_value(value)
|
||||
|
||||
def _get_value_internal(self):
|
||||
return self.slider.value()
|
||||
|
||||
def _set_value_internal(self, value):
|
||||
self.slider.setValue(value)
|
||||
|
||||
def _set_callback_internal(self, callback):
|
||||
self.slider.valueChanged.connect(callback)
|
||||
|
||||
|
||||
class PlotWidget(QWidget):
|
||||
figure: matplotlib.figure.Figure
|
||||
|
||||
toolbar: NavigationToolbar = None
|
||||
canvas: FigureCanvas
|
||||
|
||||
def __init__(self, parent, figure: matplotlib.figure.Figure, toolbar: bool = False):
|
||||
super().__init__(parent)
|
||||
|
||||
self._layout = QVBoxLayout(self)
|
||||
|
||||
self.figure = figure
|
||||
self.canvas = FigureCanvas(self.figure)
|
||||
self.canvas.setStyleSheet("background-color:transparent;")
|
||||
|
||||
if toolbar:
|
||||
self.toolbar = NavigationToolbar(self.canvas, self)
|
||||
self._layout.addWidget(self.toolbar)
|
||||
|
||||
self._layout.addWidget(self.canvas)
|
||||
|
||||
def set_figure(self, figure: matplotlib.figure.Figure):
|
||||
self.figure = figure
|
||||
self.canvas = FigureCanvas(self.figure)
|
||||
self._layout.addWidget(self.canvas)
|
||||
|
||||
self.redraw()
|
||||
|
||||
def get_figure(self) -> matplotlib.figure.Figure:
|
||||
return self.figure
|
||||
|
||||
def redraw(self):
|
||||
self.canvas.draw()
|
||||
536
Л5-В6/Программа/src/window.py
Normal file
536
Л5-В6/Программа/src/window.py
Normal file
@@ -0,0 +1,536 @@
|
||||
from PyQt6.QtWidgets import (
|
||||
QMainWindow,
|
||||
QLabel,
|
||||
QPushButton,
|
||||
QFormLayout,
|
||||
QWidget,
|
||||
QHBoxLayout,
|
||||
QTabWidget,
|
||||
)
|
||||
from PyQt6 import QtWidgets
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
matplotlib.use("QtAgg")
|
||||
|
||||
import numpy as np
|
||||
import sympy
|
||||
import typing
|
||||
|
||||
import widgets
|
||||
import task
|
||||
import algorithm
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.setup_ui()
|
||||
|
||||
def setup_ui(self):
|
||||
self.setWindowTitle("Лабораторная работа №5")
|
||||
self.setFixedSize(800, 900)
|
||||
|
||||
central_widget = QTabWidget(self)
|
||||
central_widget.setTabsClosable(False)
|
||||
central_widget.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
central_widget.addTab(Tab1(central_widget), "Задание 1")
|
||||
central_widget.addTab(Tab2(central_widget), "Задание 2")
|
||||
central_widget.addTab(Tab3(central_widget), "Задание 3")
|
||||
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
|
||||
class Tab1(QWidget):
|
||||
initialized = False
|
||||
|
||||
x_0_field: widgets.InputField
|
||||
y_0_field: widgets.InputField
|
||||
x_n_field: widgets.InputField
|
||||
|
||||
h_display: widgets.DisplayField
|
||||
h_field: widgets.InputSlider
|
||||
|
||||
x_0: float
|
||||
y_0: float
|
||||
x_n: float
|
||||
h: float
|
||||
|
||||
solution_plot: widgets.PlotWidget
|
||||
solution_figure: plt.Figure
|
||||
solution_axes: plt.Axes
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self._initialize()
|
||||
self.reset()
|
||||
self.initialized = True
|
||||
self.update_solution()
|
||||
|
||||
def _initialize(self):
|
||||
self._layout = QFormLayout(self)
|
||||
|
||||
self._function_display = widgets.DisplayField(self, "f(x, y) = ", "")
|
||||
self._layout.addWidget(self._function_display)
|
||||
|
||||
self.x_0_field = widgets.MathInputField(self, label_text="x_0: ")
|
||||
self.x_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_0_field_changed]
|
||||
)
|
||||
)
|
||||
|
||||
self._layout.addWidget(self.x_0_field)
|
||||
|
||||
self.y_0_field = widgets.MathInputField(self, label_text="y_0: ")
|
||||
self.y_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_0_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_0_field)
|
||||
|
||||
self.x_n_field = widgets.MathInputField(self, label_text="x_n: ")
|
||||
self.x_n_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_n_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.x_n_field)
|
||||
|
||||
self.h_display = widgets.DisplayField(self, "h: ", "")
|
||||
self._layout.addWidget(self.h_display)
|
||||
|
||||
self.h_field = widgets.InputSlider(
|
||||
self, label_text="", value=0, min_value=1, max_value=100, step=1
|
||||
)
|
||||
self.h_field.set_callback(self._h_field_changed)
|
||||
self._layout.addWidget(self.h_field)
|
||||
|
||||
self.solution_figure = plt.figure()
|
||||
self.solution_figure.patch.set_facecolor("none")
|
||||
self.solution_axes = self.solution_figure.add_subplot(111)
|
||||
self.solution_figure.tight_layout(pad=3.0)
|
||||
|
||||
self.solution_plot = widgets.PlotWidget(self, self.solution_figure, True)
|
||||
self._layout.addWidget(self.solution_plot)
|
||||
|
||||
def reset(self):
|
||||
self._function_display.set_text(task.task1_f_display())
|
||||
self.x_0_field.set_value("pi / 2")
|
||||
self.y_0_field.set_value("e")
|
||||
self.x_n_field.set_value("pi")
|
||||
self.h_field.set_value(5)
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _x_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_0 = float(w.get_value())
|
||||
|
||||
return True
|
||||
|
||||
def _x_n_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_n = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _h_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.h = float(w.get_value()) / 100.0
|
||||
self.h_display.set_text(str(self.h))
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def update_solution(self):
|
||||
if not self.initialized:
|
||||
return
|
||||
|
||||
solution = algorithm.runge_kutta_4(
|
||||
self.x_0, [self.y_0], self.x_n, self.h, [task.task1_f]
|
||||
)
|
||||
|
||||
x = np.array(list(solution.keys()))
|
||||
y = np.array(list(map(lambda x: x[0], solution.values())))
|
||||
|
||||
self.solution_axes.clear()
|
||||
self.solution_axes.set_xlabel("x")
|
||||
self.solution_axes.set_ylabel("y")
|
||||
self.solution_axes.grid(True)
|
||||
|
||||
self.solution_axes.plot(x, y, label="y(x)")
|
||||
|
||||
solution = algorithm.runge_kutta_4(
|
||||
self.x_0, [self.y_0], self.x_n, self.h / 2, [task.task1_f]
|
||||
)
|
||||
|
||||
x = np.array(list(solution.keys()))
|
||||
y = np.array(list(map(lambda x: x[0], solution.values())))
|
||||
|
||||
self.solution_axes.plot(x, y, label="y(x) (h/2)")
|
||||
|
||||
solution = algorithm.runge_kutta_4(
|
||||
self.x_0, [self.y_0], self.x_n, self.h * 2, [task.task1_f]
|
||||
)
|
||||
|
||||
x = np.array(list(solution.keys()))
|
||||
y = np.array(list(map(lambda x: x[0], solution.values())))
|
||||
|
||||
self.solution_axes.plot(x, y, label="y(x) (h*2)")
|
||||
|
||||
self.solution_axes.set_yscale("log")
|
||||
|
||||
self.solution_axes.legend(loc="upper left")
|
||||
self.solution_plot.redraw()
|
||||
|
||||
|
||||
class Tab2(QWidget):
|
||||
initialized = False
|
||||
|
||||
_function1_display: widgets.DisplayField
|
||||
_function2_display: widgets.DisplayField
|
||||
|
||||
x_0_field: widgets.InputField
|
||||
y_10_field: widgets.InputField
|
||||
y_20_field: widgets.InputField
|
||||
x_n_field: widgets.InputField
|
||||
|
||||
h_display: widgets.DisplayField
|
||||
h_field: widgets.InputSlider
|
||||
|
||||
x_0: float
|
||||
y_10: float
|
||||
y_20: float
|
||||
x_n: float
|
||||
h: float
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self._initialize()
|
||||
self.reset()
|
||||
self.initialized = True
|
||||
self.update_solution()
|
||||
|
||||
def _initialize(self):
|
||||
self._layout = QFormLayout(self)
|
||||
|
||||
self._function1_display = widgets.DisplayField(self, "f1(x, y) = ", "")
|
||||
self._layout.addWidget(self._function1_display)
|
||||
|
||||
self._function2_display = widgets.DisplayField(self, "f2(x, y) = ", "")
|
||||
self._layout.addWidget(self._function2_display)
|
||||
|
||||
self.x_0_field = widgets.MathInputField(self, label_text="x_0: ")
|
||||
self.x_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_0_field_changed]
|
||||
)
|
||||
)
|
||||
|
||||
self._layout.addWidget(self.x_0_field)
|
||||
|
||||
self.y_10_field = widgets.MathInputField(self, label_text="y_10: ")
|
||||
self.y_10_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_10_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_10_field)
|
||||
|
||||
self.y_20_field = widgets.MathInputField(self, label_text="y_20: ")
|
||||
self.y_20_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_20_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_20_field)
|
||||
|
||||
self.x_n_field = widgets.MathInputField(self, label_text="x_n: ")
|
||||
self.x_n_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_n_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.x_n_field)
|
||||
|
||||
self.h_display = widgets.DisplayField(self, "h: ", "")
|
||||
self._layout.addWidget(self.h_display)
|
||||
|
||||
self.h_field = widgets.InputSlider(
|
||||
self, label_text="", value=0, min_value=1, max_value=100, step=1
|
||||
)
|
||||
self.h_field.set_callback(self._h_field_changed)
|
||||
self._layout.addWidget(self.h_field)
|
||||
|
||||
self.solution_figure = plt.figure()
|
||||
self.solution_figure.patch.set_facecolor("none")
|
||||
self.solution_axes = self.solution_figure.add_subplot(111)
|
||||
self.solution_figure.tight_layout(pad=3.0)
|
||||
|
||||
self.solution_plot = widgets.PlotWidget(self, self.solution_figure)
|
||||
self._layout.addWidget(self.solution_plot)
|
||||
|
||||
def reset(self):
|
||||
self._function1_display.set_text(task.task2_f_1_display())
|
||||
self._function2_display.set_text(task.task2_f_2_display())
|
||||
self.x_0_field.set_value("0")
|
||||
self.y_10_field.set_value("0")
|
||||
self.y_20_field.set_value("0")
|
||||
self.x_n_field.set_value("4")
|
||||
self.h_field.set_value(5)
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _x_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_10_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_10 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_20_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_20 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _x_n_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_n = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _h_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.h = float(w.get_value()) / 100.0
|
||||
self.h_display.set_text(str(self.h))
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def update_solution(self):
|
||||
if not self.initialized:
|
||||
return
|
||||
|
||||
solution = algorithm.runge_kutta_4(
|
||||
self.x_0,
|
||||
[self.y_10, self.y_20],
|
||||
self.x_n,
|
||||
self.h,
|
||||
[task.task2_f_1, task.task2_f_2],
|
||||
)
|
||||
|
||||
x = np.array(list(solution.keys()))
|
||||
y1 = np.array(list(map(lambda x: x[0], solution.values())))
|
||||
y2 = np.array(list(map(lambda x: x[1], solution.values())))
|
||||
|
||||
self.solution_axes.cla()
|
||||
self.solution_axes.plot(x, y1, label="y_1")
|
||||
self.solution_axes.plot(x, y2, label="y_2")
|
||||
|
||||
self.solution_axes.legend()
|
||||
|
||||
self.solution_plot.redraw()
|
||||
|
||||
|
||||
class Tab3(QWidget):
|
||||
initialized = False
|
||||
|
||||
a1_field: widgets.InputField
|
||||
a2_field: widgets.InputField
|
||||
x_0_field: widgets.InputField
|
||||
y_0_field: widgets.InputField
|
||||
y_d_0_field: widgets.InputField
|
||||
|
||||
generic_solution_display: widgets.DisplayField
|
||||
solution_display: widgets.DisplayField
|
||||
|
||||
a1: float
|
||||
a2: float
|
||||
x_0: float
|
||||
y_0: float
|
||||
y_d_0: float
|
||||
|
||||
solution_plot: widgets.PlotWidget
|
||||
solution_figure: plt.Figure
|
||||
solution_axes: plt.Axes
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
self._initialize()
|
||||
self.reset()
|
||||
self.initialized = True
|
||||
self.update_solution()
|
||||
|
||||
def _initialize(self):
|
||||
self._layout = QFormLayout(self)
|
||||
|
||||
self._function_display = widgets.DisplayField(
|
||||
self, task.task3_f_display(), " = 0"
|
||||
)
|
||||
self._layout.addWidget(self._function_display)
|
||||
|
||||
self.a1_field = widgets.InputField(
|
||||
self,
|
||||
"a1: ",
|
||||
)
|
||||
self.a1_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._a1_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.a1_field)
|
||||
|
||||
self.a2_field = widgets.InputField(
|
||||
self,
|
||||
"a2: ",
|
||||
)
|
||||
self.a2_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._a2_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.a2_field)
|
||||
|
||||
self.x_0_field = widgets.InputField(
|
||||
self,
|
||||
"x_0: ",
|
||||
)
|
||||
self.x_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._x_0_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.x_0_field)
|
||||
|
||||
self.y_0_field = widgets.InputField(
|
||||
self,
|
||||
"y_0: ",
|
||||
)
|
||||
self.y_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_0_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_0_field)
|
||||
|
||||
self.y_d_0_field = widgets.InputField(
|
||||
self,
|
||||
"y_d_0: ",
|
||||
)
|
||||
self.y_d_0_field.set_callback(
|
||||
widgets.make_callback_chain(
|
||||
[widgets.float_check_callback, self._y_d_0_field_changed]
|
||||
)
|
||||
)
|
||||
self._layout.addWidget(self.y_d_0_field)
|
||||
|
||||
self.generic_solution_display = widgets.DisplayField(
|
||||
self, "Общее решение: ", wrap=True
|
||||
)
|
||||
self._layout.addWidget(self.generic_solution_display)
|
||||
|
||||
self.solution_display = widgets.DisplayField(self, "Решение: ", wrap=True)
|
||||
self._layout.addWidget(self.solution_display)
|
||||
|
||||
self.solution_figure = plt.figure()
|
||||
self.solution_figure.patch.set_facecolor("none")
|
||||
self.solution_axes = self.solution_figure.add_subplot(111)
|
||||
self.solution_figure.tight_layout(pad=3.0)
|
||||
|
||||
self.solution_plot = widgets.PlotWidget(self, self.solution_figure, True)
|
||||
self._layout.addWidget(self.solution_plot)
|
||||
|
||||
def reset(self):
|
||||
self.a1_field.set_value("-4.0")
|
||||
self.a2_field.set_value("4.0")
|
||||
self.x_0_field.set_value("0.3")
|
||||
self.y_0_field.set_value("1.0")
|
||||
self.y_d_0_field.set_value("1.0")
|
||||
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
def _a1_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.a1 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _a2_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.a2 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _x_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.x_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def _y_d_0_field_changed(self, w: widgets.InputWidget) -> bool:
|
||||
self.y_d_0 = float(w.get_value())
|
||||
|
||||
self.update_solution()
|
||||
|
||||
return True
|
||||
|
||||
def update_solution(self):
|
||||
if not self.initialized:
|
||||
return
|
||||
|
||||
self.generic_solution_display.set_text(
|
||||
"\n" + str(task.task3_solution_display())
|
||||
)
|
||||
solution = task.task3_make_f_sympy(
|
||||
self.a1, self.a2, self.x_0, self.y_0, self.y_d_0
|
||||
)
|
||||
self.solution_display.set_text(str(solution))
|
||||
|
||||
try:
|
||||
f = sympy.lambdify("x", solution, cse=True)
|
||||
x = np.linspace(self.x_0, self.x_0 + 5, 100)
|
||||
y = f(x)
|
||||
|
||||
self.solution_axes.clear()
|
||||
self.solution_axes.grid(True)
|
||||
self.solution_axes.plot(x, y, label="y")
|
||||
self.solution_axes.legend()
|
||||
|
||||
self.solution_plot.redraw()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
Reference in New Issue
Block a user