Время прочтения: 3 мин.
SimPy — это библиотека для моделирования дискретных событий. Поведение активных компонентов (клиенты, транспортные средства и.т.д) моделируется процессами. Все процессы существуют в среде. Компоненты взаимодействуют со средой и друг с другом посредством событий.
Моделирование в SimPy может проходить в нескольких режимах: в режиме «as fast as possible», в режиме реального времени или в режиме ручного выполнения событий.
SimPy позволяет создавать различные виды ресурсов для моделирования различных систем с ограниченной пропускной способностью (например: число касс в магазине, банкоматы).
В SimPy процессы описываются простыми Python генераторами.
Важным типом события является Timeout. Он позволяют процессу находиться в спящем режиме. События этого типа запускаются по прошествии определенного времени.
Установка происходит с помощью pip. Общий принцип работы выглядит следующим образом:
# Настройка среды
env = simpy.Environment()
# Предположим мы заранее определили функцию foo()
env.process(foo(env, **kwargs))
# Запуск моделирования
env.run(until=10)
Рассмотрим пример. Средой моделирования будет банковское отделение, ресурсом будут сотрудники и терминалы по выдаче талонов, необходимо найти среднее время обслуживание клиента.
Для начала импортируем необходимые библиотеки и создадим список времен:
import random
import simply
import statistics
times = []
Для создания среды, создадим класс Банк, ресурсами в котором будут сотрудники банка и терминалы и два процесса: выдача талона и обслуживание клиента:
class Bank(object):
def _init_(self, env, num_employee, num_terminal):
self.env = env
self.employee = simply.Resource(env, num_employee)
self.terminal = simply.Resource(env, num_terminal)
def service(self, customer):
# Обслуживание занимает время в интервале от 1 до 15 минут
yield self.env.timeout (random.randint(1, 15))
def take_token(self, customer):
# Получение талона занимает до 45 секунд
yield self.env.timeout(45/60)
Создадим функцию, которая будет отвечать за поведение клиента в среде:
def go_to_bank(env, customer, bank):
# Клиент пришел в банк
arrival_time = env.now
with bank.terminal.request() as request:
yield request
yield env.process(bank.take_token(customer))
with bank.employee.request() as request:
yield request
yield env.process(bank.service(customer))
temes.append(env.now-arrival_time)
Теперь необходимо создать функцию для запуска моделирования. Она будет отвечать за создание экземпляра банка и генерацию клиентов, до тех пор, пока симуляция не остановится:
def run_bank(env, num_employee, num_terminal):
bank = Bank(env, num_employee, num_terminal)
customer = 1
env.process(go_to_bank(env, customer, bank))
while True:
# Предположим каждые 4 минуты заходит новый клиент
Yield env.timeout(random.expovariate(1.0 / 240))
customer += 1
env.process(go_to_bank(env,customer, bank))
Отдельно создадим функцию для подсчета среднего времени:
def get_average_time(times):
average_time = statistics.mean(times)
minutes, frac_minutes = divmod(average_time, 1)
seconds = frac_minutes * 60
return round(minutes), round(seconds)
Запустим моделирование:
random.seed(42)
# Начальные данные
num_employee = 7 # В офисе работает 7 сотрудников
num_terminal = 1 # В офисе 1 терминал по выдаче талонов
# Запуск моделирования
env = simply.Environment()
env.process(run_bank(env, num_employee, num_terminal))
env.run(until=720)
# Результаты
mins, secs = get_average_time(times)
print(f’’\nСреднее время обслуживания: {mins}:{secs}’’)
Среднее время обслуживания: 6:57
Данной статьей я хотел продемонстрировать базовые возможности Python фреймворка SimPy, основное предназначение которого – моделирование процессов, в связи с чем, его использование будет полезно с различными Process Mining — инструментами, например, с pm4py.