https://github.com/imtjl/fp-interpolation-cli
Command line utility to interpolate your data using Lagrande and segment methods, written in Elixir
https://github.com/imtjl/fp-interpolation-cli
actor-model elixir genserver lagrange-interpolation linear-interpolation supervisor
Last synced: 7 months ago
JSON representation
Command line utility to interpolate your data using Lagrande and segment methods, written in Elixir
- Host: GitHub
- URL: https://github.com/imtjl/fp-interpolation-cli
- Owner: Imtjl
- Created: 2024-11-10T17:20:49.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2024-11-12T17:56:20.000Z (11 months ago)
- Last Synced: 2025-01-20T06:46:31.327Z (9 months ago)
- Topics: actor-model, elixir, genserver, lagrange-interpolation, linear-interpolation, supervisor
- Language: Elixir
- Homepage:
- Size: 57.6 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
![]()
Лабораторная работа №3
(CLI-утилита для интерполяции)
Table of Contents
- [Title](#title)
- [Architecture](#arch)
- [Conclusion](#end)---
- Студент: `Дворкин Борис Александрович`
- Группа: `P3331`
- ИСУ: `368090`
- Функциональный язык программирования: `Elixir`---
## Использование
### Потоковый режим
> Шаг дискретизации `s` - step
```bash
╭─boris at fedora in ⌁/dev/functional-programming-course/fp-lab3 (main ✚5…2)
╰─λ cat ./resources/large_input_data.csv
| ./interpolation_cli -a linear,lagrange -s 1.0
> ./resources/large_output_data.md
```### Ручной ввод
> Шаг дискретизации `s` - step
```bash
╭─boris at fedora in ⌁/dev/functional-programming-course/fp-lab3 (main ✚4…2)
╰─λ ./interpolation_cli -a linear,lagrange -s 1.0 130 (37.076s) < 14:21:11
0 0
1.571 1Linear (going from point 0.0 with step 1.0, covering all input X (1.57 < 2.0))
:
0.0 1.0 2.0
0.0 0.64 1.273.142 0
Linear (going from point 1.57 with step 1.0, covering all input X (3.14 < 3.57))
:
1.57 2.57 3.57
1.0 0.36 -0.274.712 -1
Linear (going from point 3.14 with step 1.0, covering all input X (4.71 < 5.14))
:
3.14 4.14 5.14
0.0 -0.64 -1.27Lagrange (from point 0.0 with step 1.0, covering all input X (4.71 < 5.0))
:
0.0 1.0 2.0 3.0 4.0 5.0
0.0 0.97 0.84 0.12 -0.67 -1.0312.568 0
Linear (going from point 4.71 with step 1.0, covering all input X (12.57 < 12.71))
:
4.71 5.71 6.71 7.71 8.71 9.71 10.71 11.71 12.71
-1.0 -0.87 -0.75 -0.62 -0.49 -0.36 -0.24 -0.11 0.02Lagrange (from point 1.57 with step 1.0, covering all input X (12.57 < 12.57))
:
1.57 2.57 3.57 4.57 5.57 6.57 7.57 8.57 9.57 10.57 11.57 12.57
1.0 0.37 -0.28 -0.91 -1.49 -1.95 -2.26 -2.38 -2.25 -1.84 -1.11 0.0
```## Архитектура
Модель акторов используется в модулях `Application`, `InputHandler`,
`LinearInterpolator`, `LagrangeInterpolator` и `OutputHandler`.- **Supervisor** — центральный процесс, который управляет жизненным циклом
дочерних процессов (`InputHandler`, `LinearInterpolator`,
`LagrangeInterpolator` и `OutputHandler`). При сбоях `Supervisor`
перезапускает только те процессы, которые завершились аварийно, используя
стратегию `:one_for_one`.- **GenServer** - это абстракция Elixir для реализации акторов. Это процесс,
хранящий состояние и обрабатывающий запросы. По принципу своей работы это
похоже на брокеров сообщений, по типу RabbitMQ или Kafka - по сути, просто луп
эрланговского процесса, который ждёт сообщение, исполняет релевантный ему код,
обновляет состояние если нужно и обратно возвращается в ожиданию сообщения.Таким образом, для CLI она не нужна, т.к. это точка входа, не имеющая состояния,
которое можно было сохранить между вызовами, и оно не требует какого-то
параллельного взаимодействия.В то время как остальные модули работают в изолированных процессах GenServer и
общаются между собой через асинхронные сообщения `cast` и `call`, что позволяет
избежать блокировки и синхронизации, а также компоненты могут обрабатывать
данные параллельно, независимо друг от друга. Например, OutputHandler может
вводить данные в то время, как InputHandler уже обрабатывает новую порцию
данных. Для другого алгоритма интерполяции можно просто добавить ещё один ген
сервер.```
+--------------------------------------+
| CLI |
|--------------------------------------|
| Чтение и парсинг ввода |
| Запуск и настройка компонентов |
| Передача точек в InputHandler |
+--------------------------------------+
|
v
+--------------------------------------+
| InputHandler |
|--------------------------------------|
| (GenServer) |
| Приём данных и сортировка |
| Хранение входных точек |
| Передача точек в интерполяторы |
+--------------------------------------+
|
v
+--------------------------------------+
| Linear/Lagrange Interpolator |
|--------------------------------------|
| (GenServer) |
| Линейная / полином Лагранжа |
| Получение точек от InputHandler |
| Генерация промежуточных точек |
| Передача результатов в OutputHandler |
+--------------------------------------+
|
v
+--------------------------------------+
| OutputHandler |
|--------------------------------------|
| (GenServer) |
| Приём интерполированных данных |
| Форматирование и вывод результатов |
+--------------------------------------+
```## Интерполяция
В моей программе реализованы два метода интерполяции: линейная и интерполяция с
помощью полинома Лагранжа. Оба метода выражены в виде математических формул и
реализованы на Elixir.### Линейная интерполяция
Линейная интерполяция между двумя точками `(x_1, y_1)` и `(x_2, y_2)` выражается
как:$$
y = y_1 + \frac{{y_2 - y_1}}{{x_2 - x_1}} \cdot (x - x_1)
$$В Elixir реализация разбивает отрезок между двумя точками на заданные промежутки
с шагом `step`, а затем вычисляет значения по формуле для каждого промежуточного
`x`:```elixir
def perform_linear_interpolation([{x1, y1}, {x2, y2}], step) do
xs = Stream.iterate(x1, &(&1 + step)) |> Enum.take_while(&(&1 <= x2))
ys = Enum.map(xs, fn x -> y1 + (y2 - y1) / (x2 - x1) * (x - x1) end)
Enum.zip(xs, ys)
end
```### Интерполяция полиномом Лагранжа
Интерполяция полиномом Лагранжа для набора точек
$(x_0, y_0), (x_1, y_1), \dots, (x_n, y_n)$ вычисляется с помощью полинома
степени \( n \):$$
L(x) = \sum_{i=0}^{n} y_i \cdot l_i(x)
$$где $l_i(x)$ — базисный полином, определяемый как:
$$
l_i(x) = \prod_{\substack{0 \leq j \leq n \\ j \neq i}} \frac{{x - x_j}}{{x_i - x_j}}
$$Реализация на Elixir строит этот полином, используя функции для вычисления
значений каждого базисного полинома `l_i(x)`:```elixir
def lagrange_value(points, x) do
Enum.reduce(points, 0.0, fn {x_i, y_i}, acc ->
acc + y_i * basis_polynomial(points, x, x_i)
end)
enddefp basis_polynomial(points, x, x_i) do
Enum.reduce(points, 1.0, fn {x_j, _}, prod ->
if x_i != x_j do
prod * (x - x_j) / (x_i - x_j)
else
prod
end
end)
end
```## Вывод
Познакомился с математической моделью акторов, с её реализацией в Elixir,
порадовался как тут всё прикольно, нет проблем с бесконечными блокировками и
синхронизациями, ведь общение между Эрланговскими процессами происходит простыми
сообщениями, как в брокерах сообщений с которыми довелось работать, по типу
RabbitMQ и Kafka. Также интересна работа Супервизора - легко создать и наполнить
ген серверами, и в случае возникновения любого эксепшена не падает всё
приложение, а продолжает работу в штатном режиме. Стратегий супервизора не так
много и легко изучаются, язык прямо сделан для создания хорошей нагруженной
системы. Поигрался с обработкой сообщений и общением с супервизором. Модули для
работы с потоками I/O предоставляют достаточно интуитивный и простой интерфейс
для работы, поэтому было интересно и не слишком больно работать с потоковыми
режимами.