Свертка с разрешением (5)
Свертка с функцией разрешения
В этом задании Вам предстоит изучить влияние ошибки измерения на наблюдаемую форму сигнала. В эксперименте происходит измерение плотности распределения f(x)
некоторой случайной величины. Для этого выполняют множество измерений этой величины. Измерительный прибор не идеален, он вносит случайную ошибку в измерение с плотноснотью вероятности g(xi)
. Это приводит к тому, что наблюдаемая форма распределения отличается от истинной f
. Наблюдаемая форма может быть найдена с помощью свертки функций f
и g
.
В качестве сигнального распределения f(x)
будем рассматривать два варианта:
Для разрешения прибора g(xi)
также будем рассматривать два варианта:
И сигнальные распределения и функции разрешения описываются двумя параметрами: средним значением и параметром, характеризующим ширину распрелеоения.
Базовая версия
Решите эту задачу рамках объектно-ориенториванного подхода. Базовая версия решения требует реализации классов Generator и Plotter. Первый отвечает за вычисления, а второй - за визуализацию.
Минимальная сигнатура класса Generator:
class Generator:
def __init__(self, mean, width, bias, variance, sigtype, restype, nsigma):
""" Аргументы:
- mean:float и width:float - отвечают за среднее значение и ширину
сигнального распределения, соответственно
- bias:float и variance:float - отвечают за среднее значение и ширину
функции разрешения, соответственно
- sigtype:int - определяет сигнальное распределение:
- значение 0 соответствует нормальному распределению
- значение 1 соответствует распределению Коши
- restype:int - определяет сигнальное распределение:
- значение 0 соответствует нормальному распределению
- значение 1 соответствует распределению Мояля
- nsigma:float - определяет ширину окна для вычисления функций:
[mean - nsigma*width, mean + nsigma*width] и
[-nsigma*variance, nsigma*variance]
"""
pass
Экземпляр класса Generator должен иметь следующие атрибуты:
x:np.ndarray
- значения, для которых вычислены сигнальные распределения (до и после свертки с функцией разрешения)signal:np.ndarray
- значения сигнального распределения в точкахx
smeared_signal:np.ndarray
- значения измеренного распределения в точкахx
, полученные с помощью свертки функцийf
иg
xi:np.ndarray
- значения, для которых вычислена функция разрешенияreso_pdf:np.ndarray
- значения функции разрешения в точкахxi
Минимальная сигнатура класса Plotter
:
class Plotter:
def __init__(self, gen, fig, ax):
""" Аргументы:
- gen:Generator
- fig:Figure
- ax - массив из двух объектов Axis, связанных с объектом fig
"""
def plot(self):
""" Выполняет отрисовку """
Отрисовка должна выполняться на двух объектах Axis
, расположенных в одном окне. На одних осях расположены сигнальные распределения до и после свертки с функцией разрешения, а на других должна быть показана функция разрешения. Итоговый вариант может выглядеть, например, так:
Указания 1
- Решение должно находиться в файле convolution.py
- Для вычисления плотности вероятности различных распределений используйте функции
stats.norm.pdf
,stats.cauchy.pdf
иstats.moyal.pdf
из модуляscipy.stats
- Для выполнения дискретной свертки используйте функцию
scipy.signal.convolve
илиscipy.signal.fftconvolve
с аргументомmode='same'
- Для достаточно больших значений параметра
nsigma
(скажем, больше 5) нормировка сигнального распределения не должна существенно изменяться после свертки с функцией разрешения. Чтобы соблюсти верную нормировку результат функцииconvolve
необходимо умножить на размер шага поx
. - Подумайте в каких точках необходимо вычислять функцию разрешения
g(xi)
для свертки с сигнальным распределениемf(x)
. Шаг по переменнымx
иxi
должен быть одинаковым
Как тестировать решение локально
Работа класса Generator
может быть протестирована стандартным способом. Устанавливаем необходимые пакеты:
pip install -r requirements.txt
Запускаем тесты:
pytest -vs
Реализация класса Plotter
проверяется преподавателем.
Дополнительное задание
Вы создали инструмент для изучения влияния ошибки измерения на наблюдаемый сигнал. Давайте получим еще больше информации об этой процедуре. Добавьте в класс Generator
вычисление для сигнального распределения (до и после свертки с функцией разрешения):
- Положения максимума
- Положения среднего значения
- Полной ширины на полувысоте (FWHM - full width at half maximum)
Результаты вычислений выводите в консоль.
Указания 2
- Рассмотрите следующий способ вычисления FWHM:
- Используйте класс
CubicSpline
из модуляscipy.interpolate
для создания сплайна сигнального распределения, из которого вычтена половина от максимального значения. Так, чтобы точки, соответствующие полувысоте, имели нулевые значения по вертикальной оси - Используйте метод
roots
объектаCubicSpline
для вычисления нулей функции сплайна. Расстояние между двумя полученными значениями и будет равно FWHM - Из-за особенностей алгоритма построения сплайна на границах диапазона могут появиться дополнительные нули. Эту ситуацию необходимо отследить и обработать при необходимости
- Используйте класс
Продвинутая версия
В продвинутой версии необходимо реализовать графический интерфейс пользователя (GUI), с помощью которого можно будет изменять параметры системы. Для этого надо будет добавить функциональность в класс Plotter
и создать класс SmGui
, который будет отвечать за графический интерфейс.
Реализовать GUI можно различными средствами. Например:
- Виджеты matplotlib. Этот вариант наиболее простой в реализации, но даже небольшое количество виджетов приводит к сильному замедлению работы программы
- Библиотека tkinter. Для этого варианта объект
matplotlib.Figure
необходимо будет встроить в окно Tk - Библиотека PyQt5. Эту библиотеку необходимо установить отдельно. В этом варианте также необходимо будет встроить объект
matplotlib.Figure
в окно Qt5. Больше примеров можно найти здесь.
Покажем несколько примеров GUI. Виджеты matplotlib:
С библиотекой tkinter:
С библиотекой PyQt5:
Нет необходимости копировать эти примеры. Вы можете самостоятельно разработать интерфейс.
При интерактивной работе необходимо много раз обновлять графики и изменять диапазоны значений осей. С диапазонами значений проблем не должно возникнуть. А вот обновление графиков требует внимания. Просто создавать новые графики не получится, поскольку они будут отображаться поверх предыдущих. Объекты Line2D
(которые создаются функцией pyplot.plot
) имеют методы set_xdata
и set_ydata
, позволяющие задать новые данные. После обновления всех графиков нужно выполнить отрисовку, например, так:
figure.canvas.draw_idle()
Класс Plotter
должен иметь метод update
, который обновляет все графики.
Указания для работы с виджетами matplotlib
В этой задаче могут оказаться полезными виджеты TextBox
, Button
, Slider
. Конструктор любого виджета matplotlib первым параметром принимает объект Axes
, который определяет размер и положение виджета.
Чтобы связать виджет с логикой программы необходимо связать с ним функции, которые должны вызываться при определенных событиях. Например:
- Методы
on_text_change
иon_submit
виджета ВиджетTextBox
принимают callable объект для вызова при изменеии текста и при нажатии клавишиEnter
, соответственно - Метод
on_clicked
виджетаButton
- Метод
on_changed
виджетаSlider
Ссылка на объект виджета должна быть доступна в течение времени исполнения программы, чтобы виджет был активен.
Подробное описание и примеры смотрите в документации.
Указания для работы с tkinter
Минимальный пример встраивания графика matplotlib
в программу с tkinter
находится в файле tkinter_example.py
. Рассмотрите использование виджетов tkinter.Label
, tkinter.Entry
, tkinter.Button
и tkinter.Scale
. Виджеты tkinter.Scale
и tkinter.Button
можно связать с callback-функциями через параметр конструктора command
:
s = tkinter.Scale(master=master, command=callable)
b = tkinter.Button(master=master, text=text, command=callable)
Задать и прочитать содержимое виджета tkinter.Entry
можно с помощью методов insert
и get
, соответственно.
Метод bind
позволяет связать виджет, событие и действие (callback-функцию). Так, например, можно связать главное окно программы, некоторое действие и нажатие клавиши Enter
:
class Gui(tkinter.Tk):
def __init__(self):
super().__init__()
self.bind('<Return>', callable)
Указания для работы с PyQt5
Минимальный пример встраивания графика matplotlib
в программу с PyQt5
находится в файле pyqt_example.py
. Рассмотрите использование виджетов QPushButton
, QSlider
, QLabel
, QLineEdit
из модуля PyQt5.QtWidgets
.
Связать эти виджеты с callback-функциями можно следующим образом:
button = QPushButton(label, parent)
button.clicked.connect(callable)
qle = QLineEdit(init, parent)
qle.textChanged.connect(callable)
qle.returnPressed.connect(callable)
sld = QSlider(Qt.Vertical, parent)
sld.valueChanged.connect(callable)
Класс FFPlotter
удобно сделать наследником класса FigureCanvasQTAgg
из модуля matplotlib.backends.backend_qt5agg
, как показано в примере. Класс SmGui
удобно сделать наследником класса QMainWindow
из модуля PyQt5.QtWidgets
.
В примере GUI выше реализовано сохранения окна в графический файл. С библиотекой PyQt5
снимок окна программы можно выполнить с помощью объекта QPixmap
из модуля PyQt5.QtGui
. Вот так может выглядеть метод класса SmGui
для сохранения снимка окна:
class SmGui(QMainWindow):
# ...
def save(self):
pix = QPixmap(self.size())
self.render(pix)
pix.save('ffilter.png', 'png')