Время прочтения: 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.