Свертка с функцией разрешения
В этом задании Вам предстоит изучить влияние ошибки измерения на наблюдаемую форму сигнала. В эксперименте происходит измерение плотности распределения 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- значения сигнального распределения в точкахxsmeared_signal:np.ndarray- значения измеренного распределения в точкахx, полученные с помощью свертки функцийfиgxi: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')