From d8de3737d88bb0ad84eb22e35d89d66920f6cf68 Mon Sep 17 00:00:00 2001 From: NickMur Date: Fri, 12 Sep 2025 15:54:06 +0300 Subject: [PATCH 01/32] doc/v1 --- docs/guidebook/data_preparation.md | 113 +++++++++ docs/guidebook/local_optimization.md | 125 ++++++++++ docs/guidebook/planning_tutorial.md | 121 ++++++++++ .../aircraft_stochastic_maintenance.md | 140 +++++++++++ .../py_examples_doc/capital_construction.md | 124 ++++++++++ .../dynamic_maintenance_tasks_scheduling.md | 119 ++++++++++ .../field_dev_resources_time_estimator.md | 218 ++++++++++++++++++ .../field_development_scheduling.md | 109 +++++++++ .../py_examples_doc/generator_scenarios.md | 125 ++++++++++ .../landscape_configuration.md | 151 ++++++++++++ .../simple_synthetic_graph_scheduling.md | 104 +++++++++ docs/guidebook/scheduling_project.md | 104 +++++++++ .../simple_generation_and_planning.md | 208 +++++++++++++++++ docs/guidebook/structure_estimator.md | 98 ++++++++ docs/guidebook/visualization.md | 118 ++++++++++ 15 files changed, 1977 insertions(+) create mode 100644 docs/guidebook/data_preparation.md create mode 100644 docs/guidebook/local_optimization.md create mode 100644 docs/guidebook/planning_tutorial.md create mode 100644 docs/guidebook/py_examples_doc/aircraft_stochastic_maintenance.md create mode 100644 docs/guidebook/py_examples_doc/capital_construction.md create mode 100644 docs/guidebook/py_examples_doc/dynamic_maintenance_tasks_scheduling.md create mode 100644 docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md create mode 100644 docs/guidebook/py_examples_doc/field_development_scheduling.md create mode 100644 docs/guidebook/py_examples_doc/generator_scenarios.md create mode 100644 docs/guidebook/py_examples_doc/landscape_configuration.md create mode 100644 docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md create mode 100644 docs/guidebook/scheduling_project.md create mode 100644 docs/guidebook/simple_generation_and_planning.md create mode 100644 docs/guidebook/structure_estimator.md create mode 100644 docs/guidebook/visualization.md diff --git a/docs/guidebook/data_preparation.md b/docs/guidebook/data_preparation.md new file mode 100644 index 00000000..ceee5077 --- /dev/null +++ b/docs/guidebook/data_preparation.md @@ -0,0 +1,113 @@ +# Подготовка данных для планировщика SAMPO + +## Оглавление + +* [1. Генерация графа](#1-генерация-графа) + + * [1.1 Синтетические графы](#11-синтетические-графы) + * [1.2 Сохранение/загрузка WorkGraph](#12-сохранениезагрузка-workgraph) +* [2. Генерация подрядчиков](#2-генерация-подрядчиков) + + * [2.1 Ручная генерация подрядчика](#21-ручная-генерация-подрядчика) + * [2.2 Синтетическая генерация подрядчиков](#22-синтетическая-генерация-подрядчиков) + * [2.3 Генерация подрядчика из графа](#23-генерация-подрядчика-из-графа) + * [2.4 Сохранение/загрузка Contractor](#24-сохранениезагрузка-contractor) + +## 1. Генерация графа + +### 1.1 Синтетические графы + +* Импорт: `SimpleSynthetic`, `SyntheticGraphType`. +* Воспроизводимость: фиксируем зерно `r_seed`. +* Базовый граф: кластерная структура с ограничениями на число работ. +* Продвинутый граф: задаём верхние границы по числу работ и уникальных сущностей. + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator.types import SyntheticGraphType + +r_seed = 231 +ss = SimpleSynthetic(r_seed) + +# Базовый синтетический граф +simple_wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200, +) + +# Продвинутый синтетический граф +adv_wg = ss.advanced_work_graph( + works_count_top_border=2000, + uniq_works=300, + uniq_resources=100, +) +``` + +### 1.2 Сохранение/загрузка WorkGraph + +* Сохранение: `WorkGraph.dump(dir, name)` → `name.json`. +* Загрузка: `WorkGraph.load(dir, name)`. +* Проверка идентичности по числу вершин. + +```python +from sampo.schemas.graph import WorkGraph + +simple_wg.dump(".", "wg") +loaded_simple_wg = WorkGraph.load(".", "wg") +assert simple_wg.vertex_count == loaded_simple_wg.vertex_count +``` + +## 2. Генерация подрядчиков + +### 2.1 Ручная генерация подрядчика + +* Импорт: `Contractor`, `Worker`, `uuid4`. +* Заполняем имя и набор ресурсов с количествами. + +```python +from uuid import uuid4 +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker + +manual_contractor = Contractor( + id=str(uuid4()), + name="OOO Berezka", + workers={ + "builder": Worker(id=str(uuid4()), name="builder", count=100), + }, +) +``` + +### 2.2 Синтетическая генерация подрядчиков + +* Быстрая генерация тестовых подрядчиков по масштабу ресурсообеспечения. + +```python +c5 = ss.contractor(5) +c10 = ss.contractor(10) +c15 = ss.contractor(15) +``` + +### 2.3 Генерация подрядчика из графа + +* Генерация покрытия потребностей конкретного графа. + +```python +from sampo.generator.environment import get_contractor_by_wg + +contractors = [get_contractor_by_wg(simple_wg)] +``` + +### 2.4 Сохранение/загрузка Contractor + +* Сохранение: `Contractor.dump(dir, name)` → `name.json`. +* Загрузка: `Contractor.load(dir, name)`. + +```python +contractors[0].dump(".", "contractor") + +from sampo.schemas.contractor import Contractor +loaded_contractor = Contractor.load(".", "contractor") +``` diff --git a/docs/guidebook/local_optimization.md b/docs/guidebook/local_optimization.md new file mode 100644 index 00000000..82087b51 --- /dev/null +++ b/docs/guidebook/local_optimization.md @@ -0,0 +1,125 @@ +# Локальная оптимизация в SAMPO + +## Оглавление + +* [1. Подготовка данных](#1-подготовка-данных) + + * [1.1 Генерация графа и ресурсов](#11-генерация-графа-и-ресурсов) + * [1.2 Планировщик](#12-планировщик) +* [2. Локальная оптимизация](#2-локальная-оптимизация) + + * [2.1 Оптимизация порядка (Scheduling order)](#21-оптимизация-порядка-scheduling-order) + * [2.2 Оптимизация расписания (Schedule)](#22-оптимизация-расписания-schedule) + * [2.3 Комбинированное применение](#23-комбинированное-применение) + +## 1. Подготовка данных + +### 1.1 Генерация графа и ресурсов + +* Используем синтетический генератор и подрядчика из графа. +* Фиксируем `r_seed` для воспроизводимости. + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator.types import SyntheticGraphType +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg + +r_seed = 231 +ss = SimpleSynthetic(r_seed) + +simple_wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200, +) + +contractors = [get_contractor_by_wg(simple_wg)] +``` + +### 1.2 Планировщик + +* Базовый планировщик: `HEFTScheduler`. +* Конвейер: `SchedulingPipeline`. + +```python +from sampo.scheduler.heft.base import HEFTScheduler +from sampo.pipeline import SchedulingPipeline + +scheduler = HEFTScheduler() +``` + +## 2. Локальная оптимизация + +В SAMPO два вида локальной оптимизации: + +* **Order** — перестановка порядка планирования работ для улучшения результата. +* **Schedule** — перерасчёт частей расписания для уменьшения времени выполнения. + +### 2.1 Оптимизация порядка (Scheduling order) + +* Класс: `SwapOrderLocalOptimizer`. +* Применение к поддиапазону вершин графа через `.optimize_local(...)` до вызова `.schedule(...)`. + +```python +from sampo.scheduler.utils.local_optimization import SwapOrderLocalOptimizer + +order_optimizer = SwapOrderLocalOptimizer() + +project = SchedulingPipeline.create() \ + .wg(simple_wg) \ + .contractors(contractors) \ + .optimize_local(order_optimizer, range(0, 10)) \ + .schedule(scheduler) \ + .finish()[0] + +project.schedule.execution_time +``` + +### 2.2 Оптимизация расписания (Schedule) + +* Класс: `ParallelizeScheduleLocalOptimizer`. +* Требует выбор таймлайна, например `JustInTimeTimeline`. +* Обычно применяется после базового расписания. + +```python +from sampo.scheduler.timeline.just_in_time_timeline import JustInTimeTimeline +from sampo.scheduler.utils.local_optimization import ParallelizeScheduleLocalOptimizer + +schedule_optimizer = ParallelizeScheduleLocalOptimizer(JustInTimeTimeline) + +project = SchedulingPipeline.create() \ + .wg(simple_wg) \ + .contractors(contractors) \ + .schedule(scheduler) \ + .optimize_local(schedule_optimizer, range(0, 5)) \ + .finish()[0] + +project.schedule.execution_time +``` + +### 2.3 Комбинированное применение + +* Можно стекать несколько локальных оптимизаторов. +* Пример: сначала оптимизация порядка на разных диапазонах, затем расписания. + +```python +from sampo.pipeline import SchedulingPipeline +from sampo.scheduler.utils.local_optimization import SwapOrderLocalOptimizer, ParallelizeScheduleLocalOptimizer +from sampo.scheduler.timeline.just_in_time_timeline import JustInTimeTimeline + +order_optimizer = SwapOrderLocalOptimizer() +schedule_optimizer = ParallelizeScheduleLocalOptimizer(JustInTimeTimeline) + +project = SchedulingPipeline.create() \ + .wg(simple_wg) \ + .contractors(contractors) \ + .optimize_local(order_optimizer, range(0, simple_wg.vertex_count // 2)) \ + .optimize_local(order_optimizer, range(simple_wg.vertex_count // 2, simple_wg.vertex_count)) \ + .schedule(scheduler) \ + .optimize_local(schedule_optimizer, range(0, simple_wg.vertex_count // 2)) \ + .optimize_local(schedule_optimizer, range(simple_wg.vertex_count // 2, simple_wg.vertex_count)) \ + .finish()[0] + +project.schedule.execution_time +``` diff --git a/docs/guidebook/planning_tutorial.md b/docs/guidebook/planning_tutorial.md new file mode 100644 index 00000000..a3c426a6 --- /dev/null +++ b/docs/guidebook/planning_tutorial.md @@ -0,0 +1,121 @@ +# Туториал по планированию в SAMPO + +## Оглавление + +* [1. Подготовка графа работ и подрядчиков](#1-подготовка-графа-работ-и-подрядчиков) + + * [1.1 Загрузка из CSV](#11-загрузка-из-csv) + * [1.2 Загрузка из сериализованного JSON](#12-загрузка-из-сериализованного-json) + + * [1.2.1 WorkGraph → DataFrame](#121-workgraph--dataframe) + * [1.2.2 Автоподбор подрядчиков по WorkGraph](#122-автоподбор-подрядчиков-по-workgraph) + * [1.2.3 Загрузка расписания из JSON](#123-загрузка-расписания-из-json) + * [1.x Интеграция в STAIRS](#1x-интеграция-в-stairs) +* [2. Инициализация алгоритмов планирования](#2-инициализация-алгоритмов-планирования) + + * [2.1 Пользовательский WorkTimeEstimator](#21-пользовательский-worktimeestimator) + * [2.2 Эвристические и топологические планировщики](#22-эвристические-и-топологические-планировщики) + * [2.3 Генетический алгоритм: гиперпараметры](#23-генетический-алгоритм-гиперпараметры) +* [3. Построение расписания через пайплайн](#3-построение-расписания-через-пайплайн) + + * [3.1 Настройка пайплайна](#31-настройка-пайплайна) + + * [3.1.1 Восстановление структуры и лагов по истории](#311-восстановление-структуры-и-лагов-по-истории) + * [3.1.2 Передача подрядчиков](#312-передача-подрядчиков) + * [3.1.3 Передача исторических данных](#313-передача-исторических-данных) + * [3.1.4 Реструктуризация графа](#314-реструктуризация-графа) + * [3.1.5 Подключение оценщика времени](#315-подключение-оценщика-времени) + * [3.1.6 Построение расписания](#316-построение-расписания) + * [3.2 Полное планирование и экспорт](#32-полное-планирование-и-экспорт) + +## 1. Подготовка графа работ и подрядчиков + +### 1.1 Загрузка из CSV + +* Используются колонки по работам и связям. +* Для связей задаются `predecessor_ids`, типы соединений и лаги. +* При восстановлении связей по истории допустим минимальный набор полей. + +### 1.2 Загрузка из сериализованного JSON + +* Импорт проекта целиком: структура `WorkGraph` и, при наличии, `Schedule`. +* Удобно для повторной визуализации и сравнения. + +#### 1.2.1 WorkGraph → DataFrame + +* Из `ScheduleProject` извлекается `WorkGraph`. +* Преобразование графа в `pandas.DataFrame` для анализа и восстановления лагов. + +#### 1.2.2 Автоподбор подрядчиков по WorkGraph + +* Формирование списка подрядчиков по потребностям графа. +* Учитываются требуемые ресурсы и специализации. + +#### 1.2.3 Загрузка расписания из JSON + +* Из сериализованного файла извлекается объект `Schedule`. +* Подходит для сравнения с новыми расчётами. + +### 1.x Интеграция в STAIRS + +* Источники: CSV, БД (`postgresql_url`), сериализованный проект. +* После `.finish()` доступен `ScheduleProject`; из него получают `Schedule` и `wg.to_frame(save_req=True)` для СППР. + +## 2. Инициализация алгоритмов планирования + +### 2.1 Пользовательский WorkTimeEstimator + +* Своя модель длительностей и ресурсов, например `FieldDevWorkEstimator`. +* Один экземпляр используется всеми планировщиками. + +### 2.2 Эвристические и топологические планировщики + +* `HEFTScheduler`, `HEFTBetweenScheduler`, `TopologicalScheduler`, `RandomizedTopologicalScheduler`. +* Не требуют сложной настройки. + +### 2.3 Генетический алгоритм: гиперпараметры + +* `GeneticScheduler` с авто-подбором поколений или явными параметрами. +* Проверка настроек на малых примерах перед основным запуском. + +## 3. Построение расписания через пайплайн + +### 3.1 Настройка пайплайна + +* Последовательная передача графа, подрядчиков, истории и оценщика. +* Опционально: `LagOptimizationStrategy`. + +#### 3.1.1 Восстановление структуры и лагов по истории + +* Использование `DataFrame` из `WorkGraph`. +* Восстановление типов связей и лагов по истории. + +#### 3.1.2 Передача подрядчиков + +* В пайплайн передаётся список подрядчиков. +* Возможен автоподбор по `WorkGraph`. + +#### 3.1.3 Передача исторических данных + +* Загрузка `history_df` из CSV. +* История учитывается при реструктуризации и оценке времени. + +#### 3.1.4 Реструктуризация графа + +* Нормализация структуры работ с учётом истории и ресурсов. +* Результат — обновлённый `DataFrame`. + +#### 3.1.5 Подключение оценщика времени + +* Подключение пользовательской модели для длительностей и ресурсов. +* Единый оценщик на всех этапах. + +#### 3.1.6 Построение расписания + +* Запуск выбранного планировщика (`HEFT*`, `Topological*`, `GeneticScheduler`). +* На выходе `ScheduleProject` и `Schedule`; поддерживается визуализация (`schedule_gant_chart_fig`, `VisualizationMode`). + +### 3.2 Полное планирование и экспорт + +* Сквозной прогон через `SchedulingPipeline.create() … .finish()[0]`. +* Просмотр структуры графа, получение `Schedule`, экспорт в JSON для дальнейшей загрузки в СППР. diff --git a/docs/guidebook/py_examples_doc/aircraft_stochastic_maintenance.md b/docs/guidebook/py_examples_doc/aircraft_stochastic_maintenance.md new file mode 100644 index 00000000..c9f644f1 --- /dev/null +++ b/docs/guidebook/py_examples_doc/aircraft_stochastic_maintenance.md @@ -0,0 +1,140 @@ +# Стохастическое ТО самолёта: зоны и GA-планирование + +## Оглавление + +* [1. Параметры и импорты](#1-параметры-и-импорты) +* [2. Генерация исходного WorkGraph](#2-генерация-исходного-workgraph) +* [3. Дефекты: StructureEstimator](#3-дефекты-structureestimator) +* [4. Зоны и ландшафт](#4-зоны-и-ландшафт) +* [5. Планировщик и оценщик](#5-планировщик-и-оценщик) +* [6. Пайплайн расчёта](#6-пайплайн-расчёта) +* [7. Визуализация Ганта](#7-визуализация-ганта) + +--- + +## 1. Параметры и импорты + +```python +from random import Random +import numpy as np + +from sampo.generator import SimpleSynthetic, SyntheticGraphType +from sampo.generator.environment import get_contractor_by_wg +from sampo.pipeline import SchedulingPipeline +from sampo.scheduler import GeneticScheduler +from sampo.schemas import DefaultZoneStatuses, ZoneConfiguration, LandscapeConfiguration +from sampo.schemas.structure_estimator import DefaultStructureGenerationEstimator, DefaultStructureEstimator +from sampo.schemas.time_estimator import DefaultWorkEstimator +from sampo.utilities.visualization import schedule_gant_chart_fig, VisualizationMode + +r_seed = 231 +ss = SimpleSynthetic(r_seed) +``` + + + +## 2. Генерация исходного WorkGraph + +```python +wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200 +) +``` + + + +## 3. Дефекты: StructureEstimator + +* Равномерно добавляются 5 «подработ» на каждый несервисный узел. + +```python +rand = Random(r_seed) +generator = DefaultStructureGenerationEstimator(rand) +sub_works = [f"Sub-work {i}" for i in range(5)] + +for node in wg.nodes: + if node.work_unit.is_service_unit: + continue + for sub_work in sub_works: + generator.set_probability(parent=node.work_unit.name, + child=sub_work, + probability=1/len(sub_works)) + +structure_estimator = DefaultStructureEstimator(generator, rand) +wg_with_defects = structure_estimator.restruct(wg) +contractors = [get_contractor_by_wg(wg_with_defects)] +``` + + + +## 4. Зоны и ландшафт + +* Одна зона `zone1`, 4 допустимых статуса, единичные переходные стоимости. + +```python +class AeroplaneZoneStatuses(DefaultZoneStatuses): + def statuses_available(self) -> int: + return 4 + +zone_names = ['zone1'] +zones_count = len(zone_names) + +zone_config = ZoneConfiguration( + start_statuses={zone: 1 for zone in zone_names}, + time_costs=np.array([[1 for _ in range(zones_count)] for _ in range(zones_count)]), + statuses=AeroplaneZoneStatuses() +) + +landscape_config = LandscapeConfiguration(zone_config=zone_config) +``` + + + +## 5. Планировщик и оценщик + +```python +work_estimator = DefaultWorkEstimator() + +genetic_scheduler = GeneticScheduler( + work_estimator=work_estimator, + number_of_generation=20, + mutate_order=0.05, + mutate_resources=0.005, + size_of_population=50 +) +``` + + + +## 6. Пайплайн расчёта + +```python +aircraft_project = ( + SchedulingPipeline.create() + .wg(wg_with_defects) + .contractors(contractors) + .work_estimator(work_estimator) + .landscape(landscape_config) + .schedule(genetic_scheduler) + .finish()[0] +) +``` + + + +## 7. Визуализация Ганта + +```python +merged_schedule = aircraft_project.schedule.merged_stages_datetime_df('2022-01-01') +aircraft_schedule_fig = schedule_gant_chart_fig( + merged_schedule, + VisualizationMode.ReturnFig, + remove_service_tasks=False +) +aircraft_schedule_fig.update_layout(height=1200, width=1600) +aircraft_schedule_fig.show() +``` + diff --git a/docs/guidebook/py_examples_doc/capital_construction.md b/docs/guidebook/py_examples_doc/capital_construction.md new file mode 100644 index 00000000..871dc0a7 --- /dev/null +++ b/docs/guidebook/py_examples_doc/capital_construction.md @@ -0,0 +1,124 @@ +# Мультиагентное планирование капстроительства + +## Оглавление + +* [1. Назначение и сущности](#1-назначение-и-сущности) +* [2. Построение BlockGraph: `load_queues_bg`](#2-построение-blockgraph-load_queues_bg) +* [3. Запуск стохастического мультиагентного планирования: `run_example`](#3-запуск-стохастического-мультиагентного-планирования-run_example) +* [4. Мини-пример использования](#4-мини-пример-использования) + +--- + +## 1. Назначение и сущности + +Файл задаёт две ключевые функции: сборку `BlockGraph` из очередей `WorkGraph` и стохастическое мультиагентное планирование блоков с учётом производительности работников по подрядчикам. Используются: `Scheduler`, `Agent`, `StochasticManager`, `ScheduledBlock`, `IntervalGaussian`, `DefaultWorkEstimator`. + +--- + +## 2. Построение BlockGraph: `load_queues_bg` + +**Идея.** Развернуть список очередей графов в единый `BlockGraph` и сгенерировать зависимости между стартовыми узлами соседних очередей (прямой и «обратной» проход для покрытия недостающих рёбер). + +```python +from sampo.scheduler.multi_agency.block_graph import BlockGraph +from sampo.schemas.graph import WorkGraph + +def load_queues_bg(queues: list[list[WorkGraph]]): + wgs: list[WorkGraph] = [wg for queue in queues for wg in queue] + bg = BlockGraph.pure(wgs) + + index = 0 + nodes_prev = [] + for queue in queues: + nodes = [bg[wgs[i].start.id] for i in range(index, index + len(queue))] + + # прямой проход + for i, node in enumerate(nodes[:-2]): + if i >= len(nodes_prev): + break + BlockGraph.add_edge(node, nodes_prev[i]) + + # обратный проход для покрытия оставшихся + for i, node in enumerate(nodes): + if i >= len(nodes_prev): + break + BlockGraph.add_edge(node, nodes_prev[-i]) + + nodes_prev = nodes + + return bg +``` + + + +--- + +## 3. Запуск стохастического мультиагентного планирования: `run_example` + +**Шаги.** + +1. Создать `DefaultWorkEstimator`. +2. Для каждого подрядчика `contractor[i]` задать производительность ролей `['driver','fitter','manager','handyman','electrician','engineer']` как `IntervalGaussian(0.2 * i + 0.2, 1, 0, 2)`. +3. Назначить оценщик всем планировщикам. +4. Собрать агентов `Agent(name, scheduler, [contractor])`. +5. Построить `BlockGraph` через `load_queues_bg`. +6. Запустить `StochasticManager.manage_blocks`. + +```python +from typing import Dict +from sampo.scheduler.base import Scheduler +from sampo.scheduler.multi_agency.multi_agency import Agent, ScheduledBlock, StochasticManager +from sampo.schemas import IntervalGaussian +from sampo.schemas.contractor import Contractor +from sampo.schemas.time_estimator import DefaultWorkEstimator + +def run_example( + queues: list[list[WorkGraph]], + schedulers: list[Scheduler], + contractors: list[Contractor] +) -> Dict[str, ScheduledBlock]: + work_estimator = DefaultWorkEstimator() + + # роль-специфичная производительность по подрядчику i + for worker in ['driver', 'fitter', 'manager', 'handyman', 'electrician', 'engineer']: + for i, contractor in enumerate(contractors): + work_estimator.set_worker_productivity( + IntervalGaussian(0.2 * i + 0.2, 1, 0, 2), worker, contractor.id + ) + + for scheduler in schedulers: + scheduler.work_estimator = work_estimator + + agents = [Agent(f'Agent {i}', schedulers[i % len(schedulers)], [contractor]) + for i, contractor in enumerate(contractors)] + manager = StochasticManager(agents) + + bg = load_queues_bg(queues) + blocks_schedules = manager.manage_blocks(bg, logger=print) + return blocks_schedules +``` + + + +--- + +## 4. Мини-пример использования + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft.base import HEFTScheduler +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg + +# очереди из малых графов +ss = SimpleSynthetic(rand=31) +queues = [[ss.small_work_graph() for _ in range(2)], + [ss.small_work_graph() for _ in range(2)]] + +# планировщики и подрядчики +schedulers = [HEFTScheduler(), HEFTScheduler()] +contractors = [get_contractor_by_wg(queues[0][0]), get_contractor_by_wg(queues[1][0])] + +# запуск +blocks = run_example(queues, schedulers, contractors) +print(list(blocks.keys())) +``` diff --git a/docs/guidebook/py_examples_doc/dynamic_maintenance_tasks_scheduling.md b/docs/guidebook/py_examples_doc/dynamic_maintenance_tasks_scheduling.md new file mode 100644 index 00000000..8db1fd09 --- /dev/null +++ b/docs/guidebook/py_examples_doc/dynamic_maintenance_tasks_scheduling.md @@ -0,0 +1,119 @@ +# Динамическое планирование регламентных задач (multi-agency) + +## Оглавление + +* [1. Входные сущности](#1-входные-сущности) +* [2. Построение BlockGraph из очередей](#2-построение-blockgraph-из-очередей) +* [3. Мультиагентное планирование](#3-мультиагентное-планирование) +* [4. Валидация результата](#4-валидация-результата) +* [5. Пример запуска](#5-пример-запуска) + +--- + +## 1. Входные сущности + +* `WorkGraph`: блок регламентных работ. +* `Scheduler`: планировщик агента. +* `Contractor`: ресурсы агента. +* Объекты multi-agency: `Agent`, `Manager`, `ScheduledBlock`. + +--- + +## 2. Построение BlockGraph из очередей + +Функция `load_queues_bg(queues)` превращает список очередей графов в `BlockGraph` и генерирует зависимости между стартовыми узлами соседних очередей. Шаги: собрать все `WorkGraph`, создать `BlockGraph.pure`, затем добавить рёбра между стартами по шаблону «текущая ↔ предыдущая очередь». Возврат — готовый `BlockGraph`. + +```python +from sampo.scheduler.multi_agency.block_graph import BlockGraph +from sampo.schemas.graph import WorkGraph + +def load_queues_bg(queues: list[list[WorkGraph]]) -> BlockGraph: + wgs = [wg for queue in queues for wg in queue] + bg = BlockGraph.pure(wgs) + + index = 0 + nodes_prev = [] + for queue in queues: + nodes = [bg[wgs[i].start.id] for i in range(index, index + len(queue))] + + # связи между очередями + for i, node in enumerate(nodes[:-2]): + if i >= len(nodes_prev): + break + BlockGraph.add_edge(node, nodes_prev[i]) + + for i, node in enumerate(nodes): + if i >= len(nodes_prev): + break + BlockGraph.add_edge(node, nodes_prev[-i]) + + nodes_prev = nodes + + return bg +``` + +--- + +## 3. Мультиагентное планирование + +`run_example(queues_with_obstructions, schedulers, contractors)` создаёт агентов, строит `BlockGraph`, планирует блоки через `Manager.manage_blocks`, возвращает `Dict[str, ScheduledBlock]`. Объекты препятствий (`Obstruction`, `OneInsertObstruction`) доступны для сценариев, но в базовом запуске не используются. + +```python +from typing import Dict +from sampo.scheduler.base import Scheduler +from sampo.scheduler.multi_agency.multi_agency import Agent, Manager, ScheduledBlock + +def run_example( + queues_with_obstructions: list[list[WorkGraph]], + schedulers: list[Scheduler], + contractors: list[Contractor] +) -> Dict[str, ScheduledBlock]: + + agents = [Agent(f'Agent {i}', schedulers[i % len(schedulers)], [contractor]) + for i, contractor in enumerate(contractors)] + manager = Manager(agents) + + bg = load_queues_bg(queues_with_obstructions) + blocks_schedules = manager.manage_blocks(bg, logger=print) + return blocks_schedules +``` + +--- + +## 4. Валидация результата + +После планирования проверяется корректность расписания блоков: соответствие зависимостям `BlockGraph` и агентам. + +```python +from sampo.scheduler.multi_agency import validate_block_schedule + +validate_block_schedule(bg, blocks_schedules, agents) +``` + +--- + +## 5. Пример запуска + +Минимальный сценарий с двумя очередями, двумя агентами и HEFT-планировщиками. + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft.base import HEFTScheduler +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg + +# 1) подготовка очередей графов +ss = SimpleSynthetic(rand=31) +q1 = [ss.small_work_graph() for _ in range(2)] +q2 = [ss.small_work_graph() for _ in range(2)] +queues = [q1, q2] + +# 2) планировщики и подрядчики (по одному на агента) +schedulers = [HEFTScheduler(), HEFTScheduler()] +contractors = [get_contractor_by_wg(q1[0]), get_contractor_by_wg(q2[0])] + +# 3) мультиагентное планирование +blocks_schedules = run_example(queues, schedulers, contractors) + +# 4) печать ключей запланированных блоков +print(list(blocks_schedules.keys())) +``` diff --git a/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md b/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md new file mode 100644 index 00000000..e583a72f --- /dev/null +++ b/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md @@ -0,0 +1,218 @@ +# Оценщик времени и ресурсов FieldDevWorkEstimator + +## Оглавление + +* [1. Назначение и зависимости](#1-назначение-и-зависимости) +* [2. Константы и инициализация модели](#2-константы-и-инициализация-модели) +* [3. Публичные методы](#3-публичные-методы) + + * [3.1 `estimate_time`](#31-estimate_time) + * [3.2 `find_work_resources`](#32-find_work_resources) + * [3.3 `set_estimation_mode`](#33-set_estimation_mode) + * [3.4 `set_productivity_mode`](#34-set_productivity_mode) + * [3.5 `get_recreate_info`](#35-get_recreate_info) +* [4. Пример использования](#4-пример-использования) + +--- + +## 1. Назначение и зависимости + +`FieldDevWorkEstimator` — пользовательский `WorkTimeEstimator` для SAMPO. Возвращает длительности работ и требования к ресурсам на базе внешней модели `ResTimeModel`, подключённой через адаптер `MschmAdapter`. + +```python +import logging +from random import Random +from typing import Type +from itertools import chain +from operator import attrgetter + +from sampo.schemas.time import Time +from sampo.schemas import ( + WorkTimeEstimator, WorkUnit, Worker, WorkerReq, + WorkEstimationMode, WorkerProductivityMode +) + +from idbadapter import MschmAdapter +from stairsres.res_time_model import ResTimeModel +from sampo.utilities.collections_util import build_index +``` + +--- + +## 2. Константы и инициализация модели + +Сервисные работы имеют нулевую длительность. Модель создаётся один раз и переиспользуется. Логгер ведёт предупреждения по сбоям оценки. + +```python +SERVICE_WORKS = [ + "Начало работ по марке", "Окончание работ по марке", + "NaN", "start of project", "finish of project" +] + +URL = "test" +model = ResTimeModel(MschmAdapter(url=URL)) +logger = logging.getLogger("field-dev-estimator-log") + + +class FieldDevWorkEstimator(WorkTimeEstimator): + def __init__(self, rand: Random = Random()): + self._url = URL + self._model = model + self._use_idle = True + self._estimation_mode = WorkEstimationMode.Realistic + self.rand = rand + self._productivity_mode = WorkerProductivityMode.Static +``` + +--- + +## 3. Публичные методы + +### 3.1 `estimate_time` + +Логика: + +* Имя работы очищается от суффикса `_stage_…`. +* Список ресурсов превращается в `[{name, _count}]`; недостающие из `worker_reqs` добавляются с `_count=0`. +* Режимы маппятся на квантили `"0.1" | "0.5" | "0.9"`. +* Сервисные работы → `Time(0)`. +* Ошибки логируются и не прерывают процесс. + +```python +def estimate_time(self, work_unit: WorkUnit, worker_list: list[Worker]) -> Time: + w_u = { + "name": work_unit.name.split("_stage_")[0], + "volume": work_unit.volume, + "measurement": work_unit.volume_type, + } + + w_l = [{"name": w.name, "_count": w.count} for w in worker_list] + name2worker = build_index(worker_list, attrgetter("name")) + + match self._estimation_mode: + case WorkEstimationMode.Optimistic: + mode_str = "0.1" + case WorkEstimationMode.Realistic: + mode_str = "0.5" + case _: + mode_str = "0.9" + + # добавляем отсутствующие ресурсы с нулевым количеством + for req in work_unit.worker_reqs: + if name2worker.get(req.kind) is None: + w_l.append({"name": req.kind, "_count": 0}) + + if w_u["name"] in SERVICE_WORKS: + return Time(0) + + try: + return Time(int(self._model.estimate_time(work_unit=w_u, worker_list=w_l, mode=mode_str))) + except Exception as e: + logger.warning(f"Couldn't estimate time for work unit with name='{w_u['name']}': {e}") + # допустимо вернуть 0 или эвристическое значение; здесь вернём 0 + return Time(0) +``` + +### 3.2 `find_work_resources` + +Возвращает плоский список `WorkerReq`, рассчитанный моделью. + +```python +def find_work_resources( + self, + work_name: str, + work_volume: float, + resource_name: list[str] | None = None, + measurement: str | None = None +) -> list[WorkerReq]: + if work_name in SERVICE_WORKS: + return [] + + worker_req_dict = self._model.get_resources_volumes( + work_name=work_name, + work_volume=work_volume, + measurement=measurement + ) or {} + + worker_reqs = [ + [ + WorkerReq( + kind=req["kind"], + volume=Time(req["volume"]), + min_count=req["min_count"], + max_count=req["max_count"], + ) + for req in req_list + ] + for req_list in worker_req_dict.values() + ] + return list(chain.from_iterable(worker_reqs)) +``` + +### 3.3 `set_estimation_mode` + +```python +def set_estimation_mode( + self, + use_idle: bool = True, + mode: WorkEstimationMode = WorkEstimationMode.Realistic +) -> None: + self._use_idle = use_idle + self._estimation_mode = mode +``` + +### 3.4 `set_productivity_mode` + +```python +def set_productivity_mode( + self, + mode: WorkerProductivityMode = WorkerProductivityMode.Static +) -> None: + self._productivity_mode = mode +``` + +### 3.5 `get_recreate_info` + +Возвращает конструктор и «параметры» для восстановления. Сейчас из `_url: str` берётся `tuple(self._url)`, то есть кортеж символов строки. + +```python +from typing import Type + +def get_recreate_info(self) -> tuple[Type, tuple]: + return FieldDevWorkEstimator, tuple(self._url) # ('t','e','s','t') для "test" +``` + +--- + +## 4. Пример использования + +```python +from random import Random +from sampo.schemas import WorkUnit, Worker +from sampo.schemas import WorkEstimationMode, WorkerProductivityMode + +est = FieldDevWorkEstimator(rand=Random(231)) + +# оценка времени +wu = WorkUnit(name="Бурение_stage_1", volume=100.0, volume_type="м") +workers = [Worker(id="1", name="буровик", count=5)] +t = est.estimate_time(wu, workers) + +# требования к ресурсам +reqs = est.find_work_resources("Бурение", 100.0, measurement="м") + +# переключение режимов +est.set_estimation_mode(use_idle=True, mode=WorkEstimationMode.Realistic) +est.set_productivity_mode(mode=WorkerProductivityMode.Static) +``` + +Минимальная настройка логирования: + +```python +import logging + +logging.basicConfig( + level=logging.WARNING, + format="%(asctime)s %(levelname)s %(name)s: %(message)s" +) +``` \ No newline at end of file diff --git a/docs/guidebook/py_examples_doc/field_development_scheduling.md b/docs/guidebook/py_examples_doc/field_development_scheduling.md new file mode 100644 index 00000000..f63be399 --- /dev/null +++ b/docs/guidebook/py_examples_doc/field_development_scheduling.md @@ -0,0 +1,109 @@ +# Планирование работ по разработке месторождения (field\_development\_scheduling.py) + +## Оглавление + +* [1. Параметры и импорты](#1-параметры-и-импорты) +* [2. Загрузка WorkGraph](#2-загрузка-workgraph) +* [3. Подрядчики: файл или автогенерация](#3-подрядчики-файл-или-автогенерация) +* [4. Реструктуризация графа](#4-реструктуризация-графа) +* [5. Визуализация структуры](#5-визуализация-структуры) +* [6. Настройки планировщика и вывода](#6-настройки-планировщика-и-вывода) +* [7. Расчёт расписания и даты](#7-расчёт-расписания-и-даты) +* [8. Диаграмма Ганта](#8-диаграмма-ганта) + +--- + +## 1. Параметры и импорты + +Подавление предупреждений `matplotlib`, импорты планировщика, визуализации и структуризации. + +```python +import warnings +from matplotlib import pyplot as plt + +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg +from sampo.scheduler.heft.base import HEFTScheduler +from sampo.schemas.contractor import Contractor +from sampo.schemas.graph import WorkGraph +from sampo.structurator.base import graph_restructuring +from sampo.utilities.visualization.base import VisualizationMode +from sampo.utilities.visualization.schedule import schedule_gant_chart_fig +from sampo.utilities.visualization.work_graph import work_graph_fig + +warnings.filterwarnings("ignore") +``` + +## 2. Загрузка WorkGraph + +Чтение структуры задач из подготовленного JSON (`./field_development_tasks_structure.json`). + +```python +field_development_wg = WorkGraph.load("./", "field_development_tasks_structure") +``` + +## 3. Подрядчики: файл или автогенерация + +Флаг выбора источника. По умолчанию автогенерация под граф со скейлером ресурсов `3`. + +```python +use_contractors_from_file = False + +if use_contractors_from_file: + contractors = Contractor.load("./", "field_development_contractors_info") +else: + contractors = [get_contractor_by_wg(field_development_wg, scaler=3)] +``` + +## 4. Реструктуризация графа + +Оптимизация структуры графа с учётом лагов. + +```python +structured_wg = graph_restructuring(field_development_wg, use_lag_edge_optimization=True) +``` + +## 5. Визуализация структуры + +Отрисовка `WorkGraph`: размер 20×10, подписи узлов, сдвиг легенды. + +```python +_ = work_graph_fig(structured_wg, (20, 10), legend_shift=4, show_names=True, text_size=6) +plt.show() +``` + +## 6. Настройки планировщика и вывода + +Выбор алгоритма, режим визуализации и дата старта; флаги сериализации подготовлены, но в скрипте не используются. + +```python +scheduler_type = HEFTScheduler() +graph_structure_optimization = True +csv_required = False +json_required = True + +visualization_mode = VisualizationMode.ShowFig +gant_chart_filename = "./output/schedule_gant_chart.png" +start_date = "2023-01-01" +``` + +## 7. Расчёт расписания и даты + +Планирование с валидацией и преобразование расписания к календарным датам. + +```python +schedule = scheduler_type.schedule(structured_wg, contractors, validate=True)[0] +schedule_df = schedule.merged_stages_datetime_df(start_date) +``` + +## 8. Диаграмма Ганта + +Построение диаграммы с опцией удаления сервисных задач; показ или сохранение файла по режиму. + +```python +gant_fig = schedule_gant_chart_fig( + schedule_df, + fig_file_name=gant_chart_filename, + visualization=visualization_mode, + remove_service_tasks=True, +) +``` diff --git a/docs/guidebook/py_examples_doc/generator_scenarios.md b/docs/guidebook/py_examples_doc/generator_scenarios.md new file mode 100644 index 00000000..1abd7499 --- /dev/null +++ b/docs/guidebook/py_examples_doc/generator_scenarios.md @@ -0,0 +1,125 @@ +# Сценарии генерации и модификации графов в SAMPO (generator\_scenarios.py) + +## Оглавление + +* [1. Инициализация и базовая генерация](#1-инициализация-и-базовая-генерация) +* [2. Базовое планирование HEFT](#2-базовое-планирование-heft) +* [3. Подрядчик по WorkGraph](#3-подрядчик-по-workgraph) +* [4. Расширение номенклатуры работ](#4-расширение-номенклатуры-работ) +* [5. Расширение номенклатуры ресурсов](#5-расширение-номенклатуры-ресурсов) +* [6. Проверка согласованности генерации подрядчика](#6-проверка-согласованности-генерации-подрядчика) +* [7. Визуализация WorkGraph](#7-визуализация-workgraph) +* [8. Вставка графа в граф и реструктуризация](#8-вставка-графа-в-граф-и-реструктуризация) + +## 1. Инициализация и базовая генерация + +Импорты, генератор, исходный граф, стартовые подрядчики. + +```python +import random +from itertools import chain +from matplotlib import pyplot as plt + +from sampo.generator.base import SimpleSynthetic +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg +from sampo.generator.pipeline.extension import extend_names, extend_resources +from sampo.scheduler.heft.base import HEFTScheduler +from sampo.schemas.graph import WorkGraph +from sampo.structurator import ( + graph_in_graph_insertion, work_graph_ids_simplification, graph_restructuring +) +from sampo.utilities.visualization.work_graph import work_graph_fig + +rand = random.Random(10) +p_rand = SimpleSynthetic(rand=231) +wg = p_rand.work_graph(top_border=3000) +contractors = [p_rand.contractor(i) for i in range(10, 31, 10)] +``` + +## 2. Базовое планирование HEFT + +Расчёт расписания и вывод метрик. + +```python +schedule = HEFTScheduler().schedule(wg, contractors)[0] + +print(len(wg.nodes)) +print("\nDefault contractors") +print(f"Execution time: {schedule.execution_time}") +``` + +## 3. Подрядчик по WorkGraph + +Пример оценки с автогенерацией подрядчика под граф. В файле цикл по пустому списку, для эксперимента заполните `pack_counts`. + +```python +print("\nContractor by work graph") +for pack_counts in [1, 2, 10]: # в файле: []; замените при необходимости + contractors = [get_contractor_by_wg(wg, scaler=pack_counts)] + execution_time = HEFTScheduler().schedule(wg, contractors)[0].execution_time + print(f"Execution time: {execution_time}, pack count: {pack_counts}") +``` + +## 4. Расширение номенклатуры работ + +Увеличение числа уникальных названий работ функцией `extend_names`. + +```python +print("\nNames extension") +names_wg = len({n.work_unit.name for n in wg.nodes}) +new_wg = extend_names(500, wg, rand) +names_new_wg = len({n.work_unit.name for n in new_wg.nodes}) +print(f"works in origin: {names_wg}, pack count: {names_new_wg}") +``` + +## 5. Расширение номенклатуры ресурсов + +Добавление новых типов ресурсов функцией `extend_resources`. + +```python +print("\nResource extension") +res_names_wg = len({req.kind for req in chain(*[n.work_unit.worker_reqs for n in wg.nodes])}) +new_wg = extend_resources(100, wg, rand) +res_names_new_wg = len({req.kind for req in chain(*[n.work_unit.worker_reqs for n in new_wg.nodes])}) +print(f"resources in origin: {res_names_wg}, in new: {res_names_new_wg}") +``` + +## 6. Проверка согласованности генерации подрядчика + +Сопоставление числа типов ресурсов в расширенном графе и у подрядчика, сгенерированного по этому графу. + +```python +print("\nCheck gen contractor by wg extension") +res_names_new_c = len(set(get_contractor_by_wg(new_wg, scaler=1).workers.keys())) +print(f"works in new by wg: {res_names_new_wg}, by contractor: {res_names_new_c}") +``` + +## 7. Визуализация WorkGraph + +Функция-обёртка для отрисовки графа. + +```python +def plot_wg(wg: WorkGraph) -> None: + _ = work_graph_fig(wg, (14, 8), legend_shift=4, show_names=True, text_size=4) + plt.show() +``` + +## 8. Вставка графа в граф и реструктуризация + +Композиция графов, упрощение идентификаторов, реструктуризация и визуализация этапов. + +```python +srand = SimpleSynthetic(34) +wg_master = srand.work_graph(cluster_counts=1) +wg_slave = srand.work_graph(cluster_counts=1) + +union_wg = graph_in_graph_insertion(wg_master, wg_master.start, wg_master.finish, wg_slave) +union_wg_simply = work_graph_ids_simplification(union_wg, id_offset=1000) +union_wg_restructured = graph_restructuring(union_wg_simply) + +plot_wg(wg_master) +plot_wg(wg_slave) +plot_wg(union_wg) +plot_wg(union_wg_simply) +plot_wg(union_wg_restructured) +``` diff --git a/docs/guidebook/py_examples_doc/landscape_configuration.md b/docs/guidebook/py_examples_doc/landscape_configuration.md new file mode 100644 index 00000000..096af807 --- /dev/null +++ b/docs/guidebook/py_examples_doc/landscape_configuration.md @@ -0,0 +1,151 @@ +# Конфигурация ландшафта и планирование в SAMPO (LandscapeConfiguration) + +## Оглавление + +* [1. Данные и ландшафт](#1-данные-и-ландшафт) + + * [1.1 Генерация графа и материалов](#11-генерация-графа-и-материалов) + * [1.2 Построение ландшафта](#12-построение-ландшафта) +* [2. Планировщик](#2-планировщик) +* [3. Подрядчик](#3-подрядчик) +* [4. Пайплайн и визуализация](#4-пайплайн-и-визуализация) +* [5. Диагностика](#5-диагностика) +* [6. Полный пример](#6-полный-пример) + +--- + +## 1. Данные и ландшафт + +### 1.1 Генерация графа и материалов + +* Синтетика: `SimpleSynthetic(rand=31) → small_work_graph()`. +* Материалы на узлы: `set_materials_for_wg(wg)`. + +```python +from sampo.generator import SimpleSynthetic + +ss = SimpleSynthetic(rand=31) +wg = ss.small_work_graph() +wg = ss.set_materials_for_wg(wg) +``` + +### 1.2 Построение ландшафта + +* Конфигурация площадок: `synthetic_landscape(wg)`. + +```python +landscape = ss.synthetic_landscape(wg) +``` + +--- + +## 2. Планировщик + +* Генетический алгоритм с ограниченными гиперпараметрами. +* Предупреждение: большие значения поколений и популяции замедляют расчёт при сложном ландшафте. + +```python +from sampo.scheduler import GeneticScheduler + +scheduler = GeneticScheduler( + number_of_generation=1, + mutate_order=0.05, + mutate_resources=0.005, + size_of_population=10, +) +``` + +--- + +## 3. Подрядчик + +* Автоподбор по требованиям `WorkGraph`: `get_contractor_by_wg(wg)`. + +```python +from sampo.generator.environment import get_contractor_by_wg + +contractors = [get_contractor_by_wg(wg)] +``` + +--- + +## 4. Пайплайн и визуализация + +* Используется `DefaultInputPipeline`. +* Визуализация диаграммы Ганта; режимы: `VisualizationMode.ShowFig | SaveFig`. +* Параметры: дата старта, имя файла при `SaveFig`. + +```python +from sampo.pipeline.default import DefaultInputPipeline +from sampo.utilities.visualization import VisualizationMode + +start_date = "2023-01-01" +visualization_mode = VisualizationMode.ShowFig +gant_chart_filename = "./output/synth_schedule_gant_chart.png" + +project = ( + DefaultInputPipeline() + .wg(wg) + .contractors(contractors) + .landscape(landscape) + .schedule(scheduler) + .visualization(start_date) # возвращает список проектов +)[0].show_gant_chart() +``` + +--- + +## 5. Диагностика + +* Число платформ и наличие материалов на каждом узле. + +```python +platform_number = len(landscape.platforms) +is_all_nodes_have_materials = all(node.work_unit.need_materials() for node in wg.nodes) +print(f"LandscapeConfiguration: {platform_number} platforms, " + f"All nodes have materials: {is_all_nodes_have_materials}") +``` + +--- + +## 6. Полный пример + +```python +from sampo.generator import SimpleSynthetic +from sampo.generator.environment import get_contractor_by_wg +from sampo.pipeline.default import DefaultInputPipeline +from sampo.scheduler import GeneticScheduler +from sampo.utilities.visualization import VisualizationMode + +start_date = "2023-01-01" +visualization_mode = VisualizationMode.ShowFig +gant_chart_filename = "./output/synth_schedule_gant_chart.png" + +ss = SimpleSynthetic(rand=31) +wg = ss.small_work_graph() +wg = ss.set_materials_for_wg(wg) +landscape = ss.synthetic_landscape(wg) + +scheduler = GeneticScheduler( + number_of_generation=1, + mutate_order=0.05, + mutate_resources=0.005, + size_of_population=10, +) + +platform_number = len(landscape.platforms) +is_all_nodes_have_materials = all(node.work_unit.need_materials() for node in wg.nodes) +print(f"LandscapeConfiguration: {platform_number} platforms, " + f"All nodes have materials: {is_all_nodes_have_materials}") + +contractors = [get_contractor_by_wg(wg)] + +project = ( + DefaultInputPipeline() + .wg(wg) + .contractors(contractors) + .landscape(landscape) + .schedule(scheduler) + .visualization(start_date) +)[0].show_gant_chart() +``` diff --git a/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md b/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md new file mode 100644 index 00000000..03d4f9e5 --- /dev/null +++ b/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md @@ -0,0 +1,104 @@ +# Планирование синтетического графа (simple\_synthetic\_graph\_scheduling.py) + +## Оглавление + +* [1. Параметры и импорты](#1-параметры-и-импорты) +* [2. Генерация синтетического WorkGraph](#2-генерация-синтетического-workgraph) +* [3. Диагностика атрибутов графа](#3-диагностика-атрибутов-графа) +* [4. Подрядчик по графу](#4-подрядчик-по-графу) +* [5. Планирование](#5-планирование) +* [6. Визуализация диаграммы Ганта](#6-визуализация-диаграммы-ганта) +* [7. Валидация результата](#7-валидация-результата) + +--- + +## 1. Параметры и импорты + +* Планировщик: `HEFTScheduler`. Дата старта: `"2023-01-01"`. Визуализация: `VisualizationMode.ShowFig` или `SaveFig` с именем файла. + +```python +from itertools import chain +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg +from sampo.utilities.visualization.base import VisualizationMode +from sampo.utilities.visualization.schedule import schedule_gant_chart_fig +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft.base import HEFTScheduler +from sampo.schemas.time import Time + +scheduler = HEFTScheduler() +start_date = "2023-01-01" +visualization_mode = VisualizationMode.ShowFig +gant_chart_filename = './output/synth_schedule_gant_chart.png' +``` + +## 2. Генерация синтетического WorkGraph + +* Пределы генерации: `works_count_top_border=2000`, `uniq_works=300`, `uniq_resources=100`. Фиксированный `rand=31`. + +```python +synth_works_top_border = 2000 +synth_unique_works = 300 +synth_resources = 100 + +srand = SimpleSynthetic(rand=31) +wg = srand.advanced_work_graph( + works_count_top_border=synth_works_top_border, + uniq_works=synth_unique_works, + uniq_resources=synth_resources +) +``` + +## 3. Диагностика атрибутов графа + +* Подсчёт работ, уникальных названий и типов ресурсов. Жёсткие проверки на верхние границы. + +```python +works_count = len(wg.nodes) +work_names_count = len(set(n.work_unit.name for n in wg.nodes)) +res_kind_count = len(set(req.kind for req in chain(*[n.work_unit.worker_reqs for n in wg.nodes]))) +print(works_count, work_names_count, res_kind_count) + +assert (works_count <= synth_works_top_border * 1.1) +assert (work_names_count <= synth_works_top_border) +assert (res_kind_count <= synth_works_top_border) +``` + +## 4. Подрядчик по графу + +* Автогенерация подрядчика, покрывающего ресурсы графа: `get_contractor_by_wg(wg)`. + +```python +contractors = [get_contractor_by_wg(wg)] +``` + +## 5. Планирование + +* Расчёт расписания и преобразование к датам. + +```python +schedule = scheduler.schedule(wg, contractors)[0] +schedule_df = schedule.merged_stages_datetime_df(start_date) +print(schedule.execution_time) +``` + +## 6. Визуализация диаграммы Ганта + +* Построение диаграммы. Можно показывать или сохранять. Сервисные задачи удаляются. + +```python +gant_fig = schedule_gant_chart_fig( + schedule_df, + fig_file_name=gant_chart_filename, + visualization=visualization_mode, + remove_service_tasks=True +) +``` + +## 7. Валидация результата + +* Проверка, что планирование завершилось успешно: время не бесконечность. + +```python +assert schedule.execution_time != Time.inf(), \ + f'Scheduling failed on {scheduler.scheduler_type.name}' +``` diff --git a/docs/guidebook/scheduling_project.md b/docs/guidebook/scheduling_project.md new file mode 100644 index 00000000..127fb540 --- /dev/null +++ b/docs/guidebook/scheduling_project.md @@ -0,0 +1,104 @@ +# Проект планирования в SAMPO (SchedulingProject) + +## Оглавление + +* [1. Генерация графа](#1-генерация-графа) + + * [1.1 Простой граф](#11-простой-граф) + * [1.2 Сложный граф](#12-сложный-граф) +* [2. Генерация подрядчика](#2-генерация-подрядчика) + + * [2.1 Ручная](#21-ручная) + * [2.2 Из графа](#22-из-графа) +* [3. Планирование](#3-планирование) + + * [3.1 Конструкция планировщика](#31-конструкция-планировщика) + * [3.2 Расчёт через SchedulingPipeline](#32-расчёт-через-schedulingpipeline) + * [3.3 Сериализация проекта](#33-сериализация-проекта) + +## 1. Генерация графа + +### 1.1 Простой граф + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator.types import SyntheticGraphType + +r_seed = 231 +ss = SimpleSynthetic(r_seed) + +simple_wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200, +) +``` + +### 1.2 Сложный граф + +```python +advanced_wg = ss.advanced_work_graph( + works_count_top_border=2000, + uniq_works=300, + uniq_resources=100, +) +``` + +## 2. Генерация подрядчика + +### 2.1 Ручная + +```python +from uuid import uuid4 +from sampo.schemas.resources import Worker +from sampo.schemas.contractor import Contractor + +contractors = [ + Contractor( + id=str(uuid4()), + name="OOO Berezka", + workers={"worker": Worker(id="0", name="worker", count=100)}, + ) +] +``` + +### 2.2 Из графа + +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg + +contractors = [get_contractor_by_wg(simple_wg)] +``` + +## 3. Планирование + +### 3.1 Конструкция планировщика + +```python +from sampo.scheduler.heft.base import HEFTScheduler + +scheduler = HEFTScheduler() +``` + +### 3.2 Расчёт через SchedulingPipeline + +```python +from sampo.pipeline import SchedulingPipeline + +project = ( + SchedulingPipeline.create() + .wg(simple_wg) + .contractors(contractors) + .schedule(scheduler) + .finish()[0] +) + +project.schedule.execution_time +``` + +### 3.3 Сериализация проекта + +```python +project.dump(".", "project_test") +``` diff --git a/docs/guidebook/simple_generation_and_planning.md b/docs/guidebook/simple_generation_and_planning.md new file mode 100644 index 00000000..cb8ffd71 --- /dev/null +++ b/docs/guidebook/simple_generation_and_planning.md @@ -0,0 +1,208 @@ +# Простая генерация и планирование в SAMPO + +## Оглавление + +* [1. Генерация графа](#1-генерация-графа) + + * [1.1 Простой граф](#11-простой-граф) + * [1.2 Сложный граф](#12-сложный-граф) +* [2. Генерация подрядчика](#2-генерация-подрядчика) + + * [2.1 Ручная](#21-ручная) + * [2.2 Из графа](#22-из-графа) +* [3. Планирование](#3-планирование) + + * [3.1 Конструкция планировщика](#31-конструкция-планировщика) + * [3.2 Процесс планирования](#32-процесс-планирования) +* [4. Метрики для GeneticScheduler](#4-метрики-для-geneticscheduler) + + * [4.1 DeadlineResourcesFitness](#41-deadlineresourcesfitness) + * [4.2 DeadlineCostFitness](#42-deadlinecostfitness) + * [4.3 TimeWithResourcesFitness](#43-timewithresourcesfitness) + +--- + +## 1. Генерация графа + +### 1.1 Простой граф + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator.types import SyntheticGraphType + +r_seed = 231 +ss = SimpleSynthetic(r_seed) + +# простой граф: 10 кластеров, от 100 до 200 работ в каждом +simple_wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200, +) +``` + +### 1.2 Сложный граф + +```python +# сложный граф: до 2000 работ, 300 уникальных типов работ, 100 типов ресурсов +advanced_wg = ss.advanced_work_graph( + works_count_top_border=2000, + uniq_works=300, + uniq_resources=100, +) +``` + +--- + +## 2. Генерация подрядчика + +### 2.1 Ручная + +```python +from uuid import uuid4 +from sampo.schemas.resources import Worker +from sampo.schemas.contractor import Contractor + +contractors = [ + Contractor( + id=str(uuid4()), + name="OOO Berezka", + workers={"worker": Worker(id="0", name="worker", count=100)}, + ) +] +``` + +### 2.2 Из графа + +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg + +contractors = [get_contractor_by_wg(simple_wg)] +``` + +--- + +## 3. Планирование + +### 3.1 Конструкция планировщика + +```python +from sampo.scheduler.heft.base import HEFTScheduler +from sampo.scheduler.genetic.base import GeneticScheduler + +# эвристика HEFT +scheduler = HEFTScheduler() + +# или генетический планировщик с простыми гиперпараметрами +scheduler = GeneticScheduler(mutate_order=0.05, mutate_resources=0.05) +``` + +### 3.2 Процесс планирования + +```python +from sampo.pipeline import SchedulingPipeline + +project = ( + SchedulingPipeline.create() + .wg(simple_wg) + .contractors(contractors) + .schedule(scheduler) + .finish()[0] +) + +project.schedule.execution_time +``` + +--- + +## 4. Метрики для GeneticScheduler + +### 4.1 DeadlineResourcesFitness + +Оптимизация использования ресурсов при заданном дедлайне. + +```python +from sampo.schemas.time import Time +from sampo.scheduler.genetic.operators import DeadlineResourcesFitness +from sampo.scheduler.genetic.base import GeneticScheduler +from sampo.pipeline import SchedulingPipeline + +deadline = Time(2000) +fitness_constructor = DeadlineResourcesFitness(deadline) + +scheduler = GeneticScheduler( + mutate_order=0.05, + mutate_resources=0.05, + fitness_constructor=fitness_constructor, +) +scheduler.set_deadline(deadline) + +project = ( + SchedulingPipeline.create() + .wg(simple_wg) + .contractors(contractors) + .schedule(scheduler) + .finish()[0] +) + +project.schedule.execution_time +``` + +### 4.2 DeadlineCostFitness + +Оптимизация стоимости с учётом дедлайна. + +```python +from sampo.scheduler.genetic.operators import DeadlineCostFitness +from sampo.scheduler.genetic.base import GeneticScheduler +from sampo.pipeline import SchedulingPipeline + +fitness_constructor = DeadlineCostFitness(deadline) + +scheduler = GeneticScheduler( + mutate_order=0.05, + mutate_resources=0.05, + fitness_constructor=fitness_constructor, +) +scheduler.set_deadline(deadline) + +project = ( + SchedulingPipeline.create() + .wg(simple_wg) + .contractors(contractors) + .schedule(scheduler) + .finish()[0] +) + +project.schedule.execution_time +``` + +### 4.3 TimeWithResourcesFitness + +Минимизация времени с учётом ресурсов, без явного дедлайна. + +```python +from sampo.scheduler.genetic.operators import TimeWithResourcesFitness +from sampo.scheduler.genetic.base import GeneticScheduler +from sampo.pipeline import SchedulingPipeline + +fitness_constructor = TimeWithResourcesFitness() + +scheduler = GeneticScheduler( + mutate_order=0.05, + mutate_resources=0.05, + fitness_constructor=fitness_constructor, +) +scheduler.set_deadline(deadline) # при необходимости + +project = ( + SchedulingPipeline.create() + .wg(simple_wg) + .contractors(contractors) + .schedule(scheduler) + .finish()[0] +) + +project.schedule.execution_time +``` diff --git a/docs/guidebook/structure_estimator.md b/docs/guidebook/structure_estimator.md new file mode 100644 index 00000000..c3234010 --- /dev/null +++ b/docs/guidebook/structure_estimator.md @@ -0,0 +1,98 @@ +# Оценка и восстановление структуры работ (StructureEstimator) в SAMPO + +## Оглавление + +* [1. Генерация исходного графа](#1-генерация-исходного-графа) + + * [1.1 Синтетический граф](#11-синтетический-граф) +* [2. Создание StructureEstimator](#2-создание-structureestimator) + + * [2.1 Генератор структурных связей](#21-генератор-структурных-связей) + * [2.2 Итоговый оценщик структуры](#22-итоговый-оценщик-структуры) +* [3. Применение в PreparationPipeline](#3-применение-в-preparationpipeline) + + * [3.1 Реструктуризация WorkGraph](#31-реструктуризация-workgraph) + +--- + +## 1. Генерация исходного графа + +### 1.1 Синтетический граф + +* Импорт генератора и типов: `SimpleSynthetic`, `SyntheticGraphType`. +* Фиксация зерна для воспроизводимости. +* Построение базового `WorkGraph`. + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator import SyntheticGraphType # или: from sampo.generator.types import SyntheticGraphType + +r_seed = 231 +ss = SimpleSynthetic(r_seed) + +wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200, +) +``` + +## 2. Создание StructureEstimator + +### 2.1 Генератор структурных связей + +* Используется `DefaultStructureGenerationEstimator`. +* Ему передаётся общий ГПСЧ `Random(r_seed)`. +* Задаются вероятности порождения подработ для каждого несервисного узла. + +```python +from random import Random +from sampo.schemas.structure_estimator import DefaultStructureGenerationEstimator + +rand = Random(r_seed) +generator = DefaultStructureGenerationEstimator(rand) + +sub_works = [f"Sub-work {i}" for i in range(5)] + +# равномерно распределяем 5 «подработ» на каждый несервисный узел +for node in wg.nodes: + if node.work_unit.is_service_unit: + continue + for sub_work in sub_works: + generator.set_probability( + parent=node.work_unit.name, + child=sub_work, + probability=1 / len(sub_works), + ) +``` + +### 2.2 Итоговый оценщик структуры + +* Обёртка `DefaultStructureEstimator(generator, rand)` для использования в пайплайне. + +```python +from sampo.schemas.structure_estimator import DefaultStructureEstimator + +structure_estimator = DefaultStructureEstimator(generator, rand) +``` + +## 3. Применение в PreparationPipeline + +### 3.1 Реструктуризация WorkGraph + +* Подключаем `structure_estimator` в `PreparationPipeline`. +* Строим обновлённый `WorkGraph` с учётом сгенерированных подработ и связей. + +```python +from sampo.pipeline.preparation import PreparationPipeline + +restructed_wg = ( + PreparationPipeline() + .wg(wg) + .structure_estimator(structure_estimator) + .build_wg() +) + +restructed_wg.vertex_count # проверка изменения размера графа +``` diff --git a/docs/guidebook/visualization.md b/docs/guidebook/visualization.md new file mode 100644 index 00000000..2d2ca62a --- /dev/null +++ b/docs/guidebook/visualization.md @@ -0,0 +1,118 @@ +# Визуализация в SAMPO + +## Оглавление + +* [1. Планирование (Scheduling)](#1-планирование-scheduling) + + * [1.1 Генерация данных](#11-генерация-данных) + * [1.2 Расчёт расписания](#12-расчёт-расписания) +* [2. Визуализация WorkGraph](#2-визуализация-workgraph) +* [3. Диаграмма Ганта проекта](#3-диаграмма-ганта-проекта) +* [4. Занятость ресурсов](#4-занятость-ресурсов) + + * [4.1 По работам](#41-по-работам) + * [4.2 По датам](#42-по-датам) + +--- + +## 1. Планирование (Scheduling) + +### 1.1 Генерация данных + +```python +from sampo.pipeline.lag_optimization import LagOptimizationStrategy +from sampo.generator.base import SimpleSynthetic +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod +from sampo.generator import SyntheticGraphType + +r_seed = 231 +ss = SimpleSynthetic(r_seed) + +simple_wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200, +) + +contractors = [get_contractor_by_wg(simple_wg, method=ContractorGenerationMethod.AVG)] +``` + +### 1.2 Расчёт расписания + +```python +from sampo.pipeline import SchedulingPipeline +from sampo.scheduler.heft.base import HEFTScheduler + +scheduler = HEFTScheduler() + +project = ( + SchedulingPipeline.create() + .wg(simple_wg) + .contractors(contractors) + .lag_optimize(LagOptimizationStrategy.TRUE) + .schedule(scheduler) + .finish()[0] +) + +schedule = project.schedule +``` + +--- + +## 2. Визуализация WorkGraph + +```python +from sampo.utilities.visualization.work_graph import work_graph_fig + +fig = work_graph_fig(simple_wg, (10, 10)) +fig.show() +``` + +--- + +## 3. Диаграмма Ганта проекта + +```python +from sampo.utilities.visualization.base import VisualizationMode +from sampo.utilities.visualization.schedule import schedule_gant_chart_fig + +# переводим расписание в реальные даты +merged_schedule = schedule.merged_stages_datetime_df('2022-01-01') + +fig = schedule_gant_chart_fig( + schedule_dataframe=merged_schedule, + visualization=VisualizationMode.ShowFig, + remove_service_tasks=False, +) +``` + +--- + +## 4. Занятость ресурсов + +### 4.1 По работам + +```python +from sampo.utilities.visualization.resources import resource_employment_fig, EmploymentFigType +from sampo.utilities.visualization.base import VisualizationMode + +fig = resource_employment_fig( + schedule=merged_schedule, + fig_type=EmploymentFigType.WorkLabeled, + vis_mode=VisualizationMode.ShowFig, +) +``` + +### 4.2 По датам + +```python +from sampo.utilities.visualization.resources import resource_employment_fig, EmploymentFigType +from sampo.utilities.visualization.base import VisualizationMode + +fig = resource_employment_fig( + schedule=merged_schedule, + fig_type=EmploymentFigType.DateLabeled, + vis_mode=VisualizationMode.ShowFig, +) +``` From 84a324dff114207ad822a46f74bff21e9b9cb6db Mon Sep 17 00:00:00 2001 From: NickMur Date: Fri, 12 Sep 2025 16:36:21 +0300 Subject: [PATCH 02/32] doc/v2: data_preparation.md --- docs/guidebook/data_preparation.md | 92 ++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/docs/guidebook/data_preparation.md b/docs/guidebook/data_preparation.md index ceee5077..a5e8182e 100644 --- a/docs/guidebook/data_preparation.md +++ b/docs/guidebook/data_preparation.md @@ -1,43 +1,52 @@ # Подготовка данных для планировщика SAMPO +Этот документ описывает способы генерации графов и подрядчиков для тестирования и работы планировщика **SAMPO**. + ## Оглавление * [1. Генерация графа](#1-генерация-графа) * [1.1 Синтетические графы](#11-синтетические-графы) - * [1.2 Сохранение/загрузка WorkGraph](#12-сохранениезагрузка-workgraph) + * [1.2 Сохранение и загрузка WorkGraph](#12-сохранение-и-загрузка-workgraph) * [2. Генерация подрядчиков](#2-генерация-подрядчиков) * [2.1 Ручная генерация подрядчика](#21-ручная-генерация-подрядчика) * [2.2 Синтетическая генерация подрядчиков](#22-синтетическая-генерация-подрядчиков) * [2.3 Генерация подрядчика из графа](#23-генерация-подрядчика-из-графа) - * [2.4 Сохранение/загрузка Contractor](#24-сохранениезагрузка-contractor) + * [2.4 Сохранение и загрузка Contractor](#24-сохранение-и-загрузка-contractor) + +--- ## 1. Генерация графа ### 1.1 Синтетические графы -* Импорт: `SimpleSynthetic`, `SyntheticGraphType`. -* Воспроизводимость: фиксируем зерно `r_seed`. -* Базовый граф: кластерная структура с ограничениями на число работ. -* Продвинутый граф: задаём верхние границы по числу работ и уникальных сущностей. +Импортируем генератор и типы графов: ```python from sampo.generator.base import SimpleSynthetic from sampo.generator.types import SyntheticGraphType -r_seed = 231 +r_seed = 231 # фиксируем зерно для воспроизводимости ss = SimpleSynthetic(r_seed) +``` + +**Варианты графов:** -# Базовый синтетический граф +* **Базовый граф** — кластерная структура с ограничениями на число работ: + +```python simple_wg = ss.work_graph( mode=SyntheticGraphType.GENERAL, cluster_counts=10, bottom_border=100, top_border=200, ) +``` -# Продвинутый синтетический граф +* **Продвинутый граф** — с верхними границами по числу работ и уникальных сущностей: + +```python adv_wg = ss.advanced_work_graph( works_count_top_border=2000, uniq_works=300, @@ -45,26 +54,41 @@ adv_wg = ss.advanced_work_graph( ) ``` -### 1.2 Сохранение/загрузка WorkGraph +[к оглавлению](#оглавление) + +--- + +### 1.2 Сохранение и загрузка WorkGraph + +Формат JSON. Методы одинаковы для любых объектов SAMPO. + +* `dump(dir, name)` → сохраняет объект в `name.json`. +* `load(dir, name)` → **устаревший метод** (deprecated), рекомендуется использовать `loadf`. -* Сохранение: `WorkGraph.dump(dir, name)` → `name.json`. -* Загрузка: `WorkGraph.load(dir, name)`. -* Проверка идентичности по числу вершин. +Пример: ```python from sampo.schemas.graph import WorkGraph +# Сохраняем граф simple_wg.dump(".", "wg") + +# Загружаем граф (устаревший метод, используйте loadf) loaded_simple_wg = WorkGraph.load(".", "wg") + +# Проверяем идентичность assert simple_wg.vertex_count == loaded_simple_wg.vertex_count ``` +[к оглавлению](#оглавление) + +--- + ## 2. Генерация подрядчиков ### 2.1 Ручная генерация подрядчика -* Импорт: `Contractor`, `Worker`, `uuid4`. -* Заполняем имя и набор ресурсов с количествами. +Создание подрядчика с нужными ресурсами вручную: ```python from uuid import uuid4 @@ -75,24 +99,32 @@ manual_contractor = Contractor( id=str(uuid4()), name="OOO Berezka", workers={ - "builder": Worker(id=str(uuid4()), name="builder", count=100), + "builder": Worker(id=str(uuid4()), name="builder", count=100), # 100 строителей }, ) ``` +[к оглавлению](#оглавление) + +--- + ### 2.2 Синтетическая генерация подрядчиков -* Быстрая генерация тестовых подрядчиков по масштабу ресурсообеспечения. +Быстрая генерация тестовых подрядчиков с разным масштабом ресурсов: ```python -c5 = ss.contractor(5) -c10 = ss.contractor(10) -c15 = ss.contractor(15) +c5 = ss.contractor(5) # подрядчик с малым числом ресурсов +c10 = ss.contractor(10) # средний масштаб +c15 = ss.contractor(15) # крупный масштаб ``` +[к оглавлению](#оглавление) + +--- + ### 2.3 Генерация подрядчика из графа -* Генерация покрытия потребностей конкретного графа. +Автоматическое покрытие потребностей конкретного графа: ```python from sampo.generator.environment import get_contractor_by_wg @@ -100,14 +132,26 @@ from sampo.generator.environment import get_contractor_by_wg contractors = [get_contractor_by_wg(simple_wg)] ``` -### 2.4 Сохранение/загрузка Contractor +[к оглавлению](#оглавление) + +--- + +### 2.4 Сохранение и загрузка Contractor -* Сохранение: `Contractor.dump(dir, name)` → `name.json`. -* Загрузка: `Contractor.load(dir, name)`. +Аналогично WorkGraph: + +* `Contractor.dump(dir, name)` → сохраняет объект в `name.json`. +* `Contractor.load(dir, name)` → **устаревший метод**, рекомендуется использовать `loadf`. + +Пример: ```python +# Сохраняем подрядчика contractors[0].dump(".", "contractor") +# Загружаем подрядчика (устаревший метод) from sampo.schemas.contractor import Contractor loaded_contractor = Contractor.load(".", "contractor") ``` + +[к оглавлению](#оглавление) From ec7bdf8a015537b3a3de3f9d96c9c804c04a1f28 Mon Sep 17 00:00:00 2001 From: NickMur Date: Fri, 12 Sep 2025 16:58:48 +0300 Subject: [PATCH 03/32] doc/v2: local_optimization.md --- docs/guidebook/local_optimization.md | 82 +++++++++++++++++++++------- 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/docs/guidebook/local_optimization.md b/docs/guidebook/local_optimization.md index 82087b51..bf9b57fc 100644 --- a/docs/guidebook/local_optimization.md +++ b/docs/guidebook/local_optimization.md @@ -1,5 +1,7 @@ # Локальная оптимизация в SAMPO +Документ описывает использование локальных оптимизаторов для улучшения расписаний в **SAMPO**. + ## Оглавление * [1. Подготовка данных](#1-подготовка-данных) @@ -12,16 +14,19 @@ * [2.2 Оптимизация расписания (Schedule)](#22-оптимизация-расписания-schedule) * [2.3 Комбинированное применение](#23-комбинированное-применение) +--- + ## 1. Подготовка данных ### 1.1 Генерация графа и ресурсов -* Используем синтетический генератор и подрядчика из графа. -* Фиксируем `r_seed` для воспроизводимости. +Используем синтетический генератор для построения графа и формируем подрядчика на основе его потребностей. +Чтобы избежать ошибок при локальной оптимизации, рекомендуется зафиксировать `id` подрядчика и использовать один и тот же объект на всех шагах. ```python +from uuid import uuid5, NAMESPACE_DNS from sampo.generator.base import SimpleSynthetic -from sampo.generator.types import SyntheticGraphType +from sampo.generator.pipeline import SyntheticGraphType from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg r_seed = 231 @@ -34,13 +39,22 @@ simple_wg = ss.work_graph( top_border=200, ) -contractors = [get_contractor_by_wg(simple_wg)] +# Генерация подрядчика и фиксация стабильного id +contractor = get_contractor_by_wg(simple_wg) +contractor.id = str(uuid5(NAMESPACE_DNS, "contractor-by-wg")) +contractors = [contractor] ``` +[к оглавлению](#оглавление) + +--- + ### 1.2 Планировщик -* Базовый планировщик: `HEFTScheduler`. -* Конвейер: `SchedulingPipeline`. +Для построения расписания используется планировщик и конвейер: + +* базовый планировщик: `HEFTScheduler`; +* конвейер: `SchedulingPipeline`. ```python from sampo.scheduler.heft.base import HEFTScheduler @@ -49,17 +63,23 @@ from sampo.pipeline import SchedulingPipeline scheduler = HEFTScheduler() ``` +[к оглавлению](#оглавление) + +--- + ## 2. Локальная оптимизация -В SAMPO два вида локальной оптимизации: +В SAMPO доступны два типа локальной оптимизации: -* **Order** — перестановка порядка планирования работ для улучшения результата. -* **Schedule** — перерасчёт частей расписания для уменьшения времени выполнения. +* **Order** — перестановка порядка планирования работ; +* **Schedule** — перерасчёт частей расписания с использованием альтернативных таймлайнов. + +--- ### 2.1 Оптимизация порядка (Scheduling order) -* Класс: `SwapOrderLocalOptimizer`. -* Применение к поддиапазону вершин графа через `.optimize_local(...)` до вызова `.schedule(...)`. +Класс: `SwapOrderLocalOptimizer`. +Применяется к диапазону вершин графа до вызова `.schedule(...)`. ```python from sampo.scheduler.utils.local_optimization import SwapOrderLocalOptimizer @@ -76,11 +96,16 @@ project = SchedulingPipeline.create() \ project.schedule.execution_time ``` +[к оглавлению](#оглавление) + +--- + ### 2.2 Оптимизация расписания (Schedule) -* Класс: `ParallelizeScheduleLocalOptimizer`. -* Требует выбор таймлайна, например `JustInTimeTimeline`. -* Обычно применяется после базового расписания. +Класс: `ParallelizeScheduleLocalOptimizer`. +Требует выбора таймлайна, например `JustInTimeTimeline`. +Обычно применяется после построения базового расписания. +Важно: необходимо использовать один и тот же объект подрядчика с фиксированным `id`. ```python from sampo.scheduler.timeline.just_in_time_timeline import JustInTimeTimeline @@ -98,28 +123,43 @@ project = SchedulingPipeline.create() \ project.schedule.execution_time ``` +[к оглавлению](#оглавление) + +--- + ### 2.3 Комбинированное применение -* Можно стекать несколько локальных оптимизаторов. -* Пример: сначала оптимизация порядка на разных диапазонах, затем расписания. +Можно комбинировать несколько оптимизаторов. +Пример: сначала оптимизация порядка на двух частях графа, затем оптимизация расписания на этих же диапазонах. +Следите, чтобы подрядчик был один и тот же, без пересоздания. ```python -from sampo.pipeline import SchedulingPipeline from sampo.scheduler.utils.local_optimization import SwapOrderLocalOptimizer, ParallelizeScheduleLocalOptimizer from sampo.scheduler.timeline.just_in_time_timeline import JustInTimeTimeline order_optimizer = SwapOrderLocalOptimizer() schedule_optimizer = ParallelizeScheduleLocalOptimizer(JustInTimeTimeline) +half = simple_wg.vertex_count // 2 + project = SchedulingPipeline.create() \ .wg(simple_wg) \ .contractors(contractors) \ - .optimize_local(order_optimizer, range(0, simple_wg.vertex_count // 2)) \ - .optimize_local(order_optimizer, range(simple_wg.vertex_count // 2, simple_wg.vertex_count)) \ + .optimize_local(order_optimizer, range(0, half)) \ + .optimize_local(order_optimizer, range(half, simple_wg.vertex_count)) \ .schedule(scheduler) \ - .optimize_local(schedule_optimizer, range(0, simple_wg.vertex_count // 2)) \ - .optimize_local(schedule_optimizer, range(simple_wg.vertex_count // 2, simple_wg.vertex_count)) \ + .optimize_local(schedule_optimizer, range(0, half)) \ + .optimize_local(schedule_optimizer, range(half, simple_wg.vertex_count)) \ .finish()[0] project.schedule.execution_time ``` + +[к оглавлению](#оглавление) + +--- + +## Примечания + +* Если при оптимизации появляется ошибка `KeyError` по `contractor_id`, причина обычно в том, что подрядчик пересоздан или его `id` изменился. +* Для стабильной работы используйте один и тот же объект подрядчика и фиксируйте его `id`. From 4830a33d36073f2880b445319720327b452140b9 Mon Sep 17 00:00:00 2001 From: NickMur Date: Fri, 12 Sep 2025 21:47:42 +0300 Subject: [PATCH 04/32] doc/v2 --- docs/guidebook/data_preparation.md | 6 +++--- docs/guidebook/planning_tutorial.md | 6 +++--- .../py_examples_doc/field_dev_resources_time_estimator.md | 2 +- .../py_examples_doc/field_development_scheduling.md | 4 ++-- .../py_examples_doc/simple_synthetic_graph_scheduling.md | 4 ++-- docs/guidebook/scheduling_project.md | 4 ++-- docs/guidebook/simple_generation_and_planning.md | 2 +- docs/guidebook/structure_estimator.md | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/guidebook/data_preparation.md b/docs/guidebook/data_preparation.md index a5e8182e..af5ced86 100644 --- a/docs/guidebook/data_preparation.md +++ b/docs/guidebook/data_preparation.md @@ -25,7 +25,7 @@ ```python from sampo.generator.base import SimpleSynthetic -from sampo.generator.types import SyntheticGraphType +from sampo.generator import SyntheticGraphType r_seed = 231 # фиксируем зерно для воспроизводимости ss = SimpleSynthetic(r_seed) @@ -74,7 +74,7 @@ from sampo.schemas.graph import WorkGraph simple_wg.dump(".", "wg") # Загружаем граф (устаревший метод, используйте loadf) -loaded_simple_wg = WorkGraph.load(".", "wg") +loaded_simple_wg = WorkGraph.loadf(".", "wg") # Проверяем идентичность assert simple_wg.vertex_count == loaded_simple_wg.vertex_count @@ -151,7 +151,7 @@ contractors[0].dump(".", "contractor") # Загружаем подрядчика (устаревший метод) from sampo.schemas.contractor import Contractor -loaded_contractor = Contractor.load(".", "contractor") +loaded_contractor = Contractor.loadf(".", "contractor") ``` [к оглавлению](#оглавление) diff --git a/docs/guidebook/planning_tutorial.md b/docs/guidebook/planning_tutorial.md index a3c426a6..fdb3dea0 100644 --- a/docs/guidebook/planning_tutorial.md +++ b/docs/guidebook/planning_tutorial.md @@ -43,7 +43,7 @@ #### 1.2.1 WorkGraph → DataFrame -* Из `ScheduleProject` извлекается `WorkGraph`. +* Из `ScheduledProject` извлекается `WorkGraph`. * Преобразование графа в `pandas.DataFrame` для анализа и восстановления лагов. #### 1.2.2 Автоподбор подрядчиков по WorkGraph @@ -59,7 +59,7 @@ ### 1.x Интеграция в STAIRS * Источники: CSV, БД (`postgresql_url`), сериализованный проект. -* После `.finish()` доступен `ScheduleProject`; из него получают `Schedule` и `wg.to_frame(save_req=True)` для СППР. +* После `.finish()` доступен `ScheduledProject`; из него получают `Schedule` и `wg.to_frame(save_req=True)` для СППР. ## 2. Инициализация алгоритмов планирования @@ -113,7 +113,7 @@ #### 3.1.6 Построение расписания * Запуск выбранного планировщика (`HEFT*`, `Topological*`, `GeneticScheduler`). -* На выходе `ScheduleProject` и `Schedule`; поддерживается визуализация (`schedule_gant_chart_fig`, `VisualizationMode`). +* На выходе `ScheduledProject` и `Schedule`; поддерживается визуализация (`schedule_gant_chart_fig`, `VisualizationMode`). ### 3.2 Полное планирование и экспорт diff --git a/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md b/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md index e583a72f..9e5a9424 100644 --- a/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md +++ b/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md @@ -179,7 +179,7 @@ def set_productivity_mode( from typing import Type def get_recreate_info(self) -> tuple[Type, tuple]: - return FieldDevWorkEstimator, tuple(self._url) # ('t','e','s','t') для "test" + return FieldDevWorkEstimator, (self._url,) # "test" ``` --- diff --git a/docs/guidebook/py_examples_doc/field_development_scheduling.md b/docs/guidebook/py_examples_doc/field_development_scheduling.md index f63be399..d871f96d 100644 --- a/docs/guidebook/py_examples_doc/field_development_scheduling.md +++ b/docs/guidebook/py_examples_doc/field_development_scheduling.md @@ -38,7 +38,7 @@ warnings.filterwarnings("ignore") Чтение структуры задач из подготовленного JSON (`./field_development_tasks_structure.json`). ```python -field_development_wg = WorkGraph.load("./", "field_development_tasks_structure") +field_development_wg = WorkGraph.loadf("./", "field_development_tasks_structure") ``` ## 3. Подрядчики: файл или автогенерация @@ -49,7 +49,7 @@ field_development_wg = WorkGraph.load("./", "field_development_tasks_structure") use_contractors_from_file = False if use_contractors_from_file: - contractors = Contractor.load("./", "field_development_contractors_info") + contractors = [Contractor.loadf("./", "field_development_contractors_info")] else: contractors = [get_contractor_by_wg(field_development_wg, scaler=3)] ``` diff --git a/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md b/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md index 03d4f9e5..9d402e1c 100644 --- a/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md +++ b/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md @@ -59,8 +59,8 @@ res_kind_count = len(set(req.kind for req in chain(*[n.work_unit.worker_reqs for print(works_count, work_names_count, res_kind_count) assert (works_count <= synth_works_top_border * 1.1) -assert (work_names_count <= synth_works_top_border) -assert (res_kind_count <= synth_works_top_border) +assert (work_names_count <= synth_unique_works) +assert (res_kind_count <= synth_resources) ``` ## 4. Подрядчик по графу diff --git a/docs/guidebook/scheduling_project.md b/docs/guidebook/scheduling_project.md index 127fb540..320f509e 100644 --- a/docs/guidebook/scheduling_project.md +++ b/docs/guidebook/scheduling_project.md @@ -1,4 +1,4 @@ -# Проект планирования в SAMPO (SchedulingProject) +# Проект планирования в SAMPO (ScheduledProject) ## Оглавление @@ -22,7 +22,7 @@ ```python from sampo.generator.base import SimpleSynthetic -from sampo.generator.types import SyntheticGraphType +from sampo.generator import SyntheticGraphType r_seed = 231 ss = SimpleSynthetic(r_seed) diff --git a/docs/guidebook/simple_generation_and_planning.md b/docs/guidebook/simple_generation_and_planning.md index cb8ffd71..ce6d60b5 100644 --- a/docs/guidebook/simple_generation_and_planning.md +++ b/docs/guidebook/simple_generation_and_planning.md @@ -28,7 +28,7 @@ ```python from sampo.generator.base import SimpleSynthetic -from sampo.generator.types import SyntheticGraphType +from sampo.generator import SyntheticGraphType r_seed = 231 ss = SimpleSynthetic(r_seed) diff --git a/docs/guidebook/structure_estimator.md b/docs/guidebook/structure_estimator.md index c3234010..60cab7a0 100644 --- a/docs/guidebook/structure_estimator.md +++ b/docs/guidebook/structure_estimator.md @@ -25,7 +25,7 @@ ```python from sampo.generator.base import SimpleSynthetic -from sampo.generator import SyntheticGraphType # или: from sampo.generator.types import SyntheticGraphType +from sampo.generator import SyntheticGraphType # или: from sampo.generator.pipeline import SyntheticGraphType r_seed = 231 ss = SimpleSynthetic(r_seed) From ed2c417b8d5f160b1eeb8d54654c22af8150ba8c Mon Sep 17 00:00:00 2001 From: NickMur Date: Fri, 12 Sep 2025 22:30:16 +0300 Subject: [PATCH 05/32] doc/v2: planning_tutorial.md --- docs/guidebook/planning_tutorial.md | 303 ++++++++++++++++++++++++---- 1 file changed, 268 insertions(+), 35 deletions(-) diff --git a/docs/guidebook/planning_tutorial.md b/docs/guidebook/planning_tutorial.md index fdb3dea0..6b441cb6 100644 --- a/docs/guidebook/planning_tutorial.md +++ b/docs/guidebook/planning_tutorial.md @@ -1,5 +1,7 @@ # Туториал по планированию в SAMPO +Краткий практический гид по подготовке данных, инициализации планировщиков и построению расписания + ## Оглавление * [1. Подготовка графа работ и подрядчиков](#1-подготовка-графа-работ-и-подрядчиков) @@ -28,94 +30,325 @@ * [3.1.6 Построение расписания](#316-построение-расписания) * [3.2 Полное планирование и экспорт](#32-полное-планирование-и-экспорт) +--- + +## Импорт и базовая настройка + +```python +# Планировщики +from sampo.scheduler.genetic.base import HEFTScheduler, HEFTBetweenScheduler, GeneticScheduler +from sampo.scheduler.topological import TopologicalScheduler +from sampo.scheduler.topological.base import RandomizedTopologicalScheduler + +# Схемы и пайплайн +from sampo.schemas import ScheduledProject +from sampo.pipeline import SchedulingPipeline +from sampo.pipeline.lag_optimization import LagOptimizationStrategy + +# Подрядчики и утилиты +from sampo.generator.environment import ContractorGenerationMethod, get_contractor_by_wg +from sampo.utilities.visualization.base import VisualizationMode +from sampo.utilities.visualization.schedule import schedule_gant_chart_fig + +# Пользовательский оценщик времени/ресурсов (пример из ноутбука) +from field_dev_resources_time_estimator import FieldDevWorkEstimator + +# Табличные данные +import pandas as pd +import numpy as np + +# Пути данных +DATA_PATH = 'data/12/field_development/' +DUMPS_PATH = DATA_PATH + 'dumps/' +CSV_PATH = '' +``` + +[к оглавлению](#оглавление) + +--- + ## 1. Подготовка графа работ и подрядчиков ### 1.1 Загрузка из CSV -* Используются колонки по работам и связям. -* Для связей задаются `predecessor_ids`, типы соединений и лаги. -* При восстановлении связей по истории допустим минимальный набор полей. +Колонки по работам и связям. Для связей используются `predecessor_ids`, тип соединения и лаг. Если связи будут восстанавливаться по истории, допустимо отсутствие колонок связей. + +```python +# Пример из ноутбука +df = pd.read_csv(CSV_PATH + 'electroline_field_dev_demo.csv', sep=';') +df.head() +``` + +[к оглавлению](#оглавление) + +--- ### 1.2 Загрузка из сериализованного JSON -* Импорт проекта целиком: структура `WorkGraph` и, при наличии, `Schedule`. -* Удобно для повторной визуализации и сравнения. +Импорт проекта целиком удобен для повторной визуализации и сравнения: структура работ (`WorkGraph`) и, при наличии, расписание (`Schedule`). #### 1.2.1 WorkGraph → DataFrame -* Из `ScheduledProject` извлекается `WorkGraph`. -* Преобразование графа в `pandas.DataFrame` для анализа и восстановления лагов. +```python +# Допустим, у нас есть объект ScheduledProject (загружен из JSON или получен после планирования) +# Извлекаем WorkGraph и переводим его в DataFrame для анализа/восстановления лагов +df_with_req = scheduling_project.wg.to_frame(save_req=True) +df_with_req.head() +``` + +[к оглавлению](#оглавление) + +--- #### 1.2.2 Автоподбор подрядчиков по WorkGraph -* Формирование списка подрядчиков по потребностям графа. -* Учитываются требуемые ресурсы и специализации. +```python +# project_wg — объект WorkGraph +from sampo.generator.environment import get_contractor_by_wg, ContractorGenerationMethod + +# Даже одного подрядчика оборачиваем списком +project_contractors = [get_contractor_by_wg( + wg=project_wg, + method=ContractorGenerationMethod.AVG, + contractor_name='Main contractor' +)] + +# Короткий вариант +project_contractors = [get_contractor_by_wg(wg=project_wg, contractor_name='Main contractor')] +``` + +[к оглавлению](#оглавление) + +--- #### 1.2.3 Загрузка расписания из JSON -* Из сериализованного файла извлекается объект `Schedule`. -* Подходит для сравнения с новыми расчётами. +```python +# Если из сериализованного проекта уже получен Schedule: +raw_schedule = scheduling_project.schedule + +# Переводим в человекочитаемые даты, задав дату старта проекта +project_schedule = raw_schedule.merged_stages_datetime_df('2022-09-01') +project_schedule.head() + +# Визуализация диаграммы Ганта средствами SAMPO +_ = schedule_gant_chart_fig( + schedule_dataframe=project_schedule, + visualization=VisualizationMode.ShowFig, # также доступны ReturnFig и SaveFig + remove_service_tasks=False +) +``` + +[к оглавлению](#оглавление) + +--- ### 1.x Интеграция в STAIRS -* Источники: CSV, БД (`postgresql_url`), сериализованный проект. -* После `.finish()` доступен `ScheduledProject`; из него получают `Schedule` и `wg.to_frame(save_req=True)` для СППР. +```python +# Подготовка DataFrame к загрузке в STAIRS +df = pd.read_csv(CSV_PATH + 'electroline_field_dev_demo.csv', sep=';') +df['measurement'] = df['granular_measurement'] +df['activity_id'] = list(range(200, 200 + len(df))) # пример автонумерации + +# Подключение к БД для пользовательского оценщика +db_url = "postgresql+psycopg2://testuser:pwd@10.32.15.30:25432/test" +project_work_estimator = FieldDevWorkEstimator(url=db_url) +``` + +После `.finish()` пайплайна доступен `ScheduledProject`. Из него: `Schedule` и `wg.to_frame(save_req=True)` для СППР. + +[к оглавлению](#оглавление) + +--- ## 2. Инициализация алгоритмов планирования ### 2.1 Пользовательский WorkTimeEstimator -* Своя модель длительностей и ресурсов, например `FieldDevWorkEstimator`. -* Один экземпляр используется всеми планировщиками. +Один экземпляр оценщика используется всеми планировщиками. + +```python +# Пример из ноутбука +project_work_estimator = FieldDevWorkEstimator() # модель, реализующая интерфейс WorkTimeEstimator +``` + +[к оглавлению](#оглавление) + +--- ### 2.2 Эвристические и топологические планировщики -* `HEFTScheduler`, `HEFTBetweenScheduler`, `TopologicalScheduler`, `RandomizedTopologicalScheduler`. -* Не требуют сложной настройки. +```python +# Топологические +topo_scheduler = TopologicalScheduler(work_estimator=project_work_estimator) +rand_topo_scheduler = RandomizedTopologicalScheduler(work_estimator=project_work_estimator) + +# HEFT-эвристики +heft_scheduler = HEFTScheduler(work_estimator=project_work_estimator) +heft_between_scheduler = HEFTBetweenScheduler(work_estimator=project_work_estimator) +``` + +[к оглавлению](#оглавление) + +--- ### 2.3 Генетический алгоритм: гиперпараметры -* `GeneticScheduler` с авто-подбором поколений или явными параметрами. -* Проверка настроек на малых примерах перед основным запуском. +Два режима из ноутбука: с авто-выбором числа поколений и с явными параметрами. + +```python +# Вариант с авто-подбором количества поколений (пример) +custom_number_of_generation = np.random.randint(10, 25) +custom_mutate_order = 0.05 +custom_mutate_resources = 0.005 +custom_size_of_population = 50 + +# Инициализация генетического планировщика +genetic_scheduler = GeneticScheduler( + number_of_generation=custom_number_of_generation, + mutate_order=custom_mutate_order, + mutate_resources=custom_mutate_resources, + size_of_population=custom_size_of_population, + work_estimator=project_work_estimator +) +``` + +```python +# Вариант с фиксированными настройками (пример) +custom_number_of_generation = 20 +custom_mutate_order = 0.1 # из диапазона ~0.05..0.15 +custom_mutate_resources = 0.01 # из диапазона ~0.005..0.015 +custom_size_of_population = 50 + +genetic_scheduler = GeneticScheduler( + number_of_generation=custom_number_of_generation, + mutate_order=custom_mutate_order, + mutate_resources=custom_mutate_resources, + size_of_population=custom_size_of_population, + work_estimator=project_work_estimator +) +``` + +[к оглавлению](#оглавление) + +--- ## 3. Построение расписания через пайплайн ### 3.1 Настройка пайплайна -* Последовательная передача графа, подрядчиков, истории и оценщика. -* Опционально: `LagOptimizationStrategy`. +Последовательная передача графа, подрядчиков, истории и оценщика. Опционально — оптимизация лагов. + +```python +# Старт пайплайна +scheduling_pipeline = SchedulingPipeline.create() +``` + +[к оглавлению](#оглавление) + +--- #### 3.1.1 Восстановление структуры и лагов по истории -* Использование `DataFrame` из `WorkGraph`. -* Восстановление типов связей и лагов по истории. +```python +# df_with_req — DataFrame графа (см. 1.2.1) +# Параметры восстановления: пример с отключением автосмены типов связей +scheduling_pipeline = scheduling_pipeline.wg( + wg=df_with_req, + all_connections=False, + # change_connections_* — используйте режимы восстановления только при неполных связях +) +``` + +[к оглавлению](#оглавление) + +--- #### 3.1.2 Передача подрядчиков -* В пайплайн передаётся список подрядчиков. -* Возможен автоподбор по `WorkGraph`. +```python +# Передаем список подрядчиков (см. 1.2.2) +scheduling_pipeline = scheduling_pipeline.contractors(project_contractors) +``` + +[к оглавлению](#оглавление) + +--- #### 3.1.3 Передача исторических данных -* Загрузка `history_df` из CSV. -* История учитывается при реструктуризации и оценке времени. +```python +# Исторические проекты для восстановления типов связей и лагов +history_df = pd.read_csv('historical_projects_data.csv', sep=';') +scheduling_pipeline = scheduling_pipeline.history(history_df, sep=';') +``` + +[к оглавлению](#оглавление) + +--- #### 3.1.4 Реструктуризация графа -* Нормализация структуры работ с учётом истории и ресурсов. -* Результат — обновлённый `DataFrame`. +Оптимизация параллелизма с учетом связей F–F(S) и временных лагов. + +```python +# Явно укажите стратегию. В ноутбуке рекомендуется задавать явно. +scheduling_pipeline = scheduling_pipeline.lag_optimize(LagOptimizationStrategy.TRUE) +``` + +[к оглавлению](#оглавление) + +--- #### 3.1.5 Подключение оценщика времени -* Подключение пользовательской модели для длительностей и ресурсов. -* Единый оценщик на всех этапах. +```python +# Единый оценщик на всех этапах +scheduling_pipeline = scheduling_pipeline.work_estimator(project_work_estimator) +``` + +[к оглавлению](#оглавление) + +--- #### 3.1.6 Построение расписания -* Запуск выбранного планировщика (`HEFT*`, `Topological*`, `GeneticScheduler`). -* На выходе `ScheduledProject` и `Schedule`; поддерживается визуализация (`schedule_gant_chart_fig`, `VisualizationMode`). +```python +# Выбор планировщика: topo_scheduler | rand_topo_scheduler | heft_scheduler | heft_between_scheduler | genetic_scheduler +schedule_project = scheduling_pipeline.schedule(genetic_scheduler).finish()[0] + +# Быстрый просмотр +schedule_project.schedule.pure_schedule_df.head() + +# Перевод к датам и визуализация +project_schedule = schedule_project.schedule.merged_stages_datetime_df('2022-09-01') +_ = schedule_gant_chart_fig(project_schedule, VisualizationMode.ShowFig, remove_service_tasks=False) +``` + +[к оглавлению](#оглавление) + +--- ### 3.2 Полное планирование и экспорт -* Сквозной прогон через `SchedulingPipeline.create() … .finish()[0]`. -* Просмотр структуры графа, получение `Schedule`, экспорт в JSON для дальнейшей загрузки в СППР. +```python +# Сквозной прогон +schedule_project2 = SchedulingPipeline.create() \ + .wg(df_with_req, all_connections=False) \ + .contractors(project_contractors) \ + .history(history_df, sep=';') \ + .lag_optimize(LagOptimizationStrategy.TRUE) \ + .work_estimator(project_work_estimator) \ + .schedule(genetic_scheduler) \ + .finish()[0] + +# Итоговое расписание +raw_project_schedule = schedule_project2.schedule +project_schedule = raw_project_schedule.merged_stages_datetime_df('2022-09-01') + +# Экспорт структуры проекта для СППР +schedule_project2.dump('.', 'gas_network_full_connections_genetic_upd') +``` + +[к оглавлению](#оглавление) From d2d7c8e8559978b76ef069c4323b0358d95c003d Mon Sep 17 00:00:00 2001 From: NickMur Date: Tue, 16 Sep 2025 13:36:13 +0300 Subject: [PATCH 06/32] =?UTF-8?q?=D0=9F=D0=BE=D0=B4=D0=B3=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20guidebook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guidebook/Makefile | 20 +++++++++++++++++++ docs/guidebook/make.bat | 35 +++++++++++++++++++++++++++++++++ docs/guidebook/source/conf.py | 28 ++++++++++++++++++++++++++ docs/guidebook/source/index.rst | 17 ++++++++++++++++ docs/source/conf.py | 4 +++- docs/source/guidebook/index.md | 10 ++++++++++ docs/source/index.rst | 3 ++- 7 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 docs/guidebook/Makefile create mode 100644 docs/guidebook/make.bat create mode 100644 docs/guidebook/source/conf.py create mode 100644 docs/guidebook/source/index.rst create mode 100644 docs/source/guidebook/index.md diff --git a/docs/guidebook/Makefile b/docs/guidebook/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/guidebook/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/guidebook/make.bat b/docs/guidebook/make.bat new file mode 100644 index 00000000..dc1312ab --- /dev/null +++ b/docs/guidebook/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/guidebook/source/conf.py b/docs/guidebook/source/conf.py new file mode 100644 index 00000000..64103306 --- /dev/null +++ b/docs/guidebook/source/conf.py @@ -0,0 +1,28 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'guidebook_web' +copyright = '2025, Mur' +author = 'Mur' +release = '1' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [] + +templates_path = ['_templates'] +exclude_patterns = [] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'alabaster' +html_static_path = ['_static'] diff --git a/docs/guidebook/source/index.rst b/docs/guidebook/source/index.rst new file mode 100644 index 00000000..5f30667b --- /dev/null +++ b/docs/guidebook/source/index.rst @@ -0,0 +1,17 @@ +.. guidebook_web documentation master file, created by + sphinx-quickstart on Tue Sep 16 13:18:26 2025. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +guidebook_web documentation +=========================== + +Add your content using ``reStructuredText`` syntax. See the +`reStructuredText `_ +documentation for details. + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + diff --git a/docs/source/conf.py b/docs/source/conf.py index 6228b139..b6390d17 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -34,7 +34,9 @@ # extensions = ['sphinx.ext.duration', 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.doctest', # 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig'] -extensions = ['autoapi.extension' +extensions = [ + 'autoapi.extension', + 'myst_parser' ] # todo_include_todos = True diff --git a/docs/source/guidebook/index.md b/docs/source/guidebook/index.md new file mode 100644 index 00000000..2630791e --- /dev/null +++ b/docs/source/guidebook/index.md @@ -0,0 +1,10 @@ +# Гайдбук + +```{toctree} +:maxdepth: 2 +:numbered: +caption: Содержание гайдбука +intro +setup +workflow +tips diff --git a/docs/source/index.rst b/docs/source/index.rst index f65a1dd0..3f6a4425 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,13 +7,14 @@ Welcome to SAMPO's documentation! ================================= .. toctree:: - :maxdepth: 1 + :maxdepth: 2 :caption: Contents: Install Usage Features autoapi/index + guidebook/index Supported by From d55e67cd9baf79c4239d8d297a82b917efc95ffb Mon Sep 17 00:00:00 2001 From: NickMur Date: Tue, 16 Sep 2025 23:12:37 +0300 Subject: [PATCH 07/32] =?UTF-8?q?=D0=9E=D0=B1=D0=BE=D0=B7=D0=BD=D0=B0?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D1=82=D1=80=D1=83=D0=BA?= =?UTF-8?q?=D1=82=D1=83=D1=80=D1=8B=20=D0=B3=D0=B0=D0=B9=D0=B4=D0=B1=D1=83?= =?UTF-8?q?=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guidebook/Makefile | 20 - docs/guidebook/data_preparation.md | 157 -------- docs/guidebook/local_optimization.md | 165 -------- docs/guidebook/make.bat | 35 -- docs/guidebook/planning_tutorial.md | 354 ------------------ .../aircraft_stochastic_maintenance.md | 140 ------- .../py_examples_doc/capital_construction.md | 124 ------ .../dynamic_maintenance_tasks_scheduling.md | 119 ------ .../field_dev_resources_time_estimator.md | 218 ----------- .../field_development_scheduling.md | 109 ------ .../py_examples_doc/generator_scenarios.md | 125 ------- .../landscape_configuration.md | 151 -------- .../simple_synthetic_graph_scheduling.md | 104 ----- docs/guidebook/scheduling_project.md | 104 ----- .../simple_generation_and_planning.md | 208 ---------- docs/guidebook/source/conf.py | 28 -- docs/guidebook/source/index.rst | 17 - docs/guidebook/structure_estimator.md | 98 ----- docs/guidebook/visualization.md | 118 ------ docs/source/guidebook/advanced.md | 43 +++ docs/source/guidebook/developers.md | 50 +++ docs/source/guidebook/index.md | 12 +- docs/source/guidebook/intro.md | 43 +++ docs/source/guidebook/quickstart.md | 124 ++++++ 24 files changed, 267 insertions(+), 2399 deletions(-) delete mode 100644 docs/guidebook/Makefile delete mode 100644 docs/guidebook/data_preparation.md delete mode 100644 docs/guidebook/local_optimization.md delete mode 100644 docs/guidebook/make.bat delete mode 100644 docs/guidebook/planning_tutorial.md delete mode 100644 docs/guidebook/py_examples_doc/aircraft_stochastic_maintenance.md delete mode 100644 docs/guidebook/py_examples_doc/capital_construction.md delete mode 100644 docs/guidebook/py_examples_doc/dynamic_maintenance_tasks_scheduling.md delete mode 100644 docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md delete mode 100644 docs/guidebook/py_examples_doc/field_development_scheduling.md delete mode 100644 docs/guidebook/py_examples_doc/generator_scenarios.md delete mode 100644 docs/guidebook/py_examples_doc/landscape_configuration.md delete mode 100644 docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md delete mode 100644 docs/guidebook/scheduling_project.md delete mode 100644 docs/guidebook/simple_generation_and_planning.md delete mode 100644 docs/guidebook/source/conf.py delete mode 100644 docs/guidebook/source/index.rst delete mode 100644 docs/guidebook/structure_estimator.md delete mode 100644 docs/guidebook/visualization.md create mode 100644 docs/source/guidebook/advanced.md create mode 100644 docs/source/guidebook/developers.md create mode 100644 docs/source/guidebook/intro.md create mode 100644 docs/source/guidebook/quickstart.md diff --git a/docs/guidebook/Makefile b/docs/guidebook/Makefile deleted file mode 100644 index d0c3cbf1..00000000 --- a/docs/guidebook/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/guidebook/data_preparation.md b/docs/guidebook/data_preparation.md deleted file mode 100644 index af5ced86..00000000 --- a/docs/guidebook/data_preparation.md +++ /dev/null @@ -1,157 +0,0 @@ -# Подготовка данных для планировщика SAMPO - -Этот документ описывает способы генерации графов и подрядчиков для тестирования и работы планировщика **SAMPO**. - -## Оглавление - -* [1. Генерация графа](#1-генерация-графа) - - * [1.1 Синтетические графы](#11-синтетические-графы) - * [1.2 Сохранение и загрузка WorkGraph](#12-сохранение-и-загрузка-workgraph) -* [2. Генерация подрядчиков](#2-генерация-подрядчиков) - - * [2.1 Ручная генерация подрядчика](#21-ручная-генерация-подрядчика) - * [2.2 Синтетическая генерация подрядчиков](#22-синтетическая-генерация-подрядчиков) - * [2.3 Генерация подрядчика из графа](#23-генерация-подрядчика-из-графа) - * [2.4 Сохранение и загрузка Contractor](#24-сохранение-и-загрузка-contractor) - ---- - -## 1. Генерация графа - -### 1.1 Синтетические графы - -Импортируем генератор и типы графов: - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.generator import SyntheticGraphType - -r_seed = 231 # фиксируем зерно для воспроизводимости -ss = SimpleSynthetic(r_seed) -``` - -**Варианты графов:** - -* **Базовый граф** — кластерная структура с ограничениями на число работ: - -```python -simple_wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200, -) -``` - -* **Продвинутый граф** — с верхними границами по числу работ и уникальных сущностей: - -```python -adv_wg = ss.advanced_work_graph( - works_count_top_border=2000, - uniq_works=300, - uniq_resources=100, -) -``` - -[к оглавлению](#оглавление) - ---- - -### 1.2 Сохранение и загрузка WorkGraph - -Формат JSON. Методы одинаковы для любых объектов SAMPO. - -* `dump(dir, name)` → сохраняет объект в `name.json`. -* `load(dir, name)` → **устаревший метод** (deprecated), рекомендуется использовать `loadf`. - -Пример: - -```python -from sampo.schemas.graph import WorkGraph - -# Сохраняем граф -simple_wg.dump(".", "wg") - -# Загружаем граф (устаревший метод, используйте loadf) -loaded_simple_wg = WorkGraph.loadf(".", "wg") - -# Проверяем идентичность -assert simple_wg.vertex_count == loaded_simple_wg.vertex_count -``` - -[к оглавлению](#оглавление) - ---- - -## 2. Генерация подрядчиков - -### 2.1 Ручная генерация подрядчика - -Создание подрядчика с нужными ресурсами вручную: - -```python -from uuid import uuid4 -from sampo.schemas.contractor import Contractor -from sampo.schemas.resources import Worker - -manual_contractor = Contractor( - id=str(uuid4()), - name="OOO Berezka", - workers={ - "builder": Worker(id=str(uuid4()), name="builder", count=100), # 100 строителей - }, -) -``` - -[к оглавлению](#оглавление) - ---- - -### 2.2 Синтетическая генерация подрядчиков - -Быстрая генерация тестовых подрядчиков с разным масштабом ресурсов: - -```python -c5 = ss.contractor(5) # подрядчик с малым числом ресурсов -c10 = ss.contractor(10) # средний масштаб -c15 = ss.contractor(15) # крупный масштаб -``` - -[к оглавлению](#оглавление) - ---- - -### 2.3 Генерация подрядчика из графа - -Автоматическое покрытие потребностей конкретного графа: - -```python -from sampo.generator.environment import get_contractor_by_wg - -contractors = [get_contractor_by_wg(simple_wg)] -``` - -[к оглавлению](#оглавление) - ---- - -### 2.4 Сохранение и загрузка Contractor - -Аналогично WorkGraph: - -* `Contractor.dump(dir, name)` → сохраняет объект в `name.json`. -* `Contractor.load(dir, name)` → **устаревший метод**, рекомендуется использовать `loadf`. - -Пример: - -```python -# Сохраняем подрядчика -contractors[0].dump(".", "contractor") - -# Загружаем подрядчика (устаревший метод) -from sampo.schemas.contractor import Contractor -loaded_contractor = Contractor.loadf(".", "contractor") -``` - -[к оглавлению](#оглавление) diff --git a/docs/guidebook/local_optimization.md b/docs/guidebook/local_optimization.md deleted file mode 100644 index bf9b57fc..00000000 --- a/docs/guidebook/local_optimization.md +++ /dev/null @@ -1,165 +0,0 @@ -# Локальная оптимизация в SAMPO - -Документ описывает использование локальных оптимизаторов для улучшения расписаний в **SAMPO**. - -## Оглавление - -* [1. Подготовка данных](#1-подготовка-данных) - - * [1.1 Генерация графа и ресурсов](#11-генерация-графа-и-ресурсов) - * [1.2 Планировщик](#12-планировщик) -* [2. Локальная оптимизация](#2-локальная-оптимизация) - - * [2.1 Оптимизация порядка (Scheduling order)](#21-оптимизация-порядка-scheduling-order) - * [2.2 Оптимизация расписания (Schedule)](#22-оптимизация-расписания-schedule) - * [2.3 Комбинированное применение](#23-комбинированное-применение) - ---- - -## 1. Подготовка данных - -### 1.1 Генерация графа и ресурсов - -Используем синтетический генератор для построения графа и формируем подрядчика на основе его потребностей. -Чтобы избежать ошибок при локальной оптимизации, рекомендуется зафиксировать `id` подрядчика и использовать один и тот же объект на всех шагах. - -```python -from uuid import uuid5, NAMESPACE_DNS -from sampo.generator.base import SimpleSynthetic -from sampo.generator.pipeline import SyntheticGraphType -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg - -r_seed = 231 -ss = SimpleSynthetic(r_seed) - -simple_wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200, -) - -# Генерация подрядчика и фиксация стабильного id -contractor = get_contractor_by_wg(simple_wg) -contractor.id = str(uuid5(NAMESPACE_DNS, "contractor-by-wg")) -contractors = [contractor] -``` - -[к оглавлению](#оглавление) - ---- - -### 1.2 Планировщик - -Для построения расписания используется планировщик и конвейер: - -* базовый планировщик: `HEFTScheduler`; -* конвейер: `SchedulingPipeline`. - -```python -from sampo.scheduler.heft.base import HEFTScheduler -from sampo.pipeline import SchedulingPipeline - -scheduler = HEFTScheduler() -``` - -[к оглавлению](#оглавление) - ---- - -## 2. Локальная оптимизация - -В SAMPO доступны два типа локальной оптимизации: - -* **Order** — перестановка порядка планирования работ; -* **Schedule** — перерасчёт частей расписания с использованием альтернативных таймлайнов. - ---- - -### 2.1 Оптимизация порядка (Scheduling order) - -Класс: `SwapOrderLocalOptimizer`. -Применяется к диапазону вершин графа до вызова `.schedule(...)`. - -```python -from sampo.scheduler.utils.local_optimization import SwapOrderLocalOptimizer - -order_optimizer = SwapOrderLocalOptimizer() - -project = SchedulingPipeline.create() \ - .wg(simple_wg) \ - .contractors(contractors) \ - .optimize_local(order_optimizer, range(0, 10)) \ - .schedule(scheduler) \ - .finish()[0] - -project.schedule.execution_time -``` - -[к оглавлению](#оглавление) - ---- - -### 2.2 Оптимизация расписания (Schedule) - -Класс: `ParallelizeScheduleLocalOptimizer`. -Требует выбора таймлайна, например `JustInTimeTimeline`. -Обычно применяется после построения базового расписания. -Важно: необходимо использовать один и тот же объект подрядчика с фиксированным `id`. - -```python -from sampo.scheduler.timeline.just_in_time_timeline import JustInTimeTimeline -from sampo.scheduler.utils.local_optimization import ParallelizeScheduleLocalOptimizer - -schedule_optimizer = ParallelizeScheduleLocalOptimizer(JustInTimeTimeline) - -project = SchedulingPipeline.create() \ - .wg(simple_wg) \ - .contractors(contractors) \ - .schedule(scheduler) \ - .optimize_local(schedule_optimizer, range(0, 5)) \ - .finish()[0] - -project.schedule.execution_time -``` - -[к оглавлению](#оглавление) - ---- - -### 2.3 Комбинированное применение - -Можно комбинировать несколько оптимизаторов. -Пример: сначала оптимизация порядка на двух частях графа, затем оптимизация расписания на этих же диапазонах. -Следите, чтобы подрядчик был один и тот же, без пересоздания. - -```python -from sampo.scheduler.utils.local_optimization import SwapOrderLocalOptimizer, ParallelizeScheduleLocalOptimizer -from sampo.scheduler.timeline.just_in_time_timeline import JustInTimeTimeline - -order_optimizer = SwapOrderLocalOptimizer() -schedule_optimizer = ParallelizeScheduleLocalOptimizer(JustInTimeTimeline) - -half = simple_wg.vertex_count // 2 - -project = SchedulingPipeline.create() \ - .wg(simple_wg) \ - .contractors(contractors) \ - .optimize_local(order_optimizer, range(0, half)) \ - .optimize_local(order_optimizer, range(half, simple_wg.vertex_count)) \ - .schedule(scheduler) \ - .optimize_local(schedule_optimizer, range(0, half)) \ - .optimize_local(schedule_optimizer, range(half, simple_wg.vertex_count)) \ - .finish()[0] - -project.schedule.execution_time -``` - -[к оглавлению](#оглавление) - ---- - -## Примечания - -* Если при оптимизации появляется ошибка `KeyError` по `contractor_id`, причина обычно в том, что подрядчик пересоздан или его `id` изменился. -* Для стабильной работы используйте один и тот же объект подрядчика и фиксируйте его `id`. diff --git a/docs/guidebook/make.bat b/docs/guidebook/make.bat deleted file mode 100644 index dc1312ab..00000000 --- a/docs/guidebook/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "" goto help - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/guidebook/planning_tutorial.md b/docs/guidebook/planning_tutorial.md deleted file mode 100644 index 6b441cb6..00000000 --- a/docs/guidebook/planning_tutorial.md +++ /dev/null @@ -1,354 +0,0 @@ -# Туториал по планированию в SAMPO - -Краткий практический гид по подготовке данных, инициализации планировщиков и построению расписания - -## Оглавление - -* [1. Подготовка графа работ и подрядчиков](#1-подготовка-графа-работ-и-подрядчиков) - - * [1.1 Загрузка из CSV](#11-загрузка-из-csv) - * [1.2 Загрузка из сериализованного JSON](#12-загрузка-из-сериализованного-json) - - * [1.2.1 WorkGraph → DataFrame](#121-workgraph--dataframe) - * [1.2.2 Автоподбор подрядчиков по WorkGraph](#122-автоподбор-подрядчиков-по-workgraph) - * [1.2.3 Загрузка расписания из JSON](#123-загрузка-расписания-из-json) - * [1.x Интеграция в STAIRS](#1x-интеграция-в-stairs) -* [2. Инициализация алгоритмов планирования](#2-инициализация-алгоритмов-планирования) - - * [2.1 Пользовательский WorkTimeEstimator](#21-пользовательский-worktimeestimator) - * [2.2 Эвристические и топологические планировщики](#22-эвристические-и-топологические-планировщики) - * [2.3 Генетический алгоритм: гиперпараметры](#23-генетический-алгоритм-гиперпараметры) -* [3. Построение расписания через пайплайн](#3-построение-расписания-через-пайплайн) - - * [3.1 Настройка пайплайна](#31-настройка-пайплайна) - - * [3.1.1 Восстановление структуры и лагов по истории](#311-восстановление-структуры-и-лагов-по-истории) - * [3.1.2 Передача подрядчиков](#312-передача-подрядчиков) - * [3.1.3 Передача исторических данных](#313-передача-исторических-данных) - * [3.1.4 Реструктуризация графа](#314-реструктуризация-графа) - * [3.1.5 Подключение оценщика времени](#315-подключение-оценщика-времени) - * [3.1.6 Построение расписания](#316-построение-расписания) - * [3.2 Полное планирование и экспорт](#32-полное-планирование-и-экспорт) - ---- - -## Импорт и базовая настройка - -```python -# Планировщики -from sampo.scheduler.genetic.base import HEFTScheduler, HEFTBetweenScheduler, GeneticScheduler -from sampo.scheduler.topological import TopologicalScheduler -from sampo.scheduler.topological.base import RandomizedTopologicalScheduler - -# Схемы и пайплайн -from sampo.schemas import ScheduledProject -from sampo.pipeline import SchedulingPipeline -from sampo.pipeline.lag_optimization import LagOptimizationStrategy - -# Подрядчики и утилиты -from sampo.generator.environment import ContractorGenerationMethod, get_contractor_by_wg -from sampo.utilities.visualization.base import VisualizationMode -from sampo.utilities.visualization.schedule import schedule_gant_chart_fig - -# Пользовательский оценщик времени/ресурсов (пример из ноутбука) -from field_dev_resources_time_estimator import FieldDevWorkEstimator - -# Табличные данные -import pandas as pd -import numpy as np - -# Пути данных -DATA_PATH = 'data/12/field_development/' -DUMPS_PATH = DATA_PATH + 'dumps/' -CSV_PATH = '' -``` - -[к оглавлению](#оглавление) - ---- - -## 1. Подготовка графа работ и подрядчиков - -### 1.1 Загрузка из CSV - -Колонки по работам и связям. Для связей используются `predecessor_ids`, тип соединения и лаг. Если связи будут восстанавливаться по истории, допустимо отсутствие колонок связей. - -```python -# Пример из ноутбука -df = pd.read_csv(CSV_PATH + 'electroline_field_dev_demo.csv', sep=';') -df.head() -``` - -[к оглавлению](#оглавление) - ---- - -### 1.2 Загрузка из сериализованного JSON - -Импорт проекта целиком удобен для повторной визуализации и сравнения: структура работ (`WorkGraph`) и, при наличии, расписание (`Schedule`). - -#### 1.2.1 WorkGraph → DataFrame - -```python -# Допустим, у нас есть объект ScheduledProject (загружен из JSON или получен после планирования) -# Извлекаем WorkGraph и переводим его в DataFrame для анализа/восстановления лагов -df_with_req = scheduling_project.wg.to_frame(save_req=True) -df_with_req.head() -``` - -[к оглавлению](#оглавление) - ---- - -#### 1.2.2 Автоподбор подрядчиков по WorkGraph - -```python -# project_wg — объект WorkGraph -from sampo.generator.environment import get_contractor_by_wg, ContractorGenerationMethod - -# Даже одного подрядчика оборачиваем списком -project_contractors = [get_contractor_by_wg( - wg=project_wg, - method=ContractorGenerationMethod.AVG, - contractor_name='Main contractor' -)] - -# Короткий вариант -project_contractors = [get_contractor_by_wg(wg=project_wg, contractor_name='Main contractor')] -``` - -[к оглавлению](#оглавление) - ---- - -#### 1.2.3 Загрузка расписания из JSON - -```python -# Если из сериализованного проекта уже получен Schedule: -raw_schedule = scheduling_project.schedule - -# Переводим в человекочитаемые даты, задав дату старта проекта -project_schedule = raw_schedule.merged_stages_datetime_df('2022-09-01') -project_schedule.head() - -# Визуализация диаграммы Ганта средствами SAMPO -_ = schedule_gant_chart_fig( - schedule_dataframe=project_schedule, - visualization=VisualizationMode.ShowFig, # также доступны ReturnFig и SaveFig - remove_service_tasks=False -) -``` - -[к оглавлению](#оглавление) - ---- - -### 1.x Интеграция в STAIRS - -```python -# Подготовка DataFrame к загрузке в STAIRS -df = pd.read_csv(CSV_PATH + 'electroline_field_dev_demo.csv', sep=';') -df['measurement'] = df['granular_measurement'] -df['activity_id'] = list(range(200, 200 + len(df))) # пример автонумерации - -# Подключение к БД для пользовательского оценщика -db_url = "postgresql+psycopg2://testuser:pwd@10.32.15.30:25432/test" -project_work_estimator = FieldDevWorkEstimator(url=db_url) -``` - -После `.finish()` пайплайна доступен `ScheduledProject`. Из него: `Schedule` и `wg.to_frame(save_req=True)` для СППР. - -[к оглавлению](#оглавление) - ---- - -## 2. Инициализация алгоритмов планирования - -### 2.1 Пользовательский WorkTimeEstimator - -Один экземпляр оценщика используется всеми планировщиками. - -```python -# Пример из ноутбука -project_work_estimator = FieldDevWorkEstimator() # модель, реализующая интерфейс WorkTimeEstimator -``` - -[к оглавлению](#оглавление) - ---- - -### 2.2 Эвристические и топологические планировщики - -```python -# Топологические -topo_scheduler = TopologicalScheduler(work_estimator=project_work_estimator) -rand_topo_scheduler = RandomizedTopologicalScheduler(work_estimator=project_work_estimator) - -# HEFT-эвристики -heft_scheduler = HEFTScheduler(work_estimator=project_work_estimator) -heft_between_scheduler = HEFTBetweenScheduler(work_estimator=project_work_estimator) -``` - -[к оглавлению](#оглавление) - ---- - -### 2.3 Генетический алгоритм: гиперпараметры - -Два режима из ноутбука: с авто-выбором числа поколений и с явными параметрами. - -```python -# Вариант с авто-подбором количества поколений (пример) -custom_number_of_generation = np.random.randint(10, 25) -custom_mutate_order = 0.05 -custom_mutate_resources = 0.005 -custom_size_of_population = 50 - -# Инициализация генетического планировщика -genetic_scheduler = GeneticScheduler( - number_of_generation=custom_number_of_generation, - mutate_order=custom_mutate_order, - mutate_resources=custom_mutate_resources, - size_of_population=custom_size_of_population, - work_estimator=project_work_estimator -) -``` - -```python -# Вариант с фиксированными настройками (пример) -custom_number_of_generation = 20 -custom_mutate_order = 0.1 # из диапазона ~0.05..0.15 -custom_mutate_resources = 0.01 # из диапазона ~0.005..0.015 -custom_size_of_population = 50 - -genetic_scheduler = GeneticScheduler( - number_of_generation=custom_number_of_generation, - mutate_order=custom_mutate_order, - mutate_resources=custom_mutate_resources, - size_of_population=custom_size_of_population, - work_estimator=project_work_estimator -) -``` - -[к оглавлению](#оглавление) - ---- - -## 3. Построение расписания через пайплайн - -### 3.1 Настройка пайплайна - -Последовательная передача графа, подрядчиков, истории и оценщика. Опционально — оптимизация лагов. - -```python -# Старт пайплайна -scheduling_pipeline = SchedulingPipeline.create() -``` - -[к оглавлению](#оглавление) - ---- - -#### 3.1.1 Восстановление структуры и лагов по истории - -```python -# df_with_req — DataFrame графа (см. 1.2.1) -# Параметры восстановления: пример с отключением автосмены типов связей -scheduling_pipeline = scheduling_pipeline.wg( - wg=df_with_req, - all_connections=False, - # change_connections_* — используйте режимы восстановления только при неполных связях -) -``` - -[к оглавлению](#оглавление) - ---- - -#### 3.1.2 Передача подрядчиков - -```python -# Передаем список подрядчиков (см. 1.2.2) -scheduling_pipeline = scheduling_pipeline.contractors(project_contractors) -``` - -[к оглавлению](#оглавление) - ---- - -#### 3.1.3 Передача исторических данных - -```python -# Исторические проекты для восстановления типов связей и лагов -history_df = pd.read_csv('historical_projects_data.csv', sep=';') -scheduling_pipeline = scheduling_pipeline.history(history_df, sep=';') -``` - -[к оглавлению](#оглавление) - ---- - -#### 3.1.4 Реструктуризация графа - -Оптимизация параллелизма с учетом связей F–F(S) и временных лагов. - -```python -# Явно укажите стратегию. В ноутбуке рекомендуется задавать явно. -scheduling_pipeline = scheduling_pipeline.lag_optimize(LagOptimizationStrategy.TRUE) -``` - -[к оглавлению](#оглавление) - ---- - -#### 3.1.5 Подключение оценщика времени - -```python -# Единый оценщик на всех этапах -scheduling_pipeline = scheduling_pipeline.work_estimator(project_work_estimator) -``` - -[к оглавлению](#оглавление) - ---- - -#### 3.1.6 Построение расписания - -```python -# Выбор планировщика: topo_scheduler | rand_topo_scheduler | heft_scheduler | heft_between_scheduler | genetic_scheduler -schedule_project = scheduling_pipeline.schedule(genetic_scheduler).finish()[0] - -# Быстрый просмотр -schedule_project.schedule.pure_schedule_df.head() - -# Перевод к датам и визуализация -project_schedule = schedule_project.schedule.merged_stages_datetime_df('2022-09-01') -_ = schedule_gant_chart_fig(project_schedule, VisualizationMode.ShowFig, remove_service_tasks=False) -``` - -[к оглавлению](#оглавление) - ---- - -### 3.2 Полное планирование и экспорт - -```python -# Сквозной прогон -schedule_project2 = SchedulingPipeline.create() \ - .wg(df_with_req, all_connections=False) \ - .contractors(project_contractors) \ - .history(history_df, sep=';') \ - .lag_optimize(LagOptimizationStrategy.TRUE) \ - .work_estimator(project_work_estimator) \ - .schedule(genetic_scheduler) \ - .finish()[0] - -# Итоговое расписание -raw_project_schedule = schedule_project2.schedule -project_schedule = raw_project_schedule.merged_stages_datetime_df('2022-09-01') - -# Экспорт структуры проекта для СППР -schedule_project2.dump('.', 'gas_network_full_connections_genetic_upd') -``` - -[к оглавлению](#оглавление) diff --git a/docs/guidebook/py_examples_doc/aircraft_stochastic_maintenance.md b/docs/guidebook/py_examples_doc/aircraft_stochastic_maintenance.md deleted file mode 100644 index c9f644f1..00000000 --- a/docs/guidebook/py_examples_doc/aircraft_stochastic_maintenance.md +++ /dev/null @@ -1,140 +0,0 @@ -# Стохастическое ТО самолёта: зоны и GA-планирование - -## Оглавление - -* [1. Параметры и импорты](#1-параметры-и-импорты) -* [2. Генерация исходного WorkGraph](#2-генерация-исходного-workgraph) -* [3. Дефекты: StructureEstimator](#3-дефекты-structureestimator) -* [4. Зоны и ландшафт](#4-зоны-и-ландшафт) -* [5. Планировщик и оценщик](#5-планировщик-и-оценщик) -* [6. Пайплайн расчёта](#6-пайплайн-расчёта) -* [7. Визуализация Ганта](#7-визуализация-ганта) - ---- - -## 1. Параметры и импорты - -```python -from random import Random -import numpy as np - -from sampo.generator import SimpleSynthetic, SyntheticGraphType -from sampo.generator.environment import get_contractor_by_wg -from sampo.pipeline import SchedulingPipeline -from sampo.scheduler import GeneticScheduler -from sampo.schemas import DefaultZoneStatuses, ZoneConfiguration, LandscapeConfiguration -from sampo.schemas.structure_estimator import DefaultStructureGenerationEstimator, DefaultStructureEstimator -from sampo.schemas.time_estimator import DefaultWorkEstimator -from sampo.utilities.visualization import schedule_gant_chart_fig, VisualizationMode - -r_seed = 231 -ss = SimpleSynthetic(r_seed) -``` - - - -## 2. Генерация исходного WorkGraph - -```python -wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200 -) -``` - - - -## 3. Дефекты: StructureEstimator - -* Равномерно добавляются 5 «подработ» на каждый несервисный узел. - -```python -rand = Random(r_seed) -generator = DefaultStructureGenerationEstimator(rand) -sub_works = [f"Sub-work {i}" for i in range(5)] - -for node in wg.nodes: - if node.work_unit.is_service_unit: - continue - for sub_work in sub_works: - generator.set_probability(parent=node.work_unit.name, - child=sub_work, - probability=1/len(sub_works)) - -structure_estimator = DefaultStructureEstimator(generator, rand) -wg_with_defects = structure_estimator.restruct(wg) -contractors = [get_contractor_by_wg(wg_with_defects)] -``` - - - -## 4. Зоны и ландшафт - -* Одна зона `zone1`, 4 допустимых статуса, единичные переходные стоимости. - -```python -class AeroplaneZoneStatuses(DefaultZoneStatuses): - def statuses_available(self) -> int: - return 4 - -zone_names = ['zone1'] -zones_count = len(zone_names) - -zone_config = ZoneConfiguration( - start_statuses={zone: 1 for zone in zone_names}, - time_costs=np.array([[1 for _ in range(zones_count)] for _ in range(zones_count)]), - statuses=AeroplaneZoneStatuses() -) - -landscape_config = LandscapeConfiguration(zone_config=zone_config) -``` - - - -## 5. Планировщик и оценщик - -```python -work_estimator = DefaultWorkEstimator() - -genetic_scheduler = GeneticScheduler( - work_estimator=work_estimator, - number_of_generation=20, - mutate_order=0.05, - mutate_resources=0.005, - size_of_population=50 -) -``` - - - -## 6. Пайплайн расчёта - -```python -aircraft_project = ( - SchedulingPipeline.create() - .wg(wg_with_defects) - .contractors(contractors) - .work_estimator(work_estimator) - .landscape(landscape_config) - .schedule(genetic_scheduler) - .finish()[0] -) -``` - - - -## 7. Визуализация Ганта - -```python -merged_schedule = aircraft_project.schedule.merged_stages_datetime_df('2022-01-01') -aircraft_schedule_fig = schedule_gant_chart_fig( - merged_schedule, - VisualizationMode.ReturnFig, - remove_service_tasks=False -) -aircraft_schedule_fig.update_layout(height=1200, width=1600) -aircraft_schedule_fig.show() -``` - diff --git a/docs/guidebook/py_examples_doc/capital_construction.md b/docs/guidebook/py_examples_doc/capital_construction.md deleted file mode 100644 index 871dc0a7..00000000 --- a/docs/guidebook/py_examples_doc/capital_construction.md +++ /dev/null @@ -1,124 +0,0 @@ -# Мультиагентное планирование капстроительства - -## Оглавление - -* [1. Назначение и сущности](#1-назначение-и-сущности) -* [2. Построение BlockGraph: `load_queues_bg`](#2-построение-blockgraph-load_queues_bg) -* [3. Запуск стохастического мультиагентного планирования: `run_example`](#3-запуск-стохастического-мультиагентного-планирования-run_example) -* [4. Мини-пример использования](#4-мини-пример-использования) - ---- - -## 1. Назначение и сущности - -Файл задаёт две ключевые функции: сборку `BlockGraph` из очередей `WorkGraph` и стохастическое мультиагентное планирование блоков с учётом производительности работников по подрядчикам. Используются: `Scheduler`, `Agent`, `StochasticManager`, `ScheduledBlock`, `IntervalGaussian`, `DefaultWorkEstimator`. - ---- - -## 2. Построение BlockGraph: `load_queues_bg` - -**Идея.** Развернуть список очередей графов в единый `BlockGraph` и сгенерировать зависимости между стартовыми узлами соседних очередей (прямой и «обратной» проход для покрытия недостающих рёбер). - -```python -from sampo.scheduler.multi_agency.block_graph import BlockGraph -from sampo.schemas.graph import WorkGraph - -def load_queues_bg(queues: list[list[WorkGraph]]): - wgs: list[WorkGraph] = [wg for queue in queues for wg in queue] - bg = BlockGraph.pure(wgs) - - index = 0 - nodes_prev = [] - for queue in queues: - nodes = [bg[wgs[i].start.id] for i in range(index, index + len(queue))] - - # прямой проход - for i, node in enumerate(nodes[:-2]): - if i >= len(nodes_prev): - break - BlockGraph.add_edge(node, nodes_prev[i]) - - # обратный проход для покрытия оставшихся - for i, node in enumerate(nodes): - if i >= len(nodes_prev): - break - BlockGraph.add_edge(node, nodes_prev[-i]) - - nodes_prev = nodes - - return bg -``` - - - ---- - -## 3. Запуск стохастического мультиагентного планирования: `run_example` - -**Шаги.** - -1. Создать `DefaultWorkEstimator`. -2. Для каждого подрядчика `contractor[i]` задать производительность ролей `['driver','fitter','manager','handyman','electrician','engineer']` как `IntervalGaussian(0.2 * i + 0.2, 1, 0, 2)`. -3. Назначить оценщик всем планировщикам. -4. Собрать агентов `Agent(name, scheduler, [contractor])`. -5. Построить `BlockGraph` через `load_queues_bg`. -6. Запустить `StochasticManager.manage_blocks`. - -```python -from typing import Dict -from sampo.scheduler.base import Scheduler -from sampo.scheduler.multi_agency.multi_agency import Agent, ScheduledBlock, StochasticManager -from sampo.schemas import IntervalGaussian -from sampo.schemas.contractor import Contractor -from sampo.schemas.time_estimator import DefaultWorkEstimator - -def run_example( - queues: list[list[WorkGraph]], - schedulers: list[Scheduler], - contractors: list[Contractor] -) -> Dict[str, ScheduledBlock]: - work_estimator = DefaultWorkEstimator() - - # роль-специфичная производительность по подрядчику i - for worker in ['driver', 'fitter', 'manager', 'handyman', 'electrician', 'engineer']: - for i, contractor in enumerate(contractors): - work_estimator.set_worker_productivity( - IntervalGaussian(0.2 * i + 0.2, 1, 0, 2), worker, contractor.id - ) - - for scheduler in schedulers: - scheduler.work_estimator = work_estimator - - agents = [Agent(f'Agent {i}', schedulers[i % len(schedulers)], [contractor]) - for i, contractor in enumerate(contractors)] - manager = StochasticManager(agents) - - bg = load_queues_bg(queues) - blocks_schedules = manager.manage_blocks(bg, logger=print) - return blocks_schedules -``` - - - ---- - -## 4. Мини-пример использования - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.scheduler.heft.base import HEFTScheduler -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg - -# очереди из малых графов -ss = SimpleSynthetic(rand=31) -queues = [[ss.small_work_graph() for _ in range(2)], - [ss.small_work_graph() for _ in range(2)]] - -# планировщики и подрядчики -schedulers = [HEFTScheduler(), HEFTScheduler()] -contractors = [get_contractor_by_wg(queues[0][0]), get_contractor_by_wg(queues[1][0])] - -# запуск -blocks = run_example(queues, schedulers, contractors) -print(list(blocks.keys())) -``` diff --git a/docs/guidebook/py_examples_doc/dynamic_maintenance_tasks_scheduling.md b/docs/guidebook/py_examples_doc/dynamic_maintenance_tasks_scheduling.md deleted file mode 100644 index 8db1fd09..00000000 --- a/docs/guidebook/py_examples_doc/dynamic_maintenance_tasks_scheduling.md +++ /dev/null @@ -1,119 +0,0 @@ -# Динамическое планирование регламентных задач (multi-agency) - -## Оглавление - -* [1. Входные сущности](#1-входные-сущности) -* [2. Построение BlockGraph из очередей](#2-построение-blockgraph-из-очередей) -* [3. Мультиагентное планирование](#3-мультиагентное-планирование) -* [4. Валидация результата](#4-валидация-результата) -* [5. Пример запуска](#5-пример-запуска) - ---- - -## 1. Входные сущности - -* `WorkGraph`: блок регламентных работ. -* `Scheduler`: планировщик агента. -* `Contractor`: ресурсы агента. -* Объекты multi-agency: `Agent`, `Manager`, `ScheduledBlock`. - ---- - -## 2. Построение BlockGraph из очередей - -Функция `load_queues_bg(queues)` превращает список очередей графов в `BlockGraph` и генерирует зависимости между стартовыми узлами соседних очередей. Шаги: собрать все `WorkGraph`, создать `BlockGraph.pure`, затем добавить рёбра между стартами по шаблону «текущая ↔ предыдущая очередь». Возврат — готовый `BlockGraph`. - -```python -from sampo.scheduler.multi_agency.block_graph import BlockGraph -from sampo.schemas.graph import WorkGraph - -def load_queues_bg(queues: list[list[WorkGraph]]) -> BlockGraph: - wgs = [wg for queue in queues for wg in queue] - bg = BlockGraph.pure(wgs) - - index = 0 - nodes_prev = [] - for queue in queues: - nodes = [bg[wgs[i].start.id] for i in range(index, index + len(queue))] - - # связи между очередями - for i, node in enumerate(nodes[:-2]): - if i >= len(nodes_prev): - break - BlockGraph.add_edge(node, nodes_prev[i]) - - for i, node in enumerate(nodes): - if i >= len(nodes_prev): - break - BlockGraph.add_edge(node, nodes_prev[-i]) - - nodes_prev = nodes - - return bg -``` - ---- - -## 3. Мультиагентное планирование - -`run_example(queues_with_obstructions, schedulers, contractors)` создаёт агентов, строит `BlockGraph`, планирует блоки через `Manager.manage_blocks`, возвращает `Dict[str, ScheduledBlock]`. Объекты препятствий (`Obstruction`, `OneInsertObstruction`) доступны для сценариев, но в базовом запуске не используются. - -```python -from typing import Dict -from sampo.scheduler.base import Scheduler -from sampo.scheduler.multi_agency.multi_agency import Agent, Manager, ScheduledBlock - -def run_example( - queues_with_obstructions: list[list[WorkGraph]], - schedulers: list[Scheduler], - contractors: list[Contractor] -) -> Dict[str, ScheduledBlock]: - - agents = [Agent(f'Agent {i}', schedulers[i % len(schedulers)], [contractor]) - for i, contractor in enumerate(contractors)] - manager = Manager(agents) - - bg = load_queues_bg(queues_with_obstructions) - blocks_schedules = manager.manage_blocks(bg, logger=print) - return blocks_schedules -``` - ---- - -## 4. Валидация результата - -После планирования проверяется корректность расписания блоков: соответствие зависимостям `BlockGraph` и агентам. - -```python -from sampo.scheduler.multi_agency import validate_block_schedule - -validate_block_schedule(bg, blocks_schedules, agents) -``` - ---- - -## 5. Пример запуска - -Минимальный сценарий с двумя очередями, двумя агентами и HEFT-планировщиками. - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.scheduler.heft.base import HEFTScheduler -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg - -# 1) подготовка очередей графов -ss = SimpleSynthetic(rand=31) -q1 = [ss.small_work_graph() for _ in range(2)] -q2 = [ss.small_work_graph() for _ in range(2)] -queues = [q1, q2] - -# 2) планировщики и подрядчики (по одному на агента) -schedulers = [HEFTScheduler(), HEFTScheduler()] -contractors = [get_contractor_by_wg(q1[0]), get_contractor_by_wg(q2[0])] - -# 3) мультиагентное планирование -blocks_schedules = run_example(queues, schedulers, contractors) - -# 4) печать ключей запланированных блоков -print(list(blocks_schedules.keys())) -``` diff --git a/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md b/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md deleted file mode 100644 index 9e5a9424..00000000 --- a/docs/guidebook/py_examples_doc/field_dev_resources_time_estimator.md +++ /dev/null @@ -1,218 +0,0 @@ -# Оценщик времени и ресурсов FieldDevWorkEstimator - -## Оглавление - -* [1. Назначение и зависимости](#1-назначение-и-зависимости) -* [2. Константы и инициализация модели](#2-константы-и-инициализация-модели) -* [3. Публичные методы](#3-публичные-методы) - - * [3.1 `estimate_time`](#31-estimate_time) - * [3.2 `find_work_resources`](#32-find_work_resources) - * [3.3 `set_estimation_mode`](#33-set_estimation_mode) - * [3.4 `set_productivity_mode`](#34-set_productivity_mode) - * [3.5 `get_recreate_info`](#35-get_recreate_info) -* [4. Пример использования](#4-пример-использования) - ---- - -## 1. Назначение и зависимости - -`FieldDevWorkEstimator` — пользовательский `WorkTimeEstimator` для SAMPO. Возвращает длительности работ и требования к ресурсам на базе внешней модели `ResTimeModel`, подключённой через адаптер `MschmAdapter`. - -```python -import logging -from random import Random -from typing import Type -from itertools import chain -from operator import attrgetter - -from sampo.schemas.time import Time -from sampo.schemas import ( - WorkTimeEstimator, WorkUnit, Worker, WorkerReq, - WorkEstimationMode, WorkerProductivityMode -) - -from idbadapter import MschmAdapter -from stairsres.res_time_model import ResTimeModel -from sampo.utilities.collections_util import build_index -``` - ---- - -## 2. Константы и инициализация модели - -Сервисные работы имеют нулевую длительность. Модель создаётся один раз и переиспользуется. Логгер ведёт предупреждения по сбоям оценки. - -```python -SERVICE_WORKS = [ - "Начало работ по марке", "Окончание работ по марке", - "NaN", "start of project", "finish of project" -] - -URL = "test" -model = ResTimeModel(MschmAdapter(url=URL)) -logger = logging.getLogger("field-dev-estimator-log") - - -class FieldDevWorkEstimator(WorkTimeEstimator): - def __init__(self, rand: Random = Random()): - self._url = URL - self._model = model - self._use_idle = True - self._estimation_mode = WorkEstimationMode.Realistic - self.rand = rand - self._productivity_mode = WorkerProductivityMode.Static -``` - ---- - -## 3. Публичные методы - -### 3.1 `estimate_time` - -Логика: - -* Имя работы очищается от суффикса `_stage_…`. -* Список ресурсов превращается в `[{name, _count}]`; недостающие из `worker_reqs` добавляются с `_count=0`. -* Режимы маппятся на квантили `"0.1" | "0.5" | "0.9"`. -* Сервисные работы → `Time(0)`. -* Ошибки логируются и не прерывают процесс. - -```python -def estimate_time(self, work_unit: WorkUnit, worker_list: list[Worker]) -> Time: - w_u = { - "name": work_unit.name.split("_stage_")[0], - "volume": work_unit.volume, - "measurement": work_unit.volume_type, - } - - w_l = [{"name": w.name, "_count": w.count} for w in worker_list] - name2worker = build_index(worker_list, attrgetter("name")) - - match self._estimation_mode: - case WorkEstimationMode.Optimistic: - mode_str = "0.1" - case WorkEstimationMode.Realistic: - mode_str = "0.5" - case _: - mode_str = "0.9" - - # добавляем отсутствующие ресурсы с нулевым количеством - for req in work_unit.worker_reqs: - if name2worker.get(req.kind) is None: - w_l.append({"name": req.kind, "_count": 0}) - - if w_u["name"] in SERVICE_WORKS: - return Time(0) - - try: - return Time(int(self._model.estimate_time(work_unit=w_u, worker_list=w_l, mode=mode_str))) - except Exception as e: - logger.warning(f"Couldn't estimate time for work unit with name='{w_u['name']}': {e}") - # допустимо вернуть 0 или эвристическое значение; здесь вернём 0 - return Time(0) -``` - -### 3.2 `find_work_resources` - -Возвращает плоский список `WorkerReq`, рассчитанный моделью. - -```python -def find_work_resources( - self, - work_name: str, - work_volume: float, - resource_name: list[str] | None = None, - measurement: str | None = None -) -> list[WorkerReq]: - if work_name in SERVICE_WORKS: - return [] - - worker_req_dict = self._model.get_resources_volumes( - work_name=work_name, - work_volume=work_volume, - measurement=measurement - ) or {} - - worker_reqs = [ - [ - WorkerReq( - kind=req["kind"], - volume=Time(req["volume"]), - min_count=req["min_count"], - max_count=req["max_count"], - ) - for req in req_list - ] - for req_list in worker_req_dict.values() - ] - return list(chain.from_iterable(worker_reqs)) -``` - -### 3.3 `set_estimation_mode` - -```python -def set_estimation_mode( - self, - use_idle: bool = True, - mode: WorkEstimationMode = WorkEstimationMode.Realistic -) -> None: - self._use_idle = use_idle - self._estimation_mode = mode -``` - -### 3.4 `set_productivity_mode` - -```python -def set_productivity_mode( - self, - mode: WorkerProductivityMode = WorkerProductivityMode.Static -) -> None: - self._productivity_mode = mode -``` - -### 3.5 `get_recreate_info` - -Возвращает конструктор и «параметры» для восстановления. Сейчас из `_url: str` берётся `tuple(self._url)`, то есть кортеж символов строки. - -```python -from typing import Type - -def get_recreate_info(self) -> tuple[Type, tuple]: - return FieldDevWorkEstimator, (self._url,) # "test" -``` - ---- - -## 4. Пример использования - -```python -from random import Random -from sampo.schemas import WorkUnit, Worker -from sampo.schemas import WorkEstimationMode, WorkerProductivityMode - -est = FieldDevWorkEstimator(rand=Random(231)) - -# оценка времени -wu = WorkUnit(name="Бурение_stage_1", volume=100.0, volume_type="м") -workers = [Worker(id="1", name="буровик", count=5)] -t = est.estimate_time(wu, workers) - -# требования к ресурсам -reqs = est.find_work_resources("Бурение", 100.0, measurement="м") - -# переключение режимов -est.set_estimation_mode(use_idle=True, mode=WorkEstimationMode.Realistic) -est.set_productivity_mode(mode=WorkerProductivityMode.Static) -``` - -Минимальная настройка логирования: - -```python -import logging - -logging.basicConfig( - level=logging.WARNING, - format="%(asctime)s %(levelname)s %(name)s: %(message)s" -) -``` \ No newline at end of file diff --git a/docs/guidebook/py_examples_doc/field_development_scheduling.md b/docs/guidebook/py_examples_doc/field_development_scheduling.md deleted file mode 100644 index d871f96d..00000000 --- a/docs/guidebook/py_examples_doc/field_development_scheduling.md +++ /dev/null @@ -1,109 +0,0 @@ -# Планирование работ по разработке месторождения (field\_development\_scheduling.py) - -## Оглавление - -* [1. Параметры и импорты](#1-параметры-и-импорты) -* [2. Загрузка WorkGraph](#2-загрузка-workgraph) -* [3. Подрядчики: файл или автогенерация](#3-подрядчики-файл-или-автогенерация) -* [4. Реструктуризация графа](#4-реструктуризация-графа) -* [5. Визуализация структуры](#5-визуализация-структуры) -* [6. Настройки планировщика и вывода](#6-настройки-планировщика-и-вывода) -* [7. Расчёт расписания и даты](#7-расчёт-расписания-и-даты) -* [8. Диаграмма Ганта](#8-диаграмма-ганта) - ---- - -## 1. Параметры и импорты - -Подавление предупреждений `matplotlib`, импорты планировщика, визуализации и структуризации. - -```python -import warnings -from matplotlib import pyplot as plt - -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg -from sampo.scheduler.heft.base import HEFTScheduler -from sampo.schemas.contractor import Contractor -from sampo.schemas.graph import WorkGraph -from sampo.structurator.base import graph_restructuring -from sampo.utilities.visualization.base import VisualizationMode -from sampo.utilities.visualization.schedule import schedule_gant_chart_fig -from sampo.utilities.visualization.work_graph import work_graph_fig - -warnings.filterwarnings("ignore") -``` - -## 2. Загрузка WorkGraph - -Чтение структуры задач из подготовленного JSON (`./field_development_tasks_structure.json`). - -```python -field_development_wg = WorkGraph.loadf("./", "field_development_tasks_structure") -``` - -## 3. Подрядчики: файл или автогенерация - -Флаг выбора источника. По умолчанию автогенерация под граф со скейлером ресурсов `3`. - -```python -use_contractors_from_file = False - -if use_contractors_from_file: - contractors = [Contractor.loadf("./", "field_development_contractors_info")] -else: - contractors = [get_contractor_by_wg(field_development_wg, scaler=3)] -``` - -## 4. Реструктуризация графа - -Оптимизация структуры графа с учётом лагов. - -```python -structured_wg = graph_restructuring(field_development_wg, use_lag_edge_optimization=True) -``` - -## 5. Визуализация структуры - -Отрисовка `WorkGraph`: размер 20×10, подписи узлов, сдвиг легенды. - -```python -_ = work_graph_fig(structured_wg, (20, 10), legend_shift=4, show_names=True, text_size=6) -plt.show() -``` - -## 6. Настройки планировщика и вывода - -Выбор алгоритма, режим визуализации и дата старта; флаги сериализации подготовлены, но в скрипте не используются. - -```python -scheduler_type = HEFTScheduler() -graph_structure_optimization = True -csv_required = False -json_required = True - -visualization_mode = VisualizationMode.ShowFig -gant_chart_filename = "./output/schedule_gant_chart.png" -start_date = "2023-01-01" -``` - -## 7. Расчёт расписания и даты - -Планирование с валидацией и преобразование расписания к календарным датам. - -```python -schedule = scheduler_type.schedule(structured_wg, contractors, validate=True)[0] -schedule_df = schedule.merged_stages_datetime_df(start_date) -``` - -## 8. Диаграмма Ганта - -Построение диаграммы с опцией удаления сервисных задач; показ или сохранение файла по режиму. - -```python -gant_fig = schedule_gant_chart_fig( - schedule_df, - fig_file_name=gant_chart_filename, - visualization=visualization_mode, - remove_service_tasks=True, -) -``` diff --git a/docs/guidebook/py_examples_doc/generator_scenarios.md b/docs/guidebook/py_examples_doc/generator_scenarios.md deleted file mode 100644 index 1abd7499..00000000 --- a/docs/guidebook/py_examples_doc/generator_scenarios.md +++ /dev/null @@ -1,125 +0,0 @@ -# Сценарии генерации и модификации графов в SAMPO (generator\_scenarios.py) - -## Оглавление - -* [1. Инициализация и базовая генерация](#1-инициализация-и-базовая-генерация) -* [2. Базовое планирование HEFT](#2-базовое-планирование-heft) -* [3. Подрядчик по WorkGraph](#3-подрядчик-по-workgraph) -* [4. Расширение номенклатуры работ](#4-расширение-номенклатуры-работ) -* [5. Расширение номенклатуры ресурсов](#5-расширение-номенклатуры-ресурсов) -* [6. Проверка согласованности генерации подрядчика](#6-проверка-согласованности-генерации-подрядчика) -* [7. Визуализация WorkGraph](#7-визуализация-workgraph) -* [8. Вставка графа в граф и реструктуризация](#8-вставка-графа-в-граф-и-реструктуризация) - -## 1. Инициализация и базовая генерация - -Импорты, генератор, исходный граф, стартовые подрядчики. - -```python -import random -from itertools import chain -from matplotlib import pyplot as plt - -from sampo.generator.base import SimpleSynthetic -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg -from sampo.generator.pipeline.extension import extend_names, extend_resources -from sampo.scheduler.heft.base import HEFTScheduler -from sampo.schemas.graph import WorkGraph -from sampo.structurator import ( - graph_in_graph_insertion, work_graph_ids_simplification, graph_restructuring -) -from sampo.utilities.visualization.work_graph import work_graph_fig - -rand = random.Random(10) -p_rand = SimpleSynthetic(rand=231) -wg = p_rand.work_graph(top_border=3000) -contractors = [p_rand.contractor(i) for i in range(10, 31, 10)] -``` - -## 2. Базовое планирование HEFT - -Расчёт расписания и вывод метрик. - -```python -schedule = HEFTScheduler().schedule(wg, contractors)[0] - -print(len(wg.nodes)) -print("\nDefault contractors") -print(f"Execution time: {schedule.execution_time}") -``` - -## 3. Подрядчик по WorkGraph - -Пример оценки с автогенерацией подрядчика под граф. В файле цикл по пустому списку, для эксперимента заполните `pack_counts`. - -```python -print("\nContractor by work graph") -for pack_counts in [1, 2, 10]: # в файле: []; замените при необходимости - contractors = [get_contractor_by_wg(wg, scaler=pack_counts)] - execution_time = HEFTScheduler().schedule(wg, contractors)[0].execution_time - print(f"Execution time: {execution_time}, pack count: {pack_counts}") -``` - -## 4. Расширение номенклатуры работ - -Увеличение числа уникальных названий работ функцией `extend_names`. - -```python -print("\nNames extension") -names_wg = len({n.work_unit.name for n in wg.nodes}) -new_wg = extend_names(500, wg, rand) -names_new_wg = len({n.work_unit.name for n in new_wg.nodes}) -print(f"works in origin: {names_wg}, pack count: {names_new_wg}") -``` - -## 5. Расширение номенклатуры ресурсов - -Добавление новых типов ресурсов функцией `extend_resources`. - -```python -print("\nResource extension") -res_names_wg = len({req.kind for req in chain(*[n.work_unit.worker_reqs for n in wg.nodes])}) -new_wg = extend_resources(100, wg, rand) -res_names_new_wg = len({req.kind for req in chain(*[n.work_unit.worker_reqs for n in new_wg.nodes])}) -print(f"resources in origin: {res_names_wg}, in new: {res_names_new_wg}") -``` - -## 6. Проверка согласованности генерации подрядчика - -Сопоставление числа типов ресурсов в расширенном графе и у подрядчика, сгенерированного по этому графу. - -```python -print("\nCheck gen contractor by wg extension") -res_names_new_c = len(set(get_contractor_by_wg(new_wg, scaler=1).workers.keys())) -print(f"works in new by wg: {res_names_new_wg}, by contractor: {res_names_new_c}") -``` - -## 7. Визуализация WorkGraph - -Функция-обёртка для отрисовки графа. - -```python -def plot_wg(wg: WorkGraph) -> None: - _ = work_graph_fig(wg, (14, 8), legend_shift=4, show_names=True, text_size=4) - plt.show() -``` - -## 8. Вставка графа в граф и реструктуризация - -Композиция графов, упрощение идентификаторов, реструктуризация и визуализация этапов. - -```python -srand = SimpleSynthetic(34) -wg_master = srand.work_graph(cluster_counts=1) -wg_slave = srand.work_graph(cluster_counts=1) - -union_wg = graph_in_graph_insertion(wg_master, wg_master.start, wg_master.finish, wg_slave) -union_wg_simply = work_graph_ids_simplification(union_wg, id_offset=1000) -union_wg_restructured = graph_restructuring(union_wg_simply) - -plot_wg(wg_master) -plot_wg(wg_slave) -plot_wg(union_wg) -plot_wg(union_wg_simply) -plot_wg(union_wg_restructured) -``` diff --git a/docs/guidebook/py_examples_doc/landscape_configuration.md b/docs/guidebook/py_examples_doc/landscape_configuration.md deleted file mode 100644 index 096af807..00000000 --- a/docs/guidebook/py_examples_doc/landscape_configuration.md +++ /dev/null @@ -1,151 +0,0 @@ -# Конфигурация ландшафта и планирование в SAMPO (LandscapeConfiguration) - -## Оглавление - -* [1. Данные и ландшафт](#1-данные-и-ландшафт) - - * [1.1 Генерация графа и материалов](#11-генерация-графа-и-материалов) - * [1.2 Построение ландшафта](#12-построение-ландшафта) -* [2. Планировщик](#2-планировщик) -* [3. Подрядчик](#3-подрядчик) -* [4. Пайплайн и визуализация](#4-пайплайн-и-визуализация) -* [5. Диагностика](#5-диагностика) -* [6. Полный пример](#6-полный-пример) - ---- - -## 1. Данные и ландшафт - -### 1.1 Генерация графа и материалов - -* Синтетика: `SimpleSynthetic(rand=31) → small_work_graph()`. -* Материалы на узлы: `set_materials_for_wg(wg)`. - -```python -from sampo.generator import SimpleSynthetic - -ss = SimpleSynthetic(rand=31) -wg = ss.small_work_graph() -wg = ss.set_materials_for_wg(wg) -``` - -### 1.2 Построение ландшафта - -* Конфигурация площадок: `synthetic_landscape(wg)`. - -```python -landscape = ss.synthetic_landscape(wg) -``` - ---- - -## 2. Планировщик - -* Генетический алгоритм с ограниченными гиперпараметрами. -* Предупреждение: большие значения поколений и популяции замедляют расчёт при сложном ландшафте. - -```python -from sampo.scheduler import GeneticScheduler - -scheduler = GeneticScheduler( - number_of_generation=1, - mutate_order=0.05, - mutate_resources=0.005, - size_of_population=10, -) -``` - ---- - -## 3. Подрядчик - -* Автоподбор по требованиям `WorkGraph`: `get_contractor_by_wg(wg)`. - -```python -from sampo.generator.environment import get_contractor_by_wg - -contractors = [get_contractor_by_wg(wg)] -``` - ---- - -## 4. Пайплайн и визуализация - -* Используется `DefaultInputPipeline`. -* Визуализация диаграммы Ганта; режимы: `VisualizationMode.ShowFig | SaveFig`. -* Параметры: дата старта, имя файла при `SaveFig`. - -```python -from sampo.pipeline.default import DefaultInputPipeline -from sampo.utilities.visualization import VisualizationMode - -start_date = "2023-01-01" -visualization_mode = VisualizationMode.ShowFig -gant_chart_filename = "./output/synth_schedule_gant_chart.png" - -project = ( - DefaultInputPipeline() - .wg(wg) - .contractors(contractors) - .landscape(landscape) - .schedule(scheduler) - .visualization(start_date) # возвращает список проектов -)[0].show_gant_chart() -``` - ---- - -## 5. Диагностика - -* Число платформ и наличие материалов на каждом узле. - -```python -platform_number = len(landscape.platforms) -is_all_nodes_have_materials = all(node.work_unit.need_materials() for node in wg.nodes) -print(f"LandscapeConfiguration: {platform_number} platforms, " - f"All nodes have materials: {is_all_nodes_have_materials}") -``` - ---- - -## 6. Полный пример - -```python -from sampo.generator import SimpleSynthetic -from sampo.generator.environment import get_contractor_by_wg -from sampo.pipeline.default import DefaultInputPipeline -from sampo.scheduler import GeneticScheduler -from sampo.utilities.visualization import VisualizationMode - -start_date = "2023-01-01" -visualization_mode = VisualizationMode.ShowFig -gant_chart_filename = "./output/synth_schedule_gant_chart.png" - -ss = SimpleSynthetic(rand=31) -wg = ss.small_work_graph() -wg = ss.set_materials_for_wg(wg) -landscape = ss.synthetic_landscape(wg) - -scheduler = GeneticScheduler( - number_of_generation=1, - mutate_order=0.05, - mutate_resources=0.005, - size_of_population=10, -) - -platform_number = len(landscape.platforms) -is_all_nodes_have_materials = all(node.work_unit.need_materials() for node in wg.nodes) -print(f"LandscapeConfiguration: {platform_number} platforms, " - f"All nodes have materials: {is_all_nodes_have_materials}") - -contractors = [get_contractor_by_wg(wg)] - -project = ( - DefaultInputPipeline() - .wg(wg) - .contractors(contractors) - .landscape(landscape) - .schedule(scheduler) - .visualization(start_date) -)[0].show_gant_chart() -``` diff --git a/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md b/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md deleted file mode 100644 index 9d402e1c..00000000 --- a/docs/guidebook/py_examples_doc/simple_synthetic_graph_scheduling.md +++ /dev/null @@ -1,104 +0,0 @@ -# Планирование синтетического графа (simple\_synthetic\_graph\_scheduling.py) - -## Оглавление - -* [1. Параметры и импорты](#1-параметры-и-импорты) -* [2. Генерация синтетического WorkGraph](#2-генерация-синтетического-workgraph) -* [3. Диагностика атрибутов графа](#3-диагностика-атрибутов-графа) -* [4. Подрядчик по графу](#4-подрядчик-по-графу) -* [5. Планирование](#5-планирование) -* [6. Визуализация диаграммы Ганта](#6-визуализация-диаграммы-ганта) -* [7. Валидация результата](#7-валидация-результата) - ---- - -## 1. Параметры и импорты - -* Планировщик: `HEFTScheduler`. Дата старта: `"2023-01-01"`. Визуализация: `VisualizationMode.ShowFig` или `SaveFig` с именем файла. - -```python -from itertools import chain -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg -from sampo.utilities.visualization.base import VisualizationMode -from sampo.utilities.visualization.schedule import schedule_gant_chart_fig -from sampo.generator.base import SimpleSynthetic -from sampo.scheduler.heft.base import HEFTScheduler -from sampo.schemas.time import Time - -scheduler = HEFTScheduler() -start_date = "2023-01-01" -visualization_mode = VisualizationMode.ShowFig -gant_chart_filename = './output/synth_schedule_gant_chart.png' -``` - -## 2. Генерация синтетического WorkGraph - -* Пределы генерации: `works_count_top_border=2000`, `uniq_works=300`, `uniq_resources=100`. Фиксированный `rand=31`. - -```python -synth_works_top_border = 2000 -synth_unique_works = 300 -synth_resources = 100 - -srand = SimpleSynthetic(rand=31) -wg = srand.advanced_work_graph( - works_count_top_border=synth_works_top_border, - uniq_works=synth_unique_works, - uniq_resources=synth_resources -) -``` - -## 3. Диагностика атрибутов графа - -* Подсчёт работ, уникальных названий и типов ресурсов. Жёсткие проверки на верхние границы. - -```python -works_count = len(wg.nodes) -work_names_count = len(set(n.work_unit.name for n in wg.nodes)) -res_kind_count = len(set(req.kind for req in chain(*[n.work_unit.worker_reqs for n in wg.nodes]))) -print(works_count, work_names_count, res_kind_count) - -assert (works_count <= synth_works_top_border * 1.1) -assert (work_names_count <= synth_unique_works) -assert (res_kind_count <= synth_resources) -``` - -## 4. Подрядчик по графу - -* Автогенерация подрядчика, покрывающего ресурсы графа: `get_contractor_by_wg(wg)`. - -```python -contractors = [get_contractor_by_wg(wg)] -``` - -## 5. Планирование - -* Расчёт расписания и преобразование к датам. - -```python -schedule = scheduler.schedule(wg, contractors)[0] -schedule_df = schedule.merged_stages_datetime_df(start_date) -print(schedule.execution_time) -``` - -## 6. Визуализация диаграммы Ганта - -* Построение диаграммы. Можно показывать или сохранять. Сервисные задачи удаляются. - -```python -gant_fig = schedule_gant_chart_fig( - schedule_df, - fig_file_name=gant_chart_filename, - visualization=visualization_mode, - remove_service_tasks=True -) -``` - -## 7. Валидация результата - -* Проверка, что планирование завершилось успешно: время не бесконечность. - -```python -assert schedule.execution_time != Time.inf(), \ - f'Scheduling failed on {scheduler.scheduler_type.name}' -``` diff --git a/docs/guidebook/scheduling_project.md b/docs/guidebook/scheduling_project.md deleted file mode 100644 index 320f509e..00000000 --- a/docs/guidebook/scheduling_project.md +++ /dev/null @@ -1,104 +0,0 @@ -# Проект планирования в SAMPO (ScheduledProject) - -## Оглавление - -* [1. Генерация графа](#1-генерация-графа) - - * [1.1 Простой граф](#11-простой-граф) - * [1.2 Сложный граф](#12-сложный-граф) -* [2. Генерация подрядчика](#2-генерация-подрядчика) - - * [2.1 Ручная](#21-ручная) - * [2.2 Из графа](#22-из-графа) -* [3. Планирование](#3-планирование) - - * [3.1 Конструкция планировщика](#31-конструкция-планировщика) - * [3.2 Расчёт через SchedulingPipeline](#32-расчёт-через-schedulingpipeline) - * [3.3 Сериализация проекта](#33-сериализация-проекта) - -## 1. Генерация графа - -### 1.1 Простой граф - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.generator import SyntheticGraphType - -r_seed = 231 -ss = SimpleSynthetic(r_seed) - -simple_wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200, -) -``` - -### 1.2 Сложный граф - -```python -advanced_wg = ss.advanced_work_graph( - works_count_top_border=2000, - uniq_works=300, - uniq_resources=100, -) -``` - -## 2. Генерация подрядчика - -### 2.1 Ручная - -```python -from uuid import uuid4 -from sampo.schemas.resources import Worker -from sampo.schemas.contractor import Contractor - -contractors = [ - Contractor( - id=str(uuid4()), - name="OOO Berezka", - workers={"worker": Worker(id="0", name="worker", count=100)}, - ) -] -``` - -### 2.2 Из графа - -```python -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg - -contractors = [get_contractor_by_wg(simple_wg)] -``` - -## 3. Планирование - -### 3.1 Конструкция планировщика - -```python -from sampo.scheduler.heft.base import HEFTScheduler - -scheduler = HEFTScheduler() -``` - -### 3.2 Расчёт через SchedulingPipeline - -```python -from sampo.pipeline import SchedulingPipeline - -project = ( - SchedulingPipeline.create() - .wg(simple_wg) - .contractors(contractors) - .schedule(scheduler) - .finish()[0] -) - -project.schedule.execution_time -``` - -### 3.3 Сериализация проекта - -```python -project.dump(".", "project_test") -``` diff --git a/docs/guidebook/simple_generation_and_planning.md b/docs/guidebook/simple_generation_and_planning.md deleted file mode 100644 index ce6d60b5..00000000 --- a/docs/guidebook/simple_generation_and_planning.md +++ /dev/null @@ -1,208 +0,0 @@ -# Простая генерация и планирование в SAMPO - -## Оглавление - -* [1. Генерация графа](#1-генерация-графа) - - * [1.1 Простой граф](#11-простой-граф) - * [1.2 Сложный граф](#12-сложный-граф) -* [2. Генерация подрядчика](#2-генерация-подрядчика) - - * [2.1 Ручная](#21-ручная) - * [2.2 Из графа](#22-из-графа) -* [3. Планирование](#3-планирование) - - * [3.1 Конструкция планировщика](#31-конструкция-планировщика) - * [3.2 Процесс планирования](#32-процесс-планирования) -* [4. Метрики для GeneticScheduler](#4-метрики-для-geneticscheduler) - - * [4.1 DeadlineResourcesFitness](#41-deadlineresourcesfitness) - * [4.2 DeadlineCostFitness](#42-deadlinecostfitness) - * [4.3 TimeWithResourcesFitness](#43-timewithresourcesfitness) - ---- - -## 1. Генерация графа - -### 1.1 Простой граф - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.generator import SyntheticGraphType - -r_seed = 231 -ss = SimpleSynthetic(r_seed) - -# простой граф: 10 кластеров, от 100 до 200 работ в каждом -simple_wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200, -) -``` - -### 1.2 Сложный граф - -```python -# сложный граф: до 2000 работ, 300 уникальных типов работ, 100 типов ресурсов -advanced_wg = ss.advanced_work_graph( - works_count_top_border=2000, - uniq_works=300, - uniq_resources=100, -) -``` - ---- - -## 2. Генерация подрядчика - -### 2.1 Ручная - -```python -from uuid import uuid4 -from sampo.schemas.resources import Worker -from sampo.schemas.contractor import Contractor - -contractors = [ - Contractor( - id=str(uuid4()), - name="OOO Berezka", - workers={"worker": Worker(id="0", name="worker", count=100)}, - ) -] -``` - -### 2.2 Из графа - -```python -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg - -contractors = [get_contractor_by_wg(simple_wg)] -``` - ---- - -## 3. Планирование - -### 3.1 Конструкция планировщика - -```python -from sampo.scheduler.heft.base import HEFTScheduler -from sampo.scheduler.genetic.base import GeneticScheduler - -# эвристика HEFT -scheduler = HEFTScheduler() - -# или генетический планировщик с простыми гиперпараметрами -scheduler = GeneticScheduler(mutate_order=0.05, mutate_resources=0.05) -``` - -### 3.2 Процесс планирования - -```python -from sampo.pipeline import SchedulingPipeline - -project = ( - SchedulingPipeline.create() - .wg(simple_wg) - .contractors(contractors) - .schedule(scheduler) - .finish()[0] -) - -project.schedule.execution_time -``` - ---- - -## 4. Метрики для GeneticScheduler - -### 4.1 DeadlineResourcesFitness - -Оптимизация использования ресурсов при заданном дедлайне. - -```python -from sampo.schemas.time import Time -from sampo.scheduler.genetic.operators import DeadlineResourcesFitness -from sampo.scheduler.genetic.base import GeneticScheduler -from sampo.pipeline import SchedulingPipeline - -deadline = Time(2000) -fitness_constructor = DeadlineResourcesFitness(deadline) - -scheduler = GeneticScheduler( - mutate_order=0.05, - mutate_resources=0.05, - fitness_constructor=fitness_constructor, -) -scheduler.set_deadline(deadline) - -project = ( - SchedulingPipeline.create() - .wg(simple_wg) - .contractors(contractors) - .schedule(scheduler) - .finish()[0] -) - -project.schedule.execution_time -``` - -### 4.2 DeadlineCostFitness - -Оптимизация стоимости с учётом дедлайна. - -```python -from sampo.scheduler.genetic.operators import DeadlineCostFitness -from sampo.scheduler.genetic.base import GeneticScheduler -from sampo.pipeline import SchedulingPipeline - -fitness_constructor = DeadlineCostFitness(deadline) - -scheduler = GeneticScheduler( - mutate_order=0.05, - mutate_resources=0.05, - fitness_constructor=fitness_constructor, -) -scheduler.set_deadline(deadline) - -project = ( - SchedulingPipeline.create() - .wg(simple_wg) - .contractors(contractors) - .schedule(scheduler) - .finish()[0] -) - -project.schedule.execution_time -``` - -### 4.3 TimeWithResourcesFitness - -Минимизация времени с учётом ресурсов, без явного дедлайна. - -```python -from sampo.scheduler.genetic.operators import TimeWithResourcesFitness -from sampo.scheduler.genetic.base import GeneticScheduler -from sampo.pipeline import SchedulingPipeline - -fitness_constructor = TimeWithResourcesFitness() - -scheduler = GeneticScheduler( - mutate_order=0.05, - mutate_resources=0.05, - fitness_constructor=fitness_constructor, -) -scheduler.set_deadline(deadline) # при необходимости - -project = ( - SchedulingPipeline.create() - .wg(simple_wg) - .contractors(contractors) - .schedule(scheduler) - .finish()[0] -) - -project.schedule.execution_time -``` diff --git a/docs/guidebook/source/conf.py b/docs/guidebook/source/conf.py deleted file mode 100644 index 64103306..00000000 --- a/docs/guidebook/source/conf.py +++ /dev/null @@ -1,28 +0,0 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Project information ----------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information - -project = 'guidebook_web' -copyright = '2025, Mur' -author = 'Mur' -release = '1' - -# -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration - -extensions = [] - -templates_path = ['_templates'] -exclude_patterns = [] - - - -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output - -html_theme = 'alabaster' -html_static_path = ['_static'] diff --git a/docs/guidebook/source/index.rst b/docs/guidebook/source/index.rst deleted file mode 100644 index 5f30667b..00000000 --- a/docs/guidebook/source/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. guidebook_web documentation master file, created by - sphinx-quickstart on Tue Sep 16 13:18:26 2025. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -guidebook_web documentation -=========================== - -Add your content using ``reStructuredText`` syntax. See the -`reStructuredText `_ -documentation for details. - - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - diff --git a/docs/guidebook/structure_estimator.md b/docs/guidebook/structure_estimator.md deleted file mode 100644 index 60cab7a0..00000000 --- a/docs/guidebook/structure_estimator.md +++ /dev/null @@ -1,98 +0,0 @@ -# Оценка и восстановление структуры работ (StructureEstimator) в SAMPO - -## Оглавление - -* [1. Генерация исходного графа](#1-генерация-исходного-графа) - - * [1.1 Синтетический граф](#11-синтетический-граф) -* [2. Создание StructureEstimator](#2-создание-structureestimator) - - * [2.1 Генератор структурных связей](#21-генератор-структурных-связей) - * [2.2 Итоговый оценщик структуры](#22-итоговый-оценщик-структуры) -* [3. Применение в PreparationPipeline](#3-применение-в-preparationpipeline) - - * [3.1 Реструктуризация WorkGraph](#31-реструктуризация-workgraph) - ---- - -## 1. Генерация исходного графа - -### 1.1 Синтетический граф - -* Импорт генератора и типов: `SimpleSynthetic`, `SyntheticGraphType`. -* Фиксация зерна для воспроизводимости. -* Построение базового `WorkGraph`. - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.generator import SyntheticGraphType # или: from sampo.generator.pipeline import SyntheticGraphType - -r_seed = 231 -ss = SimpleSynthetic(r_seed) - -wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200, -) -``` - -## 2. Создание StructureEstimator - -### 2.1 Генератор структурных связей - -* Используется `DefaultStructureGenerationEstimator`. -* Ему передаётся общий ГПСЧ `Random(r_seed)`. -* Задаются вероятности порождения подработ для каждого несервисного узла. - -```python -from random import Random -from sampo.schemas.structure_estimator import DefaultStructureGenerationEstimator - -rand = Random(r_seed) -generator = DefaultStructureGenerationEstimator(rand) - -sub_works = [f"Sub-work {i}" for i in range(5)] - -# равномерно распределяем 5 «подработ» на каждый несервисный узел -for node in wg.nodes: - if node.work_unit.is_service_unit: - continue - for sub_work in sub_works: - generator.set_probability( - parent=node.work_unit.name, - child=sub_work, - probability=1 / len(sub_works), - ) -``` - -### 2.2 Итоговый оценщик структуры - -* Обёртка `DefaultStructureEstimator(generator, rand)` для использования в пайплайне. - -```python -from sampo.schemas.structure_estimator import DefaultStructureEstimator - -structure_estimator = DefaultStructureEstimator(generator, rand) -``` - -## 3. Применение в PreparationPipeline - -### 3.1 Реструктуризация WorkGraph - -* Подключаем `structure_estimator` в `PreparationPipeline`. -* Строим обновлённый `WorkGraph` с учётом сгенерированных подработ и связей. - -```python -from sampo.pipeline.preparation import PreparationPipeline - -restructed_wg = ( - PreparationPipeline() - .wg(wg) - .structure_estimator(structure_estimator) - .build_wg() -) - -restructed_wg.vertex_count # проверка изменения размера графа -``` diff --git a/docs/guidebook/visualization.md b/docs/guidebook/visualization.md deleted file mode 100644 index 2d2ca62a..00000000 --- a/docs/guidebook/visualization.md +++ /dev/null @@ -1,118 +0,0 @@ -# Визуализация в SAMPO - -## Оглавление - -* [1. Планирование (Scheduling)](#1-планирование-scheduling) - - * [1.1 Генерация данных](#11-генерация-данных) - * [1.2 Расчёт расписания](#12-расчёт-расписания) -* [2. Визуализация WorkGraph](#2-визуализация-workgraph) -* [3. Диаграмма Ганта проекта](#3-диаграмма-ганта-проекта) -* [4. Занятость ресурсов](#4-занятость-ресурсов) - - * [4.1 По работам](#41-по-работам) - * [4.2 По датам](#42-по-датам) - ---- - -## 1. Планирование (Scheduling) - -### 1.1 Генерация данных - -```python -from sampo.pipeline.lag_optimization import LagOptimizationStrategy -from sampo.generator.base import SimpleSynthetic -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod -from sampo.generator import SyntheticGraphType - -r_seed = 231 -ss = SimpleSynthetic(r_seed) - -simple_wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200, -) - -contractors = [get_contractor_by_wg(simple_wg, method=ContractorGenerationMethod.AVG)] -``` - -### 1.2 Расчёт расписания - -```python -from sampo.pipeline import SchedulingPipeline -from sampo.scheduler.heft.base import HEFTScheduler - -scheduler = HEFTScheduler() - -project = ( - SchedulingPipeline.create() - .wg(simple_wg) - .contractors(contractors) - .lag_optimize(LagOptimizationStrategy.TRUE) - .schedule(scheduler) - .finish()[0] -) - -schedule = project.schedule -``` - ---- - -## 2. Визуализация WorkGraph - -```python -from sampo.utilities.visualization.work_graph import work_graph_fig - -fig = work_graph_fig(simple_wg, (10, 10)) -fig.show() -``` - ---- - -## 3. Диаграмма Ганта проекта - -```python -from sampo.utilities.visualization.base import VisualizationMode -from sampo.utilities.visualization.schedule import schedule_gant_chart_fig - -# переводим расписание в реальные даты -merged_schedule = schedule.merged_stages_datetime_df('2022-01-01') - -fig = schedule_gant_chart_fig( - schedule_dataframe=merged_schedule, - visualization=VisualizationMode.ShowFig, - remove_service_tasks=False, -) -``` - ---- - -## 4. Занятость ресурсов - -### 4.1 По работам - -```python -from sampo.utilities.visualization.resources import resource_employment_fig, EmploymentFigType -from sampo.utilities.visualization.base import VisualizationMode - -fig = resource_employment_fig( - schedule=merged_schedule, - fig_type=EmploymentFigType.WorkLabeled, - vis_mode=VisualizationMode.ShowFig, -) -``` - -### 4.2 По датам - -```python -from sampo.utilities.visualization.resources import resource_employment_fig, EmploymentFigType -from sampo.utilities.visualization.base import VisualizationMode - -fig = resource_employment_fig( - schedule=merged_schedule, - fig_type=EmploymentFigType.DateLabeled, - vis_mode=VisualizationMode.ShowFig, -) -``` diff --git a/docs/source/guidebook/advanced.md b/docs/source/guidebook/advanced.md new file mode 100644 index 00000000..b30a7fd9 --- /dev/null +++ b/docs/source/guidebook/advanced.md @@ -0,0 +1,43 @@ +## Расширённое использование и настройка + +После базового знакомства вы можете тонко настроить SAMPO: выбирать алгоритмы, тюнить параметры, работать с несколькими критериями и встраивать доменные компоненты. + +### Как выбрать алгоритм + +* **Эвристики (HEFT, Topological)** — быстрые, дают хорошую базу. HEFT/HEFTBetween используют критические пути/приоритеты. +* **Генетический алгоритм** — исследует больше вариантов и часто находит лучше, но дольше считает. Настраиваются `population_size`, `iterations`, `mutate_order`, `mutate_resources` и др.: + + ```python + from sampo.scheduler.genetic import GeneticScheduler + scheduler = GeneticScheduler(population_size=100, iterations=50, mutate_order=0.1, mutate_resources=0.1) + ``` +* **Многоагентное планирование** — делит граф на блоки, применяет разные стратегии и объединяет результат. Полезно для очень больших проектов и гибридных стратегий; требует большего сетапа (`sampo.scheduler.multi_agency`). + +**Практика:** начните с эвристики для быстрого ответа, затем при необходимости примените GeneticScheduler и сравните качества планов. + +### Кастомизация конвейера и ограничений + +`SchedulingPipeline` упрощает код и допускает расширения. Можно добавлять собственные шаги (пред-/пост-обработку), комбинировать несколько алгоритмов (напр., «черновик HEFT → доулучшение Genetic»). + +Дополнительные **ограничения** (напр., запрет параллельности для двух конкретных задач, смены/простой оборудования) можно: + +* Заложить в граф (добавить зависимости, объединить узлы). +* Учесть пост-обработкой расписания. +* Внедрить в сам алгоритм (напр., штрафы в fitness-функции у генетики или проверки допустимости). + +### Мультикритериальная оптимизация + +Цель может быть не одна: **время** и **стоимость** одновременно. Подходы: + +* **Отдельные прогоны по разным метрикам** и сравнение результатов. +* **Собственная целевая функция/веса** (часто — модификация генетики), либо режим Парето, если доступен. Итог — набор компромиссных планов (Парето-фронт). + +Важно определить метрики и подготовить данные (стоимости, дедлайны) заранее. + +### Интеграция с вашими данными и системами + +* **Парсеры входа**: читайте CSV/БД/MS Project и формируйте `WorkGraph`/`Contractor` своими скриптами. +* **Модели оценки времени/ресурсов**: можно подключать внешние модели (в т. ч. ML) и итеративно уточнять длительности («план → симуляция → корректировка → перепланирование»). +* **Вывод/визуализация**: экспорт расписания в JSON/CSV, построение диаграмм Ганта сторонними средствами и т. п. + +Во многих случаях достаточно грамотной сборки существующих компонентов без правок ядра. Если же нужно «что-то новое» — см. раздел для разработчиков. diff --git a/docs/source/guidebook/developers.md b/docs/source/guidebook/developers.md new file mode 100644 index 00000000..a884ec0f --- /dev/null +++ b/docs/source/guidebook/developers.md @@ -0,0 +1,50 @@ +## Для разработчиков: расширение и модификация SAMPO + +Ниже — обзор архитектуры и путей расширения для **power-пользователей и контрибьюторов**. + +*Высокоуровневая архитектура SAMPO. Два основных подхода: конкретный алгоритм планирования и многоагентная стратегия (разбиение на подзадачи и последующее объединение).* + +### Обзор архитектуры + +* **Схемы данных** (`sampo.schemas`): **WorkGraph**, **WorkUnit**, **Contractor/Worker**, **Schedule** — вход/выход алгоритмов. +* **Алгоритмы планирования** (`sampo.scheduler`): + + * `heft` — HEFTScheduler и варианты. + * `topological` — TopologicalScheduler. + * `genetic` — GeneticScheduler (+ структуры хромосом, мутации и пр.). + * `multi_agency` — многоагентное планирование (генерация блоков, координация). + * Вспомогательные подсистемы (таймлайн, учёт ресурсов и пр.). +* **Конвейер и утилиты**: `sampo.pipeline`, генераторы синтетики (`sampo.generator`), вспомогательные инструменты. + +Дизайн **модульный**: новые алгоритмы реализуют общий интерфейс (`schedule(work_graph, contractors)`). + +### Добавление нового планировщика + +1. **Создайте класс** (напр., `MyScheduler`) и реализуйте `schedule(self, work_graph, contractors)`. +2. **Переиспользуйте** имеющиеся утилиты (проверки допустимости назначения, управление ресурсами). +3. **Интегрируйте в pipeline** — как правило, достаточно передать экземпляр в `.schedule(...)`. + +Лицензия BSD-3 позволяет свободно модифицировать. Для PR — тесты, документация, внятные коммиты. + +### Расширение ограничений + +Примеры: + +* Запрет параллельности двух задач — добавьте фиктивное предшествование в граф или проверку в планировщике. +* Смены/простои — расширьте `WorkUnit`/`Worker` доп. полями и учитывайте их в логике планирования. + +Рекомендуется ознакомиться с кодом простого планировщика (напр., Topological/HEFT), чтобы понять точки встраивания правил. + +### Многоагентные и гибридные стратегии + +Посмотрите `sampo.scheduler.multi_agency`: как генерируются блоки (`block_generator`), как объединяются результаты. Можно экспериментировать с разными стратегиями разбиения и координации агентов (напр., критический путь — генетика; периферия — эвристика). + +### Вклад в проект + +* Ищите `CONTRIBUTING.md`/гайдлайны в репозитории. +* Пишите тесты, документируйте, открывайте PR с мотивацией изменений. +* Следите за релиз-нотами/вики/ReadTheDocs. + +--- + +Надеемся, это руководство дало вам понятный путь от базового знакомства до глубокой работы с SAMPO. Хотите просто получить план? Нужны тонкие настройки качества? Планируете расширять ядро? — SAMPO предоставляет мощную платформу для адаптивного календарного планирования. Удачи! diff --git a/docs/source/guidebook/index.md b/docs/source/guidebook/index.md index 2630791e..fd6f7ec3 100644 --- a/docs/source/guidebook/index.md +++ b/docs/source/guidebook/index.md @@ -1,10 +1,12 @@ -# Гайдбук +# Руководство ```{toctree} :maxdepth: 2 :numbered: -caption: Содержание гайдбука +caption: Руководство SAMPO intro -setup -workflow -tips +quickstart +advanced +developers +``` + diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md new file mode 100644 index 00000000..9ac1e97e --- /dev/null +++ b/docs/source/guidebook/intro.md @@ -0,0 +1,43 @@ +# Руководство SAMPO + +**SAMPO** (Scheduler for Adaptive Manufacturing Processes Optimization) — это открытый фреймворк для построения оптимальных расписаний (планов) в сложных производственных и проектных процессах. Он сочетает **эвристики**, **генетические** и **многоагентные** алгоритмы для эффективного распределения задач и ресурсов под ваши цели оптимизации. Руководство организовано так, чтобы помочь пользователям с разным уровнем подготовки: + +* **Базовый уровень** — что такое SAMPO, чем он поможет и как быстро начать работу. +* **Средний уровень** — как настраивать SAMPO под свои задачи. +* **Продвинутый уровень** — как модифицировать и расширять фреймворк. + +К концу вы поймёте назначение SAMPO, как применять его к задачам расписания и как адаптировать под специфические требования. + +## Введение + +### Что такое SAMPO? + +SAMPO — это **фреймворк адаптивного календарного планирования** для производства и других областей. Проще говоря, он автоматически упорядочивает **задачи** и назначает им **ресурсы** (людей, станки и т. п.) оптимальным образом. Он учитывает ограничения проекта (зависимости задач, лимиты ресурсов, дедлайны и т. д.) и находит план, наилучший по вашей выбранной **целевой метрике** (например, минимизация длительности или стоимости). В SAMPO реализованы эвристики (правила выбора), генетические алгоритмы (эволюционный поиск) и даже многоагентные методы, комбинирующие стратегии. Это позволяет справляться со сложными реальными сценариями, где статические/жадные подходы уступают. + +### Для кого SAMPO? + +SAMPO ориентирован на широкий круг пользователей: + +* **Инженеры и менеджеры проектов** в производстве/строительстве, которым нужно быстро получать качественные планы при множестве зависимостей и дефицитных ресурсов. +* **Исследователи и студенты** в области оптимизации/ИИ, интересующиеся эвристиками, генетическими и многоагентными подходами к планированию. +* **Разработчики ПО**, которым нужен надёжный backend для оптимизации расписаний. + +Если у вас проект с множеством зависимостей и ограничений по ресурсам, и вы хотите автоматизировать поиск хорошего плана исполнения — SAMPO для вас. Применимо к производству, строительству, R\&D, разработке ПО и любым задачам оптимального расписания. + +### Ключевые возможности и преимущества + +* **Несколько алгоритмов планирования**: + + * *Эвристики*: напр., **TopologicalScheduler** (топологический порядок) и **HEFT/HEFTBetween** (Heterogeneous Earliest Finish Time). + * *Генетический алгоритм*: **GeneticScheduler** с кроссовером, мутациями (настраиваемые) и селекцией. + * *Многоагентное планирование*: разбиение проекта на подзадачи и кооперация нескольких «агентов»-планировщиков (подробнее — в продвинутых темах). + +* **Гибкий конвейер (pipeline)** — планировщики подключаются модульно; легко заменить алгоритм или добавить шаги без правок ядра. + +* **Масштабируемость** — рассчитан на крупные графы (2 000–10 000 задач) с поиском качественных решений. + +* **Мультикритериальная оптимизация** — оптимизация по **времени**, **стоимости**, **загруженности ресурсов** и получение **Парето-наборов** компромиссных планов. + +* **Адаптация под домен** — подключение своих парсеров входных данных и моделей оценки времени/ресурсов. + +* **Генерация синтетических данных** — инструменты для создания тестовых графов и пулов ресурсов. diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md new file mode 100644 index 00000000..a6d6c6d4 --- /dev/null +++ b/docs/source/guidebook/quickstart.md @@ -0,0 +1,124 @@ +## Быстрый старт + +Покажем установку, подготовку простого примера, запуск планировщика и просмотр результата. Целевая аудитория — те, кому нужен быстрый результат с минимумом настроек. + +### Базовая терминология + +* **WorkGraph** — граф проекта (DAG): вершины — **работы** (задачи), рёбра — **предшествование**. Узлы содержат требования к ресурсам, объёмы и пр. +* **Contractor** — поставщик ресурсов (бригада, подрядчик). Список Contractor’ов описывает весь доступный пул. +* **Worker/Resource** — тип ресурса и его мощность внутри Contractor’а (напр., 100 «строителей»). Требования задач должны совпадать с имеющимися типами. +* **Scheduler** — реализация алгоритма, принимающая WorkGraph и Contractor’ов и возвращающая Schedule (HEFTScheduler, GeneticScheduler и др.). +* **Schedule** — результат планирования: порядок, времена старта/финиша, назначенные ресурсы; общая **execution\_time** и иные метрики. Часто `schedule(...)` возвращает список (напр., Парето-решения) — обычно берут первый `[0]` как основной. +* **Scheduling Pipeline** — «флюентный» интерфейс пошагового построения и запуска планирования. + +Далее — практическое знакомство. + +### Установка + +SAMPO доступен как пакет Python на PyPI. Установка: + +```bash +pip install sampo +``` + +Убедитесь, что используете Python 3.10 (SAMPO требует 3.10.x). + +### Первый план за несколько шагов + +Сделаем простейший проект и распишем его: + +1. **Граф работ** — создадим `WorkGraph`. Для быстрого старта сгенерируем синтетический. +2. **Ресурсы** — опишем список `Contractor` с рабочими. +3. **Алгоритм** — выберем планировщик (эвристика/генетика). +4. **Запуск** — получим Schedule и посмотрим результат. + +**1. Генерация WorkGraph.** Для простоты воспользуемся генератором: + +```python +from sampo.generator import SimpleSynthetic +from sampo.schemas.graph import WorkGraph + +# Инициализация синтетического генератора +synthetic = SimpleSynthetic() + +# Сгенерируем небольшой граф: ~10 задач в 2 кластерах +work_graph: WorkGraph = synthetic.work_graph( + mode="General", # общий тип структуры + cluster_counts=2, # 2 кластера задач + bottom_border=5, # в каждом кластере 5–8 задач + top_border=8 +) +print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") +``` + +`SimpleSynthetic().work_graph(...)` создаёт случайный WorkGraph. Мы задали два кластера (условно две фазы) и диапазон задач в кластере. Требования к ресурсам заполняются генератором. + +*(Альтернатива: если у вас уже есть данные, можно загрузить их через `WorkGraph.load(folder, filename)`. Для быстрого старта синтетика удобнее.)* + +**2. Ресурсы (Contractor’ы).** Опишем доступные ресурсы: + +```python +from sampo.schemas.contractor import Contractor, Worker + +# Один подрядчик с 10 работниками общего профиля +contractors = [ + Contractor( + id="Contractor1", + workers=[ + Worker(id="w1", kind="general", count=10) + ] + ) +] +``` + +В реальном проекте может быть несколько подрядчиков с разными специализациями. Поле `kind` должно соответствовать типам, которые требуют задачи. + +**3. Выбор планировщика.** Для старта — эвристика **HEFTScheduler**: + +```python +from sampo.scheduler.heft import HEFTScheduler + +# Инициализация HEFT без доп. параметров +scheduler = HEFTScheduler() +``` + +Можно попробовать и `TopologicalScheduler()` (простой базовый порядок) или `GeneticScheduler()` (более глубокий поиск). Для начала HEFT даёт быстрый и приличный результат. (В продвинутом разделе — настройка параметров, напр., `GeneticScheduler(population_size=50, mutate_order=0.1, ...)`.) + +**4. Запуск планирования.** + +```python +# Запуск +schedules = scheduler.schedule(work_graph, contractors) +best_schedule = schedules[0] # берём первое (лучшее) решение + +print(f"Total tasks scheduled: {len(best_schedule.works)}") +print(f"Projected project duration (makespan): {best_schedule.execution_time}") +``` + +`schedule` возвращает список расписаний. Часто это один элемент, поэтому берём `[0]`. Выводим число задач и суммарную длительность проекта. + +Далее можно просмотреть задачи/назначения: + +```python +for task in best_schedule.works: + print(f"Task {task.id} starts at t={task.start_time}, ends at t={task.finish_time}, " + f"assigned to resource type '{task.work_unit.worker_reqs[0].kind}'") +``` + +*(Названия атрибутов могут отличаться; ориентируйтесь на API SAMPO. Пример предполагает наличие `start_time`, `finish_time` и `work_unit` с требованиями к ресурсам.)* + +**5. (Опционально) Конвейер SchedulingPipeline:** + +```python +from sampo.pipeline import SchedulingPipeline + +schedule = SchedulingPipeline.create() \ + .wg(work_graph) \ + .contractors(contractors) \ + .schedule(HEFTScheduler()) \ + .finish()[0] + +print(f"Project duration: {schedule.execution_time}") +``` + +Это эквивалент предыдущих шагов, но в «флюентном» стиле. From 9ef00ad2ca8e2c4f21c44385ccc1b1c25f6c51cc Mon Sep 17 00:00:00 2001 From: NickMur Date: Tue, 16 Sep 2025 23:30:14 +0300 Subject: [PATCH 08/32] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20intro=20=D0=B8=20=D1=82=D0=B5=D1=80?= =?UTF-8?q?=D0=BC=D0=B8=D0=BD=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/guidebook/intro.md | 36 ++++++++++++++++------------- docs/source/guidebook/quickstart.md | 18 +++++++-------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md index 9ac1e97e..85ccc1b7 100644 --- a/docs/source/guidebook/intro.md +++ b/docs/source/guidebook/intro.md @@ -1,6 +1,8 @@ # Руководство SAMPO -**SAMPO** (Scheduler for Adaptive Manufacturing Processes Optimization) — это открытый фреймворк для построения оптимальных расписаний (планов) в сложных производственных и проектных процессах. Он сочетает **эвристики**, **генетические** и **многоагентные** алгоритмы для эффективного распределения задач и ресурсов под ваши цели оптимизации. Руководство организовано так, чтобы помочь пользователям с разным уровнем подготовки: +**SAMPO** (Scheduler for Adaptive Manufacturing Processes Optimization) — это открытый фреймворк для построения оптимальных расписаний (планов) +в сложных производственных и проектных процессах. Он сочетает **эвристики**, **генетические** и **многоагентные** алгоритмы для эффективного распределения задач и ресурсов под ваши цели оптимизации. +Руководство организовано так, чтобы помочь пользователям с разным уровнем подготовки: * **Базовый уровень** — что такое SAMPO, чем он поможет и как быстро начать работу. * **Средний уровень** — как настраивать SAMPO под свои задачи. @@ -12,32 +14,34 @@ ### Что такое SAMPO? -SAMPO — это **фреймворк адаптивного календарного планирования** для производства и других областей. Проще говоря, он автоматически упорядочивает **задачи** и назначает им **ресурсы** (людей, станки и т. п.) оптимальным образом. Он учитывает ограничения проекта (зависимости задач, лимиты ресурсов, дедлайны и т. д.) и находит план, наилучший по вашей выбранной **целевой метрике** (например, минимизация длительности или стоимости). В SAMPO реализованы эвристики (правила выбора), генетические алгоритмы (эволюционный поиск) и даже многоагентные методы, комбинирующие стратегии. Это позволяет справляться со сложными реальными сценариями, где статические/жадные подходы уступают. +SAMPO — это **фреймворк адаптивного календарного планирования** для производства и других областей. +Проще говоря, он автоматически упорядочивает **задачи** и назначает им **ресурсы** (людей, станки и т. п.) оптимальным образом. +Он учитывает ограничения проекта (зависимости задач, лимиты ресурсов, дедлайны и т. д.) и находит план, наилучший по выбранной **целевой метрике** (например, минимизация длительности или стоимости). +В SAMPO реализованы эвристики (правила выбора), генетические алгоритмы (эволюционный поиск) и многоагентные методы, комбинирующие стратегии — это позволяет справляться со сложными реальными сценариями, +где статические/жадные подходы уступают. ### Для кого SAMPO? -SAMPO ориентирован на широкий круг пользователей: +* **Инженеры и менеджеры проектов** в производстве/строительстве — быстро получать качественные планы при множестве зависимостей и дефицитных ресурсов. +* **Разработчики ПО** — использовать SAMPO как надёжный backend для оптимизации расписаний. -* **Инженеры и менеджеры проектов** в производстве/строительстве, которым нужно быстро получать качественные планы при множестве зависимостей и дефицитных ресурсов. -* **Исследователи и студенты** в области оптимизации/ИИ, интересующиеся эвристиками, генетическими и многоагентными подходами к планированию. -* **Разработчики ПО**, которым нужен надёжный backend для оптимизации расписаний. - -Если у вас проект с множеством зависимостей и ограничений по ресурсам, и вы хотите автоматизировать поиск хорошего плана исполнения — SAMPO для вас. Применимо к производству, строительству, R\&D, разработке ПО и любым задачам оптимального расписания. +Если у вас проект с множеством зависимостей и ограничений по ресурсам, и вы хотите автоматизировать поиск хорошего плана исполнения — SAMPO для вас. Применимо к производству, строительству, R&D, разработке ПО и любым задачам оптимального расписания. ### Ключевые возможности и преимущества -* **Несколько алгоритмов планирования**: +* **Реализованные алгоритмы** - * *Эвристики*: напр., **TopologicalScheduler** (топологический порядок) и **HEFT/HEFTBetween** (Heterogeneous Earliest Finish Time). - * *Генетический алгоритм*: **GeneticScheduler** с кроссовером, мутациями (настраиваемые) и селекцией. - * *Многоагентное планирование*: разбиение проекта на подзадачи и кооперация нескольких «агентов»-планировщиков (подробнее — в продвинутых темах). + * *Topological* — эвристика на основе топологической сортировки WorkGraph. + * *HEFT/HEFTBetween* — эвристики класса Heterogeneous Earliest Finish Time. + * *Genetic* — генетический алгоритм с эвристической инициализацией, кроссовером и мутациями. + * *Многоагентное планирование* — разбиение проекта на подзадачи и координация нескольких «агентов»-планировщиков. * **Гибкий конвейер (pipeline)** — планировщики подключаются модульно; легко заменить алгоритм или добавить шаги без правок ядра. -* **Масштабируемость** — рассчитан на крупные графы (2 000–10 000 задач) с поиском качественных решений. +* **Масштабируемость** — устойчив к крупным графам (порядка **2 000–10 000** работ) при поиске качественных решений. -* **Мультикритериальная оптимизация** — оптимизация по **времени**, **стоимости**, **загруженности ресурсов** и получение **Парето-наборов** компромиссных планов. +* **Мультикритериальная оптимизация** — оптимизация по **времени**, **стоимости**, **загруженности ресурсов**; получение **Парето-компромиссов**. -* **Адаптация под домен** — подключение своих парсеров входных данных и моделей оценки времени/ресурсов. +* **Адаптация под домен** — подключение собственных парсеров входных данных и моделей оценки времени/ресурсов. -* **Генерация синтетических данных** — инструменты для создания тестовых графов и пулов ресурсов. +* **Генерация синтетических данных** — инструменты для создания тестовых графов и пулов ресурсов для экспериментов и бенчмарков. diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md index a6d6c6d4..656c000e 100644 --- a/docs/source/guidebook/quickstart.md +++ b/docs/source/guidebook/quickstart.md @@ -1,17 +1,15 @@ ## Быстрый старт -Покажем установку, подготовку простого примера, запуск планировщика и просмотр результата. Целевая аудитория — те, кому нужен быстрый результат с минимумом настроек. +Покажем установку, подготовку простого примера, запуск планировщика и просмотр результата. -### Базовая терминология +### Базовые термины -* **WorkGraph** — граф проекта (DAG): вершины — **работы** (задачи), рёбра — **предшествование**. Узлы содержат требования к ресурсам, объёмы и пр. -* **Contractor** — поставщик ресурсов (бригада, подрядчик). Список Contractor’ов описывает весь доступный пул. -* **Worker/Resource** — тип ресурса и его мощность внутри Contractor’а (напр., 100 «строителей»). Требования задач должны совпадать с имеющимися типами. -* **Scheduler** — реализация алгоритма, принимающая WorkGraph и Contractor’ов и возвращающая Schedule (HEFTScheduler, GeneticScheduler и др.). -* **Schedule** — результат планирования: порядок, времена старта/финиша, назначенные ресурсы; общая **execution\_time** и иные метрики. Часто `schedule(...)` возвращает список (напр., Парето-решения) — обычно берут первый `[0]` как основной. -* **Scheduling Pipeline** — «флюентный» интерфейс пошагового построения и запуска планирования. - -Далее — практическое знакомство. +* **WorkGraph** — DAG (Directed Acyclic Graph) проекта: вершины — *работы (задачи)*, рёбра — *зависимости (предшествования)*. +* **Contractor** — поставщик ресурсов (подрядчик/бригада), содержащий наборы **Worker** (тип и количество). +* **Scheduler** — алгоритм планирования, принимающий WorkGraph и набор Contractor’ов и возвращающий **Schedule**. +* **Schedule** — результат планирования: времена стартов/финишей задач, назначения ресурсов, интегральные метрики (например, makespan). +* **Scheduling Pipeline** — «флюентный» интерфейс для пошаговой сборки и запуска процесса планирования. +P.s. «флюентным интерфейсом» называют такой стиль программирования, где методы возвращают объект, позволяя вызывать их цепочкой. ### Установка From b23b90ef6d177b10d2dd8ba1e8eef873123115b0 Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 17 Sep 2025 18:37:53 +0300 Subject: [PATCH 09/32] =?UTF-8?q?=D0=97=D0=B0=D0=BA=D0=BE=D0=BD=D1=87?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20intro.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/guidebook/intro.md | 123 +++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 23 deletions(-) diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md index 85ccc1b7..a43a98fd 100644 --- a/docs/source/guidebook/intro.md +++ b/docs/source/guidebook/intro.md @@ -1,47 +1,124 @@ # Руководство SAMPO -**SAMPO** (Scheduler for Adaptive Manufacturing Processes Optimization) — это открытый фреймворк для построения оптимальных расписаний (планов) -в сложных производственных и проектных процессах. Он сочетает **эвристики**, **генетические** и **многоагентные** алгоритмы для эффективного распределения задач и ресурсов под ваши цели оптимизации. +## Для кого руководство? + Руководство организовано так, чтобы помочь пользователям с разным уровнем подготовки: * **Базовый уровень** — что такое SAMPO, чем он поможет и как быстро начать работу. * **Средний уровень** — как настраивать SAMPO под свои задачи. * **Продвинутый уровень** — как модифицировать и расширять фреймворк. -К концу вы поймёте назначение SAMPO, как применять его к задачам расписания и как адаптировать под специфические требования. - ## Введение ### Что такое SAMPO? -SAMPO — это **фреймворк адаптивного календарного планирования** для производства и других областей. -Проще говоря, он автоматически упорядочивает **задачи** и назначает им **ресурсы** (людей, станки и т. п.) оптимальным образом. -Он учитывает ограничения проекта (зависимости задач, лимиты ресурсов, дедлайны и т. д.) и находит план, наилучший по выбранной **целевой метрике** (например, минимизация длительности или стоимости). -В SAMPO реализованы эвристики (правила выбора), генетические алгоритмы (эволюционный поиск) и многоагентные методы, комбинирующие стратегии — это позволяет справляться со сложными реальными сценариями, -где статические/жадные подходы уступают. +**SAMPO** (Scheduler for Adaptive Manufacturing Processes Optimization) — это открытый **фреймворк адаптивного +календарного планирования** для производства и других областей. -### Для кого SAMPO? +> **Адаптивное календарное планирование** — построение расписания с учётом ограничений и его автоматическое +> пересчитывание при изменениях (задержках, изменении ресурсов, приоритетов или сроков). + +Он автоматически упорядочивает **задачи** и назначает им **ресурсы** (людей, оборудование и т. п.) оптимальным образом с +учётом ограничений проекта (зависимости, лимиты ресурсов, дедлайны). Цель — построение расписания, наилучшего по +выбранной **метрике оптимизации** (например, минимизация длительности или стоимости). -* **Инженеры и менеджеры проектов** в производстве/строительстве — быстро получать качественные планы при множестве зависимостей и дефицитных ресурсов. -* **Разработчики ПО** — использовать SAMPO как надёжный backend для оптимизации расписаний. +В SAMPO реализованы **эвристики**, **генетические алгоритмы** и **многоагентные методы**, а также их комбинации, что +позволяет эффективно решать сложные реальные сценарии, где статические или жадные подходы недостаточны. Фреймворк +поддерживает **реактивное перепланирование** при изменениях и возмущениях в ходе выполнения проекта. -Если у вас проект с множеством зависимостей и ограничений по ресурсам, и вы хотите автоматизировать поиск хорошего плана исполнения — SAMPO для вас. Применимо к производству, строительству, R&D, разработке ПО и любым задачам оптимального расписания. +### Для кого SAMPO? + +Если у вас проект с множеством зависимостей и ограничений по ресурсам, и вы хотите автоматизировать поиск хорошего плана +исполнения — SAMPO для вас. ### Ключевые возможности и преимущества -* **Реализованные алгоритмы** +* **Многокритериальная оптимизация** + — работа сразу с несколькими целями: минимизация длительности и стоимости, сокращение просрочек, балансировка + загрузки, приоритизация заказов; поддержка поиска **Парето-компромиссов** и гибкой агрегации метрик. + +> **Парето-компромисс** — решение, где нельзя улучшить одну цель без ухудшения другой; множество таких решений образует +> фронт Парето. + +* **Сложные ограничения и ресурсы** + — учёт предшествования и альтернативных маршрутов, параллельных/альтернативных ресурсов, сменных календарей, + навыков/квалификаций (skills), окон доступности, ограничений мощностей, переналадок/переконфигураций и логистических + задержек. + +* **Поддерживаемые модели задач** + — RCPSP/MRCPSP (в том числе многопроектное планирование), варианты *Job Shop*, *Flow Shop* и DAG-ориентированные + процессы. + + > **RCPSP** — задача календарного планирования с ограниченными ресурсами. + + > **MRCPSP** — расширение RCPSP, где для каждой работы есть несколько способов выполнения с разными + ресурсами/длительностью. + + > **Job Shop / Flow Shop** — классические производственные модели: порядок и маршруты задач фиксированы (*Flow Shop*) + или различаются для каждого изделия (*Job Shop*). + + > **DAG-процессы** — планирование по ацикличным графам зависимостей (Directed Acyclic Graph). + +* **Гибкий конвейер (pipeline)** + — модульное подключение планировщиков; легко заменить алгоритм или добавить шаги без правок ядра. + +* **Адаптивность и реактивное планирование** + — пересчёт расписания при изменении входных данных (WorkGraph, ресурсы Contractor, дедлайны, веса критериев) за счёт + модульного SchedulingPipeline (пайплайн построен из независимых, заменяемых шагов) и настраиваемых оценщиков + времени/ресурсов. Поддерживается автоматическое перепланирование «на лету» при задержках, сбоях, изменении ресурсов, + приоритетов или поступлении новых работ. + +* **Масштабируемость** + — устойчив к крупным графам (порядка **2 000–10 000** работ) при поиске качественных решений. + +* **Адаптация под домен** + — возможность подключать свои парсеры, модели оценки времени/ресурсов, критерии качества и политики диспетчеризации. + +* **Инструментарий** + — генерация и бенчмаркинг тестовых данных, средства анализа качества решений, утилиты визуализации (диаграммы Ганта, + загрузка ресурсов). + + > **Диаграмма Ганта** — график, где работы показываются полосами на временной шкале, отражающими их начало и + длительность. + +* **Открытость и документация** + — лицензия **BSD 3-Clause**, подробная документация, примеры в **Jupyter Notebook**, удобный **Python API**. + + > BSD 3-Clause — свободная лицензия, разрешающая использовать, изменять и распространять код (в том числе в + коммерческих проектах) при сохранении уведомления об авторских правах и отказа от ответственности. + +--- + +### Реализованные алгоритмы и подходы + +* **Эвристики** + + > Эвристики — упрощённые правила «разумного выбора», дающие быстрое приближённое решение. + + - *Topological* — алгоритм, который строит порядок выполнения задач на основе их зависимостей. Использует + топологическую сортировку графа работ (DAG), то есть размещает каждую задачу только после всех её + предшественников. + + - *HEFT/HEFTBetween* — жадные эвристики *Heterogeneous Earliest Finish Time*, минимизирующие время завершения. + + > HEFT/HEFTBetween — метод планирования, который идёт по задачам и назначает каждую туда, где она закончится + быстрее всего, учитывая зависимости и разную скорость ресурсов. + +* **Генетические алгоритмы** + + > Генетические алгоритмы — оптимизация по принципам эволюции: популяция решений подвергается «скрещиванию» и + «мутациям» для улучшения качества. - * *Topological* — эвристика на основе топологической сортировки WorkGraph. - * *HEFT/HEFTBetween* — эвристики класса Heterogeneous Earliest Finish Time. - * *Genetic* — генетический алгоритм с эвристической инициализацией, кроссовером и мутациями. - * *Многоагентное планирование* — разбиение проекта на подзадачи и координация нескольких «агентов»-планировщиков. + - *Genetic* — алгоритм с эвристической инициализацией, кроссовером и мутациями. -* **Гибкий конвейер (pipeline)** — планировщики подключаются модульно; легко заменить алгоритм или добавить шаги без правок ядра. + > Позволяет находить более качественные планы, чем простые жадные методы, так как исследует разные комбинации решений. -* **Масштабируемость** — устойчив к крупным графам (порядка **2 000–10 000** работ) при поиске качественных решений. + > Жадные методы — алгоритмы, которые на каждом шаге выбирают локально «лучшее» решение (например, самую короткую + задачу или ресурс с наименьшей загрузкой), не анализируя последствия для всего расписания. -* **Мультикритериальная оптимизация** — оптимизация по **времени**, **стоимости**, **загруженности ресурсов**; получение **Парето-компромиссов**. +* **Многоагентные методы** -* **Адаптация под домен** — подключение собственных парсеров входных данных и моделей оценки времени/ресурсов. + > Многоагентные методы — решение задач группой независимых планировщиков, каждый из которых отвечает за свою часть + проекта. -* **Генерация синтетических данных** — инструменты для создания тестовых графов и пулов ресурсов для экспериментов и бенчмарков. + - *Многоагентное планирование* — разбиение проекта на подзадачи и координация агентов. From 76a48015fd84d52de4c846ce68d8bef470cbb6d3 Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 17 Sep 2025 20:04:54 +0300 Subject: [PATCH 10/32] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=BD=20quickstart.md:=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D0=B9=20=D1=81=D1=82=D0=B0=D1=80=D1=82=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=BE=D0=B2=D0=B8=D1=87=D0=BA=D0=BE?= =?UTF-8?q?=D0=B2,=20=D0=B0=20=D1=82=D0=B0=D0=BA=D0=B6=D0=B5=20=D1=88?= =?UTF-8?q?=D0=BF=D0=B0=D1=80=D0=B3=D0=B0=D0=BB=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/guidebook/quickstart.md | 229 +++++++++++++++++++++------- 1 file changed, 174 insertions(+), 55 deletions(-) diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md index 656c000e..1ec533a2 100644 --- a/docs/source/guidebook/quickstart.md +++ b/docs/source/guidebook/quickstart.md @@ -1,17 +1,9 @@ -## Быстрый старт +# Быстрый старт -Покажем установку, подготовку простого примера, запуск планировщика и просмотр результата. +Покажем установку, подготовку простого примера, запуск планировщика и просмотр результата, а также предоставим +«шпаргалку». -### Базовые термины - -* **WorkGraph** — DAG (Directed Acyclic Graph) проекта: вершины — *работы (задачи)*, рёбра — *зависимости (предшествования)*. -* **Contractor** — поставщик ресурсов (подрядчик/бригада), содержащий наборы **Worker** (тип и количество). -* **Scheduler** — алгоритм планирования, принимающий WorkGraph и набор Contractor’ов и возвращающий **Schedule**. -* **Schedule** — результат планирования: времена стартов/финишей задач, назначения ресурсов, интегральные метрики (например, makespan). -* **Scheduling Pipeline** — «флюентный» интерфейс для пошаговой сборки и запуска процесса планирования. -P.s. «флюентным интерфейсом» называют такой стиль программирования, где методы возвращают объект, позволяя вызывать их цепочкой. - -### Установка +## Установка SAMPO доступен как пакет Python на PyPI. Установка: @@ -21,39 +13,41 @@ pip install sampo Убедитесь, что используете Python 3.10 (SAMPO требует 3.10.x). -### Первый план за несколько шагов +## Первый план за несколько шагов Сделаем простейший проект и распишем его: -1. **Граф работ** — создадим `WorkGraph`. Для быстрого старта сгенерируем синтетический. -2. **Ресурсы** — опишем список `Contractor` с рабочими. -3. **Алгоритм** — выберем планировщик (эвристика/генетика). -4. **Запуск** — получим Schedule и посмотрим результат. +1. Граф работ — создадим WorkGraph. Для быстрого старта сгенерируем синтетический. +2. Ресурсы — опишем список Contractor с рабочими. +3. Алгоритм — выберем планировщик (эвристика/генетика). +4. Запуск — получим Schedule и посмотрим результат. -**1. Генерация WorkGraph.** Для простоты воспользуемся генератором: +1) Генерация WorkGraph. Для простоты воспользуемся генератором ```python -from sampo.generator import SimpleSynthetic +from sampo.generator import SimpleSynthetic, SyntheticGraphType from sampo.schemas.graph import WorkGraph # Инициализация синтетического генератора synthetic = SimpleSynthetic() -# Сгенерируем небольшой граф: ~10 задач в 2 кластерах +# Сгенерируем небольшой граф: ~2 кластера по 5–8 задач work_graph: WorkGraph = synthetic.work_graph( - mode="General", # общий тип структуры - cluster_counts=2, # 2 кластера задач - bottom_border=5, # в каждом кластере 5–8 задач + mode=SyntheticGraphType.GENERAL, # тип структуры + cluster_counts=2, # 2 кластера + bottom_border=5, # в кластере 5–8 задач top_border=8 ) print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") ``` -`SimpleSynthetic().work_graph(...)` создаёт случайный WorkGraph. Мы задали два кластера (условно две фазы) и диапазон задач в кластере. Требования к ресурсам заполняются генератором. +Альтернатива: если у вас уже есть данные, загрузите их из файла: -*(Альтернатива: если у вас уже есть данные, можно загрузить их через `WorkGraph.load(folder, filename)`. Для быстрого старта синтетика удобнее.)* +```python +wg = WorkGraph.load(folder, filename) +``` -**2. Ресурсы (Contractor’ы).** Опишем доступные ресурсы: +2) Ресурсы (Contractor’ы) ```python from sampo.schemas.contractor import Contractor, Worker @@ -62,61 +56,186 @@ from sampo.schemas.contractor import Contractor, Worker contractors = [ Contractor( id="Contractor1", - workers=[ - Worker(id="w1", kind="general", count=10) - ] + workers=[Worker(id="w1", kind="general", count=10)] ) ] ``` -В реальном проекте может быть несколько подрядчиков с разными специализациями. Поле `kind` должно соответствовать типам, которые требуют задачи. +В реальном проекте может быть несколько подрядчиков с разными специализациями. Значение kind должно соответствовать +типам, которых требуют задачи в WorkGraph. -**3. Выбор планировщика.** Для старта — эвристика **HEFTScheduler**: +3) Выбор планировщика ```python from sampo.scheduler.heft import HEFTScheduler -# Инициализация HEFT без доп. параметров -scheduler = HEFTScheduler() +# или: from sampo.scheduler import HEFTScheduler, TopologicalScheduler, GeneticScheduler + +scheduler = HEFTScheduler() # быстрая эвристика для старта ``` -Можно попробовать и `TopologicalScheduler()` (простой базовый порядок) или `GeneticScheduler()` (более глубокий поиск). Для начала HEFT даёт быстрый и приличный результат. (В продвинутом разделе — настройка параметров, напр., `GeneticScheduler(population_size=50, mutate_order=0.1, ...)`.) +Также можно попробовать: + +- TopologicalScheduler() — простой базовый порядок +- GeneticScheduler(...) — более глубокий поиск (с настройкой гиперпараметров, например mutate_order, mutate_resources и + т.п.) -**4. Запуск планирования.** +4) Запуск планирования + Важный момент: метод schedule возвращает список кортежей; нам нужен сам Schedule из первого элемента. ```python -# Запуск -schedules = scheduler.schedule(work_graph, contractors) -best_schedule = schedules[0] # берём первое (лучшее) решение +# Планирование: берём первое (лучшее) решение +best_schedule, start_time, timeline, node_order = scheduler.schedule(work_graph, contractors)[0] -print(f"Total tasks scheduled: {len(best_schedule.works)}") print(f"Projected project duration (makespan): {best_schedule.execution_time}") ``` -`schedule` возвращает список расписаний. Часто это один элемент, поэтому берём `[0]`. Выводим число задач и суммарную длительность проекта. - -Далее можно просмотреть задачи/назначения: +Просмотр расписания +У разных версий могут отличаться детали структур расписания. Надёжный способ получить агрегированное представление и +визуализировать — воспользоваться встроенной функцией Ганта: ```python -for task in best_schedule.works: - print(f"Task {task.id} starts at t={task.start_time}, ends at t={task.finish_time}, " - f"assigned to resource type '{task.work_unit.worker_reqs[0].kind}'") -``` +from sampo.utilities.visualization import schedule_gant_chart_fig, VisualizationMode -*(Названия атрибутов могут отличаться; ориентируйтесь на API SAMPO. Пример предполагает наличие `start_time`, `finish_time` и `work_unit` с требованиями к ресурсам.)* +merged = best_schedule.merged_stages_datetime_df(start_date='2025-01-01') +fig = schedule_gant_chart_fig(merged, VisualizationMode.ReturnFig, remove_service_tasks=False) +fig.show() +``` -**5. (Опционально) Конвейер SchedulingPipeline:** +5) (Опционально) Конвейер SchedulingPipeline + Эквивалент тех же шагов во «флюентном» стиле. finish() возвращает список ScheduledProject, из которого берём [0] и + затем читаем project.schedule. ```python from sampo.pipeline import SchedulingPipeline +from sampo.scheduler.heft import HEFTScheduler -schedule = SchedulingPipeline.create() \ - .wg(work_graph) \ - .contractors(contractors) \ - .schedule(HEFTScheduler()) \ - .finish()[0] +project = (SchedulingPipeline.create() +.wg(work_graph) +.contractors(contractors) +.schedule(HEFTScheduler()) +.finish()[0]) -print(f"Project duration: {schedule.execution_time}") +print(f"Project duration: {project.schedule.execution_time}") ``` -Это эквивалент предыдущих шагов, но в «флюентном» стиле. +Подсказки, что попробовать дальше: + +- Замените HEFTScheduler на GeneticScheduler для поиска лучших планов. +- Добавьте .optimize_local(...) до/после .schedule(...) для локальных улучшений порядка/расписания. +- Укажите .lag_optimize(...) и .work_estimator(...) при необходимости учитывать дополнительные ограничения и модели + оценки времени. + +## «Шпаргалка» + +### Базовые сущности + +- WorkGraph — DAG проекта с двумя служебными вершинами start/finish; вершины — GraphNode с вложенным WorkUnit (описание + работы), рёбра — зависимости с типом EdgeType (не только FS) и поддержкой лагов. + > WorkGraph — граф работ проекта, где зафиксированы все задачи и их зависимости. + +- GraphNode — вершина WorkGraph, хранящая WorkUnit и ссылки на родителей/потомков (с типами рёбер и лагами). + > GraphNode — контейнер WorkUnit и связей в графе. + +- WorkUnit — работа: объём, единицы измерения, приоритет, требования к ресурсам (WorkerReq: вид ресурса и мин/макс + количество), сервисные флаги и др. + > WorkUnit — описание одной задачи проекта с её характеристиками и потребностями в ресурсах. + +- WorkerReq — требование работы к виду ресурса: specialty/kind, min_count, max_count, объём для нормирования + длительности. + > WorkerReq — «каких и сколько ресурсов нужно на эту задачу». + +- Contractor — поставщик ресурсов: словари workers: dict[str, Worker] и equipments: dict[str, Equipment]; каждому Worker + проставляется contractor_id. + > Contractor — организация или подразделение, предоставляющее людей и технику. + +- Worker — ресурс «люди»: имя (специальность), count, contractor_id, распределение производительности ( + Static/Stochastic). + > Worker — трудовой ресурс с численностью и производительностью (иногда со стоимостной оценкой). + +- Equipment — ресурс «техника» с типом и количеством. + > Equipment — оборудование, используемое в проекте. + +--- + +### Планировщик и результат + +- Scheduler — алгоритм планирования. Принимает WorkGraph и список Contractor (а также опционально spec, landscape, + work_estimator). Возвращает список Schedule. Расширенный метод schedule_with_cache возвращает кортежи (Schedule, + start_time: Time, Timeline, node_order: list[GraphNode]). + > Scheduler — модуль, который строит расписание по задачам и ресурсам. + Примечание: базовый schedule возвращает list[Schedule]; на практике берут первый план: schedule(...)[0]. + +- Schedule — расписание: обёртка над pandas DataFrame со стартами/финишами, назначениями ресурсов, стоимостью и пр.; + содержит объекты ScheduledWork. Метрика execution_time — makespan (время завершения последней работы). + > Schedule — итоговый план выполнения работ с назначенными ресурсами и сроками. + +- ScheduledWork — элемент расписания: конкретная работа с назначенными ресурсами, стартом/финишем и длительностью. + > ScheduledWork — «строка» плана по одной работе. + +- ScheduleSpec — спецификация для ограничений/назначений ресурсов на работы (в т.ч. фиксирование объёмов команд). + > ScheduleSpec — настройки, ограничивающие или уточняющие распределение ресурсов. + +- Timeline — внутренняя временная шкала использования ресурсов, которую поддерживает планировщик при вычислениях. + > Timeline — модель занятости ресурсов во времени. + +--- + +### Оценки и окружение + +- WorkTimeEstimator — оценщик длительности работ, подставляется в планировщик через work_estimator. + > WorkTimeEstimator — функция или модель, определяющая длительность выполнения задачи. + +- LandscapeConfiguration / ZoneConfiguration — конфигурация пространственных/зональных ограничений и смен статусов зон ( + при необходимости). + > Landscape/ZoneConfiguration — описание территории или зоны выполнения работ с ограничениями. + +--- + +### Пайплайн + +- Scheduling Pipeline — «флюентный» builder-интерфейс для конфигурирования и запуска планирования: + SchedulingPipeline.create() → ... → .finish() → ScheduledProject (project.schedule). + > «Флюентный» builder — цепочка методов, где вы задаёте граф работ, ресурсы и опции, выбираете алгоритм и получаете + результат как ScheduledProject. + + > Кратко о методах пайплайна (в рекомендуемом порядке): + + 1) create() — старт билдера. + 2) name_mapper(fn) — (опционально) нормализация/переименование работ и ресурсов. + 3) wg(x) — задать WorkGraph (объект/таблица/файл). + 4) contractors(x) — задать подрядчиков (список/таблица/генерация). + 5) history(data) — (опционально) исторические данные для калибровки оценок. + 6) work_estimator(est) — (опционально) модель оценки длительностей. + 7) spec(spec) — (опционально) ограничения/фиксация ресурсов и этапов. + 8) landscape(cfg) — (опционально) пространственные/зональные ограничения. + 9) time_shift(offset) — (опционально) сдвиг начала расписания/дат. + 10) lag_optimize(strategy) — (опционально) обработка лагов/разбиение стадий; если не указать — подберётся + автоматически. + 11) node_order(order|optimizer) — (опционально) зафиксировать/улучшить порядок узлов до планирования. + 12) schedule(scheduler, validate=False) — выбрать алгоритм и построить расписание. + 13) optimize_local(optimizer, …) — (опционально) локальная оптимизация готового плана после schedule. + 14) visualization(opts) — (опционально) визуализация плана. + 15) finish() — получить результат как ScheduledProject (обычно берут [0]). + +> Примечание: optimize_local может вызываться также ДО schedule для оптимизации порядка (альтернатива пункту 11). + +- ScheduledProject — контейнер с результатом проекта (включая project.schedule), доступен после finish(). + > ScheduledProject — объект с итоговым расписанием проекта. + +--- + +### Многоагентность + +- BlockGraph / BlockNode — декомпозиция проекта на блоки (подграфы) с зависимостями между блоками; возможна обратная + сборка в единый WorkGraph. + > BlockGraph / BlockNode — представление проекта как набора крупных блоков задач. + +- Agent — независимый планировщик с собственными Contractor’ами и Timeline; протокол offer/confirm для выдачи и фиксации + плана блока. + > Agent — отдельный планировщик, отвечающий за свой участок проекта. + Важно: подрядчики разных агентов не должны пересекаться (валидация проверяет уникальность). + +- Manager / NeuralManager — координация агентов, распределение блоков и выбор последовательности с учётом зависимостей и + метрик; NeuralManager использует обучаемую политику выбора. + > Manager / NeuralManager — управляющий модуль, который согласует работу агентов. From b92b202eaa76b628450ddcbed7cae792dab20f1a Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 17 Sep 2025 20:53:23 +0300 Subject: [PATCH 11/32] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82?= =?UTF-8?q?=D1=83=D1=80=D1=8B=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/conf.py | 2 +- docs/source/index.rst | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index b6390d17..342bcf32 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -40,7 +40,7 @@ ] # todo_include_todos = True -source_suffix = '.rst' +source_suffix = {'.rst': 'restructuredtext', '.md': 'markdown'} master_doc = 'index' add_function_parentheses = True diff --git a/docs/source/index.rst b/docs/source/index.rst index 3f6a4425..32ee396e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -10,11 +10,8 @@ Welcome to SAMPO's documentation! :maxdepth: 2 :caption: Contents: - Install - Usage - Features - autoapi/index guidebook/index + autoapi/sampo/index Supported by From bedee8c2e10c5b862dc1ef0a263cce3aa7547af0 Mon Sep 17 00:00:00 2001 From: NickMur Date: Mon, 22 Sep 2025 20:53:46 +0300 Subject: [PATCH 12/32] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B9=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/guidebook/advanced.md | 273 +++++++++++++++++++++++++--- docs/source/guidebook/checklist.md | 103 +++++++++++ docs/source/guidebook/developers.md | 50 ----- docs/source/guidebook/index.md | 1 + docs/source/guidebook/intro.md | 4 +- docs/source/guidebook/quickstart.md | 152 +--------------- 6 files changed, 362 insertions(+), 221 deletions(-) create mode 100644 docs/source/guidebook/checklist.md delete mode 100644 docs/source/guidebook/developers.md diff --git a/docs/source/guidebook/advanced.md b/docs/source/guidebook/advanced.md index b30a7fd9..133d0273 100644 --- a/docs/source/guidebook/advanced.md +++ b/docs/source/guidebook/advanced.md @@ -1,43 +1,262 @@ -## Расширённое использование и настройка +# Расширённое использование и настройка -После базового знакомства вы можете тонко настроить SAMPO: выбирать алгоритмы, тюнить параметры, работать с несколькими критериями и встраивать доменные компоненты. +После базового знакомства вы можете тонко настроить SAMPO: выбирать алгоритмы, тюнить параметры, работать с несколькими +критериями и встраивать доменные компоненты. -### Как выбрать алгоритм -* **Эвристики (HEFT, Topological)** — быстрые, дают хорошую базу. HEFT/HEFTBetween используют критические пути/приоритеты. -* **Генетический алгоритм** — исследует больше вариантов и часто находит лучше, но дольше считает. Настраиваются `population_size`, `iterations`, `mutate_order`, `mutate_resources` и др.: +## Как собрать WorkGraph - ```python - from sampo.scheduler.genetic import GeneticScheduler - scheduler = GeneticScheduler(population_size=100, iterations=50, mutate_order=0.1, mutate_resources=0.1) - ``` -* **Многоагентное планирование** — делит граф на блоки, применяет разные стратегии и объединяет результат. Полезно для очень больших проектов и гибридных стратегий; требует большего сетапа (`sampo.scheduler.multi_agency`). +### Способ A. Сгенерировать синтетический граф +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator.pipeline import SyntheticGraphType -**Практика:** начните с эвристики для быстрого ответа, затем при необходимости примените GeneticScheduler и сравните качества планов. +ss = SimpleSynthetic() +wg = ss.work_graph(mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200) +``` -### Кастомизация конвейера и ограничений +### Способ B. Загрузить из CSV +```python +from sampo.pipeline import SchedulingPipeline +from sampo.scheduler.heft import HEFTScheduler +from sampo.pipeline.lag_optimization import LagOptimizationStrategy -`SchedulingPipeline` упрощает код и допускает расширения. Можно добавлять собственные шаги (пред-/пост-обработку), комбинировать несколько алгоритмов (напр., «черновик HEFT → доулучшение Genetic»). +project = (SchedulingPipeline.create() + .wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) + .lag_optimize(LagOptimizationStrategy.TRUE) + .schedule(HEFTScheduler()) + .finish()[0]) +``` +> О структуре CSV: -Дополнительные **ограничения** (напр., запрет параллельности для двух конкретных задач, смены/простой оборудования) можно: +- Обязательные колонки: + - activity_id, activity_name, granular_name, volume, measurement, priority +- Зависимости (списки через запятую, длины строго совпадают): + - predecessor_ids, connection_types, lags + - Типы связей: FS, SS, FF, IFS, FFS; лаги — числа (обычно 0) +- Опционально (требования по ресурсам в ячейках-словарях): + - min_req, max_req, req_volume (формат: {"worker_kind": value}) +- Примечания: + - Сервисные узлы start/finish в CSV не нужны — добавляются автоматически + - В .wg(path, sep=';') укажите разделитель файла; внутри списков — запятая -* Заложить в граф (добавить зависимости, объединить узлы). -* Учесть пост-обработкой расписания. -* Внедрить в сам алгоритм (напр., штрафы в fitness-функции у генетики или проверки допустимости). +Мини-пример CSV с двумя задачами и зависимостью B от A (FS, лаг 0): +``` +activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags +A;Task A;A;1.0;unit;0;;; +B;Task B;B;1.0;unit;0;A;FS;0 +``` +> Пример можно посмотреть в `tests/parser/test_wg.csv`. -### Мультикритериальная оптимизация +### Способ C. Программно из узлов -Цель может быть не одна: **время** и **стоимость** одновременно. Подходы: +- Создайте WorkUnit для каждой задачи (с требованиями WorkerReq по необходимости). +- Для зависимостей используйте GraphNode(work_unit, parents), где parents — список кортежей (parent_node, lag, EdgeType). +- Соберите граф: WorkGraph.from_nodes([...]) — start/finish добавятся автоматически. -* **Отдельные прогоны по разным метрикам** и сравнение результатов. -* **Собственная целевая функция/веса** (часто — модификация генетики), либо режим Парето, если доступен. Итог — набор компромиссных планов (Парето-фронт). +```python +from sampo.schemas.works import WorkUnit +from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType +from sampo.schemas.requirements import WorkerReq +from sampo.schemas.time import Time -Важно определить метрики и подготовить данные (стоимости, дедлайны) заранее. +# Работы (пример: A -> B -> C, FS с лагом 0) +wu_a = WorkUnit(id='A', name='Task A', + worker_reqs=[WorkerReq(kind='general', volume=Time(10), min_count=2, max_count=4)], + volume=1.0, is_service_unit=False) -### Интеграция с вашими данными и системами +wu_b = WorkUnit(id='B', name='Task B', + worker_reqs=[], volume=1.0, is_service_unit=False) -* **Парсеры входа**: читайте CSV/БД/MS Project и формируйте `WorkGraph`/`Contractor` своими скриптами. -* **Модели оценки времени/ресурсов**: можно подключать внешние модели (в т. ч. ML) и итеративно уточнять длительности («план → симуляция → корректировка → перепланирование»). -* **Вывод/визуализация**: экспорт расписания в JSON/CSV, построение диаграмм Ганта сторонними средствами и т. п. +wu_c = WorkUnit(id='C', name='Task C', + worker_reqs=[], volume=1.0, is_service_unit=False) -Во многих случаях достаточно грамотной сборки существующих компонентов без правок ядра. Если же нужно «что-то новое» — см. раздел для разработчиков. +n_a = GraphNode(wu_a, []) # корень +n_b = GraphNode(wu_b, [(n_a, 0, EdgeType.FinishStart)]) # A -> B (FS, 0) +n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # B -> C (FS, 0) + +wg = WorkGraph.from_nodes([n_a, n_b, n_c]) +``` + +Коротко: +- Типы связей: EdgeType.FinishStart (FS), StartStart (SS), FinishFinish (FF), InseparableFinishStart (IFS), LagFinishStart (FFS). +- Лаг — второй элемент кортежа в parents (число, обычно 0). + +## Как создаются/получаются подрядчики (Contractor) + +### Вручную: + +```python +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker +from sampo.schemas.interval import IntervalGaussian + +contractor = Contractor(workers={ + 'driver': Worker(id='w1', name='driver', count=8, productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5)), + 'fitter': Worker(id='w2', name='fitter', count=6, productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6))}, + id='c1', name='Contractor A') +``` + +### Генератором по параметру “размер пакета”: +```python +from sampo.generator.base import SimpleSynthetic +ss = SimpleSynthetic() +contractor = ss.contractor(pack_worker_count=10) +``` + +### По графу работ (из требований): +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod +contractor = get_contractor_by_wg(wg, scaler=1.0, method=ContractorGenerationMethod.AVG) +``` + +Метод берёт требования по работам (min/max), агрегирует и формирует пул. + +## Как выбрать алгоритм + +### Эвристические планировщики (HEFT, HEFTBetween, Topological) + - Очень быстрые и дают хорошую стартовую базу. + - HEFT/HEFTBetween ранжируют работы по приоритетам/критичности и оцениваемым временам выполнения. + - Topological строит порядок по зависимостям без сложной оптимизации. + +Импорты: + +```python +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.heft import HEFTBetweenScheduler +from sampo.scheduler.topological import TopologicalScheduler +``` + +### Генетический планировщик + - Перебирает много альтернатив и часто улучшает результат, но требует больше времени на расчёт. + - Ключевые параметры: + - number_of_generation, size_of_population — глубина и ширина поиска; + - mutate_order — вероятность мутации гена порядка (перестановки работ с сохранением зависимостей). Выше → шире + поиск по допустимым порядкам, но медленнее сходимость. + - mutate_resources — вероятность мутации ресурсных генов (перераспределение видов/объемов и подрядчиков в рамках + min/max и доступности). Выше → больше вариантов параллельности, но при дефиците ресурсов растёт риск + конфликтов. + - при необходимости — work_estimator (модель оценки длительностей), seed (для воспроизводимости). + + > * **number_of_generation** — число итераций генетического алгоритма (↑ поколений → лучше результат, но дольше). + > * **size_of_population** — размер популяции (↑ особей → выше разнообразие, но дороже по времени/памяти). + > * **mutate_order** — вероятность мутации порядка работ (↑ → активнее поиск, но медленнее сходимость). + > * **mutate_resources** — вероятность мутации распределения ресурсов (↑ → больше параллельности, но риск конфликтов). + +Пример: + +```python +from sampo.scheduler.genetic import GeneticScheduler + +scheduler = GeneticScheduler( + number_of_generation=50, + size_of_population=100, + mutate_order=0.1, + mutate_resources=0.1 +) +``` + +### Многоагентное планирование + - делит граф на блоки, применяет разные стратегии и объединяет результат. Полезно для очень + больших проектов и гибридных стратегий; требует большего сетапа (модуль `sampo.scheduler.multi_agency`). + +#### Пример без разбиения на блоки: +- два агента с разными планировщиками «соревнуются» за лучший план (аукцион). + +```python +from uuid import uuid4 +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.topological import TopologicalScheduler +from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker + +# 1) Небольшой граф работ +ss = SimpleSynthetic(231) +wg = ss.work_graph(bottom_border=30, top_border=40) + +# 2) Универсальный подрядчик с запасом по всем требуемым видам работников +kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} +cid = str(uuid4()) +workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} +contractors = [Contractor(id=cid, name="Universal", workers=workers, equipments={})] + +# 3) Два агента с разными стратегиями +agents = [ + Agent("HEFT", HEFTScheduler(), contractors), + Agent("Topological", TopologicalScheduler(), contractors), +] +manager = StochasticManager(agents) + +# 4) Аукцион: кто даст расписание с меньшим окончанием — тот победил +start, end, schedule, winner = manager.run_auction(wg) +print("Победил агент:", winner.name, "Мейкспан:", end - start) +``` + +#### Пример многоагентного планирования с разбиением на блоки. + +```python +from random import Random +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.topological import TopologicalScheduler +from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager +from sampo.scheduler.multi_agency.block_generator import generate_blocks, SyntheticBlockGraphType + +# 1) Генерируем граф блоков (BlockGraph). +# Каждый блок — это отдельный WorkGraph, который можно планировать изолированно. +# Межблочные зависимости задаются через edge_prob. +seed = 231 +rand = Random(seed) +bg = generate_blocks( + SyntheticBlockGraphType.RANDOM, + n_blocks=4, # сколько блоков + type_prop=[1, 1, 1], # пропорции типов внутренних графов (General/Parallel/Sequential) + count_supplier=lambda i: (10, 15),# размер каждого блока (нижняя/верхняя границы числа работ) + edge_prob=0.3, # вероятность ребра между блоками (межблочные зависимости) + rand=rand +) + +# 2) Создаём простых подрядчиков для агентов +ss = SimpleSynthetic(rand) +contractor_a = ss.contractor(40) +contractor_b = ss.contractor(40) + +# 3) Два агента с разными стратегиями планирования и своими ресурсами. +agents = [ + Agent("HEFT", HEFTScheduler(), [contractor_a]), + Agent("Topo", TopologicalScheduler(), [contractor_b]), +] +manager = StochasticManager(agents) + +# 4) Менеджер идёт по блокам в топологическом порядке и проводит «аукцион» для каждого блока: +# - Вычисляет parent_time = max(окончаний всех родительских блоков) + 1. +# - Каждый агент предлагает расписание (offer) c учётом parent_time и своей текущей занятости. +# - Выбирается предложение с минимальным end_time и подтверждается (confirm). +scheduled_blocks = manager.manage_blocks(bg) + +# 5) Результаты: кто какой блок выиграл и глобальные времена. +print("Scheduled blocks:") +for block_id, sblock in scheduled_blocks.items(): + print(f"Block {block_id}: agent={sblock.agent.name}, " + f"start={sblock.start_time}, end={sblock.end_time}, " + f"duration={sblock.duration}") + +# Суммарная длительность проекта. +makespan = max(sb.end_time for sb in scheduled_blocks.values()) +print("Project makespan:", makespan) +``` +> Коротко про блоки и менеджера: + +> - Блок — это самостоятельный подграф работ (WorkGraph), который планируется целиком как единица. +> - BlockGraph — это DAG из блоков; ребро A → B означает: B можно начать только после завершения A. +> - Менеджер идёт по блокам в топологическом порядке. +> - Для каждого блока считает parent_time = максимум окончаний его родительских блоков. +> - Агенты делают офферы: строят расписание блока с учётом parent_time и своей текущей занятости. +> - Побеждает агент с минимальным временем окончания блока; его план подтверждается, таймлайн обновляется. +> - Зачем: масштабируемость, параллельность, изолируемость пересчётов и удобство управления этапами/зонами. +--- diff --git a/docs/source/guidebook/checklist.md b/docs/source/guidebook/checklist.md new file mode 100644 index 00000000..739637ba --- /dev/null +++ b/docs/source/guidebook/checklist.md @@ -0,0 +1,103 @@ +# «Шпаргалка» + +## Базовые сущности + +- WorkGraph — DAG проекта с двумя служебными вершинами start/finish; вершины — GraphNode с вложенным WorkUnit (описание + работы), рёбра — зависимости (поддерживаются разные типы связей и лаги). + > WorkGraph — граф работ проекта, где зафиксированы все задачи и их зависимости. + +- GraphNode — вершина WorkGraph, хранящая WorkUnit и ссылки на родителей/потомков (с типами рёбер и лагами). + > GraphNode — контейнер WorkUnit и связей в графе. + +- WorkUnit — работа: объём, единицы измерения, приоритет, требования к ресурсам (WorkerReq: вид ресурса и мин/макс + количество), сервисные флаги и др. + > WorkUnit — описание одной задачи проекта с её характеристиками и потребностями в ресурсах. + +- WorkerReq — требование работы к виду ресурса: specialty/kind, min_count, max_count, объём для нормирования + длительности. + > WorkerReq — «каких и сколько ресурсов нужно на эту задачу». + +- Contractor — поставщик ресурсов: словари workers: dict[str, Worker] и equipments: dict[str, Equipment]; каждому Worker + проставляется contractor_id. + > Contractor — организация или подразделение, предоставляющее людей и технику. + +- Worker — ресурс «люди»: наименование специальности, count, contractor_id, режим/распределение производительности. + > Worker — трудовой ресурс с численностью и производительностью (иногда со стоимостной оценкой). + +- Equipment — ресурс «техника» с типом и количеством. + > Equipment — оборудование, используемое в проекте. + +--- + +## Планировщик и результат + +- Scheduler — алгоритм планирования. Принимает WorkGraph и список Contractor (опционально spec, landscape, + work_estimator). Возвращает список Schedule. Расширенный метод `schedule_with_cache` возвращает кортежи + `(Schedule, start_time: Time, Timeline, node_order: list[GraphNode])`. + > Scheduler — модуль, который строит расписание по задачам и ресурсам. Базовый `schedule` возвращает `list[Schedule]`; + на практике берут первый план: `schedule(...)[0]`. + +- Schedule — расписание: агрегирует данные о стартах/финишах, назначениях ресурсов и др.; содержит метрику + `execution_time` (makespan). + > Schedule — итоговый план выполнения работ с назначенными ресурсами и сроками. + +- ScheduledWork — элемент расписания: конкретная работа с назначенными ресурсами, стартом/финишом и длительностью. + > ScheduledWork — «строка» плана по одной работе. + +- ScheduleSpec — спецификация для ограничений/назначений ресурсов на работы (в т.ч. фиксирование объёмов команд). + > ScheduleSpec — настройки, ограничивающие или уточняющие распределение ресурсов. + +- Timeline — внутренняя временная шкала использования ресурсов, которую поддерживает планировщик при вычислениях. + > Timeline — модель занятости ресурсов во времени. + +--- + +## Оценки и окружение + +- WorkTimeEstimator — оценщик длительности работ, подставляется в планировщик через `work_estimator`. + > WorkTimeEstimator — функция или модель, определяющая длительность выполнения задачи. + +- LandscapeConfiguration / ZoneConfiguration — конфигурация пространственных/зональных ограничений и смен статусов зон ( + при необходимости). + > Landscape/ZoneConfiguration — описание территории или зоны выполнения работ с ограничениями. + +--- + +## Пайплайн + +- SchedulingPipeline — «флюентный» builder-интерфейс для конфигурирования и запуска планирования: + `SchedulingPipeline.create() → ... → .finish() → ScheduledProject` (`project.schedule`). + > «Флюентный» builder — цепочка методов, где вы задаёте граф работ, ресурсы и опции, выбираете алгоритм и получаете + результат как `ScheduledProject`. + +Кратко о методах пайплайна (рекомендуемый порядок): + +1) `create()` — старт билдера. +2) `name_mapper(mapper|path)` — (опционально) нормализация/переименование работ и ресурсов. +3) `wg(x, all_connections=False, change_connections_info=False, sep=',')` — задать WorkGraph (объект/таблица/файл). +4) `contractors(x)` — задать подрядчиков (список/таблица/файл или генерация). +5) `history(data, sep=';')` — (опционально) исторические данные со связями; требуется, если в графе нет информации о + зависимостях. +6) `work_estimator(est)` — (опционально) модель оценки длительностей. +7) `spec(spec)` — (опционально) ограничения/фиксация ресурсов и этапов. +8) `landscape(cfg)` — (опционально) пространственные/зональные ограничения. +9) `time_shift(offset: Time)` — (опционально) сдвиг начала расписания/дат. +10) `lag_optimize(strategy)` — (желательно) стратегия оптимизации лагов; в дефолтном пайплайне может быть подобрана + автоматически, если не указана. +11) `node_order(orders: list[list[GraphNode]])` — (опционально) зафиксировать порядок узлов до планирования. +12) `optimize_local(optimizer: OrderLocalOptimizer, area: range)` — (опционально) локальная оптимизация порядка ДО + `schedule`. +13) `schedule(scheduler, validate=False)` — выбрать алгоритм и построить расписание. +14) `optimize_local(optimizer: ScheduleLocalOptimizer, area: range)` — (опционально) локальная оптимизация расписания + ПОСЛЕ `schedule`. +15) `visualization(start_date)` — (опционально) визуализация плана; далее можно вызвать `.shape(...)`, + `.color_type(...)`, `.show_gant_chart()`. +16) `finish()` — получить результат как `list[ScheduledProject]` (обычно берут `[0]`). + +- ScheduledProject — контейнер с результатом проекта (включая `project.schedule`), доступен после `finish()`. + > ScheduledProject — объект с итоговым расписанием проекта. + +Примечание: `optimize_local` присутствует в двух вариантах — для порядка (OrderLocalOptimizer, до планирования) и для +готового расписания (ScheduleLocalOptimizer, после планирования). + +--- diff --git a/docs/source/guidebook/developers.md b/docs/source/guidebook/developers.md deleted file mode 100644 index a884ec0f..00000000 --- a/docs/source/guidebook/developers.md +++ /dev/null @@ -1,50 +0,0 @@ -## Для разработчиков: расширение и модификация SAMPO - -Ниже — обзор архитектуры и путей расширения для **power-пользователей и контрибьюторов**. - -*Высокоуровневая архитектура SAMPO. Два основных подхода: конкретный алгоритм планирования и многоагентная стратегия (разбиение на подзадачи и последующее объединение).* - -### Обзор архитектуры - -* **Схемы данных** (`sampo.schemas`): **WorkGraph**, **WorkUnit**, **Contractor/Worker**, **Schedule** — вход/выход алгоритмов. -* **Алгоритмы планирования** (`sampo.scheduler`): - - * `heft` — HEFTScheduler и варианты. - * `topological` — TopologicalScheduler. - * `genetic` — GeneticScheduler (+ структуры хромосом, мутации и пр.). - * `multi_agency` — многоагентное планирование (генерация блоков, координация). - * Вспомогательные подсистемы (таймлайн, учёт ресурсов и пр.). -* **Конвейер и утилиты**: `sampo.pipeline`, генераторы синтетики (`sampo.generator`), вспомогательные инструменты. - -Дизайн **модульный**: новые алгоритмы реализуют общий интерфейс (`schedule(work_graph, contractors)`). - -### Добавление нового планировщика - -1. **Создайте класс** (напр., `MyScheduler`) и реализуйте `schedule(self, work_graph, contractors)`. -2. **Переиспользуйте** имеющиеся утилиты (проверки допустимости назначения, управление ресурсами). -3. **Интегрируйте в pipeline** — как правило, достаточно передать экземпляр в `.schedule(...)`. - -Лицензия BSD-3 позволяет свободно модифицировать. Для PR — тесты, документация, внятные коммиты. - -### Расширение ограничений - -Примеры: - -* Запрет параллельности двух задач — добавьте фиктивное предшествование в граф или проверку в планировщике. -* Смены/простои — расширьте `WorkUnit`/`Worker` доп. полями и учитывайте их в логике планирования. - -Рекомендуется ознакомиться с кодом простого планировщика (напр., Topological/HEFT), чтобы понять точки встраивания правил. - -### Многоагентные и гибридные стратегии - -Посмотрите `sampo.scheduler.multi_agency`: как генерируются блоки (`block_generator`), как объединяются результаты. Можно экспериментировать с разными стратегиями разбиения и координации агентов (напр., критический путь — генетика; периферия — эвристика). - -### Вклад в проект - -* Ищите `CONTRIBUTING.md`/гайдлайны в репозитории. -* Пишите тесты, документируйте, открывайте PR с мотивацией изменений. -* Следите за релиз-нотами/вики/ReadTheDocs. - ---- - -Надеемся, это руководство дало вам понятный путь от базового знакомства до глубокой работы с SAMPO. Хотите просто получить план? Нужны тонкие настройки качества? Планируете расширять ядро? — SAMPO предоставляет мощную платформу для адаптивного календарного планирования. Удачи! diff --git a/docs/source/guidebook/index.md b/docs/source/guidebook/index.md index fd6f7ec3..38d2ca5a 100644 --- a/docs/source/guidebook/index.md +++ b/docs/source/guidebook/index.md @@ -6,6 +6,7 @@ caption: Руководство SAMPO intro quickstart +checklist advanced developers ``` diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md index a43a98fd..1e970a77 100644 --- a/docs/source/guidebook/intro.md +++ b/docs/source/guidebook/intro.md @@ -5,8 +5,7 @@ Руководство организовано так, чтобы помочь пользователям с разным уровнем подготовки: * **Базовый уровень** — что такое SAMPO, чем он поможет и как быстро начать работу. -* **Средний уровень** — как настраивать SAMPO под свои задачи. -* **Продвинутый уровень** — как модифицировать и расширять фреймворк. +* **Продвинутый уровень** — как настраивать SAMPO под свои задачи. ## Введение @@ -58,6 +57,7 @@ или различаются для каждого изделия (*Job Shop*). > **DAG-процессы** — планирование по ацикличным графам зависимостей (Directed Acyclic Graph). + > **Ациклический граф** — граф без циклов, где нельзя вернуться к исходной вершине, следуя по рёбрам. * **Гибкий конвейер (pipeline)** — модульное подключение планировщиков; легко заменить алгоритм или добавить шаги без правок ядра. diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md index 1ec533a2..84f61568 100644 --- a/docs/source/guidebook/quickstart.md +++ b/docs/source/guidebook/quickstart.md @@ -1,7 +1,6 @@ # Быстрый старт -Покажем установку, подготовку простого примера, запуск планировщика и просмотр результата, а также предоставим -«шпаргалку». +Покажем установку, подготовку простого примера, запуск планировщика и просмотр результата. ## Установка @@ -41,29 +40,25 @@ work_graph: WorkGraph = synthetic.work_graph( print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") ``` -Альтернатива: если у вас уже есть данные, загрузите их из файла: - -```python -wg = WorkGraph.load(folder, filename) -``` - 2) Ресурсы (Contractor’ы) ```python -from sampo.schemas.contractor import Contractor, Worker +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker + +# Создаем рабочего +worker = Worker(id="w1", name="handyman", count=10) # Используем 'handyman' как пример рабочего общего профиля -# Один подрядчик с 10 работниками общего профиля +# Один подрядчик с 10 работниками contractors = [ Contractor( - id="Contractor1", - workers=[Worker(id="w1", kind="general", count=10)] + id="c1", + name="General Contractor", + workers={worker.id: worker} ) ] ``` -В реальном проекте может быть несколько подрядчиков с разными специализациями. Значение kind должно соответствовать -типам, которых требуют задачи в WorkGraph. - 3) Выбор планировщика ```python @@ -74,12 +69,6 @@ from sampo.scheduler.heft import HEFTScheduler scheduler = HEFTScheduler() # быстрая эвристика для старта ``` -Также можно попробовать: - -- TopologicalScheduler() — простой базовый порядок -- GeneticScheduler(...) — более глубокий поиск (с настройкой гиперпараметров, например mutate_order, mutate_resources и - т.п.) - 4) Запуск планирования Важный момент: метод schedule возвращает список кортежей; нам нужен сам Schedule из первого элемента. @@ -118,124 +107,3 @@ project = (SchedulingPipeline.create() print(f"Project duration: {project.schedule.execution_time}") ``` - -Подсказки, что попробовать дальше: - -- Замените HEFTScheduler на GeneticScheduler для поиска лучших планов. -- Добавьте .optimize_local(...) до/после .schedule(...) для локальных улучшений порядка/расписания. -- Укажите .lag_optimize(...) и .work_estimator(...) при необходимости учитывать дополнительные ограничения и модели - оценки времени. - -## «Шпаргалка» - -### Базовые сущности - -- WorkGraph — DAG проекта с двумя служебными вершинами start/finish; вершины — GraphNode с вложенным WorkUnit (описание - работы), рёбра — зависимости с типом EdgeType (не только FS) и поддержкой лагов. - > WorkGraph — граф работ проекта, где зафиксированы все задачи и их зависимости. - -- GraphNode — вершина WorkGraph, хранящая WorkUnit и ссылки на родителей/потомков (с типами рёбер и лагами). - > GraphNode — контейнер WorkUnit и связей в графе. - -- WorkUnit — работа: объём, единицы измерения, приоритет, требования к ресурсам (WorkerReq: вид ресурса и мин/макс - количество), сервисные флаги и др. - > WorkUnit — описание одной задачи проекта с её характеристиками и потребностями в ресурсах. - -- WorkerReq — требование работы к виду ресурса: specialty/kind, min_count, max_count, объём для нормирования - длительности. - > WorkerReq — «каких и сколько ресурсов нужно на эту задачу». - -- Contractor — поставщик ресурсов: словари workers: dict[str, Worker] и equipments: dict[str, Equipment]; каждому Worker - проставляется contractor_id. - > Contractor — организация или подразделение, предоставляющее людей и технику. - -- Worker — ресурс «люди»: имя (специальность), count, contractor_id, распределение производительности ( - Static/Stochastic). - > Worker — трудовой ресурс с численностью и производительностью (иногда со стоимостной оценкой). - -- Equipment — ресурс «техника» с типом и количеством. - > Equipment — оборудование, используемое в проекте. - ---- - -### Планировщик и результат - -- Scheduler — алгоритм планирования. Принимает WorkGraph и список Contractor (а также опционально spec, landscape, - work_estimator). Возвращает список Schedule. Расширенный метод schedule_with_cache возвращает кортежи (Schedule, - start_time: Time, Timeline, node_order: list[GraphNode]). - > Scheduler — модуль, который строит расписание по задачам и ресурсам. - Примечание: базовый schedule возвращает list[Schedule]; на практике берут первый план: schedule(...)[0]. - -- Schedule — расписание: обёртка над pandas DataFrame со стартами/финишами, назначениями ресурсов, стоимостью и пр.; - содержит объекты ScheduledWork. Метрика execution_time — makespan (время завершения последней работы). - > Schedule — итоговый план выполнения работ с назначенными ресурсами и сроками. - -- ScheduledWork — элемент расписания: конкретная работа с назначенными ресурсами, стартом/финишем и длительностью. - > ScheduledWork — «строка» плана по одной работе. - -- ScheduleSpec — спецификация для ограничений/назначений ресурсов на работы (в т.ч. фиксирование объёмов команд). - > ScheduleSpec — настройки, ограничивающие или уточняющие распределение ресурсов. - -- Timeline — внутренняя временная шкала использования ресурсов, которую поддерживает планировщик при вычислениях. - > Timeline — модель занятости ресурсов во времени. - ---- - -### Оценки и окружение - -- WorkTimeEstimator — оценщик длительности работ, подставляется в планировщик через work_estimator. - > WorkTimeEstimator — функция или модель, определяющая длительность выполнения задачи. - -- LandscapeConfiguration / ZoneConfiguration — конфигурация пространственных/зональных ограничений и смен статусов зон ( - при необходимости). - > Landscape/ZoneConfiguration — описание территории или зоны выполнения работ с ограничениями. - ---- - -### Пайплайн - -- Scheduling Pipeline — «флюентный» builder-интерфейс для конфигурирования и запуска планирования: - SchedulingPipeline.create() → ... → .finish() → ScheduledProject (project.schedule). - > «Флюентный» builder — цепочка методов, где вы задаёте граф работ, ресурсы и опции, выбираете алгоритм и получаете - результат как ScheduledProject. - - > Кратко о методах пайплайна (в рекомендуемом порядке): - - 1) create() — старт билдера. - 2) name_mapper(fn) — (опционально) нормализация/переименование работ и ресурсов. - 3) wg(x) — задать WorkGraph (объект/таблица/файл). - 4) contractors(x) — задать подрядчиков (список/таблица/генерация). - 5) history(data) — (опционально) исторические данные для калибровки оценок. - 6) work_estimator(est) — (опционально) модель оценки длительностей. - 7) spec(spec) — (опционально) ограничения/фиксация ресурсов и этапов. - 8) landscape(cfg) — (опционально) пространственные/зональные ограничения. - 9) time_shift(offset) — (опционально) сдвиг начала расписания/дат. - 10) lag_optimize(strategy) — (опционально) обработка лагов/разбиение стадий; если не указать — подберётся - автоматически. - 11) node_order(order|optimizer) — (опционально) зафиксировать/улучшить порядок узлов до планирования. - 12) schedule(scheduler, validate=False) — выбрать алгоритм и построить расписание. - 13) optimize_local(optimizer, …) — (опционально) локальная оптимизация готового плана после schedule. - 14) visualization(opts) — (опционально) визуализация плана. - 15) finish() — получить результат как ScheduledProject (обычно берут [0]). - -> Примечание: optimize_local может вызываться также ДО schedule для оптимизации порядка (альтернатива пункту 11). - -- ScheduledProject — контейнер с результатом проекта (включая project.schedule), доступен после finish(). - > ScheduledProject — объект с итоговым расписанием проекта. - ---- - -### Многоагентность - -- BlockGraph / BlockNode — декомпозиция проекта на блоки (подграфы) с зависимостями между блоками; возможна обратная - сборка в единый WorkGraph. - > BlockGraph / BlockNode — представление проекта как набора крупных блоков задач. - -- Agent — независимый планировщик с собственными Contractor’ами и Timeline; протокол offer/confirm для выдачи и фиксации - плана блока. - > Agent — отдельный планировщик, отвечающий за свой участок проекта. - Важно: подрядчики разных агентов не должны пересекаться (валидация проверяет уникальность). - -- Manager / NeuralManager — координация агентов, распределение блоков и выбор последовательности с учётом зависимостей и - метрик; NeuralManager использует обучаемую политику выбора. - > Manager / NeuralManager — управляющий модуль, который согласует работу агентов. From 8ed6bb977469428a39bbc84717d5ef1dc3da7998 Mon Sep 17 00:00:00 2001 From: NickMur Date: Mon, 22 Sep 2025 22:31:29 +0300 Subject: [PATCH 13/32] =?UTF-8?q?=D0=9E=D0=B1=D1=8A=D0=B5=D0=B4=D0=B8?= =?UTF-8?q?=D0=BD=D1=91=D0=BD=D0=BD=D0=B0=D1=8F=20=D0=B2=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/guidebook/advanced.md | 338 ++++++++++++++++++++--------- docs/source/guidebook/checklist.md | 103 --------- docs/source/guidebook/index.md | 2 - 3 files changed, 238 insertions(+), 205 deletions(-) delete mode 100644 docs/source/guidebook/checklist.md diff --git a/docs/source/guidebook/advanced.md b/docs/source/guidebook/advanced.md index 133d0273..58823924 100644 --- a/docs/source/guidebook/advanced.md +++ b/docs/source/guidebook/advanced.md @@ -1,12 +1,77 @@ # Расширённое использование и настройка -После базового знакомства вы можете тонко настроить SAMPO: выбирать алгоритмы, тюнить параметры, работать с несколькими -критериями и встраивать доменные компоненты. +Здесь объясняются ключевые концепции, показаны примеры кода и даны рекомендации по расширенному использованию +библиотеки. +--- + +## Базовые сущности + +* **WorkGraph** — ориентированный ациклический граф работ проекта (DAG) с двумя служебными вершинами `start`/`finish` ( + может содержать служебные узлы, но не по умолчанию). + Вершины — `GraphNode`, внутри — `WorkUnit`, рёбра — зависимости (поддерживаются типы связей и лаги). + + > WorkGraph фиксирует все задачи и зависимости проекта. + +* **Тип связи** — вид предшествования между работами в WorkGraph: + + * **FS (Finish–Start)**: `S(B) ≥ F(A) + лаг`. + * **SS (Start–Start)**: `S(B) ≥ S(A) + лаг`. + * **FF (Finish–Finish)**: `F(B) ≥ F(A) + лаг`. + * **IFS (Inseparable Finish–Start)**: неразрывная FS — B сразу после A без разрывов; узлы образуют слитную цепочку. + * **FFS (LagFinishStart)**: поточная связь; потомок стартует после выполнения предком части объёма, равной лагу. + +* **Лаг** — сдвиг ограничения: + + * Для FS/SS/FF — числовой сдвиг во времени (обычно `0`). Знак: `>0` — задержка, `<0` — «лид». + * Для FFS — лаг в единицах объёма предка (например, км). Движок делит работу на стадии; старт потомка — после стадии + объёмом `лаг`. При отключённой оптимизации лагов FFS ведёт себя как обычный FS. + +* **GraphNode** — вершина `WorkGraph`, хранящая `WorkUnit` и ссылки на родителей/потомков (с типом связи и лагом). + + > Контейнер `WorkUnit` и его связей. + +* **WorkUnit** — описание работы: объём, единицы, приоритет, требования к ресурсам (`WorkerReq`), сервисные флаги. + + > «Что за задача и что ей нужно». + +* **WorkerReq** — требование к ресурсу: `kind`, `min_count`, `max_count`, `volume` (норма для расчёта длительности). + + > «Каких специалистов и сколько нужно». + +* **Contractor** — поставщик ресурсов: `workers: dict[str, Worker]`, `equipments: dict[str, Equipment]`. При + инициализации `contractor_id` у `Worker` проставляется автоматически. + + > Организация/подразделение, предоставляющее людей и технику. + +* **Worker** — трудовой ресурс: `id`, `name` (специализация), `count`, `contractor_id`, `productivity` (распределение), + опционально `cost_one_unit`. + + > `Worker.name` должен совпадать с `WorkerReq.kind` для корректного сопоставления. + +* **Equipment** — технический ресурс: тип и количество. + + > Оборудование, используемое в проекте. + +* **Scheduler** — алгоритм планирования. Принимает `WorkGraph` и `list[Contractor]` (опц. `spec`, `landscape`, + `work_estimator`). Базовые реализация/методы возвращают Schedule и доп.структуры (например, schedule_with_cache -> + список кортежей (Schedule, Time, Timeline, ...)). В практическом использовании берут результат/первый план + + > Планировщик строит расписание. + +* **Schedule / ScheduledWork / Timeline** — итоговый план (старты/финиши, назначения ресурсов, внутренняя шкала + занятости). + + > `Schedule.execution_time` — мейкспан (длительность проекта). + +> Примечание: **DAG** — ориентированный ацикличный граф; циклы запрещены, что гарантирует упорядочиваемость плана. + +--- ## Как собрать WorkGraph ### Способ A. Сгенерировать синтетический граф + ```python from sampo.generator.base import SimpleSynthetic from sampo.generator.pipeline import SyntheticGraphType @@ -18,44 +83,53 @@ wg = ss.work_graph(mode=SyntheticGraphType.GENERAL, top_border=200) ``` +Коротко: удобно для тестов и прототипов; управляем размером и «кластерностью». + +--- + ### Способ B. Загрузить из CSV + ```python from sampo.pipeline import SchedulingPipeline from sampo.scheduler.heft import HEFTScheduler from sampo.pipeline.lag_optimization import LagOptimizationStrategy project = (SchedulingPipeline.create() - .wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) - .lag_optimize(LagOptimizationStrategy.TRUE) - .schedule(HEFTScheduler()) - .finish()[0]) +.wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) +.lag_optimize(LagOptimizationStrategy.TRUE) +.schedule(HEFTScheduler()) +.finish()[0]) ``` -> О структуре CSV: - -- Обязательные колонки: - - activity_id, activity_name, granular_name, volume, measurement, priority -- Зависимости (списки через запятую, длины строго совпадают): - - predecessor_ids, connection_types, lags - - Типы связей: FS, SS, FF, IFS, FFS; лаги — числа (обычно 0) -- Опционально (требования по ресурсам в ячейках-словарях): - - min_req, max_req, req_volume (формат: {"worker_kind": value}) -- Примечания: - - Сервисные узлы start/finish в CSV не нужны — добавляются автоматически - - В .wg(path, sep=';') укажите разделитель файла; внутри списков — запятая - -Мини-пример CSV с двумя задачами и зависимостью B от A (FS, лаг 0): + +**Структура CSV** + +* Обязательные колонки: `activity_id, activity_name, granular_name, volume, measurement, priority`. +* Зависимости (списки через запятую, длины совпадают): `predecessor_ids, connection_types, lags`. +* Типы: `FS`, `SS`, `FF`, `IFS`, `FFS`; лаги — числа (обычно `0`). +* Опционально (требования в словарях-ячейках): `min_req`, `max_req`, `req_volume` вида `{"worker_kind": value}`. +* Примечания: + + * `start/finish` в CSV не нужны — добавляются автоматически. + * В `.wg(path, sep=';')` `sep` — разделитель файла; внутри списков — запятая. + * Ошибка `Parameter 'id' unfilled/name unfilled` часто означает пустые `activity_id`/`activity_name`. + +**Мини-пример (B зависит от A по FS, лаг 0)** + ``` activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags A;Task A;A;1.0;unit;0;;; B;Task B;B;1.0;unit;0;A;FS;0 ``` -> Пример можно посмотреть в `tests/parser/test_wg.csv`. + +> Полный пример: `tests/parser/test_wg.csv`. + +--- ### Способ C. Программно из узлов -- Создайте WorkUnit для каждой задачи (с требованиями WorkerReq по необходимости). -- Для зависимостей используйте GraphNode(work_unit, parents), где parents — список кортежей (parent_node, lag, EdgeType). -- Соберите граф: WorkGraph.from_nodes([...]) — start/finish добавятся автоматически. +* Создайте `WorkUnit` для каждой задачи (с `WorkerReq` при необходимости). +* Зависимости: `GraphNode(work_unit, parents)`, где `parents` — список кортежей `(parent_node, lag, EdgeType)`. +* Сборка: `WorkGraph.from_nodes([...])` — `start/finish` добавятся автоматически. ```python from sampo.schemas.works import WorkUnit @@ -63,64 +137,83 @@ from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType from sampo.schemas.requirements import WorkerReq from sampo.schemas.time import Time -# Работы (пример: A -> B -> C, FS с лагом 0) +# A -> B -> C (FS, лаг 0) wu_a = WorkUnit(id='A', name='Task A', worker_reqs=[WorkerReq(kind='general', volume=Time(10), min_count=2, max_count=4)], volume=1.0, is_service_unit=False) +wu_b = WorkUnit(id='B', name='Task B', worker_reqs=[], volume=1.0, is_service_unit=False) +wu_c = WorkUnit(id='C', name='Task C', worker_reqs=[], volume=1.0, is_service_unit=False) -wu_b = WorkUnit(id='B', name='Task B', - worker_reqs=[], volume=1.0, is_service_unit=False) - -wu_c = WorkUnit(id='C', name='Task C', - worker_reqs=[], volume=1.0, is_service_unit=False) - -n_a = GraphNode(wu_a, []) # корень -n_b = GraphNode(wu_b, [(n_a, 0, EdgeType.FinishStart)]) # A -> B (FS, 0) -n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # B -> C (FS, 0) +n_a = GraphNode(wu_a, []) +n_b = GraphNode(wu_b, [(n_a, 0, EdgeType.FinishStart)]) # FS +n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS wg = WorkGraph.from_nodes([n_a, n_b, n_c]) ``` -Коротко: -- Типы связей: EdgeType.FinishStart (FS), StartStart (SS), FinishFinish (FF), InseparableFinishStart (IFS), LagFinishStart (FFS). -- Лаг — второй элемент кортежа в parents (число, обычно 0). +Коротко по зависимостям: + +* Типы: `FinishStart (FS)`, `StartStart (SS)`, `FinishFinish (FF)`, `InseparableFinishStart (IFS)`, + `LagFinishStart (FFS)`. +* Лаг — второй элемент кортежа в `parents`. +* Обязательные поля `WorkUnit`: `id`, `name`. + +--- -## Как создаются/получаются подрядчики (Contractor) +## Подрядчики (Contractor) -### Вручную: +### Вручную ```python from sampo.schemas.contractor import Contractor from sampo.schemas.resources import Worker from sampo.schemas.interval import IntervalGaussian -contractor = Contractor(workers={ - 'driver': Worker(id='w1', name='driver', count=8, productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5)), - 'fitter': Worker(id='w2', name='fitter', count=6, productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6))}, - id='c1', name='Contractor A') +contractor = Contractor( + workers={ + 'driver': Worker(id='w1', name='driver', count=8, productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5)), + 'fitter': Worker(id='w2', name='fitter', count=6, productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6)), + }, + id='c1', + name='Contractor A' +) ``` -### Генератором по параметру “размер пакета”: +> Важно: +> +> * Ключи словаря `workers` должны совпадать с `Worker.name`. +> * `WorkerReq.kind` должен совпадать с `Worker.name`, иначе бригада не подберётся. +> * `contractor_id` у `Worker` берётся из `Contractor.id`. +> * При необходимости задайте `cost_one_unit` явно. + +### Генератором по параметру «размер пакета» + ```python from sampo.generator.base import SimpleSynthetic + ss = SimpleSynthetic() contractor = ss.contractor(pack_worker_count=10) ``` -### По графу работ (из требований): +### По графу работ (из требований) + ```python from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod + contractor = get_contractor_by_wg(wg, scaler=1.0, method=ContractorGenerationMethod.AVG) ``` -Метод берёт требования по работам (min/max), агрегирует и формирует пул. +Коротко: агрегирует `min/max` из `WorkerReq` по задачам и формирует пул ресурсов. + +--- -## Как выбрать алгоритм +## Выбор алгоритма ### Эвристические планировщики (HEFT, HEFTBetween, Topological) - - Очень быстрые и дают хорошую стартовую базу. - - HEFT/HEFTBetween ранжируют работы по приоритетам/критичности и оцениваемым временам выполнения. - - Topological строит порядок по зависимостям без сложной оптимизации. + +* Быстрые стартовые решения. +* HEFT/HEFTBetween ранжируют узлы по приоритетам и оценкам длительностей. +* Topological строит порядок по зависимостям без сложной оптимизации. Импорты: @@ -130,21 +223,20 @@ from sampo.scheduler.heft import HEFTBetweenScheduler from sampo.scheduler.topological import TopologicalScheduler ``` +--- + ### Генетический планировщик - - Перебирает много альтернатив и часто улучшает результат, но требует больше времени на расчёт. - - Ключевые параметры: - - number_of_generation, size_of_population — глубина и ширина поиска; - - mutate_order — вероятность мутации гена порядка (перестановки работ с сохранением зависимостей). Выше → шире - поиск по допустимым порядкам, но медленнее сходимость. - - mutate_resources — вероятность мутации ресурсных генов (перераспределение видов/объемов и подрядчиков в рамках - min/max и доступности). Выше → больше вариантов параллельности, но при дефиците ресурсов растёт риск - конфликтов. - - при необходимости — work_estimator (модель оценки длительностей), seed (для воспроизводимости). - - > * **number_of_generation** — число итераций генетического алгоритма (↑ поколений → лучше результат, но дольше). - > * **size_of_population** — размер популяции (↑ особей → выше разнообразие, но дороже по времени/памяти). - > * **mutate_order** — вероятность мутации порядка работ (↑ → активнее поиск, но медленнее сходимость). - > * **mutate_resources** — вероятность мутации распределения ресурсов (↑ → больше параллельности, но риск конфликтов). + +* Перебирает множество альтернатив и часто улучшает мейкспан. Требует больше времени. +* Ключевые параметры: + + * `number_of_generation` — число итераций (↑ поколений → выше шанс улучшить, но дольше расчёт). + * `size_of_population` — размер популяции (↑ особей → выше диверсификация, но дороже по времени/памяти). + * `mutate_order` — вероятность мутации порядка работ при сохранении зависимостей (↑ → шире поиск, медленнее + сходимость). + * `mutate_resources` — вероятность мутации распределения ресурсов/подрядчиков (↑ → больше параллельности; при + дефиците растёт риск конфликтов). + * Дополнительно: `work_estimator`, `seed`. Пример: @@ -159,12 +251,14 @@ scheduler = GeneticScheduler( ) ``` -### Многоагентное планирование - - делит граф на блоки, применяет разные стратегии и объединяет результат. Полезно для очень - больших проектов и гибридных стратегий; требует большего сетапа (модуль `sampo.scheduler.multi_agency`). +--- + +## Многоагентное планирование -#### Пример без разбиения на блоки: -- два агента с разными планировщиками «соревнуются» за лучший план (аукцион). +Делит граф на блоки, применяет разные стратегии и объединяет результат. Полезно для крупных проектов и гибридных +стратегий (`sampo.scheduler.multi_agency`). + +### «Аукцион» без разбиения на блоки ```python from uuid import uuid4 @@ -179,25 +273,25 @@ from sampo.schemas.resources import Worker ss = SimpleSynthetic(231) wg = ss.work_graph(bottom_border=30, top_border=40) -# 2) Универсальный подрядчик с запасом по всем требуемым видам работников +# 2) Универсальный подрядчик по требуемым видам работников kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} cid = str(uuid4()) workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} contractors = [Contractor(id=cid, name="Universal", workers=workers, equipments={})] -# 3) Два агента с разными стратегиями +# 3) Два агента agents = [ Agent("HEFT", HEFTScheduler(), contractors), Agent("Topological", TopologicalScheduler(), contractors), ] manager = StochasticManager(agents) -# 4) Аукцион: кто даст расписание с меньшим окончанием — тот победил +# 4) Аукцион start, end, schedule, winner = manager.run_auction(wg) print("Победил агент:", winner.name, "Мейкспан:", end - start) ``` -#### Пример многоагентного планирования с разбиением на блоки. +### С разбиением на блоки ```python from random import Random @@ -207,56 +301,100 @@ from sampo.scheduler.topological import TopologicalScheduler from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager from sampo.scheduler.multi_agency.block_generator import generate_blocks, SyntheticBlockGraphType -# 1) Генерируем граф блоков (BlockGraph). -# Каждый блок — это отдельный WorkGraph, который можно планировать изолированно. -# Межблочные зависимости задаются через edge_prob. +# 1) Граф блоков (BlockGraph) seed = 231 rand = Random(seed) bg = generate_blocks( SyntheticBlockGraphType.RANDOM, - n_blocks=4, # сколько блоков - type_prop=[1, 1, 1], # пропорции типов внутренних графов (General/Parallel/Sequential) - count_supplier=lambda i: (10, 15),# размер каждого блока (нижняя/верхняя границы числа работ) - edge_prob=0.3, # вероятность ребра между блоками (межблочные зависимости) + n_blocks=4, + type_prop=[1, 1, 1], + count_supplier=lambda i: (10, 15), + edge_prob=0.3, rand=rand ) -# 2) Создаём простых подрядчиков для агентов +# 2) Подрядчики для агентов ss = SimpleSynthetic(rand) contractor_a = ss.contractor(40) contractor_b = ss.contractor(40) -# 3) Два агента с разными стратегиями планирования и своими ресурсами. +# 3) Агенты agents = [ Agent("HEFT", HEFTScheduler(), [contractor_a]), Agent("Topo", TopologicalScheduler(), [contractor_b]), ] manager = StochasticManager(agents) -# 4) Менеджер идёт по блокам в топологическом порядке и проводит «аукцион» для каждого блока: -# - Вычисляет parent_time = max(окончаний всех родительских блоков) + 1. -# - Каждый агент предлагает расписание (offer) c учётом parent_time и своей текущей занятости. -# - Выбирается предложение с минимальным end_time и подтверждается (confirm). +# 4) Планирование по блокам в топологическом порядке scheduled_blocks = manager.manage_blocks(bg) -# 5) Результаты: кто какой блок выиграл и глобальные времена. +# 5) Итоги print("Scheduled blocks:") for block_id, sblock in scheduled_blocks.items(): - print(f"Block {block_id}: agent={sblock.agent.name}, " - f"start={sblock.start_time}, end={sblock.end_time}, " - f"duration={sblock.duration}") + print( + f"Block {block_id}: agent={sblock.agent.name}, start={sblock.start_time}, end={sblock.end_time}, duration={sblock.duration}") -# Суммарная длительность проекта. makespan = max(sb.end_time for sb in scheduled_blocks.values()) print("Project makespan:", makespan) ``` -> Коротко про блоки и менеджера: - -> - Блок — это самостоятельный подграф работ (WorkGraph), который планируется целиком как единица. -> - BlockGraph — это DAG из блоков; ребро A → B означает: B можно начать только после завершения A. -> - Менеджер идёт по блокам в топологическом порядке. -> - Для каждого блока считает parent_time = максимум окончаний его родительских блоков. -> - Агенты делают офферы: строят расписание блока с учётом parent_time и своей текущей занятости. -> - Побеждает агент с минимальным временем окончания блока; его план подтверждается, таймлайн обновляется. -> - Зачем: масштабируемость, параллельность, изолируемость пересчётов и удобство управления этапами/зонами. + +Коротко: + +* **Блок** — самостоятельный подграф (`WorkGraph`) как единица планирования. +* **BlockGraph** — DAG блоков; `A → B` означает старт `B` после завершения `A`. +* Менеджер считает `parent_time = max(окончаний родителей)`; агенты делают офферы; побеждает минимальный `end_time`. + --- + +## Пайплайн: рекомендуемый порядок шагов + +1. `create()` — старт билдера. +2. `name_mapper(mapper|path)` — (опц.) нормализация имён работ и ресурсов. +3. `wg(x, all_connections=False, change_connections_info=False, sep=',')` — задать `WorkGraph` (объект/таблица/файл). +4. `contractors(x)` — задать/сгенерировать подрядчиков. +5. `history(data, sep=';')` — (опц.) связи из истории, если в графе их нет. +6. `work_estimator(est)` — (опц.) модель длительностей. +7. `spec(spec)` — (опц.) ограничения/фиксации ресурсов и этапов. +8. `landscape(cfg)` — (опц.) пространственные/зональные ограничения. +9. `time_shift(offset: Time)` — (опц.) сдвиг начала. +10. `lag_optimize(strategy)` — стратегия оптимизации лагов. +11. `node_order(orders: list[list[GraphNode]])` — (опц.) зафиксировать порядок узлов. +12. `optimize_local(optimizer: OrderLocalOptimizer, area: range)` — (опц.) локальная оптимизация порядка до `schedule`. +13. `schedule(scheduler, validate=False)` — построить расписание. +14. `optimize_local(optimizer: ScheduleLocalOptimizer, area: range)` — (опц.) локальная оптимизация после `schedule`. +15. `visualization(start_date)` — (опц.) визуализация → `.shape(...)`, `.color_type(...)`, `.show_gant_chart()`. +16. `finish()` — `list[ScheduledProject]` (обычно `[0]`). + +> `ScheduledProject` содержит итоговый `project.schedule` с `execution_time` и деталями по работам. + +--- + +## Оценка длительностей и производительности + +* **WorkTimeEstimator** считает длительность задачи и передаётся через `work_estimator`. +* **Режимы производительности** работников: + + * `Static` — детерминированный (среднее), для воспроизводимых запусков. + * `Stochastic` — случайный (выборка из распределений), для анализа рисков. +* Интуитивная формула: `duration ≈ volume / (sum(productivity × count) × communication_coeff)`. + + * `productivity` — из распределений у `Worker` (например, `IntervalGaussian`). + * `communication_coeff` снижает эффективность при росте команды к `max_count` (`WorkerReq`). +* Воспроизводимость: используйте `Static` или задайте `sigma=0` у распределений. +* Практика: + + * Реалистично задавайте `min_count/max_count` в `WorkerReq`. + * В `Stochastic` усредняйте по нескольким прогонам. + +Пример подключения: + +```python +from sampo.scheduler.heft import HEFTScheduler +from sampo.schemas.time_estimator import DefaultWorkEstimator +from sampo.schemas.resources import WorkerProductivityMode + +est = DefaultWorkEstimator() +est.set_productivity_mode(WorkerProductivityMode.Static) + +scheduler = HEFTScheduler(work_estimator=est) +``` diff --git a/docs/source/guidebook/checklist.md b/docs/source/guidebook/checklist.md deleted file mode 100644 index 739637ba..00000000 --- a/docs/source/guidebook/checklist.md +++ /dev/null @@ -1,103 +0,0 @@ -# «Шпаргалка» - -## Базовые сущности - -- WorkGraph — DAG проекта с двумя служебными вершинами start/finish; вершины — GraphNode с вложенным WorkUnit (описание - работы), рёбра — зависимости (поддерживаются разные типы связей и лаги). - > WorkGraph — граф работ проекта, где зафиксированы все задачи и их зависимости. - -- GraphNode — вершина WorkGraph, хранящая WorkUnit и ссылки на родителей/потомков (с типами рёбер и лагами). - > GraphNode — контейнер WorkUnit и связей в графе. - -- WorkUnit — работа: объём, единицы измерения, приоритет, требования к ресурсам (WorkerReq: вид ресурса и мин/макс - количество), сервисные флаги и др. - > WorkUnit — описание одной задачи проекта с её характеристиками и потребностями в ресурсах. - -- WorkerReq — требование работы к виду ресурса: specialty/kind, min_count, max_count, объём для нормирования - длительности. - > WorkerReq — «каких и сколько ресурсов нужно на эту задачу». - -- Contractor — поставщик ресурсов: словари workers: dict[str, Worker] и equipments: dict[str, Equipment]; каждому Worker - проставляется contractor_id. - > Contractor — организация или подразделение, предоставляющее людей и технику. - -- Worker — ресурс «люди»: наименование специальности, count, contractor_id, режим/распределение производительности. - > Worker — трудовой ресурс с численностью и производительностью (иногда со стоимостной оценкой). - -- Equipment — ресурс «техника» с типом и количеством. - > Equipment — оборудование, используемое в проекте. - ---- - -## Планировщик и результат - -- Scheduler — алгоритм планирования. Принимает WorkGraph и список Contractor (опционально spec, landscape, - work_estimator). Возвращает список Schedule. Расширенный метод `schedule_with_cache` возвращает кортежи - `(Schedule, start_time: Time, Timeline, node_order: list[GraphNode])`. - > Scheduler — модуль, который строит расписание по задачам и ресурсам. Базовый `schedule` возвращает `list[Schedule]`; - на практике берут первый план: `schedule(...)[0]`. - -- Schedule — расписание: агрегирует данные о стартах/финишах, назначениях ресурсов и др.; содержит метрику - `execution_time` (makespan). - > Schedule — итоговый план выполнения работ с назначенными ресурсами и сроками. - -- ScheduledWork — элемент расписания: конкретная работа с назначенными ресурсами, стартом/финишом и длительностью. - > ScheduledWork — «строка» плана по одной работе. - -- ScheduleSpec — спецификация для ограничений/назначений ресурсов на работы (в т.ч. фиксирование объёмов команд). - > ScheduleSpec — настройки, ограничивающие или уточняющие распределение ресурсов. - -- Timeline — внутренняя временная шкала использования ресурсов, которую поддерживает планировщик при вычислениях. - > Timeline — модель занятости ресурсов во времени. - ---- - -## Оценки и окружение - -- WorkTimeEstimator — оценщик длительности работ, подставляется в планировщик через `work_estimator`. - > WorkTimeEstimator — функция или модель, определяющая длительность выполнения задачи. - -- LandscapeConfiguration / ZoneConfiguration — конфигурация пространственных/зональных ограничений и смен статусов зон ( - при необходимости). - > Landscape/ZoneConfiguration — описание территории или зоны выполнения работ с ограничениями. - ---- - -## Пайплайн - -- SchedulingPipeline — «флюентный» builder-интерфейс для конфигурирования и запуска планирования: - `SchedulingPipeline.create() → ... → .finish() → ScheduledProject` (`project.schedule`). - > «Флюентный» builder — цепочка методов, где вы задаёте граф работ, ресурсы и опции, выбираете алгоритм и получаете - результат как `ScheduledProject`. - -Кратко о методах пайплайна (рекомендуемый порядок): - -1) `create()` — старт билдера. -2) `name_mapper(mapper|path)` — (опционально) нормализация/переименование работ и ресурсов. -3) `wg(x, all_connections=False, change_connections_info=False, sep=',')` — задать WorkGraph (объект/таблица/файл). -4) `contractors(x)` — задать подрядчиков (список/таблица/файл или генерация). -5) `history(data, sep=';')` — (опционально) исторические данные со связями; требуется, если в графе нет информации о - зависимостях. -6) `work_estimator(est)` — (опционально) модель оценки длительностей. -7) `spec(spec)` — (опционально) ограничения/фиксация ресурсов и этапов. -8) `landscape(cfg)` — (опционально) пространственные/зональные ограничения. -9) `time_shift(offset: Time)` — (опционально) сдвиг начала расписания/дат. -10) `lag_optimize(strategy)` — (желательно) стратегия оптимизации лагов; в дефолтном пайплайне может быть подобрана - автоматически, если не указана. -11) `node_order(orders: list[list[GraphNode]])` — (опционально) зафиксировать порядок узлов до планирования. -12) `optimize_local(optimizer: OrderLocalOptimizer, area: range)` — (опционально) локальная оптимизация порядка ДО - `schedule`. -13) `schedule(scheduler, validate=False)` — выбрать алгоритм и построить расписание. -14) `optimize_local(optimizer: ScheduleLocalOptimizer, area: range)` — (опционально) локальная оптимизация расписания - ПОСЛЕ `schedule`. -15) `visualization(start_date)` — (опционально) визуализация плана; далее можно вызвать `.shape(...)`, - `.color_type(...)`, `.show_gant_chart()`. -16) `finish()` — получить результат как `list[ScheduledProject]` (обычно берут `[0]`). - -- ScheduledProject — контейнер с результатом проекта (включая `project.schedule`), доступен после `finish()`. - > ScheduledProject — объект с итоговым расписанием проекта. - -Примечание: `optimize_local` присутствует в двух вариантах — для порядка (OrderLocalOptimizer, до планирования) и для -готового расписания (ScheduleLocalOptimizer, после планирования). - ---- diff --git a/docs/source/guidebook/index.md b/docs/source/guidebook/index.md index 38d2ca5a..da273d61 100644 --- a/docs/source/guidebook/index.md +++ b/docs/source/guidebook/index.md @@ -6,8 +6,6 @@ caption: Руководство SAMPO intro quickstart -checklist advanced -developers ``` From 9a8bad9f2d97cc78e072c2e2e266178206c45e51 Mon Sep 17 00:00:00 2001 From: NickMur Date: Tue, 23 Sep 2025 12:49:19 +0300 Subject: [PATCH 14/32] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B9=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/guidebook/advanced.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/source/guidebook/advanced.md b/docs/source/guidebook/advanced.md index 58823924..26745db2 100644 --- a/docs/source/guidebook/advanced.md +++ b/docs/source/guidebook/advanced.md @@ -8,8 +8,8 @@ ## Базовые сущности * **WorkGraph** — ориентированный ациклический граф работ проекта (DAG) с двумя служебными вершинами `start`/`finish` ( - может содержать служебные узлы, но не по умолчанию). - Вершины — `GraphNode`, внутри — `WorkUnit`, рёбра — зависимости (поддерживаются типы связей и лаги). + всегда содержит служебные узлы; при сборке из узлов они добавляются автоматически). Вершины — `GraphNode`, внутри — + `WorkUnit`, рёбра — зависимости (поддерживаются типы связей и лаги). > WorkGraph фиксирует все задачи и зависимости проекта. @@ -21,6 +21,9 @@ * **IFS (Inseparable Finish–Start)**: неразрывная FS — B сразу после A без разрывов; узлы образуют слитную цепочку. * **FFS (LagFinishStart)**: поточная связь; потомок стартует после выполнения предком части объёма, равной лагу. + > Примечание: в проверках «жестких зависимостей» обычно учитываются FS/IFS/FFS; SS/FF могут обрабатываться отдельно в + планировщиках. + * **Лаг** — сдвиг ограничения: * Для FS/SS/FF — числовой сдвиг во времени (обычно `0`). Знак: `>0` — задержка, `<0` — «лид». @@ -31,7 +34,8 @@ > Контейнер `WorkUnit` и его связей. -* **WorkUnit** — описание работы: объём, единицы, приоритет, требования к ресурсам (`WorkerReq`), сервисные флаги. +* **WorkUnit** — описание работы: `id`, `name`, `volume`, `measurement` (единицы), `priority`, требования к ресурсам ( + `WorkerReq`), сервисные флаги. > «Что за задача и что ей нужно». @@ -54,8 +58,9 @@ > Оборудование, используемое в проекте. * **Scheduler** — алгоритм планирования. Принимает `WorkGraph` и `list[Contractor]` (опц. `spec`, `landscape`, - `work_estimator`). Базовые реализация/методы возвращают Schedule и доп.структуры (например, schedule_with_cache -> - список кортежей (Schedule, Time, Timeline, ...)). В практическом использовании берут результат/первый план + `work_estimator`). Базовые реализации/методы могут возвращать `Schedule` и доп. структуры (например, + `schedule_with_cache` → список кортежей `(Schedule, Time, Timeline, ...)`). В практическом использовании берут + результат/первый план. > Планировщик строит расписание. @@ -156,7 +161,7 @@ wg = WorkGraph.from_nodes([n_a, n_b, n_c]) * Типы: `FinishStart (FS)`, `StartStart (SS)`, `FinishFinish (FF)`, `InseparableFinishStart (IFS)`, `LagFinishStart (FFS)`. * Лаг — второй элемент кортежа в `parents`. -* Обязательные поля `WorkUnit`: `id`, `name`. +* Обязательные поля `WorkUnit`: `id`, `name`. Для экспорта также задавайте `volume`, `measurement`, `priority`. --- @@ -352,17 +357,19 @@ print("Project makespan:", makespan) 2. `name_mapper(mapper|path)` — (опц.) нормализация имён работ и ресурсов. 3. `wg(x, all_connections=False, change_connections_info=False, sep=',')` — задать `WorkGraph` (объект/таблица/файл). 4. `contractors(x)` — задать/сгенерировать подрядчиков. -5. `history(data, sep=';')` — (опц.) связи из истории, если в графе их нет. +5. `history(data, sep=',')` — (опц.) связи из истории, если в графе их нет. 6. `work_estimator(est)` — (опц.) модель длительностей. 7. `spec(spec)` — (опц.) ограничения/фиксации ресурсов и этапов. 8. `landscape(cfg)` — (опц.) пространственные/зональные ограничения. 9. `time_shift(offset: Time)` — (опц.) сдвиг начала. -10. `lag_optimize(strategy)` — стратегия оптимизации лагов. +10. `lag_optimize(strategy)` — стратегия оптимизации лагов (поддерживаются `TRUE`, `FALSE`, `AUTO`/`NONE`). 11. `node_order(orders: list[list[GraphNode]])` — (опц.) зафиксировать порядок узлов. 12. `optimize_local(optimizer: OrderLocalOptimizer, area: range)` — (опц.) локальная оптимизация порядка до `schedule`. 13. `schedule(scheduler, validate=False)` — построить расписание. 14. `optimize_local(optimizer: ScheduleLocalOptimizer, area: range)` — (опц.) локальная оптимизация после `schedule`. -15. `visualization(start_date)` — (опц.) визуализация → `.shape(...)`, `.color_type(...)`, `.show_gant_chart()`. +15. `visualization(start_date)` — (опц.) визуализация: + * `.shape(w, h)`, `.color_type('contractor' | ... )` + * `.show_gant_chart()`, `.show_resource_charts()`, `.show_work_graph()` 16. `finish()` — `list[ScheduledProject]` (обычно `[0]`). > `ScheduledProject` содержит итоговый `project.schedule` с `execution_time` и деталями по работам. @@ -373,16 +380,13 @@ print("Project makespan:", makespan) * **WorkTimeEstimator** считает длительность задачи и передаётся через `work_estimator`. * **Режимы производительности** работников: - * `Static` — детерминированный (среднее), для воспроизводимых запусков. * `Stochastic` — случайный (выборка из распределений), для анализа рисков. * Интуитивная формула: `duration ≈ volume / (sum(productivity × count) × communication_coeff)`. - * `productivity` — из распределений у `Worker` (например, `IntervalGaussian`). * `communication_coeff` снижает эффективность при росте команды к `max_count` (`WorkerReq`). * Воспроизводимость: используйте `Static` или задайте `sigma=0` у распределений. * Практика: - * Реалистично задавайте `min_count/max_count` в `WorkerReq`. * В `Stochastic` усредняйте по нескольким прогонам. @@ -397,4 +401,4 @@ est = DefaultWorkEstimator() est.set_productivity_mode(WorkerProductivityMode.Static) scheduler = HEFTScheduler(work_estimator=est) -``` +``` \ No newline at end of file From 16008aa7b78b6a557f8908d515cfd567c1e6e46e Mon Sep 17 00:00:00 2001 From: NickMur Date: Sat, 27 Sep 2025 14:44:56 +0300 Subject: [PATCH 15/32] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82?= =?UTF-8?q?=D1=83=D1=80=D1=8B=20=D0=B4=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/guidebook/advanced.md | 404 ------------------ docs/source/guidebook/advanced/algorithms.md | 140 ++++++ docs/source/guidebook/advanced/connections.md | 92 ++++ docs/source/guidebook/advanced/contractors.md | 66 +++ docs/source/guidebook/advanced/index.md | 14 + docs/source/guidebook/advanced/scheduling.md | 126 ++++++ docs/source/guidebook/advanced/work_graph.md | 118 +++++ docs/source/guidebook/index.md | 5 +- docs/source/guidebook/intro.md | 88 ++-- docs/source/guidebook/quickstart.md | 54 ++- docs/source/index.rst | 1 + 11 files changed, 627 insertions(+), 481 deletions(-) delete mode 100644 docs/source/guidebook/advanced.md create mode 100644 docs/source/guidebook/advanced/algorithms.md create mode 100644 docs/source/guidebook/advanced/connections.md create mode 100644 docs/source/guidebook/advanced/contractors.md create mode 100644 docs/source/guidebook/advanced/index.md create mode 100644 docs/source/guidebook/advanced/scheduling.md create mode 100644 docs/source/guidebook/advanced/work_graph.md diff --git a/docs/source/guidebook/advanced.md b/docs/source/guidebook/advanced.md deleted file mode 100644 index 26745db2..00000000 --- a/docs/source/guidebook/advanced.md +++ /dev/null @@ -1,404 +0,0 @@ -# Расширённое использование и настройка - -Здесь объясняются ключевые концепции, показаны примеры кода и даны рекомендации по расширенному использованию -библиотеки. - ---- - -## Базовые сущности - -* **WorkGraph** — ориентированный ациклический граф работ проекта (DAG) с двумя служебными вершинами `start`/`finish` ( - всегда содержит служебные узлы; при сборке из узлов они добавляются автоматически). Вершины — `GraphNode`, внутри — - `WorkUnit`, рёбра — зависимости (поддерживаются типы связей и лаги). - - > WorkGraph фиксирует все задачи и зависимости проекта. - -* **Тип связи** — вид предшествования между работами в WorkGraph: - - * **FS (Finish–Start)**: `S(B) ≥ F(A) + лаг`. - * **SS (Start–Start)**: `S(B) ≥ S(A) + лаг`. - * **FF (Finish–Finish)**: `F(B) ≥ F(A) + лаг`. - * **IFS (Inseparable Finish–Start)**: неразрывная FS — B сразу после A без разрывов; узлы образуют слитную цепочку. - * **FFS (LagFinishStart)**: поточная связь; потомок стартует после выполнения предком части объёма, равной лагу. - - > Примечание: в проверках «жестких зависимостей» обычно учитываются FS/IFS/FFS; SS/FF могут обрабатываться отдельно в - планировщиках. - -* **Лаг** — сдвиг ограничения: - - * Для FS/SS/FF — числовой сдвиг во времени (обычно `0`). Знак: `>0` — задержка, `<0` — «лид». - * Для FFS — лаг в единицах объёма предка (например, км). Движок делит работу на стадии; старт потомка — после стадии - объёмом `лаг`. При отключённой оптимизации лагов FFS ведёт себя как обычный FS. - -* **GraphNode** — вершина `WorkGraph`, хранящая `WorkUnit` и ссылки на родителей/потомков (с типом связи и лагом). - - > Контейнер `WorkUnit` и его связей. - -* **WorkUnit** — описание работы: `id`, `name`, `volume`, `measurement` (единицы), `priority`, требования к ресурсам ( - `WorkerReq`), сервисные флаги. - - > «Что за задача и что ей нужно». - -* **WorkerReq** — требование к ресурсу: `kind`, `min_count`, `max_count`, `volume` (норма для расчёта длительности). - - > «Каких специалистов и сколько нужно». - -* **Contractor** — поставщик ресурсов: `workers: dict[str, Worker]`, `equipments: dict[str, Equipment]`. При - инициализации `contractor_id` у `Worker` проставляется автоматически. - - > Организация/подразделение, предоставляющее людей и технику. - -* **Worker** — трудовой ресурс: `id`, `name` (специализация), `count`, `contractor_id`, `productivity` (распределение), - опционально `cost_one_unit`. - - > `Worker.name` должен совпадать с `WorkerReq.kind` для корректного сопоставления. - -* **Equipment** — технический ресурс: тип и количество. - - > Оборудование, используемое в проекте. - -* **Scheduler** — алгоритм планирования. Принимает `WorkGraph` и `list[Contractor]` (опц. `spec`, `landscape`, - `work_estimator`). Базовые реализации/методы могут возвращать `Schedule` и доп. структуры (например, - `schedule_with_cache` → список кортежей `(Schedule, Time, Timeline, ...)`). В практическом использовании берут - результат/первый план. - - > Планировщик строит расписание. - -* **Schedule / ScheduledWork / Timeline** — итоговый план (старты/финиши, назначения ресурсов, внутренняя шкала - занятости). - - > `Schedule.execution_time` — мейкспан (длительность проекта). - -> Примечание: **DAG** — ориентированный ацикличный граф; циклы запрещены, что гарантирует упорядочиваемость плана. - ---- - -## Как собрать WorkGraph - -### Способ A. Сгенерировать синтетический граф - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.generator.pipeline import SyntheticGraphType - -ss = SimpleSynthetic() -wg = ss.work_graph(mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200) -``` - -Коротко: удобно для тестов и прототипов; управляем размером и «кластерностью». - ---- - -### Способ B. Загрузить из CSV - -```python -from sampo.pipeline import SchedulingPipeline -from sampo.scheduler.heft import HEFTScheduler -from sampo.pipeline.lag_optimization import LagOptimizationStrategy - -project = (SchedulingPipeline.create() -.wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) -.lag_optimize(LagOptimizationStrategy.TRUE) -.schedule(HEFTScheduler()) -.finish()[0]) -``` - -**Структура CSV** - -* Обязательные колонки: `activity_id, activity_name, granular_name, volume, measurement, priority`. -* Зависимости (списки через запятую, длины совпадают): `predecessor_ids, connection_types, lags`. -* Типы: `FS`, `SS`, `FF`, `IFS`, `FFS`; лаги — числа (обычно `0`). -* Опционально (требования в словарях-ячейках): `min_req`, `max_req`, `req_volume` вида `{"worker_kind": value}`. -* Примечания: - - * `start/finish` в CSV не нужны — добавляются автоматически. - * В `.wg(path, sep=';')` `sep` — разделитель файла; внутри списков — запятая. - * Ошибка `Parameter 'id' unfilled/name unfilled` часто означает пустые `activity_id`/`activity_name`. - -**Мини-пример (B зависит от A по FS, лаг 0)** - -``` -activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags -A;Task A;A;1.0;unit;0;;; -B;Task B;B;1.0;unit;0;A;FS;0 -``` - -> Полный пример: `tests/parser/test_wg.csv`. - ---- - -### Способ C. Программно из узлов - -* Создайте `WorkUnit` для каждой задачи (с `WorkerReq` при необходимости). -* Зависимости: `GraphNode(work_unit, parents)`, где `parents` — список кортежей `(parent_node, lag, EdgeType)`. -* Сборка: `WorkGraph.from_nodes([...])` — `start/finish` добавятся автоматически. - -```python -from sampo.schemas.works import WorkUnit -from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType -from sampo.schemas.requirements import WorkerReq -from sampo.schemas.time import Time - -# A -> B -> C (FS, лаг 0) -wu_a = WorkUnit(id='A', name='Task A', - worker_reqs=[WorkerReq(kind='general', volume=Time(10), min_count=2, max_count=4)], - volume=1.0, is_service_unit=False) -wu_b = WorkUnit(id='B', name='Task B', worker_reqs=[], volume=1.0, is_service_unit=False) -wu_c = WorkUnit(id='C', name='Task C', worker_reqs=[], volume=1.0, is_service_unit=False) - -n_a = GraphNode(wu_a, []) -n_b = GraphNode(wu_b, [(n_a, 0, EdgeType.FinishStart)]) # FS -n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS - -wg = WorkGraph.from_nodes([n_a, n_b, n_c]) -``` - -Коротко по зависимостям: - -* Типы: `FinishStart (FS)`, `StartStart (SS)`, `FinishFinish (FF)`, `InseparableFinishStart (IFS)`, - `LagFinishStart (FFS)`. -* Лаг — второй элемент кортежа в `parents`. -* Обязательные поля `WorkUnit`: `id`, `name`. Для экспорта также задавайте `volume`, `measurement`, `priority`. - ---- - -## Подрядчики (Contractor) - -### Вручную - -```python -from sampo.schemas.contractor import Contractor -from sampo.schemas.resources import Worker -from sampo.schemas.interval import IntervalGaussian - -contractor = Contractor( - workers={ - 'driver': Worker(id='w1', name='driver', count=8, productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5)), - 'fitter': Worker(id='w2', name='fitter', count=6, productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6)), - }, - id='c1', - name='Contractor A' -) -``` - -> Важно: -> -> * Ключи словаря `workers` должны совпадать с `Worker.name`. -> * `WorkerReq.kind` должен совпадать с `Worker.name`, иначе бригада не подберётся. -> * `contractor_id` у `Worker` берётся из `Contractor.id`. -> * При необходимости задайте `cost_one_unit` явно. - -### Генератором по параметру «размер пакета» - -```python -from sampo.generator.base import SimpleSynthetic - -ss = SimpleSynthetic() -contractor = ss.contractor(pack_worker_count=10) -``` - -### По графу работ (из требований) - -```python -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod - -contractor = get_contractor_by_wg(wg, scaler=1.0, method=ContractorGenerationMethod.AVG) -``` - -Коротко: агрегирует `min/max` из `WorkerReq` по задачам и формирует пул ресурсов. - ---- - -## Выбор алгоритма - -### Эвристические планировщики (HEFT, HEFTBetween, Topological) - -* Быстрые стартовые решения. -* HEFT/HEFTBetween ранжируют узлы по приоритетам и оценкам длительностей. -* Topological строит порядок по зависимостям без сложной оптимизации. - -Импорты: - -```python -from sampo.scheduler.heft import HEFTScheduler -from sampo.scheduler.heft import HEFTBetweenScheduler -from sampo.scheduler.topological import TopologicalScheduler -``` - ---- - -### Генетический планировщик - -* Перебирает множество альтернатив и часто улучшает мейкспан. Требует больше времени. -* Ключевые параметры: - - * `number_of_generation` — число итераций (↑ поколений → выше шанс улучшить, но дольше расчёт). - * `size_of_population` — размер популяции (↑ особей → выше диверсификация, но дороже по времени/памяти). - * `mutate_order` — вероятность мутации порядка работ при сохранении зависимостей (↑ → шире поиск, медленнее - сходимость). - * `mutate_resources` — вероятность мутации распределения ресурсов/подрядчиков (↑ → больше параллельности; при - дефиците растёт риск конфликтов). - * Дополнительно: `work_estimator`, `seed`. - -Пример: - -```python -from sampo.scheduler.genetic import GeneticScheduler - -scheduler = GeneticScheduler( - number_of_generation=50, - size_of_population=100, - mutate_order=0.1, - mutate_resources=0.1 -) -``` - ---- - -## Многоагентное планирование - -Делит граф на блоки, применяет разные стратегии и объединяет результат. Полезно для крупных проектов и гибридных -стратегий (`sampo.scheduler.multi_agency`). - -### «Аукцион» без разбиения на блоки - -```python -from uuid import uuid4 -from sampo.generator.base import SimpleSynthetic -from sampo.scheduler.heft import HEFTScheduler -from sampo.scheduler.topological import TopologicalScheduler -from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager -from sampo.schemas.contractor import Contractor -from sampo.schemas.resources import Worker - -# 1) Небольшой граф работ -ss = SimpleSynthetic(231) -wg = ss.work_graph(bottom_border=30, top_border=40) - -# 2) Универсальный подрядчик по требуемым видам работников -kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} -cid = str(uuid4()) -workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} -contractors = [Contractor(id=cid, name="Universal", workers=workers, equipments={})] - -# 3) Два агента -agents = [ - Agent("HEFT", HEFTScheduler(), contractors), - Agent("Topological", TopologicalScheduler(), contractors), -] -manager = StochasticManager(agents) - -# 4) Аукцион -start, end, schedule, winner = manager.run_auction(wg) -print("Победил агент:", winner.name, "Мейкспан:", end - start) -``` - -### С разбиением на блоки - -```python -from random import Random -from sampo.generator.base import SimpleSynthetic -from sampo.scheduler.heft import HEFTScheduler -from sampo.scheduler.topological import TopologicalScheduler -from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager -from sampo.scheduler.multi_agency.block_generator import generate_blocks, SyntheticBlockGraphType - -# 1) Граф блоков (BlockGraph) -seed = 231 -rand = Random(seed) -bg = generate_blocks( - SyntheticBlockGraphType.RANDOM, - n_blocks=4, - type_prop=[1, 1, 1], - count_supplier=lambda i: (10, 15), - edge_prob=0.3, - rand=rand -) - -# 2) Подрядчики для агентов -ss = SimpleSynthetic(rand) -contractor_a = ss.contractor(40) -contractor_b = ss.contractor(40) - -# 3) Агенты -agents = [ - Agent("HEFT", HEFTScheduler(), [contractor_a]), - Agent("Topo", TopologicalScheduler(), [contractor_b]), -] -manager = StochasticManager(agents) - -# 4) Планирование по блокам в топологическом порядке -scheduled_blocks = manager.manage_blocks(bg) - -# 5) Итоги -print("Scheduled blocks:") -for block_id, sblock in scheduled_blocks.items(): - print( - f"Block {block_id}: agent={sblock.agent.name}, start={sblock.start_time}, end={sblock.end_time}, duration={sblock.duration}") - -makespan = max(sb.end_time for sb in scheduled_blocks.values()) -print("Project makespan:", makespan) -``` - -Коротко: - -* **Блок** — самостоятельный подграф (`WorkGraph`) как единица планирования. -* **BlockGraph** — DAG блоков; `A → B` означает старт `B` после завершения `A`. -* Менеджер считает `parent_time = max(окончаний родителей)`; агенты делают офферы; побеждает минимальный `end_time`. - ---- - -## Пайплайн: рекомендуемый порядок шагов - -1. `create()` — старт билдера. -2. `name_mapper(mapper|path)` — (опц.) нормализация имён работ и ресурсов. -3. `wg(x, all_connections=False, change_connections_info=False, sep=',')` — задать `WorkGraph` (объект/таблица/файл). -4. `contractors(x)` — задать/сгенерировать подрядчиков. -5. `history(data, sep=',')` — (опц.) связи из истории, если в графе их нет. -6. `work_estimator(est)` — (опц.) модель длительностей. -7. `spec(spec)` — (опц.) ограничения/фиксации ресурсов и этапов. -8. `landscape(cfg)` — (опц.) пространственные/зональные ограничения. -9. `time_shift(offset: Time)` — (опц.) сдвиг начала. -10. `lag_optimize(strategy)` — стратегия оптимизации лагов (поддерживаются `TRUE`, `FALSE`, `AUTO`/`NONE`). -11. `node_order(orders: list[list[GraphNode]])` — (опц.) зафиксировать порядок узлов. -12. `optimize_local(optimizer: OrderLocalOptimizer, area: range)` — (опц.) локальная оптимизация порядка до `schedule`. -13. `schedule(scheduler, validate=False)` — построить расписание. -14. `optimize_local(optimizer: ScheduleLocalOptimizer, area: range)` — (опц.) локальная оптимизация после `schedule`. -15. `visualization(start_date)` — (опц.) визуализация: - * `.shape(w, h)`, `.color_type('contractor' | ... )` - * `.show_gant_chart()`, `.show_resource_charts()`, `.show_work_graph()` -16. `finish()` — `list[ScheduledProject]` (обычно `[0]`). - -> `ScheduledProject` содержит итоговый `project.schedule` с `execution_time` и деталями по работам. - ---- - -## Оценка длительностей и производительности - -* **WorkTimeEstimator** считает длительность задачи и передаётся через `work_estimator`. -* **Режимы производительности** работников: - * `Static` — детерминированный (среднее), для воспроизводимых запусков. - * `Stochastic` — случайный (выборка из распределений), для анализа рисков. -* Интуитивная формула: `duration ≈ volume / (sum(productivity × count) × communication_coeff)`. - * `productivity` — из распределений у `Worker` (например, `IntervalGaussian`). - * `communication_coeff` снижает эффективность при росте команды к `max_count` (`WorkerReq`). -* Воспроизводимость: используйте `Static` или задайте `sigma=0` у распределений. -* Практика: - * Реалистично задавайте `min_count/max_count` в `WorkerReq`. - * В `Stochastic` усредняйте по нескольким прогонам. - -Пример подключения: - -```python -from sampo.scheduler.heft import HEFTScheduler -from sampo.schemas.time_estimator import DefaultWorkEstimator -from sampo.schemas.resources import WorkerProductivityMode - -est = DefaultWorkEstimator() -est.set_productivity_mode(WorkerProductivityMode.Static) - -scheduler = HEFTScheduler(work_estimator=est) -``` \ No newline at end of file diff --git a/docs/source/guidebook/advanced/algorithms.md b/docs/source/guidebook/advanced/algorithms.md new file mode 100644 index 00000000..bad70ef3 --- /dev/null +++ b/docs/source/guidebook/advanced/algorithms.md @@ -0,0 +1,140 @@ +# Алгоритмы планирования + +## Выбор алгоритма + +### Эвристические планировщики (HEFT, HEFTBetween, Topological) + +* Быстрые стартовые решения. +* HEFT/HEFTBetween ранжируют узлы по приоритетам и оценкам длительностей. +* Topological строит порядок по зависимостям без сложной оптимизации. + +Импорты: + +```python +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.heft import HEFTBetweenScheduler +from sampo.scheduler.topological import TopologicalScheduler +``` + +--- + +### Генетический планировщик + +* Перебирает множество альтернатив и часто улучшает мейкспан. Требует больше времени. +* Ключевые параметры: + + * `number_of_generation` — число итераций (↑ поколений → выше шанс улучшить, но дольше расчёт). + * `size_of_population` — размер популяции (↑ особей → выше диверсификация, но дороже по времени/памяти). + * `mutate_order` — вероятность мутации порядка работ при сохранении зависимостей (↑ → шире поиск, медленнее + сходимость). + * `mutate_resources` — вероятность мутации распределения ресурсов/подрядчиков (↑ → больше параллельности; при + дефиците растёт риск конфликтов). + * Дополнительно: `work_estimator`, `seed`. + +Пример: + +```python +from sampo.scheduler.genetic import GeneticScheduler + +scheduler = GeneticScheduler( + number_of_generation=50, + size_of_population=100, + mutate_order=0.1, + mutate_resources=0.1 +) +``` + +--- + +## Многоагентное планирование + +Делит граф на блоки, применяет разные стратегии и объединяет результат. Полезно для крупных проектов и гибридных +стратегий (`sampo.scheduler.multi_agency`). + +### «Аукцион» без разбиения на блоки + +```python +from uuid import uuid4 +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.topological import TopologicalScheduler +from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker + +# 1) Небольшой граф работ +ss = SimpleSynthetic(231) +wg = ss.work_graph(bottom_border=30, top_border=40) + +# 2) Универсальный подрядчик по требуемым видам работников +kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} +cid = str(uuid4()) +workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} +contractors = [Contractor(id=cid, name="Universal", workers=workers, equipments={})] + +# 3) Два агента +agents = [ + Agent("HEFT", HEFTScheduler(), contractors), + Agent("Topological", TopologicalScheduler(), contractors), +] +manager = StochasticManager(agents) + +# 4) Аукцион +start, end, schedule, winner = manager.run_auction(wg) +print("Победил агент:", winner.name, "Мейкспан:", end - start) +``` + +### С разбиением на блоки + +```python +from random import Random +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.topological import TopologicalScheduler +from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager +from sampo.scheduler.multi_agency.block_generator import generate_blocks, SyntheticBlockGraphType + +# 1) Граф блоков (BlockGraph) +seed = 231 +rand = Random(seed) +bg = generate_blocks( + SyntheticBlockGraphType.RANDOM, + n_blocks=4, + type_prop=[1, 1, 1], + count_supplier=lambda i: (10, 15), + edge_prob=0.3, + rand=rand +) + +# 2) Подрядчики для агентов +ss = SimpleSynthetic(rand) +contractor_a = ss.contractor(40) +contractor_b = ss.contractor(40) + +# 3) Агенты +agents = [ + Agent("HEFT", HEFTScheduler(), [contractor_a]), + Agent("Topo", TopologicalScheduler(), [contractor_b]), +] +manager = StochasticManager(agents) + +# 4) Планирование по блокам в топологическом порядке +scheduled_blocks = manager.manage_blocks(bg) + +# 5) Итоги +print("Scheduled blocks:") +for block_id, sblock in scheduled_blocks.items(): + print( + f"Block {block_id}: agent={sblock.agent.name}, start={sblock.start_time}, end={sblock.end_time}, duration={sblock.duration}") + +makespan = max(sb.end_time for sb in scheduled_blocks.values()) +print("Project makespan:", makespan) +``` + +Коротко: + +* **Блок** — самостоятельный подграф (`WorkGraph`) как единица планирования. +* **BlockGraph** — DAG блоков; `A → B` означает старт `B` после завершения `A`. +* Менеджер считает `parent_time = max(окончаний родителей)`; агенты делают офферы; побеждает минимальный `end_time`. + +--- \ No newline at end of file diff --git a/docs/source/guidebook/advanced/connections.md b/docs/source/guidebook/advanced/connections.md new file mode 100644 index 00000000..d87dcb13 --- /dev/null +++ b/docs/source/guidebook/advanced/connections.md @@ -0,0 +1,92 @@ +# Связи между работами + +## Термины + +**Связь (GraphEdge)** — направленное ребро между узлами с типом (`EdgeType`) и лагом. +> Определяет правило допуска старта/финиша зависимой работы. + +**Лаг (lag)** — числовая задержка после финиша родителя перед тем, как зависимая работа может стартовать. +> Используется для технологических выдержек (сушка, остывание и т.п.). + +**IFS‑цепочка** — последовательность узлов, связанных типом IFS без пауз и без освобождения команды. +> Выполняется непрерывно одной бригадой. + +--- + +## Типы связей (EdgeType) + +| Тип | Формат | Семантика (кратко) | Блокирует старт? | +|-----|------------------------|-----------------------------------------------|-------------------------------------| +| FS | Finish→Start | Потомок стартует после финиша родителя (+лаг) | Да | +| FFS | LagFinishStart | То же, акцент на обязательном лаге | Да | +| IFS | InseparableFinishStart | Неразрывная передача выполнения (цепочка) | Да | +| SS | Start→Start | Синхронный старт с родителем | Нет (не родитель в смысле задержки) | +| FF | Finish→Finish | Должен закончиться не раньше связанного узла | Нет (контролируется по финишу) | + +> В вычислении раннего старта участвуют только FS / FFS / IFS (они считаются «жёсткими» зависимостями). + +--- + +## Когда использовать + +- Обычная последовательность: **FS**. +- Технологическая выдержка: **FFS** (или FS с лагом, если семантика очевидна). +- Неразрывный проход одной командой: **IFS**. +- Одновременный запуск: **SS**. +- Синхронный (или не ранний) общий финиш: **FF**. + +--- + +## Формулировки типов (единообразно) + +- **FS** — B стартует после финиша A (можно добавить лаг). +- **FFS** — B стартует после финиша A с обязательной задержкой (лаг критичен). +- **IFS** — B немедленно следует за A теми же ресурсами (без интервала). +- **SS** — B может начинаться одновременно с A. +- **FF** — B должен завершиться не раньше A (контроль на уровне финиша). + +--- + +## Мини‑примеры + +1. FS + лаг + A: «Залить бетон» (финиш День 2) → лаг 3 дня → B: «Кладка стен» (старт не раньше День 5). + +2. IFS + A: «Сверление» ⇒ B: «Анкерение» ⇒ C: «Монтаж стойки» (одна бригада без пауз). + +3. SS + A: «Пуск серверов» ≡ B: «Старт мониторинга» (одновременно). + +4. FF + A: «Основная настройка» || B: «Документация» (должны завершиться к одному дедлайну). + +5. FFS + A: «Нанести грунт» → лаг 4 часа → B: «Покраска». + +> Условные символы выше: → (FS/FFS), ⇒ (IFS), ≡ (SS), || (FF). + +--- + +## Мини CSV (логика экспорта зависимостей) + +Формат (условный `predecessors.csv`): + +``` +child_id,parent_id,type,lag +B,A,FS,3 +C,B,IFS,0 +D,C,IFS,0 +E,A,SS,0 +``` + +Расшифровка: + +- B после A + 3 (сушка, лаг 3). +- C и D образуют с B неразрывную цепь (IFS). +- E стартует синхронно с A (SS, не задерживает A и не задерживается им). + +> В итоговой таблице проекта сервисные `start` / `finish` не выводятся. + +--- + diff --git a/docs/source/guidebook/advanced/contractors.md b/docs/source/guidebook/advanced/contractors.md new file mode 100644 index 00000000..e54d26ca --- /dev/null +++ b/docs/source/guidebook/advanced/contractors.md @@ -0,0 +1,66 @@ +# Contractor + +## Термины + +**WorkerReq** — требование к рабочей силе: профессия (`kind`), диапазон численности (`min_count`…`max_count`), +объём/норма (`volume`). +> Определяет потребность задачи в людях. + +**Worker** — доступный тип рабочей силы: `name` (специализация), `count`, `productivity`, `contractor_id`, опционально +стоимость. +> `Worker.name` сопоставляется с `WorkerReq.kind`. + +**Equipment** — единица или группа техники с типом и количеством. +> Используется задачами аналогично рабочим ресурсам. + +**Contractor** — поставщик ресурсов (словарь работников, техники и пр.). +> Предоставляет набор доступных `Worker` / `Equipment`. + +--- + +## Подрядчики (Contractor) + +### Вручную + +```python +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker +from sampo.schemas.interval import IntervalGaussian + +contractor = Contractor( + workers={ + 'driver': Worker(id='w1', name='driver', count=8, productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5)), + 'fitter': Worker(id='w2', name='fitter', count=6, productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6)), + }, + id='c1', + name='Contractor A' +) +``` + +> Важно: +> +> * Ключи словаря `workers` должны совпадать с `Worker.name`. +> * `WorkerReq.kind` должен совпадать с `Worker.name`, иначе бригада не подберётся. +> * `contractor_id` у `Worker` берётся из `Contractor.id`. +> * При необходимости задайте `cost_one_unit` явно. + +### Генератором по параметру «размер пакета» + +```python +from sampo.generator.base import SimpleSynthetic + +ss = SimpleSynthetic() +contractor = ss.contractor(pack_worker_count=10) +``` + +### По графу работ (из требований) + +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod + +contractor = get_contractor_by_wg(wg, scaler=1.0, method=ContractorGenerationMethod.AVG) +``` + +Коротко: агрегирует `min/max` из `WorkerReq` по задачам и формирует пул ресурсов. + +--- \ No newline at end of file diff --git a/docs/source/guidebook/advanced/index.md b/docs/source/guidebook/advanced/index.md new file mode 100644 index 00000000..3af88365 --- /dev/null +++ b/docs/source/guidebook/advanced/index.md @@ -0,0 +1,14 @@ +# Основное руководство + +```{toctree} +:maxdepth: 2 +:numbered: +caption: Основное руководство SAMPO + +work_graph +connections +contractors +scheduling +algorithms +``` + diff --git a/docs/source/guidebook/advanced/scheduling.md b/docs/source/guidebook/advanced/scheduling.md new file mode 100644 index 00000000..71b162c9 --- /dev/null +++ b/docs/source/guidebook/advanced/scheduling.md @@ -0,0 +1,126 @@ +# Планирование + +## Что такое Scheduler + +`Scheduler` — объект, который по графу работ (`WorkGraph`) и ресурсам (список `Contractor`) строит расписание ( +`Schedule`). +Обычно вы просто создаёте конкретный планировщик (`HEFTScheduler`, `GeneticScheduler`, `TopologicalScheduler` и т.д.) и +вызываете `.schedule(...)`. + +| Параметр | Назначение | Значение по умолчанию | Меняем когда | +|----------------------|-----------------------------------------------------------------------|-----------------------------|----------------------------------------------------------------------| +| `scheduler_type` | Метка семейства алгоритма (для внутренней логики и логов). | Задаётся самим наследником. | Пишете свой класс планировщика. | +| `resource_optimizer` | Стратегия подбора/урезания численности команды (баланс время / люди). | Координатный спуск. | Нужна другая эвристика (усреднение, полный поиск, ничего не делать). | +| `work_estimator` | Как считать длительность работ (объём → время). | `DefaultWorkEstimator()` | Нужна доменная или стохастическая модель. | + +### Минимальный паттерн + +```python +# У вас есть: wg (WorkGraph), contractors (list[Contractor]) +from sampo.scheduler.heft.base import HEFTScheduler + +scheduler = HEFTScheduler() +schedule = scheduler.schedule(wg, contractors)[0] +print(schedule.execution_time) +``` + +Через пайплайн: + +```python +from sampo.pipeline import SchedulingPipeline +from sampo.scheduler import GeneticScheduler + +result = (SchedulingPipeline.create() + .wg('input.csv', sep=';') + .schedule(GeneticScheduler(number_of_generation=10)) + .finish() + )[0] + +print(result.schedule.execution_time) +``` + +### Кастомизация (подсказка) + +| Цель | Действие | +|-----------------------------|--------------------------------------------------| +| Своя формула времени | Передать свой `work_estimator`. | +| Эксперименты с численностью | Передать иной `resource_optimizer`. | +| Новая стратегия | Создать наследник + установить `scheduler_type`. | + +### Самая короткая формулировка + +Scheduler = (Граф работ + Ресурсы) → Schedule. + +--- + +## Что такое Timeline (коротко) + +`Timeline` — внутренняя структура учёта занятости ресурсов по времени. +Планировщик спрашивает её: «Когда можно стартовать эту работу с таким набором ресурсов?» — и фиксирует занятые интервалы +после назначения. + +> В обычном использовании вы напрямую с `Timeline` не взаимодействуете. + +### Что хранит концептуально + +- Интервалы занятости по видам или конкретным группам ресурсов. +- Логику поиска минимального допустимого старта с учётом: + - зависимостей (время доступно не раньше родителя + лаг), + - занятости уже назначенных работ, + - возможных доп. ограничений (например, зоны / ландшафт). + +### Когда имеет смысл лезть внутрь + +- Пишете свою стратегию планирования или визуализатор занятости. +- Отлаживаете конфликт ресурса (почему старт сдвинулся). + +### Самая короткая формулировка + +Timeline = «Календарь занятости ресурсов + функция earliest feasible start». + +--- + +## Что такое ScheduledWork (коротко) + +`ScheduledWork` — конкретное запланированное выполнение одной работы: + +- ссылка на исходный `GraphNode` / `WorkUnit`, +- время старта и окончания, +- назначенные ресурсы (workers, equipment, материалы), +- дополнительные атрибуты (зона, производительность и т.п. если применимо). + +> Это «живая» инстанциация вершины графа внутри результата. + +### Использование + +Обычно получаете список/слой через объекты `Schedule` (например, DataFrame или итерацию по структурам), а не создаёте +вручную. + +### Самая короткая формулировка + +ScheduledWork = WorkUnit + (start, finish, назначенные ресурсы). + +--- + +## Что такое Schedule (коротко) + +`Schedule` — агрегат всех `ScheduledWork`. Содержит: + +- `execution_time` (makespan — от первого старта до последнего финиша), +- производные представления (таблицы, диаграммы), +- методы экспорта/фильтрации/слияния сервисных задач, +- (опционально) метаданные о порядке, ресурсной статистике, критическом пути. + +> Конечный продукт планировщика. + +### Пример получения длительности и таблицы + +```python +schedule = scheduler.schedule(wg, contractors)[0] +print(schedule.execution_time) + +df = schedule.merged_stages_datetime_df(start_date="2025-01-01") +print(df.head()) +``` + +--- \ No newline at end of file diff --git a/docs/source/guidebook/advanced/work_graph.md b/docs/source/guidebook/advanced/work_graph.md new file mode 100644 index 00000000..4109d784 --- /dev/null +++ b/docs/source/guidebook/advanced/work_graph.md @@ -0,0 +1,118 @@ +# WorkGraph + +## Термины + +**WorkGraph** — ориентированный ациклический граф (DAG) проекта с двумя служебными вершинами `start` / `finish`; при +сборке из набора узлов сервисные вершины добавляются автоматически. + +> Фиксирует задачи и их зависимости (узлы — `GraphNode`, рёбра — связи с типом и лагом). Циклы запрещены. + + +**GraphNode** — вершина графа, содержащая `WorkUnit` и ссылки на родительские/дочерние узлы (каждая связь имеет тип и +лаг). + +> Контейнер задачи и её входящих/исходящих зависимостей. + +**WorkUnit** — задача проекта с объёмом и набором требований к ресурсам. + +--- + +## Как собрать WorkGraph + +### Способ A. Сгенерировать синтетический граф + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator.pipeline import SyntheticGraphType + +ss = SimpleSynthetic() +wg = ss.work_graph(mode=SyntheticGraphType.GENERAL, + cluster_counts=10, + bottom_border=100, + top_border=200) +``` + +> mode: 'general' or 'sequence' or 'parallel - the type of the returned graph +> cluster_counts: Number of clusters for the graph +> bottom_border: bottom border for number of works for the graph +> top_border: top border for number of works for the graph + +--- + +### Способ B. Загрузить из CSV + +```python +from sampo.pipeline import SchedulingPipeline +from sampo.pipeline.lag_optimization import LagOptimizationStrategy +from sampo.scheduler.heft import HEFTScheduler + +project = (SchedulingPipeline.create() +.wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) +.lag_optimize(LagOptimizationStrategy.TRUE) +.schedule(HEFTScheduler()) +.finish()[0]) +``` + +> all_connections: Пытаться достроить или дополнить связи между работами, если их не хватает во входных данных. +> sep: Разделитель столбцов CSV +> LagOptimizationStrategy: Shows should graph be lag-optimized or not. +If not defined, pipeline should search the best variant of this argument in result. + +**Структура CSV** + +* Обязательные колонки: `activity_id, activity_name, granular_name, volume, measurement, priority`. +* Зависимости (списки через запятую, длины совпадают): `predecessor_ids, connection_types, lags`. +* Типы: `FS`, `SS`, `FF`, `IFS`, `FFS`; лаги — числа (обычно `0`). +* Опционально (требования в словарях-ячейках): `min_req`, `max_req`, `req_volume` вида `{"worker_kind": value}`. +* Примечания: + + * `start/finish` в CSV не нужны — добавляются автоматически. + * В `.wg(path, sep=';')` `sep` — разделитель файла; внутри списков — запятая. + * Ошибка `Parameter 'id' unfilled/name unfilled` часто означает пустые `activity_id`/`activity_name`. + +**Мини-пример (B зависит от A по FS, лаг 0)** + +``` +activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags +A;Task A;A;1.0;unit;0;;; +B;Task B;B;1.0;unit;0;A;FS;0 +``` + +> Полный пример: `tests/parser/test_wg.csv`. + +--- + +### Способ C. Программно из узлов + +* Создайте `WorkUnit` для каждой задачи (с `WorkerReq` при необходимости). +* Зависимости: `GraphNode(work_unit, parents)`, где `parents` — список кортежей `(parent_node, lag, EdgeType)`. +* Сборка: `WorkGraph.from_nodes([...])` — `start/finish` добавятся автоматически. + +```python +from sampo.schemas.works import WorkUnit +from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType +from sampo.schemas.requirements import WorkerReq +from sampo.schemas.time import Time + +# A -> B -> C (FS, лаг 0) +wu_a = WorkUnit(id='A', name='Task A', + worker_reqs=[WorkerReq(kind='general', volume=Time(10), min_count=2, max_count=4)], + volume=1.0, is_service_unit=False) +wu_b = WorkUnit(id='B', name='Task B', worker_reqs=[], volume=1.0, is_service_unit=False) +wu_c = WorkUnit(id='C', name='Task C', worker_reqs=[], volume=1.0, is_service_unit=False) + +n_a = GraphNode(wu_a, []) +n_b = GraphNode(wu_b, [(n_a, 0, EdgeType.FinishStart)]) # FS +n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS + +wg = WorkGraph.from_nodes([n_a, n_b, n_c]) +``` + +Коротко по зависимостям: + +* Типы: `FinishStart (FS)`, `StartStart (SS)`, `FinishFinish (FF)`, `InseparableFinishStart (IFS)`, + `LagFinishStart (FFS)`. +* Лаг — второй элемент кортежа в `parents`. +* Обязательные поля `WorkUnit`: `id`, `name`. Для экспорта также задавайте `volume`, `measurement`, `priority`. + +--- \ No newline at end of file diff --git a/docs/source/guidebook/index.md b/docs/source/guidebook/index.md index da273d61..09930a03 100644 --- a/docs/source/guidebook/index.md +++ b/docs/source/guidebook/index.md @@ -1,11 +1,10 @@ -# Руководство +# Начальное руководство ```{toctree} :maxdepth: 2 :numbered: -caption: Руководство SAMPO +caption: Начальное руководство SAMPO intro quickstart -advanced ``` diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md index 1e970a77..e120e96b 100644 --- a/docs/source/guidebook/intro.md +++ b/docs/source/guidebook/intro.md @@ -1,95 +1,63 @@ -# Руководство SAMPO +# Введение ## Для кого руководство? Руководство организовано так, чтобы помочь пользователям с разным уровнем подготовки: -* **Базовый уровень** — что такое SAMPO, чем он поможет и как быстро начать работу. +* **Ознакомительный уровень** — что такое SAMPO, чем он поможет и как быстро начать работу. * **Продвинутый уровень** — как настраивать SAMPO под свои задачи. -## Введение - -### Что такое SAMPO? +## Что такое SAMPO? **SAMPO** (Scheduler for Adaptive Manufacturing Processes Optimization) — это открытый **фреймворк адаптивного -календарного планирования** для производства и других областей. +календарного планирования**. -> **Адаптивное календарное планирование** — построение расписания с учётом ограничений и его автоматическое -> пересчитывание при изменениях (задержках, изменении ресурсов, приоритетов или сроков). +> Адаптивное календарное планирование — это построение расписания с учётом ограничений ресурсов. Оптимизация при +> изменениях входных данных достигается за счёт кеширования, частичной фиксации уже +> принятых решений и специализированных процедур. -Он автоматически упорядочивает **задачи** и назначает им **ресурсы** (людей, оборудование и т. п.) оптимальным образом с -учётом ограничений проекта (зависимости, лимиты ресурсов, дедлайны). Цель — построение расписания, наилучшего по -выбранной **метрике оптимизации** (например, минимизация длительности или стоимости). +Цель — построение расписания, наилучшего по выбранной **метрике оптимизации** (например, минимизация длительности или +стоимости). В SAMPO реализованы **эвристики**, **генетические алгоритмы** и **многоагентные методы**, а также их комбинации, что -позволяет эффективно решать сложные реальные сценарии, где статические или жадные подходы недостаточны. Фреймворк -поддерживает **реактивное перепланирование** при изменениях и возмущениях в ходе выполнения проекта. +позволяет эффективно решать сложные реальные сценарии, где статические или жадные подходы недостаточны. -### Для кого SAMPO? +## Для кого SAMPO? Если у вас проект с множеством зависимостей и ограничений по ресурсам, и вы хотите автоматизировать поиск хорошего плана исполнения — SAMPO для вас. -### Ключевые возможности и преимущества - -* **Многокритериальная оптимизация** - — работа сразу с несколькими целями: минимизация длительности и стоимости, сокращение просрочек, балансировка - загрузки, приоритизация заказов; поддержка поиска **Парето-компромиссов** и гибкой агрегации метрик. - -> **Парето-компромисс** — решение, где нельзя улучшить одну цель без ухудшения другой; множество таких решений образует -> фронт Парето. - -* **Сложные ограничения и ресурсы** - — учёт предшествования и альтернативных маршрутов, параллельных/альтернативных ресурсов, сменных календарей, - навыков/квалификаций (skills), окон доступности, ограничений мощностей, переналадок/переконфигураций и логистических - задержек. +## Ключевые возможности и преимущества -* **Поддерживаемые модели задач** - — RCPSP/MRCPSP (в том числе многопроектное планирование), варианты *Job Shop*, *Flow Shop* и DAG-ориентированные - процессы. +- Упорядочивает задачи и назначает ресурсы с учетом ограничений - > **RCPSP** — задача календарного планирования с ограниченными ресурсами. + > Под «ограничениями» понимаются: предшествования задач, минимальные/максимальные потребности по видам ресурсов, + доступность у подрядчиков, возможные логистические задержки/окна поставок и пространственные ограничения ( + зоны/площадки). - > **MRCPSP** — расширение RCPSP, где для каждой работы есть несколько способов выполнения с разными - ресурсами/длительностью. +- Оптимизирует по выбранной метрике (время, ресурсы, стоимость, мультикритерий) - > **Job Shop / Flow Shop** — классические производственные модели: порядок и маршруты задач фиксированы (*Flow Shop*) - или различаются для каждого изделия (*Job Shop*). + > «Выбранная метрика» — это настраиваемая целевая функция: можно использовать готовые (время/ресурсы/стоимость, с + дедлайном или без) или добавить свою, если нужны специфические KPI. - > **DAG-процессы** — планирование по ацикличным графам зависимостей (Directed Acyclic Graph). - > **Ациклический граф** — граф без циклов, где нельзя вернуться к исходной вершине, следуя по рёбрам. +- Адаптивно перепланирует под дедлайн -* **Гибкий конвейер (pipeline)** - — модульное подключение планировщиков; легко заменить алгоритм или добавить шаги без правок ядра. + > Алгоритмы сами выбирают параметры/назначения так, чтобы соблюсти ограничение по времени. -* **Адаптивность и реактивное планирование** - — пересчёт расписания при изменении входных данных (WorkGraph, ресурсы Contractor, дедлайны, веса критериев) за счёт - модульного SchedulingPipeline (пайплайн построен из независимых, заменяемых шагов) и настраиваемых оценщиков - времени/ресурсов. Поддерживается автоматическое перепланирование «на лету» при задержках, сбоях, изменении ресурсов, - приоритетов или поступлении новых работ. +- Ускорение перепланирования -* **Масштабируемость** - — устойчив к крупным графам (порядка **2 000–10 000** работ) при поиске качественных решений. + > Идея: не начинать «с нуля» и не трогать то, что уже согласовано/выполнено; это ускоряет реакцию на изменения и + уменьшает нежелательные перестановки в расписании. -* **Адаптация под домен** - — возможность подключать свои парсеры, модели оценки времени/ресурсов, критерии качества и политики диспетчеризации. - -* **Инструментарий** - — генерация и бенчмаркинг тестовых данных, средства анализа качества решений, утилиты визуализации (диаграммы Ганта, - загрузка ресурсов). - - > **Диаграмма Ганта** — график, где работы показываются полосами на временной шкале, отражающими их начало и - длительность. - -* **Открытость и документация** - — лицензия **BSD 3-Clause**, подробная документация, примеры в **Jupyter Notebook**, удобный **Python API**. +- Открытость и документация + — лицензия BSD 3-Clause, подробная документация, примеры в Jupyter Notebook. > BSD 3-Clause — свободная лицензия, разрешающая использовать, изменять и распространять код (в том числе в коммерческих проектах) при сохранении уведомления об авторских правах и отказа от ответственности. --- -### Реализованные алгоритмы и подходы +## Реализованные алгоритмы и подходы * **Эвристики** @@ -98,7 +66,7 @@ - *Topological* — алгоритм, который строит порядок выполнения задач на основе их зависимостей. Использует топологическую сортировку графа работ (DAG), то есть размещает каждую задачу только после всех её предшественников. - + - *HEFT/HEFTBetween* — жадные эвристики *Heterogeneous Earliest Finish Time*, минимизирующие время завершения. > HEFT/HEFTBetween — метод планирования, который идёт по задачам и назначает каждую туда, где она закончится diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md index 84f61568..e43e9b88 100644 --- a/docs/source/guidebook/quickstart.md +++ b/docs/source/guidebook/quickstart.md @@ -24,7 +24,8 @@ pip install sampo 1) Генерация WorkGraph. Для простоты воспользуемся генератором ```python -from sampo.generator import SimpleSynthetic, SyntheticGraphType +from sampo.generator.base import SimpleSynthetic +from sampo.generator.pipeline import SyntheticGraphType from sampo.schemas.graph import WorkGraph # Инициализация синтетического генератора @@ -42,23 +43,49 @@ print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") 2) Ресурсы (Contractor’ы) +Важно: синтетический граф использует типовые профессии `driver`, `fitter`, `manager`, `handyman`, `electrician`, +`engineer`. Подрядчик должен содержать работников для каждого вида требования, а словарь `workers` должен быть +индексирован по имени вида ресурса (т.е. `req.kind`). + ```python from sampo.schemas.contractor import Contractor from sampo.schemas.resources import Worker -# Создаем рабочего -worker = Worker(id="w1", name="handyman", count=10) # Используем 'handyman' как пример рабочего общего профиля +# Зададим по нескольку работников каждого нужного типа +workers = [ + Worker(id="w_driver", name="driver", count=20), + Worker(id="w_fitter", name="fitter", count=20), + Worker(id="w_manager", name="manager", count=10), + Worker(id="w_handyman", name="handyman", count=20), + Worker(id="w_electrician", name="electrician", count=10), + Worker(id="w_engineer", name="engineer", count=10), +] -# Один подрядчик с 10 работниками +# Один подрядчик с полным пулом работников contractors = [ Contractor( id="c1", name="General Contractor", - workers={worker.id: worker} + # Ключи — имена видов ресурсов (совпадают с WorkerReq.kind) + workers={w.name: w for w in workers} ) ] ``` +Альтернатива: можно автоматически сгенерировать подрядчика «по графу», чтобы ресурсы точно покрывали требования: + +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod + +contractors = [get_contractor_by_wg( + work_graph, + scaler=1.0, # множитель мощностей (>= 1.0) + method=ContractorGenerationMethod.AVG, # брать среднее между min/max потребностями + contractor_id="c1", + contractor_name="General Contractor" +)] +``` + 3) Выбор планировщика ```python @@ -69,8 +96,8 @@ from sampo.scheduler.heft import HEFTScheduler scheduler = HEFTScheduler() # быстрая эвристика для старта ``` -4) Запуск планирования - Важный момент: метод schedule возвращает список кортежей; нам нужен сам Schedule из первого элемента. +4) Запуск планирования + Метод `schedule` возвращает список решений; берём первое. ```python # Планирование: берём первое (лучшее) решение @@ -79,9 +106,8 @@ best_schedule, start_time, timeline, node_order = scheduler.schedule(work_graph, print(f"Projected project duration (makespan): {best_schedule.execution_time}") ``` -Просмотр расписания -У разных версий могут отличаться детали структур расписания. Надёжный способ получить агрегированное представление и -визуализировать — воспользоваться встроенной функцией Ганта: +Просмотр расписания +Надёжный способ получить агрегированное представление и визуализировать — воспользоваться встроенной функцией Ганта: ```python from sampo.utilities.visualization import schedule_gant_chart_fig, VisualizationMode @@ -91,9 +117,9 @@ fig = schedule_gant_chart_fig(merged, VisualizationMode.ReturnFig, remove_servic fig.show() ``` -5) (Опционально) Конвейер SchedulingPipeline - Эквивалент тех же шагов во «флюентном» стиле. finish() возвращает список ScheduledProject, из которого берём [0] и - затем читаем project.schedule. +5) (Опционально) Конвейер SchedulingPipeline + Эквивалент тех же шагов во «флюентном» стиле. `finish()` возвращает список `ScheduledProject`, из которого берём + `[0]` и затем читаем `project.schedule`. ```python from sampo.pipeline import SchedulingPipeline @@ -106,4 +132,4 @@ project = (SchedulingPipeline.create() .finish()[0]) print(f"Project duration: {project.schedule.execution_time}") -``` +``` \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index 32ee396e..3a256d64 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -11,6 +11,7 @@ Welcome to SAMPO's documentation! :caption: Contents: guidebook/index + guidebook/advanced/index autoapi/sampo/index From 37a8dd9754d63c1434f33546114ff46ddd5b4a41 Mon Sep 17 00:00:00 2001 From: NickMur Date: Sun, 28 Sep 2025 17:29:33 +0300 Subject: [PATCH 16/32] Update intro.md --- docs/source/guidebook/intro.md | 64 ++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md index e120e96b..6314efac 100644 --- a/docs/source/guidebook/intro.md +++ b/docs/source/guidebook/intro.md @@ -4,23 +4,25 @@ Руководство организовано так, чтобы помочь пользователям с разным уровнем подготовки: -* **Ознакомительный уровень** — что такое SAMPO, чем он поможет и как быстро начать работу. -* **Продвинутый уровень** — как настраивать SAMPO под свои задачи. +* Ознакомительный уровень — что такое SAMPO, чем он поможет и как быстро начать работу. +* Продвинутый уровень — как настраивать SAMPO под свои задачи. ## Что такое SAMPO? **SAMPO** (Scheduler for Adaptive Manufacturing Processes Optimization) — это открытый **фреймворк адаптивного календарного планирования**. -> Адаптивное календарное планирование — это построение расписания с учётом ограничений ресурсов. Оптимизация при -> изменениях входных данных достигается за счёт кеширования, частичной фиксации уже -> принятых решений и специализированных процедур. +> Адаптивное календарное планирование — построение расписания с учётом технологических, ресурсных и логистических +> ограничений и его целевая переоптимизация при изменениях входных данных или целей (например, дедлайнов). +> Переоптимизация выполняется по событию/запуску алгоритмов за счёт тёплого старта (кеширования), +> частичной фиксации уже принятых решений и специализированных процедур (например, бинпоиск ресурсоёмкости под дедлайн). Цель — построение расписания, наилучшего по выбранной **метрике оптимизации** (например, минимизация длительности или стоимости). -В SAMPO реализованы **эвристики**, **генетические алгоритмы** и **многоагентные методы**, а также их комбинации, что -позволяет эффективно решать сложные реальные сценарии, где статические или жадные подходы недостаточны. +В SAMPO реализованы **эвристики** и **генетические алгоритмы**, а также предусмотрен модуль **многоагентного +моделирования** для комбинирования стратегий — это помогает решать сложные сценарии, где статические или чисто жадные +подходы недостаточны. ## Для кого SAMPO? @@ -31,26 +33,28 @@ - Упорядочивает задачи и назначает ресурсы с учетом ограничений - > Под «ограничениями» понимаются: предшествования задач, минимальные/максимальные потребности по видам ресурсов, - доступность у подрядчиков, возможные логистические задержки/окна поставок и пространственные ограничения ( - зоны/площадки). + > Под «ограничениями» понимаются: предшествования задач, минимальные/максимальные потребности по видам ресурсов... + Порядок работ задаётся эвристиками (например, Topological, HEFT), а состав бригад подбирается + оптимизаторами ресурсов с учётом доступности. -- Оптимизирует по выбранной метрике (время, ресурсы, стоимость, мультикритерий) - - > «Выбранная метрика» — это настраиваемая целевая функция: можно использовать готовые (время/ресурсы/стоимость, с - дедлайном или без) или добавить свою, если нужны специфические KPI. +- Оптимизирует по выбранной метрике (время, ресурсы, стоимость, мультикритерий) - Адаптивно перепланирует под дедлайн - > Алгоритмы сами выбирают параметры/назначения так, чтобы соблюсти ограничение по времени. + > При заданном сроке выполняется целевая переоптимизация: «обёртка» с двоичным поиском подбирает уровень + ресурсообеспечения (параметр k), многократно вызывая базовый планировщик, чтобы уложиться в дедлайн; в генетическом + режиме используется двухфазная схема — сначала добиваются соблюдения времени, затем оптимизируют ресурсы/стоимость + среди планов, укладывающихся в срок. - Ускорение перепланирования - > Идея: не начинать «с нуля» и не трогать то, что уже согласовано/выполнено; это ускоряет реакцию на изменения и - уменьшает нежелательные перестановки в расписании. + > Чтобы не начинать «с нуля», используется тёплый старт (кеширование и повторное использование подготовленных + структур), мультипроцессная оценка популяции в генетике и частичная фиксация уже принятых назначений ( + переоптимизируются только свободные степени свободы). Это ускоряет реакцию на изменения и уменьшает нежелательные + перестановки в расписании. -- Открытость и документация - — лицензия BSD 3-Clause, подробная документация, примеры в Jupyter Notebook. +- **Открытость и документация** + — лицензия **BSD 3-Clause**, подробная документация, примеры в **Jupyter Notebook**. > BSD 3-Clause — свободная лицензия, разрешающая использовать, изменять и распространять код (в том числе в коммерческих проектах) при сохранении уведомления об авторских правах и отказа от ответственности. @@ -67,26 +71,24 @@ топологическую сортировку графа работ (DAG), то есть размещает каждую задачу только после всех её предшественников. - - *HEFT/HEFTBetween* — жадные эвристики *Heterogeneous Earliest Finish Time*, минимизирующие время завершения. + - *HEFT/HEFTBetween* — жадные эвристики *Heterogeneous Earliest Finish Time*, стремящиеся минимизировать время + завершения с учётом разнородных ресурсов. - > HEFT/HEFTBetween — метод планирования, который идёт по задачам и назначает каждую туда, где она закончится - быстрее всего, учитывая зависимости и разную скорость ресурсов. + > HEFT/HEFTBetween — метод планирования, который идёт по задачам и назначает каждую туда, где она закончится быстрее + всего, учитывая зависимости и производительность ресурсов. * **Генетические алгоритмы** > Генетические алгоритмы — оптимизация по принципам эволюции: популяция решений подвергается «скрещиванию» и «мутациям» для улучшения качества. - - *Genetic* — алгоритм с эвристической инициализацией, кроссовером и мутациями. - - > Позволяет находить более качественные планы, чем простые жадные методы, так как исследует разные комбинации решений. + - *Genetic* — алгоритм с эвристической инициализацией, кроссовером и мутациями; поддерживает разные функции + пригодности (время, ресурсы, стоимость), мультикритерий и режим «с дедлайном». - > Жадные методы — алгоритмы, которые на каждом шаге выбирают локально «лучшее» решение (например, самую короткую - задачу или ресурс с наименьшей загрузкой), не анализируя последствия для всего расписания. + > Позволяет находить более качественные планы, чем простые жадные методы, так как исследует разные комбинации решений + и отбирает лучшие. * **Многоагентные методы** - > Многоагентные методы — решение задач группой независимых планировщиков, каждый из которых отвечает за свою часть - проекта. - - - *Многоагентное планирование* — разбиение проекта на подзадачи и координация агентов. + > Подходы, в которых разные «агенты»-планировщики решают подзадачи и координируются между собой — для комбинирования + сильных сторон алгоритмов в больших проектах (модуль предусмотрен для интеграции). From bcf70dcafd5b87a5ec85a4c572da6efd836f437f Mon Sep 17 00:00:00 2001 From: NickMur Date: Tue, 30 Sep 2025 16:30:13 +0300 Subject: [PATCH 17/32] Simplification of documentation --- docs/source/guidebook/advanced/algorithms.md | 61 ++++--- docs/source/guidebook/advanced/connections.md | 89 ++++------ docs/source/guidebook/advanced/contractors.md | 127 +++++++++++---- docs/source/guidebook/advanced/scheduling.md | 119 ++++---------- docs/source/guidebook/advanced/work_graph.md | 154 +++++++++++------- docs/source/guidebook/intro.md | 9 + 6 files changed, 304 insertions(+), 255 deletions(-) diff --git a/docs/source/guidebook/advanced/algorithms.md b/docs/source/guidebook/advanced/algorithms.md index bad70ef3..63b96116 100644 --- a/docs/source/guidebook/advanced/algorithms.md +++ b/docs/source/guidebook/advanced/algorithms.md @@ -1,12 +1,12 @@ # Алгоритмы планирования -## Выбор алгоритма +## Как выбрать алгоритм ### Эвристические планировщики (HEFT, HEFTBetween, Topological) -* Быстрые стартовые решения. -* HEFT/HEFTBetween ранжируют узлы по приоритетам и оценкам длительностей. -* Topological строит порядок по зависимостям без сложной оптимизации. +- Быстро дают рабочий план. +- HEFT/HEFTBetween рассчитывают порядок задач и примерное время выполнения, чтобы закончить быстрее. +- Topological просто выстраивает задачи по зависимостям, без сложной оптимизации. Импорты: @@ -20,16 +20,16 @@ from sampo.scheduler.topological import TopologicalScheduler ### Генетический планировщик -* Перебирает множество альтернатив и часто улучшает мейкспан. Требует больше времени. -* Ключевые параметры: - - * `number_of_generation` — число итераций (↑ поколений → выше шанс улучшить, но дольше расчёт). - * `size_of_population` — размер популяции (↑ особей → выше диверсификация, но дороже по времени/памяти). - * `mutate_order` — вероятность мутации порядка работ при сохранении зависимостей (↑ → шире поиск, медленнее - сходимость). - * `mutate_resources` — вероятность мутации распределения ресурсов/подрядчиков (↑ → больше параллельности; при - дефиците растёт риск конфликтов). - * Дополнительно: `work_estimator`, `seed`. +- Пробует много разных вариантов и часто находит план с меньшим временем завершения проекта. Работает дольше простых. +- Главные настройки: + - `number_of_generation` — сколько раз улучшать решения (больше — выше шанс улучшить, но дольше). + - `size_of_population` — сколько вариантов держать одновременно (больше — больше идей, но медленнее и больше + памяти). + - `mutate_order` — как часто менять порядок задач (сохраняя зависимости). Больше — шире поиск, но может сходиться + медленнее. + - `mutate_resources` — как часто менять распределение ресурсов/подрядчиков. Больше — выше шанс параллелить, но при + дефиците ресурсов может чаще «конфликтовать». + - Дополнительно: `work_estimator`, `seed` (для воспроизводимости). Пример: @@ -48,8 +48,8 @@ scheduler = GeneticScheduler( ## Многоагентное планирование -Делит граф на блоки, применяет разные стратегии и объединяет результат. Полезно для крупных проектов и гибридных -стратегий (`sampo.scheduler.multi_agency`). +Разбивает проект на части (блоки), планирует их разными способами и собирает общий план. Полезно на больших проектах и +при комбинировании стратегий (`sampo.scheduler.multi_agency`). ### «Аукцион» без разбиения на блоки @@ -66,22 +66,22 @@ from sampo.schemas.resources import Worker ss = SimpleSynthetic(231) wg = ss.work_graph(bottom_border=30, top_border=40) -# 2) Универсальный подрядчик по требуемым видам работников +# 2) Подрядчик с нужными типами работников kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} cid = str(uuid4()) workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} contractors = [Contractor(id=cid, name="Universal", workers=workers, equipments={})] -# 3) Два агента +# 3) Два агента с разными подходами agents = [ Agent("HEFT", HEFTScheduler(), contractors), Agent("Topological", TopologicalScheduler(), contractors), ] manager = StochasticManager(agents) -# 4) Аукцион +# 4) «Аукцион»: агенты предлагают свои планы, выбираем лучший по времени завершения start, end, schedule, winner = manager.run_auction(wg) -print("Победил агент:", winner.name, "Мейкспан:", end - start) +print("Победил агент:", winner.name, "Время завершения:", end - start) ``` ### С разбиением на блоки @@ -94,7 +94,7 @@ from sampo.scheduler.topological import TopologicalScheduler from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager from sampo.scheduler.multi_agency.block_generator import generate_blocks, SyntheticBlockGraphType -# 1) Граф блоков (BlockGraph) +# 1) Строим «граф блоков» (каждый блок — это свой маленький WorkGraph) seed = 231 rand = Random(seed) bg = generate_blocks( @@ -111,30 +111,29 @@ ss = SimpleSynthetic(rand) contractor_a = ss.contractor(40) contractor_b = ss.contractor(40) -# 3) Агенты +# 3) Агенты с разными алгоритмами agents = [ Agent("HEFT", HEFTScheduler(), [contractor_a]), Agent("Topo", TopologicalScheduler(), [contractor_b]), ] manager = StochasticManager(agents) -# 4) Планирование по блокам в топологическом порядке +# 4) Планируем блоки по порядку, учитывая зависимости между ними scheduled_blocks = manager.manage_blocks(bg) -# 5) Итоги +# 5) Итоги: кто какой блок взял и когда он выполнялся print("Scheduled blocks:") for block_id, sblock in scheduled_blocks.items(): print( f"Block {block_id}: agent={sblock.agent.name}, start={sblock.start_time}, end={sblock.end_time}, duration={sblock.duration}") -makespan = max(sb.end_time for sb in scheduled_blocks.values()) -print("Project makespan:", makespan) +project_finish = max(sb.end_time for sb in scheduled_blocks.values()) +print("Время завершения проекта:", project_finish) ``` Коротко: -* **Блок** — самостоятельный подграф (`WorkGraph`) как единица планирования. -* **BlockGraph** — DAG блоков; `A → B` означает старт `B` после завершения `A`. -* Менеджер считает `parent_time = max(окончаний родителей)`; агенты делают офферы; побеждает минимальный `end_time`. - ---- \ No newline at end of file +- Блок — самостоятельная часть проекта (свой небольшой граф работ). +- Граф блоков — связки между блоками: «A → B» значит, что блок B можно начинать после завершения A. +- Менеджер ждёт, пока закончатся все предыдущие блоки, собирает предложения от агентов и выбирает то, где блок + завершится раньше. diff --git a/docs/source/guidebook/advanced/connections.md b/docs/source/guidebook/advanced/connections.md index d87dcb13..f3621bb8 100644 --- a/docs/source/guidebook/advanced/connections.md +++ b/docs/source/guidebook/advanced/connections.md @@ -2,75 +2,63 @@ ## Термины -**Связь (GraphEdge)** — направленное ребро между узлами с типом (`EdgeType`) и лагом. -> Определяет правило допуска старта/финиша зависимой работы. +- Связь — стрелка между задачами, которая говорит, когда можно начинать следующую. +- Пауза (lag) — сколько времени нужно подождать после одной задачи, прежде чем начинать следующую. +- Неразрывная цепочка (IFS) — несколько задач подряд без перерыва, их делает одна и та же бригада. -**Лаг (lag)** — числовая задержка после финиша родителя перед тем, как зависимая работа может стартовать. -> Используется для технологических выдержек (сушка, остывание и т.п.). - -**IFS‑цепочка** — последовательность узлов, связанных типом IFS без пауз и без освобождения команды. -> Выполняется непрерывно одной бригадой. +> Зачем это нужно (IFS): чтобы не начать следующую задачу слишком рано и соблюдать технологические паузы (сушка, остывание и +> т.п.). --- -## Типы связей (EdgeType) - -| Тип | Формат | Семантика (кратко) | Блокирует старт? | -|-----|------------------------|-----------------------------------------------|-------------------------------------| -| FS | Finish→Start | Потомок стартует после финиша родителя (+лаг) | Да | -| FFS | LagFinishStart | То же, акцент на обязательном лаге | Да | -| IFS | InseparableFinishStart | Неразрывная передача выполнения (цепочка) | Да | -| SS | Start→Start | Синхронный старт с родителем | Нет (не родитель в смысле задержки) | -| FF | Finish→Finish | Должен закончиться не раньше связанного узла | Нет (контролируется по финишу) | - -> В вычислении раннего старта участвуют только FS / FFS / IFS (они считаются «жёсткими» зависимостями). - ---- +## Типы связей -## Когда использовать +| Тип | Как читать | Что значит коротко | Задерживает старт следующей? | +|-----|---------------------------|-------------------------------------------------------|------------------------------| +| FS | Конец → Старт | B можно начать после окончания A (+ пауза, если есть) | Да | +| FFS | Конец → Старт (с паузой) | То же, но с обязательной паузой | Да | +| IFS | Неразрывно: Конец → Старт | B идёт сразу за A без перерыва той же бригадой | Да | +| SS | Старт ↔ Старт | A и B могут стартовать вместе | Нет | +| FF | Конец ↔ Конец | B должен закончиться не раньше A | Нет (проверяем по финишу) | -- Обычная последовательность: **FS**. -- Технологическая выдержка: **FFS** (или FS с лагом, если семантика очевидна). -- Неразрывный проход одной командой: **IFS**. -- Одновременный запуск: **SS**. -- Синхронный (или не ранний) общий финиш: **FF**. +Подсказка: время самого раннего старта реально задерживают только FS / FFS / IFS. SS и FF старт не “тормозят”. --- -## Формулировки типов (единообразно) +## Когда какой тип ставить -- **FS** — B стартует после финиша A (можно добавить лаг). -- **FFS** — B стартует после финиша A с обязательной задержкой (лаг критичен). -- **IFS** — B немедленно следует за A теми же ресурсами (без интервала). -- **SS** — B может начинаться одновременно с A. -- **FF** — B должен завершиться не раньше A (контроль на уровне финиша). +- Обычная последовательность действий: FS. +- Нужно выдержать технологическую паузу: FFS (или FS с паузой). +- Нужна непрерывная работа одной бригады без перерыва: IFS. +- Общий одновременный запуск: SS. +- Хотим “сойтись” к одному времени окончания: FF. --- ## Мини‑примеры -1. FS + лаг - A: «Залить бетон» (финиш День 2) → лаг 3 дня → B: «Кладка стен» (старт не раньше День 5). +1) FS + пауза + A: «Залить бетон» (конец День 2) → пауза 3 дня → B: «Кладка стен» (не раньше Дня 5). -2. IFS - A: «Сверление» ⇒ B: «Анкерение» ⇒ C: «Монтаж стойки» (одна бригада без пауз). +2) IFS + A: «Сверление» ⇒ B: «Анкерение» ⇒ C: «Монтаж стойки» (одна бригада, без перерыва). -3. SS +3) SS A: «Пуск серверов» ≡ B: «Старт мониторинга» (одновременно). -4. FF - A: «Основная настройка» || B: «Документация» (должны завершиться к одному дедлайну). +4) FF + A: «Основная настройка» || B: «Документация» (закончить к одному сроку). -5. FFS - A: «Нанести грунт» → лаг 4 часа → B: «Покраска». +5) FFS + A: «Нанести грунт» → пауза 4 часа → B: «Покраска». -> Условные символы выше: → (FS/FFS), ⇒ (IFS), ≡ (SS), || (FF). +Условные значки: → (FS/FFS), ⇒ (IFS), ≡ (SS), || (FF). --- -## Мини CSV (логика экспорта зависимостей) +## Пример файла с зависимостями (CSV) -Формат (условный `predecessors.csv`): +Пусть это файл `predecessors.csv`: ``` child_id,parent_id,type,lag @@ -80,13 +68,8 @@ D,C,IFS,0 E,A,SS,0 ``` -Расшифровка: - -- B после A + 3 (сушка, лаг 3). -- C и D образуют с B неразрывную цепь (IFS). -- E стартует синхронно с A (SS, не задерживает A и не задерживается им). - -> В итоговой таблице проекта сервисные `start` / `finish` не выводятся. - ---- +Что здесь: +- B после A + 3 (пауза 3). +- C и D вместе с B образуют неразрывную цепочку (IFS). +- E стартует вместе с A (SS, не задерживает). diff --git a/docs/source/guidebook/advanced/contractors.md b/docs/source/guidebook/advanced/contractors.md index e54d26ca..1a4bfef8 100644 --- a/docs/source/guidebook/advanced/contractors.md +++ b/docs/source/guidebook/advanced/contractors.md @@ -2,49 +2,61 @@ ## Термины -**WorkerReq** — требование к рабочей силе: профессия (`kind`), диапазон численности (`min_count`…`max_count`), -объём/норма (`volume`). -> Определяет потребность задачи в людях. - -**Worker** — доступный тип рабочей силы: `name` (специализация), `count`, `productivity`, `contractor_id`, опционально -стоимость. -> `Worker.name` сопоставляется с `WorkerReq.kind`. - -**Equipment** — единица или группа техники с типом и количеством. -> Используется задачами аналогично рабочим ресурсам. - -**Contractor** — поставщик ресурсов (словарь работников, техники и пр.). -> Предоставляет набор доступных `Worker` / `Equipment`. +- Требование к людям (WorkerReq) — кого и сколько нужно для задачи: профессия (`kind`), минимум/максимум людей ( + `min_count…max_count`), и объём/норма (`volume`). +- Работник (Worker) — что у нас есть «на складе»: специализация (`name`), количество (`count`), производительность ( + `productivity`), владелец (`contractor_id`), при необходимости — стоимость. + - Важно: `Worker.name` должен совпадать с `WorkerReq.kind` из задач — так система понимает, что это подходящий + ресурс. +- Техника (Equipment) — тоже ресурс, только не люди: тип и количество. Задачи используют её так же, как и людей. +- Подрядчик (Contractor) — поставщик ресурсов. По сути — словарь доступных «людей» и «техники» для планировщика. --- -## Подрядчики (Contractor) +## Как задать подрядчика -### Вручную +### Вариант 1. Вручную ```python from sampo.schemas.contractor import Contractor from sampo.schemas.resources import Worker from sampo.schemas.interval import IntervalGaussian +# Пример: подрядчик с двумя профессиями (водители и слесари) contractor = Contractor( workers={ - 'driver': Worker(id='w1', name='driver', count=8, productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5)), - 'fitter': Worker(id='w2', name='fitter', count=6, productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6)), + # ключ словаря 'driver' совпадает с Worker.name='driver' + 'driver': Worker( + id='w1', + name='driver', # профессия (должна совпадать с WorkerReq.kind) + count=8, # сколько таких людей доступно + productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5) # «средняя скорость» с разбросом + # cost_one_unit=..., # при расчёте стоимости — укажите цену за единицу (опционально) + # contractor_id='c1', # как правило, совпадает с Contractor.id (если задаёте вручную) + ), + 'fitter': Worker( + id='w2', + name='fitter', + count=6, + productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6) + ), }, id='c1', name='Contractor A' ) ``` -> Важно: -> -> * Ключи словаря `workers` должны совпадать с `Worker.name`. -> * `WorkerReq.kind` должен совпадать с `Worker.name`, иначе бригада не подберётся. -> * `contractor_id` у `Worker` берётся из `Contractor.id`. -> * При необходимости задайте `cost_one_unit` явно. +Подсказки: + +- Ключи словаря `workers` должны совпадать с `Worker.name`. +- `WorkerReq.kind` из задач должен совпадать с `Worker.name`, иначе бригада не подберётся. +- `contractor_id` у работников обычно совпадает с `Contractor.id`. +- `cost_one_unit` укажите, если хотите считать деньги, иначе можно опустить. +- `IntervalGaussian(μ, σ, low, high)` — «средняя производительность μ», с разбросом ±σ и ограничениями `low…high`. + +--- -### Генератором по параметру «размер пакета» +### Вариант 2. Быстрый генератор «пакета» ресурсов ```python from sampo.generator.base import SimpleSynthetic @@ -53,14 +65,73 @@ ss = SimpleSynthetic() contractor = ss.contractor(pack_worker_count=10) ``` -### По графу работ (из требований) +Что это даёт: + +- Один «универсальный» подрядчик с типовым набором работников. +- Параметр `pack_worker_count` задаёт «размер пакета» — грубо, среднее количество людей на каждый востребованный тип. + +Когда полезно: + +- Когда нужно быстро запустить примеры/эксперименты без ручного ввода ресурсов. + +--- + +### Вариант 3. Подрядчик «по графу» (из требований задач) ```python from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod -contractor = get_contractor_by_wg(wg, scaler=1.0, method=ContractorGenerationMethod.AVG) +# wg — ваш WorkGraph с задачами и их WorkerReq +contractor = get_contractor_by_wg( + wg, + scaler=1.0, # можно умножить ресурсы (1.5 = +50%) + method=ContractorGenerationMethod.AVG # агрегировать требования «в среднем» +) ``` -Коротко: агрегирует `min/max` из `WorkerReq` по задачам и формирует пул ресурсов. +Как это работает: + +- Смотрит на все `WorkerReq` в проекте. +- Складывает/усредняет потребности по профессиям. +- Собирает подрядчика с подходящим количеством людей/типов. +- `scaler` позволяет быстро «дать больше/меньше» ресурсов, не переписывая всё вручную. + +--- + +--- + +## Ещё пара мини‑примеров + +Два подрядчика раздельно: + +```python +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker + +contractor_a = Contractor( + id='ca', name='Team A', + workers={'welder': Worker(id='wa', name='welder', count=4)} +) + +contractor_b = Contractor( + id='cb', name='Team B', + workers={'driver': Worker(id='wb', name='driver', count=6)} +) + +contractors = [contractor_a, contractor_b] +``` + +Быстро «нарастить» ресурсы у существующего: + +```python +# Было 6 водителей, станет 10 +contractor.workers['driver'].count = 10 +``` + +--- + +Коротко: ---- \ No newline at end of file +- Contractor — это «корзина» ресурсов: кто у нас есть и в каком количестве. +- Главное правило соответствия: `WorkerReq.kind` (в задачах) = `Worker.name` (у подрядчиков). +- Начните с простого: сгенерируйте подрядчика автоматически, а потом подправьте руками узкие места. diff --git a/docs/source/guidebook/advanced/scheduling.md b/docs/source/guidebook/advanced/scheduling.md index 71b162c9..7a9e4501 100644 --- a/docs/source/guidebook/advanced/scheduling.md +++ b/docs/source/guidebook/advanced/scheduling.md @@ -1,30 +1,33 @@ # Планирование -## Что такое Scheduler +## Что такое планировщик (Scheduler) -`Scheduler` — объект, который по графу работ (`WorkGraph`) и ресурсам (список `Contractor`) строит расписание ( -`Schedule`). -Обычно вы просто создаёте конкретный планировщик (`HEFTScheduler`, `GeneticScheduler`, `TopologicalScheduler` и т.д.) и -вызываете `.schedule(...)`. +Планировщик — это объект, который по графу работ (`WorkGraph`) и ресурсам (список `Contractor`) строит итоговый план ( +`Schedule`). -| Параметр | Назначение | Значение по умолчанию | Меняем когда | -|----------------------|-----------------------------------------------------------------------|-----------------------------|----------------------------------------------------------------------| -| `scheduler_type` | Метка семейства алгоритма (для внутренней логики и логов). | Задаётся самим наследником. | Пишете свой класс планировщика. | -| `resource_optimizer` | Стратегия подбора/урезания численности команды (баланс время / люди). | Координатный спуск. | Нужна другая эвристика (усреднение, полный поиск, ничего не делать). | -| `work_estimator` | Как считать длительность работ (объём → время). | `DefaultWorkEstimator()` | Нужна доменная или стохастическая модель. | +Обычно вы просто создаёте нужный планировщик (например, `HEFTScheduler`, `GeneticScheduler`, `TopologicalScheduler`) и +вызываете `.schedule(wg, contractors)`. -### Минимальный паттерн +Импорты (часто используемые): ```python -# У вас есть: wg (WorkGraph), contractors (list[Contractor]) -from sampo.scheduler.heft.base import HEFTScheduler +from sampo.scheduler.heft import HEFTScheduler, HEFTBetweenScheduler +from sampo.scheduler.topological import TopologicalScheduler +from sampo.scheduler.genetic import GeneticScheduler +``` + +Мини‑пример: + +```python +# есть: wg (WorkGraph), contractors (list[Contractor]) +from sampo.scheduler.heft import HEFTScheduler scheduler = HEFTScheduler() schedule = scheduler.schedule(wg, contractors)[0] -print(schedule.execution_time) +print("Время проекта:", schedule.execution_time) ``` -Через пайплайн: +Через «конвейер» (pipeline): ```python from sampo.pipeline import SchedulingPipeline @@ -33,91 +36,33 @@ from sampo.scheduler import GeneticScheduler result = (SchedulingPipeline.create() .wg('input.csv', sep=';') .schedule(GeneticScheduler(number_of_generation=10)) - .finish() - )[0] + .finish())[0] -print(result.schedule.execution_time) +print("Время проекта:", result.schedule.execution_time) ``` -### Кастомизация (подсказка) - -| Цель | Действие | -|-----------------------------|--------------------------------------------------| -| Своя формула времени | Передать свой `work_estimator`. | -| Эксперименты с численностью | Передать иной `resource_optimizer`. | -| Новая стратегия | Создать наследник + установить `scheduler_type`. | - -### Самая короткая формулировка - -Scheduler = (Граф работ + Ресурсы) → Schedule. - ---- - -## Что такое Timeline (коротко) - -`Timeline` — внутренняя структура учёта занятости ресурсов по времени. -Планировщик спрашивает её: «Когда можно стартовать эту работу с таким набором ресурсов?» — и фиксирует занятые интервалы -после назначения. - -> В обычном использовании вы напрямую с `Timeline` не взаимодействуете. - -### Что хранит концептуально - -- Интервалы занятости по видам или конкретным группам ресурсов. -- Логику поиска минимального допустимого старта с учётом: - - зависимостей (время доступно не раньше родителя + лаг), - - занятости уже назначенных работ, - - возможных доп. ограничений (например, зоны / ландшафт). +### Настройки планировщика -### Когда имеет смысл лезть внутрь - -- Пишете свою стратегию планирования или визуализатор занятости. -- Отлаживаете конфликт ресурса (почему старт сдвинулся). - -### Самая короткая формулировка - -Timeline = «Календарь занятости ресурсов + функция earliest feasible start». - ---- - -## Что такое ScheduledWork (коротко) - -`ScheduledWork` — конкретное запланированное выполнение одной работы: - -- ссылка на исходный `GraphNode` / `WorkUnit`, -- время старта и окончания, -- назначенные ресурсы (workers, equipment, материалы), -- дополнительные атрибуты (зона, производительность и т.п. если применимо). - -> Это «живая» инстанциация вершины графа внутри результата. - -### Использование - -Обычно получаете список/слой через объекты `Schedule` (например, DataFrame или итерацию по структурам), а не создаёте -вручную. - -### Самая короткая формулировка - -ScheduledWork = WorkUnit + (start, finish, назначенные ресурсы). +- Как считать длительности работ: передайте свой `work_estimator` (например, если у вас особая формула времени). +- Как подбирать размер бригад/ресурсов: передайте другой `resource_optimizer` (например, пробовать больше/меньше людей). +- Свой алгоритм: сделайте класс‑наследник и задайте `scheduler_type` для внутренней идентификации. --- -## Что такое Schedule (коротко) - -`Schedule` — агрегат всех `ScheduledWork`. Содержит: +## Что такое Schedule -- `execution_time` (makespan — от первого старта до последнего финиша), -- производные представления (таблицы, диаграммы), -- методы экспорта/фильтрации/слияния сервисных задач, -- (опционально) метаданные о порядке, ресурсной статистике, критическом пути. +`Schedule` — это весь итоговый план: -> Конечный продукт планировщика. +- `execution_time` — длительность проекта (от первого старта до последнего финиша), +- готовые представления (таблицы, диаграммы), +- экспорт/фильтрация, служебные операции, +- (опционально) порядок выполнения, статистика по ресурсам, «критический путь». -### Пример получения длительности и таблицы +Пример: ```python schedule = scheduler.schedule(wg, contractors)[0] -print(schedule.execution_time) +print("Время проекта:", schedule.execution_time) df = schedule.merged_stages_datetime_df(start_date="2025-01-01") print(df.head()) diff --git a/docs/source/guidebook/advanced/work_graph.md b/docs/source/guidebook/advanced/work_graph.md index 4109d784..0b72ab55 100644 --- a/docs/source/guidebook/advanced/work_graph.md +++ b/docs/source/guidebook/advanced/work_graph.md @@ -2,44 +2,39 @@ ## Термины -**WorkGraph** — ориентированный ациклический граф (DAG) проекта с двумя служебными вершинами `start` / `finish`; при -сборке из набора узлов сервисные вершины добавляются автоматически. - -> Фиксирует задачи и их зависимости (узлы — `GraphNode`, рёбра — связи с типом и лагом). Циклы запрещены. - - -**GraphNode** — вершина графа, содержащая `WorkUnit` и ссылки на родительские/дочерние узлы (каждая связь имеет тип и -лаг). - -> Контейнер задачи и её входящих/исходящих зависимостей. - -**WorkUnit** — задача проекта с объёмом и набором требований к ресурсам. +- WorkGraph — это «карта проекта» в виде графа без циклов. Есть две служебные точки: начало и конец (добавляются сами). +- GraphNode — узел графа: внутри лежит задача (`WorkUnit`) и ссылки на связи с родителями/детьми (у связи есть тип и + пауза). +- WorkUnit — описание самой работы: объём и требования к ресурсам (какие люди/сколько и т.п.). --- ## Как собрать WorkGraph -### Способ A. Сгенерировать синтетический граф +### А) Сгенерировать автоматически (быстрый старт) ```python from sampo.generator.base import SimpleSynthetic from sampo.generator.pipeline import SyntheticGraphType ss = SimpleSynthetic() -wg = ss.work_graph(mode=SyntheticGraphType.GENERAL, - cluster_counts=10, - bottom_border=100, - top_border=200) +wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, # тип структуры: General / Parallel / Sequential + cluster_counts=10, # сколько «кластеров» (логических групп работ) + bottom_border=100, # нижняя граница количества работ + top_border=200 # верхняя граница количества работ +) ``` -> mode: 'general' or 'sequence' or 'parallel - the type of the returned graph -> cluster_counts: Number of clusters for the graph -> bottom_border: bottom border for number of works for the graph -> top_border: top border for number of works for the graph +Коротко про параметры: + +- `mode` — форма графа (общий, параллельный, последовательный). +- `cluster_counts` — сколько групп работ в графе. +- `bottom_border` / `top_border` — примерно сколько работ генерировать. --- -### Способ B. Загрузить из CSV +### Б) Загрузить из CSV ```python from sampo.pipeline import SchedulingPipeline @@ -48,29 +43,62 @@ from sampo.scheduler.heft import HEFTScheduler project = (SchedulingPipeline.create() .wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) -.lag_optimize(LagOptimizationStrategy.TRUE) +.lag_optimize(LagOptimizationStrategy.TRUE) # можно AUTO/NONE/TRUE .schedule(HEFTScheduler()) .finish()[0]) -``` - -> all_connections: Пытаться достроить или дополнить связи между работами, если их не хватает во входных данных. -> sep: Разделитель столбцов CSV -> LagOptimizationStrategy: Shows should graph be lag-optimized or not. -If not defined, pipeline should search the best variant of this argument in result. - -**Структура CSV** -* Обязательные колонки: `activity_id, activity_name, granular_name, volume, measurement, priority`. -* Зависимости (списки через запятую, длины совпадают): `predecessor_ids, connection_types, lags`. -* Типы: `FS`, `SS`, `FF`, `IFS`, `FFS`; лаги — числа (обычно `0`). -* Опционально (требования в словарях-ячейках): `min_req`, `max_req`, `req_volume` вида `{"worker_kind": value}`. -* Примечания: - - * `start/finish` в CSV не нужны — добавляются автоматически. - * В `.wg(path, sep=';')` `sep` — разделитель файла; внутри списков — запятая. - * Ошибка `Parameter 'id' unfilled/name unfilled` часто означает пустые `activity_id`/`activity_name`. +wg = project.wg # готовый WorkGraph +schedule = project.schedule +``` -**Мини-пример (B зависит от A по FS, лаг 0)** +Пояснения: + +- `all_connections=True` — попытаться «достроить» связи, если во входных данных чего‑то не хватает. +- `sep=';'` — разделитель колонок в CSV. +- `LagOptimizationStrategy` — как обращаться с технологическими паузами (можно доверить выбор «AUTO»). + +## Структура CSV + +- Обязательные колонки: `activity_id, activity_name, granular_name, volume, measurement, priority` + - `activity_id` — уникальный идентификатор задачи (строка/число). Должен быть уникальным во всём файле. + - `activity_name` — понятное человеку название задачи (строка). + - `granular_name` — короткий код/метка задачи (строка). Если не используете — можно дублировать `activity_id`/ + `activity_name`. + - `volume` — объём работы (число). Используется оценщиком времени; смысл объёма зависит от вашей предметной области. + - `measurement` — единица измерения объёма (строка), например: `unit`, `m3`, `m`, `pcs`. + - `priority` — целое число для приоритизации. Если нет особого порядка — ставьте `0` для всех. + +- Зависимости (списки через запятую, длины совпадают): `predecessor_ids, connection_types, lags` + - Это три «синхронных» списка. Элементы с одинаковым индексом относятся к одной и той же связи. + - Пример: + `predecessor_ids="A,B"`, `connection_types="FS,SS"`, `lags="0,3"` + означает две связи: A —(FS, 0)→ текущая задача и B —(SS, 3)→ текущая задача. + - Пустая ячейка в `predecessor_ids` означает «нет предшественников». + - Допустимые типы связей перечислены ниже. Паузы (`lags`) — числа (обычно `0`). + - Важно: длина всех трёх списков должна совпадать, порядок элементов — согласованный. + +- Типы связей: `FS`, `SS`, `FF`, `IFS`, `FFS` (паузы — числа, чаще `0`) + - `FS` (Finish→Start) — старт после финиша предшественника (+ пауза). + - `FFS` (LagFinishStart) — то же, но с обязательной паузой (логически равно `FS` с критичной паузой). + - `IFS` (InseparableFinishStart) — неразрывная цепочка: следующая задача сразу после предыдущей той же бригадой. + - `SS` (Start→Start) — задачи могут стартовать вместе (не задерживает старт из‑за предшественника). + - `FF` (Finish→Finish) — финиш не раньше предшественника (контролируется по времени окончания). + - Подсказка: реальное «задерживание старта» делают `FS/FFS/IFS`. `SS/FF` старт напрямую не задерживают. + +- По ресурсам (опционально, словари в ячейках): `min_req`, `max_req`, `req_volume` вида `{"worker_kind": value}` + - Формат — JSON‑подобный словарь в строке. Ключ — имя профессии/ресурса, значение — число. + - `min_req` — минимально требуемое количество данного ресурса (например, людей нужной профессии). + - `max_req` — максимально допустимое количество (верхняя граница бригады). + - `req_volume` — «норма/объём» по данному виду ресурса (как правило, влияет на расчёт времени через оценщик). + - Примеры значений ячейки: + - `{"welder": 2, "driver": 1}` + - `{"operator": 3}` + - Если колонки отсутствуют или ячейка пуста — считается, что ограничений по этому виду нет (или берутся значения по + умолчанию). + - Имена ключей должны совпадать с `Worker.name` у ваших подрядчиков. Иначе планировщик не сопоставит требуемые и + доступные ресурсы. + +Мини‑пример зависимостей (B после A по FS, пауза 0): ``` activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags @@ -78,15 +106,13 @@ A;Task A;A;1.0;unit;0;;; B;Task B;B;1.0;unit;0;A;FS;0 ``` -> Полный пример: `tests/parser/test_wg.csv`. - --- -### Способ C. Программно из узлов +### В) Собрать программно из узлов -* Создайте `WorkUnit` для каждой задачи (с `WorkerReq` при необходимости). -* Зависимости: `GraphNode(work_unit, parents)`, где `parents` — список кортежей `(parent_node, lag, EdgeType)`. -* Сборка: `WorkGraph.from_nodes([...])` — `start/finish` добавятся автоматически. +- Сначала создайте `WorkUnit` для каждой работы (при необходимости укажите `WorkerReq`). +- Связи задаются при создании `GraphNode`: список родителей в виде кортежей `(parent_node, lag, EdgeType)`. +- Сборка: `WorkGraph.from_nodes([...])` — служебные `start/finish` добавятся сами. ```python from sampo.schemas.works import WorkUnit @@ -94,10 +120,12 @@ from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType from sampo.schemas.requirements import WorkerReq from sampo.schemas.time import Time -# A -> B -> C (FS, лаг 0) -wu_a = WorkUnit(id='A', name='Task A', - worker_reqs=[WorkerReq(kind='general', volume=Time(10), min_count=2, max_count=4)], - volume=1.0, is_service_unit=False) +# A -> B -> C (FS, паузы 0) +wu_a = WorkUnit( + id='A', name='Task A', + worker_reqs=[WorkerReq(kind='general', volume=Time(10), min_count=2, max_count=4)], + volume=1.0, is_service_unit=False +) wu_b = WorkUnit(id='B', name='Task B', worker_reqs=[], volume=1.0, is_service_unit=False) wu_c = WorkUnit(id='C', name='Task C', worker_reqs=[], volume=1.0, is_service_unit=False) @@ -108,11 +136,25 @@ n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS wg = WorkGraph.from_nodes([n_a, n_b, n_c]) ``` -Коротко по зависимостям: +Коротко про зависимости: -* Типы: `FinishStart (FS)`, `StartStart (SS)`, `FinishFinish (FF)`, `InseparableFinishStart (IFS)`, +- Типы: `FinishStart (FS)`, `StartStart (SS)`, `FinishFinish (FF)`, `InseparableFinishStart (IFS)`, `LagFinishStart (FFS)`. -* Лаг — второй элемент кортежа в `parents`. -* Обязательные поля `WorkUnit`: `id`, `name`. Для экспорта также задавайте `volume`, `measurement`, `priority`. +- Пауза (lag) — второй элемент кортежа в `parents`. +- Обязательные поля у `WorkUnit`: `id`, `name`. Для экспорта удобно также задавать `volume`, `measurement`, `priority`. + +--- + +## Частые вопросы + +- «Пишет, что есть цикл» — проверьте, нет ли обратных стрелок (граф должен быть без циклов). +- «Ругается на пустой id/name» — проверьте CSV: у всех строк должны быть `activity_id` и `activity_name`. +- «Связи не применились» — убедитесь, что списки `predecessor_ids`, `connection_types`, `lags` одинаковой длины и + разделены запятыми. +- «Неправильный тип связи» — разрешены только `FS`, `SS`, `FF`, `IFS`, `FFS`. + +Коротко: ---- \ No newline at end of file +- WorkGraph — это «скелет» проекта: работы и их связи. +- Сделать граф можно тремя путями: сгенерировать, загрузить из CSV, собрать кодом. +- Служебные `start/finish` добавляются автоматически — вы задаёте только свои работы и зависимости между ними. \ No newline at end of file diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md index 6314efac..fc3ba32b 100644 --- a/docs/source/guidebook/intro.md +++ b/docs/source/guidebook/intro.md @@ -61,6 +61,15 @@ --- +## Публичное API + +- `sampo.generator` — генераторы синтетических графов, окружения и вспомогательные функции (включая + `SimpleSynthetic` и `get_contractor_by_wg`). +- `sampo.schemas` — базовые структуры данных: графы работ, контракты, ресурсы, расписания, спецификации. +- `sampo.scheduler` — планировщики и связанные компоненты (эвристические, генетические, мультиагентные и т.д.). +- `sampo.pipeline` — декларативный `SchedulingPipeline` для запуска всех шагов планирования. +- `sampo.utilities.visualization` — утилиты визуализации (графики Ганта, диаграммы загрузки ресурсов и т.д.). + ## Реализованные алгоритмы и подходы * **Эвристики** From 8554aa377b57cf2774a7c5de890b56ef5b88fd82 Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 1 Oct 2025 10:24:14 +0300 Subject: [PATCH 18/32] Update work_graph.md --- docs/source/guidebook/advanced/work_graph.md | 29 ++------------------ 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/docs/source/guidebook/advanced/work_graph.md b/docs/source/guidebook/advanced/work_graph.md index 0b72ab55..1f7c2f26 100644 --- a/docs/source/guidebook/advanced/work_graph.md +++ b/docs/source/guidebook/advanced/work_graph.md @@ -77,13 +77,7 @@ schedule = project.schedule - Допустимые типы связей перечислены ниже. Паузы (`lags`) — числа (обычно `0`). - Важно: длина всех трёх списков должна совпадать, порядок элементов — согласованный. -- Типы связей: `FS`, `SS`, `FF`, `IFS`, `FFS` (паузы — числа, чаще `0`) - - `FS` (Finish→Start) — старт после финиша предшественника (+ пауза). - - `FFS` (LagFinishStart) — то же, но с обязательной паузой (логически равно `FS` с критичной паузой). - - `IFS` (InseparableFinishStart) — неразрывная цепочка: следующая задача сразу после предыдущей той же бригадой. - - `SS` (Start→Start) — задачи могут стартовать вместе (не задерживает старт из‑за предшественника). - - `FF` (Finish→Finish) — финиш не раньше предшественника (контролируется по времени окончания). - - Подсказка: реальное «задерживание старта» делают `FS/FFS/IFS`. `SS/FF` старт напрямую не задерживают. +- Про типы связи можно прочитать в отдельной главе. - По ресурсам (опционально, словари в ячейках): `min_req`, `max_req`, `req_volume` вида `{"worker_kind": value}` - Формат — JSON‑подобный словарь в строке. Ключ — имя профессии/ресурса, значение — число. @@ -136,25 +130,6 @@ n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS wg = WorkGraph.from_nodes([n_a, n_b, n_c]) ``` -Коротко про зависимости: - -- Типы: `FinishStart (FS)`, `StartStart (SS)`, `FinishFinish (FF)`, `InseparableFinishStart (IFS)`, - `LagFinishStart (FFS)`. -- Пауза (lag) — второй элемент кортежа в `parents`. - Обязательные поля у `WorkUnit`: `id`, `name`. Для экспорта удобно также задавать `volume`, `measurement`, `priority`. ---- - -## Частые вопросы - -- «Пишет, что есть цикл» — проверьте, нет ли обратных стрелок (граф должен быть без циклов). -- «Ругается на пустой id/name» — проверьте CSV: у всех строк должны быть `activity_id` и `activity_name`. -- «Связи не применились» — убедитесь, что списки `predecessor_ids`, `connection_types`, `lags` одинаковой длины и - разделены запятыми. -- «Неправильный тип связи» — разрешены только `FS`, `SS`, `FF`, `IFS`, `FFS`. - -Коротко: - -- WorkGraph — это «скелет» проекта: работы и их связи. -- Сделать граф можно тремя путями: сгенерировать, загрузить из CSV, собрать кодом. -- Служебные `start/finish` добавляются автоматически — вы задаёте только свои работы и зависимости между ними. \ No newline at end of file +--- \ No newline at end of file From 4008ec47259c486edbbe018717593015e1478d31 Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 1 Oct 2025 21:25:43 +0300 Subject: [PATCH 19/32] Simplification intro.md --- docs/source/guidebook/intro.md | 97 +++++------------------------ docs/source/guidebook/quickstart.md | 4 +- 2 files changed, 16 insertions(+), 85 deletions(-) diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md index fc3ba32b..e2c538f7 100644 --- a/docs/source/guidebook/intro.md +++ b/docs/source/guidebook/intro.md @@ -9,95 +9,26 @@ ## Что такое SAMPO? -**SAMPO** (Scheduler for Adaptive Manufacturing Processes Optimization) — это открытый **фреймворк адаптивного -календарного планирования**. - -> Адаптивное календарное планирование — построение расписания с учётом технологических, ресурсных и логистических -> ограничений и его целевая переоптимизация при изменениях входных данных или целей (например, дедлайнов). -> Переоптимизация выполняется по событию/запуску алгоритмов за счёт тёплого старта (кеширования), -> частичной фиксации уже принятых решений и специализированных процедур (например, бинпоиск ресурсоёмкости под дедлайн). - -Цель — построение расписания, наилучшего по выбранной **метрике оптимизации** (например, минимизация длительности или -стоимости). - -В SAMPO реализованы **эвристики** и **генетические алгоритмы**, а также предусмотрен модуль **многоагентного -моделирования** для комбинирования стратегий — это помогает решать сложные сценарии, где статические или чисто жадные -подходы недостаточны. - -## Для кого SAMPO? - -Если у вас проект с множеством зависимостей и ограничений по ресурсам, и вы хотите автоматизировать поиск хорошего плана -исполнения — SAMPO для вас. +SAMPO — фреймворк для автоматизированной оптимизации планирования бизнес-процессов. Он позволяет эффективно использовать +наработанный корпоративный опыт на ретроспективных данных для автоматизированного построения план-графиков выполнения +бизнес-процессов, оптимальных по требуемым целевым показателям. ## Ключевые возможности и преимущества -- Упорядочивает задачи и назначает ресурсы с учетом ограничений - - > Под «ограничениями» понимаются: предшествования задач, минимальные/максимальные потребности по видам ресурсов... - Порядок работ задаётся эвристиками (например, Topological, HEFT), а состав бригад подбирается - оптимизаторами ресурсов с учётом доступности. +В основе SAMPO лежат мета-эвристические, генетические и нейросетевые алгоритмы, которые позволяют анализировать +ретроспективные производственные данные и решать задачу мультикритериальной оптимизации планирования с учетом +накопленного опыта. Особенностью фреймворка является способность эффективно решать задачу планирования на больших +временных горизонтах, в условиях неопределенности и неполноты ретроспективных данных. -- Оптимизирует по выбранной метрике (время, ресурсы, стоимость, мультикритерий) +За счет своей модульной структуры SAMPO допускает интеграцию алгоритмов и моделей, учитывающих специфику определенной +предметной области, поэтому фреймворк может быть применим для решения промышленных задач из различных отраслей: +добывающей и обрабатывающей промышленности, строительства, транспорта и энергетики. -- Адаптивно перепланирует под дедлайн +## Открытость и документация - > При заданном сроке выполняется целевая переоптимизация: «обёртка» с двоичным поиском подбирает уровень - ресурсообеспечения (параметр k), многократно вызывая базовый планировщик, чтобы уложиться в дедлайн; в генетическом - режиме используется двухфазная схема — сначала добиваются соблюдения времени, затем оптимизируют ресурсы/стоимость - среди планов, укладывающихся в срок. +Лицензия **BSD 3-Clause**, подробная документация, примеры в **Jupyter Notebook**. -- Ускорение перепланирования - - > Чтобы не начинать «с нуля», используется тёплый старт (кеширование и повторное использование подготовленных - структур), мультипроцессная оценка популяции в генетике и частичная фиксация уже принятых назначений ( - переоптимизируются только свободные степени свободы). Это ускоряет реакцию на изменения и уменьшает нежелательные - перестановки в расписании. - -- **Открытость и документация** - — лицензия **BSD 3-Clause**, подробная документация, примеры в **Jupyter Notebook**. - - > BSD 3-Clause — свободная лицензия, разрешающая использовать, изменять и распространять код (в том числе в - коммерческих проектах) при сохранении уведомления об авторских правах и отказа от ответственности. +> BSD 3-Clause — свободная лицензия, разрешающая использовать, изменять и распространять код (в том числе в +коммерческих проектах) при сохранении уведомления об авторских правах и отказа от ответственности. --- - -## Публичное API - -- `sampo.generator` — генераторы синтетических графов, окружения и вспомогательные функции (включая - `SimpleSynthetic` и `get_contractor_by_wg`). -- `sampo.schemas` — базовые структуры данных: графы работ, контракты, ресурсы, расписания, спецификации. -- `sampo.scheduler` — планировщики и связанные компоненты (эвристические, генетические, мультиагентные и т.д.). -- `sampo.pipeline` — декларативный `SchedulingPipeline` для запуска всех шагов планирования. -- `sampo.utilities.visualization` — утилиты визуализации (графики Ганта, диаграммы загрузки ресурсов и т.д.). - -## Реализованные алгоритмы и подходы - -* **Эвристики** - - > Эвристики — упрощённые правила «разумного выбора», дающие быстрое приближённое решение. - - - *Topological* — алгоритм, который строит порядок выполнения задач на основе их зависимостей. Использует - топологическую сортировку графа работ (DAG), то есть размещает каждую задачу только после всех её - предшественников. - - - *HEFT/HEFTBetween* — жадные эвристики *Heterogeneous Earliest Finish Time*, стремящиеся минимизировать время - завершения с учётом разнородных ресурсов. - - > HEFT/HEFTBetween — метод планирования, который идёт по задачам и назначает каждую туда, где она закончится быстрее - всего, учитывая зависимости и производительность ресурсов. - -* **Генетические алгоритмы** - - > Генетические алгоритмы — оптимизация по принципам эволюции: популяция решений подвергается «скрещиванию» и - «мутациям» для улучшения качества. - - - *Genetic* — алгоритм с эвристической инициализацией, кроссовером и мутациями; поддерживает разные функции - пригодности (время, ресурсы, стоимость), мультикритерий и режим «с дедлайном». - - > Позволяет находить более качественные планы, чем простые жадные методы, так как исследует разные комбинации решений - и отбирает лучшие. - -* **Многоагентные методы** - - > Подходы, в которых разные «агенты»-планировщики решают подзадачи и координируются между собой — для комбинирования - сильных сторон алгоритмов в больших проектах (модуль предусмотрен для интеграции). diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md index e43e9b88..7a3ac002 100644 --- a/docs/source/guidebook/quickstart.md +++ b/docs/source/guidebook/quickstart.md @@ -4,13 +4,13 @@ ## Установка -SAMPO доступен как пакет Python на PyPI. Установка: +SAMPO доступен в виде пакета для языка Python. Установка: ```bash pip install sampo ``` -Убедитесь, что используете Python 3.10 (SAMPO требует 3.10.x). +SAMPO требует версию Python 3.10.x ## Первый план за несколько шагов From 8df1d30c57f801d140ab3127d718ea0f37e7ea5f Mon Sep 17 00:00:00 2001 From: NickMur Date: Thu, 2 Oct 2025 16:29:12 +0300 Subject: [PATCH 20/32] error correction --- docs/source/guidebook/advanced/algorithms.md | 2 + docs/source/guidebook/advanced/connections.md | 20 +-- docs/source/guidebook/advanced/contractors.md | 33 ++-- docs/source/guidebook/advanced/index.md | 1 - docs/source/guidebook/advanced/scheduling.md | 71 -------- docs/source/guidebook/advanced/work_graph.md | 159 +++++++++++------- docs/source/guidebook/quickstart.md | 85 +++++++--- 7 files changed, 176 insertions(+), 195 deletions(-) delete mode 100644 docs/source/guidebook/advanced/scheduling.md diff --git a/docs/source/guidebook/advanced/algorithms.md b/docs/source/guidebook/advanced/algorithms.md index 63b96116..77d79e10 100644 --- a/docs/source/guidebook/advanced/algorithms.md +++ b/docs/source/guidebook/advanced/algorithms.md @@ -7,6 +7,8 @@ - Быстро дают рабочий план. - HEFT/HEFTBetween рассчитывают порядок задач и примерное время выполнения, чтобы закончить быстрее. - Topological просто выстраивает задачи по зависимостям, без сложной оптимизации. +- HEFT строит план с нуля по всему графу, а HEFTBetween вставляет новые работы в уже занятый календарь, стараясь не + перекраивать весь ранее построенный план. Импорты: diff --git a/docs/source/guidebook/advanced/connections.md b/docs/source/guidebook/advanced/connections.md index f3621bb8..f5933ff6 100644 --- a/docs/source/guidebook/advanced/connections.md +++ b/docs/source/guidebook/advanced/connections.md @@ -6,22 +6,20 @@ - Пауза (lag) — сколько времени нужно подождать после одной задачи, прежде чем начинать следующую. - Неразрывная цепочка (IFS) — несколько задач подряд без перерыва, их делает одна и та же бригада. -> Зачем это нужно (IFS): чтобы не начать следующую задачу слишком рано и соблюдать технологические паузы (сушка, остывание и -> т.п.). +> Зачем это нужно (IFS): чтобы не начать следующую задачу слишком рано и соблюдать технологические паузы (сушка, +> остывание и т.п.). --- ## Типы связей -| Тип | Как читать | Что значит коротко | Задерживает старт следующей? | -|-----|---------------------------|-------------------------------------------------------|------------------------------| -| FS | Конец → Старт | B можно начать после окончания A (+ пауза, если есть) | Да | -| FFS | Конец → Старт (с паузой) | То же, но с обязательной паузой | Да | -| IFS | Неразрывно: Конец → Старт | B идёт сразу за A без перерыва той же бригадой | Да | -| SS | Старт ↔ Старт | A и B могут стартовать вместе | Нет | -| FF | Конец ↔ Конец | B должен закончиться не раньше A | Нет (проверяем по финишу) | - -Подсказка: время самого раннего старта реально задерживают только FS / FFS / IFS. SS и FF старт не “тормозят”. +| Тип | Как читать | Что значит коротко | +|-----|---------------------------|-------------------------------------------------------| +| FS | Конец → Старт | B можно начать после окончания A (+ пауза, если есть) | +| FFS | Конец → Старт (с паузой) | То же, но с обязательной паузой | +| IFS | Неразрывно: Конец → Старт | B идёт сразу за A без перерыва той же бригадой | +| SS | Старт ↔ Старт | A и B могут стартовать вместе | +| FF | Конец ↔ Конец | B должен закончиться не раньше A | --- diff --git a/docs/source/guidebook/advanced/contractors.md b/docs/source/guidebook/advanced/contractors.md index 1a4bfef8..afedeeda 100644 --- a/docs/source/guidebook/advanced/contractors.md +++ b/docs/source/guidebook/advanced/contractors.md @@ -4,14 +4,19 @@ - Требование к людям (WorkerReq) — кого и сколько нужно для задачи: профессия (`kind`), минимум/максимум людей ( `min_count…max_count`), и объём/норма (`volume`). -- Работник (Worker) — что у нас есть «на складе»: специализация (`name`), количество (`count`), производительность ( - `productivity`), владелец (`contractor_id`), при необходимости — стоимость. - - Важно: `Worker.name` должен совпадать с `WorkerReq.kind` из задач — так система понимает, что это подходящий - ресурс. +- Работник (Worker) — кадровые ресурсы: специализация (`name`), количество (`count`), производительность ( + `productivity`), владелец (`contractor_id`), при необходимости — стоимость (`cost_one_unit`). - Техника (Equipment) — тоже ресурс, только не люди: тип и количество. Задачи используют её так же, как и людей. - Подрядчик (Contractor) — поставщик ресурсов. По сути — словарь доступных «людей» и «техники» для планировщика. --- +## Варианты проффесий: +- 'driver' +- 'fitter' +- 'handyman' +- 'electrician' +- 'manager' +- 'engineer' ## Как задать подрядчика @@ -51,7 +56,6 @@ contractor = Contractor( - Ключи словаря `workers` должны совпадать с `Worker.name`. - `WorkerReq.kind` из задач должен совпадать с `Worker.name`, иначе бригада не подберётся. - `contractor_id` у работников обычно совпадает с `Contractor.id`. -- `cost_one_unit` укажите, если хотите считать деньги, иначе можно опустить. - `IntervalGaussian(μ, σ, low, high)` — «средняя производительность μ», с разбросом ±σ и ограничениями `low…high`. --- @@ -65,15 +69,6 @@ ss = SimpleSynthetic() contractor = ss.contractor(pack_worker_count=10) ``` -Что это даёт: - -- Один «универсальный» подрядчик с типовым набором работников. -- Параметр `pack_worker_count` задаёт «размер пакета» — грубо, среднее количество людей на каждый востребованный тип. - -Когда полезно: - -- Когда нужно быстро запустить примеры/эксперименты без ручного ввода ресурсов. - --- ### Вариант 3. Подрядчик «по графу» (из требований задач) @@ -98,8 +93,6 @@ contractor = get_contractor_by_wg( --- ---- - ## Ещё пара мини‑примеров Два подрядчика раздельно: @@ -128,10 +121,4 @@ contractors = [contractor_a, contractor_b] contractor.workers['driver'].count = 10 ``` ---- - -Коротко: - -- Contractor — это «корзина» ресурсов: кто у нас есть и в каком количестве. -- Главное правило соответствия: `WorkerReq.kind` (в задачах) = `Worker.name` (у подрядчиков). -- Начните с простого: сгенерируйте подрядчика автоматически, а потом подправьте руками узкие места. +--- \ No newline at end of file diff --git a/docs/source/guidebook/advanced/index.md b/docs/source/guidebook/advanced/index.md index 3af88365..715e5d57 100644 --- a/docs/source/guidebook/advanced/index.md +++ b/docs/source/guidebook/advanced/index.md @@ -8,7 +8,6 @@ caption: Основное руководство SAMPO work_graph connections contractors -scheduling algorithms ``` diff --git a/docs/source/guidebook/advanced/scheduling.md b/docs/source/guidebook/advanced/scheduling.md deleted file mode 100644 index 7a9e4501..00000000 --- a/docs/source/guidebook/advanced/scheduling.md +++ /dev/null @@ -1,71 +0,0 @@ -# Планирование - -## Что такое планировщик (Scheduler) - -Планировщик — это объект, который по графу работ (`WorkGraph`) и ресурсам (список `Contractor`) строит итоговый план ( -`Schedule`). - -Обычно вы просто создаёте нужный планировщик (например, `HEFTScheduler`, `GeneticScheduler`, `TopologicalScheduler`) и -вызываете `.schedule(wg, contractors)`. - -Импорты (часто используемые): - -```python -from sampo.scheduler.heft import HEFTScheduler, HEFTBetweenScheduler -from sampo.scheduler.topological import TopologicalScheduler -from sampo.scheduler.genetic import GeneticScheduler -``` - -Мини‑пример: - -```python -# есть: wg (WorkGraph), contractors (list[Contractor]) -from sampo.scheduler.heft import HEFTScheduler - -scheduler = HEFTScheduler() -schedule = scheduler.schedule(wg, contractors)[0] -print("Время проекта:", schedule.execution_time) -``` - -Через «конвейер» (pipeline): - -```python -from sampo.pipeline import SchedulingPipeline -from sampo.scheduler import GeneticScheduler - -result = (SchedulingPipeline.create() - .wg('input.csv', sep=';') - .schedule(GeneticScheduler(number_of_generation=10)) - .finish())[0] - -print("Время проекта:", result.schedule.execution_time) -``` - -### Настройки планировщика - -- Как считать длительности работ: передайте свой `work_estimator` (например, если у вас особая формула времени). -- Как подбирать размер бригад/ресурсов: передайте другой `resource_optimizer` (например, пробовать больше/меньше людей). -- Свой алгоритм: сделайте класс‑наследник и задайте `scheduler_type` для внутренней идентификации. - ---- - -## Что такое Schedule - -`Schedule` — это весь итоговый план: - -- `execution_time` — длительность проекта (от первого старта до последнего финиша), -- готовые представления (таблицы, диаграммы), -- экспорт/фильтрация, служебные операции, -- (опционально) порядок выполнения, статистика по ресурсам, «критический путь». - -Пример: - -```python -schedule = scheduler.schedule(wg, contractors)[0] -print("Время проекта:", schedule.execution_time) - -df = schedule.merged_stages_datetime_df(start_date="2025-01-01") -print(df.head()) -``` - ---- \ No newline at end of file diff --git a/docs/source/guidebook/advanced/work_graph.md b/docs/source/guidebook/advanced/work_graph.md index 1f7c2f26..e2c71f39 100644 --- a/docs/source/guidebook/advanced/work_graph.md +++ b/docs/source/guidebook/advanced/work_graph.md @@ -2,10 +2,11 @@ ## Термины -- WorkGraph — это «карта проекта» в виде графа без циклов. Есть две служебные точки: начало и конец (добавляются сами). -- GraphNode — узел графа: внутри лежит задача (`WorkUnit`) и ссылки на связи с родителями/детьми (у связи есть тип и +- **WorkGraph** — «карта проекта» в виде ациклического графа. Две служебные точки — начало и конец — добавляются + автоматически. +- **GraphNode** — узел графа: содержит задачу (`WorkUnit`) и ссылки на связи с родителями/детьми (у связи есть тип и пауза). -- WorkUnit — описание самой работы: объём и требования к ресурсам (какие люди/сколько и т.п.). +- **WorkUnit** — описание самой работы: объём и требования к ресурсам (какие люди/сколько). --- @@ -20,17 +21,15 @@ from sampo.generator.pipeline import SyntheticGraphType ss = SimpleSynthetic() wg = ss.work_graph( mode=SyntheticGraphType.GENERAL, # тип структуры: General / Parallel / Sequential - cluster_counts=10, # сколько «кластеров» (логических групп работ) + cluster_counts=10, # число кластеров (групп работ) bottom_border=100, # нижняя граница количества работ top_border=200 # верхняя граница количества работ ) ``` -Коротко про параметры: - -- `mode` — форма графа (общий, параллельный, последовательный). -- `cluster_counts` — сколько групп работ в графе. -- `bottom_border` / `top_border` — примерно сколько работ генерировать. +* `mode` — форма графа (общий, параллельный, последовательный). +* `cluster_counts` — сколько групп работ в графе. +* `bottom_border` / `top_border` — диапазон количества задач. --- @@ -41,72 +40,111 @@ from sampo.pipeline import SchedulingPipeline from sampo.pipeline.lag_optimization import LagOptimizationStrategy from sampo.scheduler.heft import HEFTScheduler -project = (SchedulingPipeline.create() -.wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) -.lag_optimize(LagOptimizationStrategy.TRUE) # можно AUTO/NONE/TRUE -.schedule(HEFTScheduler()) -.finish()[0]) +project = ( + SchedulingPipeline.create() + .wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) + .lag_optimize(LagOptimizationStrategy.TRUE) # AUTO / NONE / TRUE + .schedule(HEFTScheduler()) + .finish()[0] +) wg = project.wg # готовый WorkGraph schedule = project.schedule ``` -Пояснения: +* `all_connections=True` — попытаться «достроить» связи, если во входных данных чего‑то не хватает. +* `sep=';'` — разделитель колонок в CSV. +* `LagOptimizationStrategy` — как обращаться с технологическими паузами (можно доверить выбор «AUTO»). -- `all_connections=True` — попытаться «достроить» связи, если во входных данных чего‑то не хватает. -- `sep=';'` — разделитель колонок в CSV. -- `LagOptimizationStrategy` — как обращаться с технологическими паузами (можно доверить выбор «AUTO»). +> Если используете автогенерацию подрядчика через `get_contractor_by_wg`, **ресурсные колонки можно не +добавлять**. Достаточно обязательных полей и зависимостей. + +--- ## Структура CSV -- Обязательные колонки: `activity_id, activity_name, granular_name, volume, measurement, priority` - - `activity_id` — уникальный идентификатор задачи (строка/число). Должен быть уникальным во всём файле. - - `activity_name` — понятное человеку название задачи (строка). - - `granular_name` — короткий код/метка задачи (строка). Если не используете — можно дублировать `activity_id`/ - `activity_name`. - - `volume` — объём работы (число). Используется оценщиком времени; смысл объёма зависит от вашей предметной области. - - `measurement` — единица измерения объёма (строка), например: `unit`, `m3`, `m`, `pcs`. - - `priority` — целое число для приоритизации. Если нет особого порядка — ставьте `0` для всех. - -- Зависимости (списки через запятую, длины совпадают): `predecessor_ids, connection_types, lags` - - Это три «синхронных» списка. Элементы с одинаковым индексом относятся к одной и той же связи. - - Пример: - `predecessor_ids="A,B"`, `connection_types="FS,SS"`, `lags="0,3"` - означает две связи: A —(FS, 0)→ текущая задача и B —(SS, 3)→ текущая задача. - - Пустая ячейка в `predecessor_ids` означает «нет предшественников». - - Допустимые типы связей перечислены ниже. Паузы (`lags`) — числа (обычно `0`). - - Важно: длина всех трёх списков должна совпадать, порядок элементов — согласованный. - -- Про типы связи можно прочитать в отдельной главе. - -- По ресурсам (опционально, словари в ячейках): `min_req`, `max_req`, `req_volume` вида `{"worker_kind": value}` - - Формат — JSON‑подобный словарь в строке. Ключ — имя профессии/ресурса, значение — число. - - `min_req` — минимально требуемое количество данного ресурса (например, людей нужной профессии). - - `max_req` — максимально допустимое количество (верхняя граница бригады). - - `req_volume` — «норма/объём» по данному виду ресурса (как правило, влияет на расчёт времени через оценщик). - - Примеры значений ячейки: - - `{"welder": 2, "driver": 1}` - - `{"operator": 3}` - - Если колонки отсутствуют или ячейка пуста — считается, что ограничений по этому виду нет (или берутся значения по - умолчанию). - - Имена ключей должны совпадать с `Worker.name` у ваших подрядчиков. Иначе планировщик не сопоставит требуемые и - доступные ресурсы. - -Мини‑пример зависимостей (B после A по FS, пауза 0): +### Обязательные колонки + +| Поле | Описание | +|-----------------|----------------------------------------------------------------------| +| `activity_id` | Уникальный идентификатор задачи | +| `activity_name` | Человекочитаемое название задачи | +| `granular_name` | Короткий код/метка задачи | +| `volume` | Объём работы (число) | +| `measurement` | Единица измерения (например `unit`, `m3`, `pcs`) | +| `priority` | Целое число для приоритизации (если не нужно — ставьте `0` для всех) | + +### Зависимости + +Три синхронных списка (длины совпадают): + +* `predecessor_ids` — ID предшественников через запятую +* `connection_types` — типы связей (FS, SS, FF и т.п.) +* `lags` — паузы (числа) + +> Пустая ячейка = просто **три разделителя подряд без пробелов** (`;;;`). +> +> Пример: +> `predecessor_ids="A,B"`, `connection_types="FS,SS"`, `lags="0,3"` + +### Ресурсы (опционально) + +| Поле | Формат | +|--------------|-----------------------------------------------| +| `min_req` | JSON-словарь минимально требуемых ресурсов | +| `max_req` | JSON-словарь максимально допустимых ресурсов | +| `req_volume` | JSON-словарь норм/объёмов для расчёта времени | + +* Используйте **валидный JSON** с двойными кавычками: + `{"welder": 2, "driver": 1}` +* Если этих колонок нет, планировщик использует автогенерацию ресурсов (например, через `get_contractor_by_wg`). +* Ключи должны совпадать с `Worker.name`. + +--- + +### Минимальный CSV (без ресурсов) ``` activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags A;Task A;A;1.0;unit;0;;; B;Task B;B;1.0;unit;0;A;FS;0 +C;Task C;C;1.0;unit;0;B;FS;0 +``` + +### CSV с ресурсами (пример) + +``` +activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags;min_req;max_req;req_volume +A;Site prep;A;1.0;unit;0;;;;"{""driver"":1,""handyman"":1}";"{""driver"":2,""handyman"":2}";"{""driver"":8,""handyman"":8}" +B;Foundation;B;1.0;unit;0;A;FS;0;"{""fitter"":2,""engineer"":1}";"{""fitter"":4,""engineer"":2}";"{""fitter"":12,""engineer"":12}" ``` --- -### В) Собрать программно из узлов +### Автогенерация подрядчика для CSV-графа + +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod +from sampo.scheduler.heft import HEFTScheduler + +wg = project.wg -- Сначала создайте `WorkUnit` для каждой работы (при необходимости укажите `WorkerReq`). -- Связи задаются при создании `GraphNode`: список родителей в виде кортежей `(parent_node, lag, EdgeType)`. -- Сборка: `WorkGraph.from_nodes([...])` — служебные `start/finish` добавятся сами. +contractors = [get_contractor_by_wg( + wg, + scaler=1.0, + method=ContractorGenerationMethod.AVG, + contractor_id="c_csv", + contractor_name="CSV Contractor" +)] + +scheduler = HEFTScheduler() +best_schedule, *_ = scheduler.schedule_with_cache(wg, contractors)[0] +print(f"Makespan: {best_schedule.execution_time}") +``` + +--- + +### В) Собрать программно из узлов ```python from sampo.schemas.works import WorkUnit @@ -114,10 +152,10 @@ from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType from sampo.schemas.requirements import WorkerReq from sampo.schemas.time import Time -# A -> B -> C (FS, паузы 0) +# Пример цепочки: A -> B -> C (FS, паузы 0) wu_a = WorkUnit( id='A', name='Task A', - worker_reqs=[WorkerReq(kind='general', volume=Time(10), min_count=2, max_count=4)], + worker_reqs=[WorkerReq(kind='driver', volume=Time(10), min_count=2, max_count=4)], volume=1.0, is_service_unit=False ) wu_b = WorkUnit(id='B', name='Task B', worker_reqs=[], volume=1.0, is_service_unit=False) @@ -130,6 +168,5 @@ n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS wg = WorkGraph.from_nodes([n_a, n_b, n_c]) ``` -- Обязательные поля у `WorkUnit`: `id`, `name`. Для экспорта удобно также задавать `volume`, `measurement`, `priority`. - ---- \ No newline at end of file +* Обязательные поля `WorkUnit`: `id`, `name`. +* Для экспорта полезно также указывать `volume`, `measurement`, `priority`. diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md index 7a3ac002..676e6a3b 100644 --- a/docs/source/guidebook/quickstart.md +++ b/docs/source/guidebook/quickstart.md @@ -4,24 +4,24 @@ ## Установка -SAMPO доступен в виде пакета для языка Python. Установка: +SAMPO доступен в виде пакета Python (требуется Python 3.10.x): ```bash pip install sampo ``` -SAMPO требует версию Python 3.10.x - ## Первый план за несколько шагов Сделаем простейший проект и распишем его: -1. Граф работ — создадим WorkGraph. Для быстрого старта сгенерируем синтетический. -2. Ресурсы — опишем список Contractor с рабочими. -3. Алгоритм — выберем планировщик (эвристика/генетика). -4. Запуск — получим Schedule и посмотрим результат. +1) Граф работ — создадим WorkGraph. Для быстрого старта сгенерируем синтетический. +2) Ресурсы — опишем список Contractor с рабочими. +3) Алгоритм — выберем планировщик (эвристика/генетика). +4) Запуск — получим Schedule и посмотрим результат. + +--- -1) Генерация WorkGraph. Для простоты воспользуемся генератором +### 1) Генерация WorkGraph (быстрый способ — генератор) ```python from sampo.generator.base import SimpleSynthetic @@ -33,7 +33,7 @@ synthetic = SimpleSynthetic() # Сгенерируем небольшой граф: ~2 кластера по 5–8 задач work_graph: WorkGraph = synthetic.work_graph( - mode=SyntheticGraphType.GENERAL, # тип структуры + mode=SyntheticGraphType.GENERAL, # тип структуры: GENERAL / PARALLEL / SEQUENTIAL cluster_counts=2, # 2 кластера bottom_border=5, # в кластере 5–8 задач top_border=8 @@ -41,11 +41,14 @@ work_graph: WorkGraph = synthetic.work_graph( print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") ``` -2) Ресурсы (Contractor’ы) +--- + +### 2) Ресурсы (Contractor’ы) Важно: синтетический граф использует типовые профессии `driver`, `fitter`, `manager`, `handyman`, `electrician`, -`engineer`. Подрядчик должен содержать работников для каждого вида требования, а словарь `workers` должен быть -индексирован по имени вида ресурса (т.е. `req.kind`). +`engineer`. +Подрядчик должен содержать работников для каждого требуемого вида, а словарь `workers` индексируется по имени вида +ресурса (`req.kind`). ```python from sampo.schemas.contractor import Contractor @@ -72,7 +75,7 @@ contractors = [ ] ``` -Альтернатива: можно автоматически сгенерировать подрядчика «по графу», чтобы ресурсы точно покрывали требования: +Альтернатива: автогенерация подрядчика «по графу», чтобы ресурсы точно покрывали требования: ```python from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod @@ -80,46 +83,72 @@ from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, C contractors = [get_contractor_by_wg( work_graph, scaler=1.0, # множитель мощностей (>= 1.0) - method=ContractorGenerationMethod.AVG, # брать среднее между min/max потребностями + method=ContractorGenerationMethod.AVG, # усреднение между min/max потребностями contractor_id="c1", contractor_name="General Contractor" )] ``` -3) Выбор планировщика +--- + +### 3) Выбор планировщика ```python from sampo.scheduler.heft import HEFTScheduler -# или: from sampo.scheduler import HEFTScheduler, TopologicalScheduler, GeneticScheduler +# также доступны: +# from sampo.scheduler.topological import TopologicalScheduler +# from sampo.scheduler.genetic import GeneticScheduler scheduler = HEFTScheduler() # быстрая эвристика для старта ``` -4) Запуск планирования - Метод `schedule` возвращает список решений; берём первое. +--- -```python -# Планирование: берём первое (лучшее) решение -best_schedule, start_time, timeline, node_order = scheduler.schedule(work_graph, contractors)[0] +### 4) Запуск планирования +Метод `schedule(...)` возвращает список объектов `Schedule`. Берём первое (лучшее) решение: + +```python +best_schedule = scheduler.schedule(work_graph, contractors)[0] print(f"Projected project duration (makespan): {best_schedule.execution_time}") ``` -Просмотр расписания -Надёжный способ получить агрегированное представление и визуализировать — воспользоваться встроенной функцией Ганта: +Если нужна дополнительная информация (время, таймлайн, порядок узлов), используйте расширенный метод: + +```python +best_schedule, finish_time, timeline, node_order = scheduler.schedule_with_cache(work_graph, contractors)[0] +print(f"Makespan: {best_schedule.execution_time}") +``` + +--- + +### Просмотр расписания (диаграмма Ганта) + +Надёжный способ — получить агрегированное представление и визуализировать его: ```python from sampo.utilities.visualization import schedule_gant_chart_fig, VisualizationMode -merged = best_schedule.merged_stages_datetime_df(start_date='2025-01-01') -fig = schedule_gant_chart_fig(merged, VisualizationMode.ReturnFig, remove_service_tasks=False) +merged = best_schedule.merged_stages_datetime_df(offset='2025-01-01') + +fig = schedule_gant_chart_fig( + merged, + visualization=VisualizationMode.ReturnFig, + color_type='contractor' # можно сменить раскраску при необходимости +) fig.show() ``` -5) (Опционально) Конвейер SchedulingPipeline - Эквивалент тех же шагов во «флюентном» стиле. `finish()` возвращает список `ScheduledProject`, из которого берём - `[0]` и затем читаем `project.schedule`. +- Если хотите увидеть только свои задачи без «внутренних» технических, возьмите таблицу best_schedule.pure_schedule_df. +- Для диаграммы Ганта обычно используют календарное представление best_schedule.merged_stages_datetime_df целиком. + +--- + +### 5) (Опционально) Конвейер SchedulingPipeline + +Эквивалент тех же шагов во «флюентном» стиле. `finish()` возвращает список `ScheduledProject`, из которого берём `[0]` и +читаем `project.schedule`. ```python from sampo.pipeline import SchedulingPipeline From 38c0721698f6cd88b51ac25e6e9bac8a781b96d6 Mon Sep 17 00:00:00 2001 From: NickMur Date: Thu, 2 Oct 2025 18:10:17 +0300 Subject: [PATCH 21/32] Removed the division into levels --- docs/source/guidebook/advanced/index.md | 13 ------------- .../guidebook/{advanced => }/algorithms.md | 16 +++++++--------- .../guidebook/{advanced => }/connections.md | 0 .../guidebook/{advanced => }/contractors.md | 0 docs/source/guidebook/index.md | 8 ++++++-- .../guidebook/{advanced => }/work_graph.md | 0 docs/source/index.rst | 1 - 7 files changed, 13 insertions(+), 25 deletions(-) delete mode 100644 docs/source/guidebook/advanced/index.md rename docs/source/guidebook/{advanced => }/algorithms.md (90%) rename docs/source/guidebook/{advanced => }/connections.md (100%) rename docs/source/guidebook/{advanced => }/contractors.md (100%) rename docs/source/guidebook/{advanced => }/work_graph.md (100%) diff --git a/docs/source/guidebook/advanced/index.md b/docs/source/guidebook/advanced/index.md deleted file mode 100644 index 715e5d57..00000000 --- a/docs/source/guidebook/advanced/index.md +++ /dev/null @@ -1,13 +0,0 @@ -# Основное руководство - -```{toctree} -:maxdepth: 2 -:numbered: -caption: Основное руководство SAMPO - -work_graph -connections -contractors -algorithms -``` - diff --git a/docs/source/guidebook/advanced/algorithms.md b/docs/source/guidebook/algorithms.md similarity index 90% rename from docs/source/guidebook/advanced/algorithms.md rename to docs/source/guidebook/algorithms.md index 77d79e10..4290c401 100644 --- a/docs/source/guidebook/advanced/algorithms.md +++ b/docs/source/guidebook/algorithms.md @@ -63,6 +63,8 @@ from sampo.scheduler.topological import TopologicalScheduler from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager from sampo.schemas.contractor import Contractor from sampo.schemas.resources import Worker +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod + # 1) Небольшой граф работ ss = SimpleSynthetic(231) @@ -72,8 +74,11 @@ wg = ss.work_graph(bottom_border=30, top_border=40) kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} cid = str(uuid4()) workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} -contractors = [Contractor(id=cid, name="Universal", workers=workers, equipments={})] - +contractors = [get_contractor_by_wg( + wg, + scaler=1.0, # при необходимости увеличьте, например 1.2 или 1.5 + method=ContractorGenerationMethod.AVG, +)] # 3) Два агента с разными подходами agents = [ Agent("HEFT", HEFTScheduler(), contractors), @@ -132,10 +137,3 @@ for block_id, sblock in scheduled_blocks.items(): project_finish = max(sb.end_time for sb in scheduled_blocks.values()) print("Время завершения проекта:", project_finish) ``` - -Коротко: - -- Блок — самостоятельная часть проекта (свой небольшой граф работ). -- Граф блоков — связки между блоками: «A → B» значит, что блок B можно начинать после завершения A. -- Менеджер ждёт, пока закончатся все предыдущие блоки, собирает предложения от агентов и выбирает то, где блок - завершится раньше. diff --git a/docs/source/guidebook/advanced/connections.md b/docs/source/guidebook/connections.md similarity index 100% rename from docs/source/guidebook/advanced/connections.md rename to docs/source/guidebook/connections.md diff --git a/docs/source/guidebook/advanced/contractors.md b/docs/source/guidebook/contractors.md similarity index 100% rename from docs/source/guidebook/advanced/contractors.md rename to docs/source/guidebook/contractors.md diff --git a/docs/source/guidebook/index.md b/docs/source/guidebook/index.md index 09930a03..7c1b6632 100644 --- a/docs/source/guidebook/index.md +++ b/docs/source/guidebook/index.md @@ -1,10 +1,14 @@ -# Начальное руководство +# Руководство ```{toctree} :maxdepth: 2 :numbered: -caption: Начальное руководство SAMPO +caption: Руководство SAMPO intro quickstart +work_graph +connections +contractors +algorithms ``` diff --git a/docs/source/guidebook/advanced/work_graph.md b/docs/source/guidebook/work_graph.md similarity index 100% rename from docs/source/guidebook/advanced/work_graph.md rename to docs/source/guidebook/work_graph.md diff --git a/docs/source/index.rst b/docs/source/index.rst index 3a256d64..32ee396e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -11,7 +11,6 @@ Welcome to SAMPO's documentation! :caption: Contents: guidebook/index - guidebook/advanced/index autoapi/sampo/index From 5c3248b745590e8bc7385dfcac84235a3dc16aee Mon Sep 17 00:00:00 2001 From: NickMur Date: Thu, 2 Oct 2025 18:20:08 +0300 Subject: [PATCH 22/32] English version --- docs/source/guidebook/algorithms.md | 77 +++++++++--------- docs/source/guidebook/connections.md | 69 ++++++++-------- docs/source/guidebook/contractors.md | 74 ++++++++--------- docs/source/guidebook/intro.md | 44 +++++------ docs/source/guidebook/quickstart.md | 80 +++++++++---------- docs/source/guidebook/work_graph.md | 114 +++++++++++++-------------- 6 files changed, 229 insertions(+), 229 deletions(-) diff --git a/docs/source/guidebook/algorithms.md b/docs/source/guidebook/algorithms.md index 4290c401..4731a731 100644 --- a/docs/source/guidebook/algorithms.md +++ b/docs/source/guidebook/algorithms.md @@ -1,16 +1,16 @@ -# Алгоритмы планирования +# Scheduling Algorithms -## Как выбрать алгоритм +## How to choose an algorithm -### Эвристические планировщики (HEFT, HEFTBetween, Topological) +### Heuristic schedulers (HEFT, HEFTBetween, Topological) -- Быстро дают рабочий план. -- HEFT/HEFTBetween рассчитывают порядок задач и примерное время выполнения, чтобы закончить быстрее. -- Topological просто выстраивает задачи по зависимостям, без сложной оптимизации. -- HEFT строит план с нуля по всему графу, а HEFTBetween вставляет новые работы в уже занятый календарь, стараясь не - перекраивать весь ранее построенный план. +- Quickly produce a workable plan. +- HEFT/HEFTBetween compute the task order and approximate execution time to finish sooner. +- Topological simply orders tasks by dependencies, without complex optimization. +- HEFT builds a plan from scratch over the entire graph, while HEFTBetween inserts new tasks into an already occupied + calendar, trying not to rework the previously built plan. -Импорты: +Imports: ```python from sampo.scheduler.heft import HEFTScheduler @@ -20,20 +20,20 @@ from sampo.scheduler.topological import TopologicalScheduler --- -### Генетический планировщик +### Genetic scheduler -- Пробует много разных вариантов и часто находит план с меньшим временем завершения проекта. Работает дольше простых. -- Главные настройки: - - `number_of_generation` — сколько раз улучшать решения (больше — выше шанс улучшить, но дольше). - - `size_of_population` — сколько вариантов держать одновременно (больше — больше идей, но медленнее и больше - памяти). - - `mutate_order` — как часто менять порядок задач (сохраняя зависимости). Больше — шире поиск, но может сходиться - медленнее. - - `mutate_resources` — как часто менять распределение ресурсов/подрядчиков. Больше — выше шанс параллелить, но при - дефиците ресурсов может чаще «конфликтовать». - - Дополнительно: `work_estimator`, `seed` (для воспроизводимости). +- Tries many different variants and often finds a plan with a shorter project completion time. Runs longer than the + simple ones. +- Main settings: + - number_of_generation — how many times to improve the solutions (more — higher chance to improve, but longer). + - size_of_population — how many variants to keep simultaneously (more — more ideas, but slower and more memory). + - mutate_order — how often to change the task order (preserving dependencies). Higher — broader search, but may + converge more slowly. + - mutate_resources — how often to change the allocation of resources/contractors. Higher — greater chance to + parallelize, but with resource scarcity may “conflict” more often. + - Additionally: work_estimator, seed (for reproducibility). -Пример: +Example: ```python from sampo.scheduler.genetic import GeneticScheduler @@ -48,12 +48,12 @@ scheduler = GeneticScheduler( --- -## Многоагентное планирование +## Multi-agent scheduling -Разбивает проект на части (блоки), планирует их разными способами и собирает общий план. Полезно на больших проектах и -при комбинировании стратегий (`sampo.scheduler.multi_agency`). +Splits the project into parts (blocks), plans them in different ways, and assembles the overall plan. Useful on large +projects and when combining strategies (sampo.scheduler.multi_agency). -### «Аукцион» без разбиения на блоки +### “Auction” without splitting into blocks ```python from uuid import uuid4 @@ -65,33 +65,32 @@ from sampo.schemas.contractor import Contractor from sampo.schemas.resources import Worker from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod - -# 1) Небольшой граф работ +# 1) Small work graph ss = SimpleSynthetic(231) wg = ss.work_graph(bottom_border=30, top_border=40) -# 2) Подрядчик с нужными типами работников +# 2) A contractor with the required worker types kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} cid = str(uuid4()) workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} contractors = [get_contractor_by_wg( wg, - scaler=1.0, # при необходимости увеличьте, например 1.2 или 1.5 + scaler=1.0, # increase if needed, e.g., 1.2 or 1.5 method=ContractorGenerationMethod.AVG, )] -# 3) Два агента с разными подходами +# 3) Two agents with different approaches agents = [ Agent("HEFT", HEFTScheduler(), contractors), Agent("Topological", TopologicalScheduler(), contractors), ] manager = StochasticManager(agents) -# 4) «Аукцион»: агенты предлагают свои планы, выбираем лучший по времени завершения +# 4) “Auction”: agents propose their plans, choose the best by completion time start, end, schedule, winner = manager.run_auction(wg) -print("Победил агент:", winner.name, "Время завершения:", end - start) +print("Winner agent:", winner.name, "Completion time:", end - start) ``` -### С разбиением на блоки +### With block partitioning ```python from random import Random @@ -101,7 +100,7 @@ from sampo.scheduler.topological import TopologicalScheduler from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager from sampo.scheduler.multi_agency.block_generator import generate_blocks, SyntheticBlockGraphType -# 1) Строим «граф блоков» (каждый блок — это свой маленький WorkGraph) +# 1) Build a “block graph” (each block is its own small WorkGraph) seed = 231 rand = Random(seed) bg = generate_blocks( @@ -113,27 +112,27 @@ bg = generate_blocks( rand=rand ) -# 2) Подрядчики для агентов +# 2) Contractors for the agents ss = SimpleSynthetic(rand) contractor_a = ss.contractor(40) contractor_b = ss.contractor(40) -# 3) Агенты с разными алгоритмами +# 3) Agents with different algorithms agents = [ Agent("HEFT", HEFTScheduler(), [contractor_a]), Agent("Topo", TopologicalScheduler(), [contractor_b]), ] manager = StochasticManager(agents) -# 4) Планируем блоки по порядку, учитывая зависимости между ними +# 4) Schedule the blocks in order, taking dependencies between them into account scheduled_blocks = manager.manage_blocks(bg) -# 5) Итоги: кто какой блок взял и когда он выполнялся +# 5) Results: who took which block and when it was executed print("Scheduled blocks:") for block_id, sblock in scheduled_blocks.items(): print( f"Block {block_id}: agent={sblock.agent.name}, start={sblock.start_time}, end={sblock.end_time}, duration={sblock.duration}") project_finish = max(sb.end_time for sb in scheduled_blocks.values()) -print("Время завершения проекта:", project_finish) +print("Project completion time:", project_finish) ``` diff --git a/docs/source/guidebook/connections.md b/docs/source/guidebook/connections.md index f5933ff6..dfa8d7bc 100644 --- a/docs/source/guidebook/connections.md +++ b/docs/source/guidebook/connections.md @@ -1,62 +1,61 @@ -# Связи между работами +# Connections between tasks -## Термины +## Terms -- Связь — стрелка между задачами, которая говорит, когда можно начинать следующую. -- Пауза (lag) — сколько времени нужно подождать после одной задачи, прежде чем начинать следующую. -- Неразрывная цепочка (IFS) — несколько задач подряд без перерыва, их делает одна и та же бригада. +- Connections — an arrow between tasks that indicates when the next one can start. +- Lag — how long you need to wait after one task before starting the next. +- Uninterrupted chain (IFS) — several tasks in a row without a break, performed by the same crew. -> Зачем это нужно (IFS): чтобы не начать следующую задачу слишком рано и соблюдать технологические паузы (сушка, -> остывание и т.п.). +> Why this is needed (IFS): to avoid starting the next task too early and to observe technological pauses (drying, cooling, etc.). --- -## Типы связей +## Connections types -| Тип | Как читать | Что значит коротко | -|-----|---------------------------|-------------------------------------------------------| -| FS | Конец → Старт | B можно начать после окончания A (+ пауза, если есть) | -| FFS | Конец → Старт (с паузой) | То же, но с обязательной паузой | -| IFS | Неразрывно: Конец → Старт | B идёт сразу за A без перерыва той же бригадой | -| SS | Старт ↔ Старт | A и B могут стартовать вместе | -| FF | Конец ↔ Конец | B должен закончиться не раньше A | +| Type | How to read | Short meaning | +|------|---------------------------|---------------------------------------------------------| +| FS | Finish → Start | B can start after A finishes (+ lag, if any) | +| FFS | Finish → Start (with lag) | Same, but with a mandatory lag | +| IFS | Uninterrupted: Finish → Start | B goes right after A without a break by the same crew | +| SS | Start ↔ Start | A and B can start together | +| FF | Finish ↔ Finish | B must finish no earlier than A | --- -## Когда какой тип ставить +## When to use which type -- Обычная последовательность действий: FS. -- Нужно выдержать технологическую паузу: FFS (или FS с паузой). -- Нужна непрерывная работа одной бригады без перерыва: IFS. -- Общий одновременный запуск: SS. -- Хотим “сойтись” к одному времени окончания: FF. +- Ordinary sequence of actions: FS. +- Need to observe a technological pause: FFS (or FS with a lag). +- Need uninterrupted work by the same crew without a break: IFS. +- Common simultaneous start: SS. +- Want to “converge” to a single finish time: FF. --- -## Мини‑примеры +## Mini examples -1) FS + пауза - A: «Залить бетон» (конец День 2) → пауза 3 дня → B: «Кладка стен» (не раньше Дня 5). +1) FS + lag + A: “Pour concrete” (finish Day 2) → lag 3 days → B: “Bricklaying” (no earlier than Day 5). 2) IFS - A: «Сверление» ⇒ B: «Анкерение» ⇒ C: «Монтаж стойки» (одна бригада, без перерыва). + A: “Drilling” ⇒ B: “Anchoring” ⇒ C: “Stand installation” (one crew, without a break). 3) SS - A: «Пуск серверов» ≡ B: «Старт мониторинга» (одновременно). + A: “Server launch” ≡ B: “Start of monitoring” (simultaneously). 4) FF - A: «Основная настройка» || B: «Документация» (закончить к одному сроку). + A: “Primary configuration” || B: “Documentation” (finish by the same deadline). 5) FFS - A: «Нанести грунт» → пауза 4 часа → B: «Покраска». + A: “Apply primer” → lag 4 hours → B: “Painting”. -Условные значки: → (FS/FFS), ⇒ (IFS), ≡ (SS), || (FF). +Legend: → (FS/FFS), ⇒ (IFS), ≡ (SS), || (FF). --- -## Пример файла с зависимостями (CSV) +## Example file with dependencies (CSV) -Пусть это файл `predecessors.csv`: +Let this be the file predecessors.csv: ``` child_id,parent_id,type,lag @@ -66,8 +65,8 @@ D,C,IFS,0 E,A,SS,0 ``` -Что здесь: +What’s here: -- B после A + 3 (пауза 3). -- C и D вместе с B образуют неразрывную цепочку (IFS). -- E стартует вместе с A (SS, не задерживает). +- B after A + 3 (lag 3). +- C and D together with B form an uninterrupted chain (IFS). +- E starts together with A (SS, does not delay). \ No newline at end of file diff --git a/docs/source/guidebook/contractors.md b/docs/source/guidebook/contractors.md index afedeeda..d6bfc946 100644 --- a/docs/source/guidebook/contractors.md +++ b/docs/source/guidebook/contractors.md @@ -1,16 +1,18 @@ # Contractor -## Термины +## Terms -- Требование к людям (WorkerReq) — кого и сколько нужно для задачи: профессия (`kind`), минимум/максимум людей ( - `min_count…max_count`), и объём/норма (`volume`). -- Работник (Worker) — кадровые ресурсы: специализация (`name`), количество (`count`), производительность ( - `productivity`), владелец (`contractor_id`), при необходимости — стоимость (`cost_one_unit`). -- Техника (Equipment) — тоже ресурс, только не люди: тип и количество. Задачи используют её так же, как и людей. -- Подрядчик (Contractor) — поставщик ресурсов. По сути — словарь доступных «людей» и «техники» для планировщика. +- Worker requirement (WorkerReq) — who and how many are needed for a task: profession (kind), minimum/maximum people ( + min_count…max_count), and volume/rate (volume). +- Worker — human resources: specialization (name), count (count), productivity (productivity), owner (contractor_id), + and optionally cost (cost_one_unit). +- Equipment — also a resource, just not people: type and quantity. Tasks use it the same way as people. +- Contractor — a resource supplier. Essentially, a dictionary of available “people” and “equipment” for the scheduler. --- -## Варианты проффесий: + +## Available professions: + - 'driver' - 'fitter' - 'handyman' @@ -18,26 +20,26 @@ - 'manager' - 'engineer' -## Как задать подрядчика +## How to define a contractor -### Вариант 1. Вручную +### Option 1. Manually ```python from sampo.schemas.contractor import Contractor from sampo.schemas.resources import Worker from sampo.schemas.interval import IntervalGaussian -# Пример: подрядчик с двумя профессиями (водители и слесари) +# Example: contractor with two professions (drivers and fitters) contractor = Contractor( workers={ - # ключ словаря 'driver' совпадает с Worker.name='driver' + # the dictionary key 'driver' matches Worker.name='driver' 'driver': Worker( id='w1', - name='driver', # профессия (должна совпадать с WorkerReq.kind) - count=8, # сколько таких людей доступно - productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5) # «средняя скорость» с разбросом - # cost_one_unit=..., # при расчёте стоимости — укажите цену за единицу (опционально) - # contractor_id='c1', # как правило, совпадает с Contractor.id (если задаёте вручную) + name='driver', # profession (must match WorkerReq.kind) + count=8, # how many such people are available + productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5) # “average rate” with spread + # cost_one_unit=..., # when calculating cost — specify unit price (optional) + # contractor_id='c1', # usually matches Contractor.id (if set manually) ), 'fitter': Worker( id='w2', @@ -51,16 +53,16 @@ contractor = Contractor( ) ``` -Подсказки: +Tips: -- Ключи словаря `workers` должны совпадать с `Worker.name`. -- `WorkerReq.kind` из задач должен совпадать с `Worker.name`, иначе бригада не подберётся. -- `contractor_id` у работников обычно совпадает с `Contractor.id`. -- `IntervalGaussian(μ, σ, low, high)` — «средняя производительность μ», с разбросом ±σ и ограничениями `low…high`. +- The keys of the workers dictionary must match Worker.name. +- WorkerReq.kind in tasks must match Worker.name, otherwise a crew cannot be assigned. +- contractor_id for workers usually matches Contractor.id. +- IntervalGaussian(μ, σ, low, high) — “average productivity μ”, with spread ±σ and bounds low…high. --- -### Вариант 2. Быстрый генератор «пакета» ресурсов +### Option 2. Quick generator of a resource “pack” ```python from sampo.generator.base import SimpleSynthetic @@ -71,31 +73,31 @@ contractor = ss.contractor(pack_worker_count=10) --- -### Вариант 3. Подрядчик «по графу» (из требований задач) +### Option 3. Contractor “based on the graph” (from task requirements) ```python from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod -# wg — ваш WorkGraph с задачами и их WorkerReq +# wg — your WorkGraph with tasks and their WorkerReq contractor = get_contractor_by_wg( wg, - scaler=1.0, # можно умножить ресурсы (1.5 = +50%) - method=ContractorGenerationMethod.AVG # агрегировать требования «в среднем» + scaler=1.0, # you can scale resources (1.5 = +50%) + method=ContractorGenerationMethod.AVG # aggregate requirements “on average” ) ``` -Как это работает: +How it works: -- Смотрит на все `WorkerReq` в проекте. -- Складывает/усредняет потребности по профессиям. -- Собирает подрядчика с подходящим количеством людей/типов. -- `scaler` позволяет быстро «дать больше/меньше» ресурсов, не переписывая всё вручную. +- Looks at all WorkerReq in the project. +- Sums/averages needs by profession. +- Assembles a contractor with a suitable number and types of people. +- scaler lets you quickly “give more/less” resources without rewriting everything manually. --- -## Ещё пара мини‑примеров +## A couple more mini-examples -Два подрядчика раздельно: +Two separate contractors: ```python from sampo.schemas.contractor import Contractor @@ -114,10 +116,10 @@ contractor_b = Contractor( contractors = [contractor_a, contractor_b] ``` -Быстро «нарастить» ресурсы у существующего: +Quickly “scale up” resources for an existing one: ```python -# Было 6 водителей, станет 10 +# There were 6 drivers, it will become 10 contractor.workers['driver'].count = 10 ``` diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md index e2c538f7..2ede5fcf 100644 --- a/docs/source/guidebook/intro.md +++ b/docs/source/guidebook/intro.md @@ -1,34 +1,34 @@ -# Введение +# Introduction -## Для кого руководство? +## Who is this guide for? -Руководство организовано так, чтобы помочь пользователям с разным уровнем подготовки: +The guide is organized to help users with different levels of expertise: -* Ознакомительный уровень — что такое SAMPO, чем он поможет и как быстро начать работу. -* Продвинутый уровень — как настраивать SAMPO под свои задачи. +* Introductory level — what SAMPO is, how it can help, and how to get started quickly. +* Advanced level — how to configure SAMPO for your tasks. -## Что такое SAMPO? +## What is SAMPO? -SAMPO — фреймворк для автоматизированной оптимизации планирования бизнес-процессов. Он позволяет эффективно использовать -наработанный корпоративный опыт на ретроспективных данных для автоматизированного построения план-графиков выполнения -бизнес-процессов, оптимальных по требуемым целевым показателям. +SAMPO is a framework for automated optimization of business process scheduling. It makes it possible to effectively +leverage accumulated corporate expertise based on retrospective data to automatically build schedules for executing +business processes that are optimal with respect to the required target metrics. -## Ключевые возможности и преимущества +## Key features and benefits -В основе SAMPO лежат мета-эвристические, генетические и нейросетевые алгоритмы, которые позволяют анализировать -ретроспективные производственные данные и решать задачу мультикритериальной оптимизации планирования с учетом -накопленного опыта. Особенностью фреймворка является способность эффективно решать задачу планирования на больших -временных горизонтах, в условиях неопределенности и неполноты ретроспективных данных. +SAMPO is based on metaheuristic, genetic, and neural network algorithms that make it possible to analyze retrospective +production data and solve the problem of multi-criteria optimization of planning, taking accumulated experience into +account. A distinguishing feature of the framework is its ability to efficiently solve the scheduling problem over long +time horizons, under uncertainty and with incomplete retrospective data. -За счет своей модульной структуры SAMPO допускает интеграцию алгоритмов и моделей, учитывающих специфику определенной -предметной области, поэтому фреймворк может быть применим для решения промышленных задач из различных отраслей: -добывающей и обрабатывающей промышленности, строительства, транспорта и энергетики. +By virtue of its modular structure, SAMPO supports the integration of algorithms and models that take into account the +specifics of a particular domain, so the framework can be applied to industrial problems across various sectors: mining +and manufacturing, construction, transportation, and energy. -## Открытость и документация +## Openness and documentation -Лицензия **BSD 3-Clause**, подробная документация, примеры в **Jupyter Notebook**. +License: **BSD 3-Clause**, detailed documentation, examples in **Jupyter Notebook**. -> BSD 3-Clause — свободная лицензия, разрешающая использовать, изменять и распространять код (в том числе в -коммерческих проектах) при сохранении уведомления об авторских правах и отказа от ответственности. +> BSD 3-Clause is a permissive license that allows using, modifying, and distributing the code (including in commercial +> projects) provided that copyright notices and disclaimers are retained. ---- +--- \ No newline at end of file diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md index 676e6a3b..16476bd1 100644 --- a/docs/source/guidebook/quickstart.md +++ b/docs/source/guidebook/quickstart.md @@ -1,41 +1,41 @@ -# Быстрый старт +# Quick Start -Покажем установку, подготовку простого примера, запуск планировщика и просмотр результата. +We will show installation, preparation of a simple example, running the scheduler, and viewing the result. -## Установка +## Installation -SAMPO доступен в виде пакета Python (требуется Python 3.10.x): +SAMPO is available as a Python package (Python 3.10.x required): ```bash pip install sampo ``` -## Первый план за несколько шагов +## First plan in a few steps -Сделаем простейший проект и распишем его: +We will create the simplest project and lay it out: -1) Граф работ — создадим WorkGraph. Для быстрого старта сгенерируем синтетический. -2) Ресурсы — опишем список Contractor с рабочими. -3) Алгоритм — выберем планировщик (эвристика/генетика). -4) Запуск — получим Schedule и посмотрим результат. +1) Work graph — create a WorkGraph. For a quick start, we will generate a synthetic one. +2) Resources — describe a list of Contractors with workers. +3) Algorithm — choose a scheduler (heuristic/genetic). +4) Run — get a Schedule and look at the result. --- -### 1) Генерация WorkGraph (быстрый способ — генератор) +### 1) Generating a WorkGraph (quick way — generator) ```python from sampo.generator.base import SimpleSynthetic from sampo.generator.pipeline import SyntheticGraphType from sampo.schemas.graph import WorkGraph -# Инициализация синтетического генератора +# Initialize the synthetic generator synthetic = SimpleSynthetic() -# Сгенерируем небольшой граф: ~2 кластера по 5–8 задач +# Generate a small graph: ~2 clusters with 5–8 tasks each work_graph: WorkGraph = synthetic.work_graph( - mode=SyntheticGraphType.GENERAL, # тип структуры: GENERAL / PARALLEL / SEQUENTIAL - cluster_counts=2, # 2 кластера - bottom_border=5, # в кластере 5–8 задач + mode=SyntheticGraphType.GENERAL, # structure type: GENERAL / PARALLEL / SEQUENTIAL + cluster_counts=2, # 2 clusters + bottom_border=5, # 5–8 tasks per cluster top_border=8 ) print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") @@ -43,18 +43,18 @@ print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") --- -### 2) Ресурсы (Contractor’ы) +### 2) Resources (Contractors) -Важно: синтетический граф использует типовые профессии `driver`, `fitter`, `manager`, `handyman`, `electrician`, +Important: the synthetic graph uses typical professions `driver`, `fitter`, `manager`, `handyman`, `electrician`, `engineer`. -Подрядчик должен содержать работников для каждого требуемого вида, а словарь `workers` индексируется по имени вида -ресурса (`req.kind`). +The contractor must include workers for each required type, and the `workers` dictionary is keyed by the resource kind +name (`req.kind`). ```python from sampo.schemas.contractor import Contractor from sampo.schemas.resources import Worker -# Зададим по нескольку работников каждого нужного типа +# Specify several workers of each required type workers = [ Worker(id="w_driver", name="driver", count=20), Worker(id="w_fitter", name="fitter", count=20), @@ -64,26 +64,26 @@ workers = [ Worker(id="w_engineer", name="engineer", count=10), ] -# Один подрядчик с полным пулом работников +# One contractor with a complete pool of workers contractors = [ Contractor( id="c1", name="General Contractor", - # Ключи — имена видов ресурсов (совпадают с WorkerReq.kind) + # Keys are resource kind names (match WorkerReq.kind) workers={w.name: w for w in workers} ) ] ``` -Альтернатива: автогенерация подрядчика «по графу», чтобы ресурсы точно покрывали требования: +Alternative: auto-generate a contractor “based on the graph” so that resources exactly cover the requirements: ```python from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod contractors = [get_contractor_by_wg( work_graph, - scaler=1.0, # множитель мощностей (>= 1.0) - method=ContractorGenerationMethod.AVG, # усреднение между min/max потребностями + scaler=1.0, # capacity multiplier (>= 1.0) + method=ContractorGenerationMethod.AVG, # averaging between min/max needs contractor_id="c1", contractor_name="General Contractor" )] @@ -91,30 +91,30 @@ contractors = [get_contractor_by_wg( --- -### 3) Выбор планировщика +### 3) Choosing a scheduler ```python from sampo.scheduler.heft import HEFTScheduler -# также доступны: +# also available: # from sampo.scheduler.topological import TopologicalScheduler # from sampo.scheduler.genetic import GeneticScheduler -scheduler = HEFTScheduler() # быстрая эвристика для старта +scheduler = HEFTScheduler() # fast heuristic to get started ``` --- -### 4) Запуск планирования +### 4) Running the scheduling -Метод `schedule(...)` возвращает список объектов `Schedule`. Берём первое (лучшее) решение: +The schedule(...) method returns a list of Schedule objects. Take the first (best) solution: ```python best_schedule = scheduler.schedule(work_graph, contractors)[0] print(f"Projected project duration (makespan): {best_schedule.execution_time}") ``` -Если нужна дополнительная информация (время, таймлайн, порядок узлов), используйте расширенный метод: +If you need additional information (finish time, timeline, node order), use the extended method: ```python best_schedule, finish_time, timeline, node_order = scheduler.schedule_with_cache(work_graph, contractors)[0] @@ -123,9 +123,9 @@ print(f"Makespan: {best_schedule.execution_time}") --- -### Просмотр расписания (диаграмма Ганта) +### Viewing the schedule (Gantt chart) -Надёжный способ — получить агрегированное представление и визуализировать его: +A reliable way is to obtain an aggregated representation and visualize it: ```python from sampo.utilities.visualization import schedule_gant_chart_fig, VisualizationMode @@ -135,20 +135,20 @@ merged = best_schedule.merged_stages_datetime_df(offset='2025-01-01') fig = schedule_gant_chart_fig( merged, visualization=VisualizationMode.ReturnFig, - color_type='contractor' # можно сменить раскраску при необходимости + color_type='contractor' # you can change the coloring if needed ) fig.show() ``` -- Если хотите увидеть только свои задачи без «внутренних» технических, возьмите таблицу best_schedule.pure_schedule_df. -- Для диаграммы Ганта обычно используют календарное представление best_schedule.merged_stages_datetime_df целиком. +- If you want to see only your tasks without “internal” technical ones, use the table best_schedule.pure_schedule_df. +- For a Gantt chart, it is common to use the full calendar representation best_schedule.merged_stages_datetime_df. --- -### 5) (Опционально) Конвейер SchedulingPipeline +### 5) (Optional) SchedulingPipeline -Эквивалент тех же шагов во «флюентном» стиле. `finish()` возвращает список `ScheduledProject`, из которого берём `[0]` и -читаем `project.schedule`. +An equivalent of the same steps in a “fluent” style. finish() returns a list of ScheduledProject; take [0] and read +project.schedule. ```python from sampo.pipeline import SchedulingPipeline diff --git a/docs/source/guidebook/work_graph.md b/docs/source/guidebook/work_graph.md index e2c71f39..47fb6dbf 100644 --- a/docs/source/guidebook/work_graph.md +++ b/docs/source/guidebook/work_graph.md @@ -1,18 +1,18 @@ # WorkGraph -## Термины +## Terms -- **WorkGraph** — «карта проекта» в виде ациклического графа. Две служебные точки — начало и конец — добавляются - автоматически. -- **GraphNode** — узел графа: содержит задачу (`WorkUnit`) и ссылки на связи с родителями/детьми (у связи есть тип и - пауза). -- **WorkUnit** — описание самой работы: объём и требования к ресурсам (какие люди/сколько). +- WorkGraph — the “project map” in the form of a directed acyclic graph. Two service points — start and finish — are + added automatically. +- GraphNode — a graph node: contains a task (WorkUnit) and references to links with parents/children (a link has a type + and a lag). +- WorkUnit — a description of the work itself: volume and resource requirements (which people/how many). --- -## Как собрать WorkGraph +## How to build a WorkGraph -### А) Сгенерировать автоматически (быстрый старт) +### A) Generate automatically (quick start) ```python from sampo.generator.base import SimpleSynthetic @@ -20,20 +20,20 @@ from sampo.generator.pipeline import SyntheticGraphType ss = SimpleSynthetic() wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, # тип структуры: General / Parallel / Sequential - cluster_counts=10, # число кластеров (групп работ) - bottom_border=100, # нижняя граница количества работ - top_border=200 # верхняя граница количества работ + mode=SyntheticGraphType.GENERAL, # structure type: General / Parallel / Sequential + cluster_counts=10, # number of clusters (groups of tasks) + bottom_border=100, # lower bound for the number of tasks + top_border=200 # upper bound for the number of tasks ) ``` -* `mode` — форма графа (общий, параллельный, последовательный). -* `cluster_counts` — сколько групп работ в графе. -* `bottom_border` / `top_border` — диапазон количества задач. +* mode — the graph shape (general, parallel, sequential). +* cluster_counts — how many groups of tasks are in the graph. +* bottom_border / top_border — the range of the number of tasks. --- -### Б) Загрузить из CSV +### B) Load from CSV ```python from sampo.pipeline import SchedulingPipeline @@ -48,61 +48,61 @@ project = ( .finish()[0] ) -wg = project.wg # готовый WorkGraph +wg = project.wg # ready WorkGraph schedule = project.schedule ``` -* `all_connections=True` — попытаться «достроить» связи, если во входных данных чего‑то не хватает. -* `sep=';'` — разделитель колонок в CSV. -* `LagOptimizationStrategy` — как обращаться с технологическими паузами (можно доверить выбор «AUTO»). +* all_connections=True — attempt to “complete” links if something is missing in the input data. +* sep=';' — column delimiter in the CSV. +* LagOptimizationStrategy — how to handle technological lags (you can leave the choice to “AUTO”). -> Если используете автогенерацию подрядчика через `get_contractor_by_wg`, **ресурсные колонки можно не -добавлять**. Достаточно обязательных полей и зависимостей. +> If you use auto-generation of a contractor via get_contractor_by_wg, you can omit the resource columns. The required +> fields and dependencies are sufficient. --- -## Структура CSV +## CSV structure -### Обязательные колонки +### Required columns -| Поле | Описание | -|-----------------|----------------------------------------------------------------------| -| `activity_id` | Уникальный идентификатор задачи | -| `activity_name` | Человекочитаемое название задачи | -| `granular_name` | Короткий код/метка задачи | -| `volume` | Объём работы (число) | -| `measurement` | Единица измерения (например `unit`, `m3`, `pcs`) | -| `priority` | Целое число для приоритизации (если не нужно — ставьте `0` для всех) | +| Field | Description | +|---------------|-----------------------------------------------------------| +| activity_id | Unique task identifier | +| activity_name | Human-readable task name | +| granular_name | Short task code/label | +| volume | Work volume (number) | +| measurement | Unit of measure (for example, unit, m3, pcs) | +| priority | Integer for prioritization (if not needed, set 0 for all) | -### Зависимости +### Dependencies -Три синхронных списка (длины совпадают): +Three synchronized lists (lengths match): -* `predecessor_ids` — ID предшественников через запятую -* `connection_types` — типы связей (FS, SS, FF и т.п.) -* `lags` — паузы (числа) +* predecessor_ids — predecessor IDs separated by commas +* connection_types — types of links (FS, SS, FF, etc.) +* lags — lags (numbers) -> Пустая ячейка = просто **три разделителя подряд без пробелов** (`;;;`). +> An empty cell = simply three delimiters in a row without spaces (;;;). > -> Пример: -> `predecessor_ids="A,B"`, `connection_types="FS,SS"`, `lags="0,3"` +> Example: +> predecessor_ids="A,B", connection_types="FS,SS", lags="0,3" -### Ресурсы (опционально) +### Resources (optional) -| Поле | Формат | -|--------------|-----------------------------------------------| -| `min_req` | JSON-словарь минимально требуемых ресурсов | -| `max_req` | JSON-словарь максимально допустимых ресурсов | -| `req_volume` | JSON-словарь норм/объёмов для расчёта времени | +| Field | Format | +|------------|-------------------------------------------------------| +| min_req | JSON dictionary of minimally required resources | +| max_req | JSON dictionary of maximum allowed resources | +| req_volume | JSON dictionary of norms/volumes for time calculation | -* Используйте **валидный JSON** с двойными кавычками: - `{"welder": 2, "driver": 1}` -* Если этих колонок нет, планировщик использует автогенерацию ресурсов (например, через `get_contractor_by_wg`). -* Ключи должны совпадать с `Worker.name`. +* Use valid JSON with double quotes: + {"welder": 2, "driver": 1} +* If these columns are not present, the scheduler uses resource auto-generation (for example, via get_contractor_by_wg). +* Keys must match Worker.name. --- -### Минимальный CSV (без ресурсов) +### Minimal CSV (without resources) ``` activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags @@ -111,7 +111,7 @@ B;Task B;B;1.0;unit;0;A;FS;0 C;Task C;C;1.0;unit;0;B;FS;0 ``` -### CSV с ресурсами (пример) +### CSV with resources (example) ``` activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags;min_req;max_req;req_volume @@ -121,7 +121,7 @@ B;Foundation;B;1.0;unit;0;A;FS;0;"{""fitter"":2,""engineer"":1}";"{""fitter"":4, --- -### Автогенерация подрядчика для CSV-графа +### Auto-generating a contractor for a CSV graph ```python from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod @@ -144,7 +144,7 @@ print(f"Makespan: {best_schedule.execution_time}") --- -### В) Собрать программно из узлов +### C) Build programmatically from nodes ```python from sampo.schemas.works import WorkUnit @@ -152,7 +152,7 @@ from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType from sampo.schemas.requirements import WorkerReq from sampo.schemas.time import Time -# Пример цепочки: A -> B -> C (FS, паузы 0) +# Example chain: A -> B -> C (FS, zero lags) wu_a = WorkUnit( id='A', name='Task A', worker_reqs=[WorkerReq(kind='driver', volume=Time(10), min_count=2, max_count=4)], @@ -168,5 +168,5 @@ n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS wg = WorkGraph.from_nodes([n_a, n_b, n_c]) ``` -* Обязательные поля `WorkUnit`: `id`, `name`. -* Для экспорта полезно также указывать `volume`, `measurement`, `priority`. +* Required WorkUnit fields: id, name. +* For export, it is also useful to specify volume, measurement, priority. \ No newline at end of file From 9f732988c577707796048d56acd3c8e38bb00924 Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 8 Oct 2025 21:05:06 +0300 Subject: [PATCH 23/32] Translated it into.md into English. --- docs/source/eng_guidebook/intro.md | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/source/eng_guidebook/intro.md diff --git a/docs/source/eng_guidebook/intro.md b/docs/source/eng_guidebook/intro.md new file mode 100644 index 00000000..feba5d28 --- /dev/null +++ b/docs/source/eng_guidebook/intro.md @@ -0,0 +1,33 @@ +# Introduction + +## Who is this guidebook for? + +The guidebook is structured to help users with different levels of experience: + +* Introductory level — what SAMPO is, how it can help, and how to get started quickly. +* Advanced level — how to customize SAMPO for your specific tasks. + +## What is SAMPO? + +**SAMPO** is a framework for automated optimization of business process planning. +It enables effective use of accumulated corporate knowledge and historical data to automatically build business +process schedules optimized for the required performance indicators. + +## Key Features and Advantages + +SAMPO is built on **metaheuristic, genetic, and neural-network-based scheduling algorithms** that analyze historical +production data and solve the **multi-criteria project scheduling optimization** problem while leveraging accumulated +experience. +A key feature of the framework is its ability to efficiently handle **long-term project schedules** under +uncertainty and with incomplete historical data. + +Thanks to its **modular architecture**, SAMPO supports the integration of **domain-specific algorithms and models**. +This makes the framework applicable to **industrial scheduling problems** across various sectors, including +mining and manufacturing, construction, transportation, and energy. + +## Openness and Documentation + +Licensed under **BSD 3-Clause**, with comprehensive documentation and examples available in **Jupyter Notebooks**. + +> **BSD 3-Clause** is a permissive open-source license that permits the use, modification, and distribution of the code +> (including in commercial projects) as long as copyright notices and disclaimers of liability are retained. From 020097811a0fb2ce07f2d8aea4b19ba0093f5f27 Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 8 Oct 2025 22:57:04 +0300 Subject: [PATCH 24/32] Translated it quickstart.md into English. Improved translation into.md --- docs/source/eng_guidebook/intro.md | 4 +- docs/source/eng_guidebook/quickstart.md | 168 ++++++++++++++++++++++++ docs/source/index.rst | 2 +- 3 files changed, 171 insertions(+), 3 deletions(-) create mode 100644 docs/source/eng_guidebook/quickstart.md diff --git a/docs/source/eng_guidebook/intro.md b/docs/source/eng_guidebook/intro.md index feba5d28..6ef81102 100644 --- a/docs/source/eng_guidebook/intro.md +++ b/docs/source/eng_guidebook/intro.md @@ -18,7 +18,7 @@ process schedules optimized for the required performance indicators. SAMPO is built on **metaheuristic, genetic, and neural-network-based scheduling algorithms** that analyze historical production data and solve the **multi-criteria project scheduling optimization** problem while leveraging accumulated experience. -A key feature of the framework is its ability to efficiently handle **long-term project schedules** under +A key feature of the framework is its ability to efficiently over **long-term** time horizons under uncertainty and with incomplete historical data. Thanks to its **modular architecture**, SAMPO supports the integration of **domain-specific algorithms and models**. @@ -27,7 +27,7 @@ mining and manufacturing, construction, transportation, and energy. ## Openness and Documentation -Licensed under **BSD 3-Clause**, with comprehensive documentation and examples available in **Jupyter Notebooks**. +Licensed under **BSD 3-Clause**, with detailed documentation and examples available in **Jupyter Notebooks**. > **BSD 3-Clause** is a permissive open-source license that permits the use, modification, and distribution of the code > (including in commercial projects) as long as copyright notices and disclaimers of liability are retained. diff --git a/docs/source/eng_guidebook/quickstart.md b/docs/source/eng_guidebook/quickstart.md new file mode 100644 index 00000000..fa8ebb6d --- /dev/null +++ b/docs/source/eng_guidebook/quickstart.md @@ -0,0 +1,168 @@ +# Quick Start + +This section shows how to install the package, prepare a simple example, run the scheduler, and view the result. + +## Installation + +SAMPO is available as a Python package (requires Python 3.10.x): + +```bash +pip install sampo +``` + +## First Plan in a Few Steps + +Let's create a simple project and define it step by step: + +1) **Work graph** — create a `WorkGraph`. For a quick start, we will generate a synthetic one. +2) **Resources** — define a list of `Contractor` objects with workers. +3) **Algorithm** — choose a `Scheduler` (heuristic or genetic). +4) **Run** — generate a `Schedule` and review the result. + + +--- + +### 1) Create a WorkGraph (quick method — synthetic generator) + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator.pipeline import SyntheticGraphType +from sampo.schemas.graph import WorkGraph + +# Initialize the synthetic generator +synthetic = SimpleSynthetic() + +# Generate a small graph: ~2 clusters with 5–8 tasks each +work_graph: WorkGraph = synthetic.work_graph( + mode=SyntheticGraphType.GENERAL, # structure type: GENERAL / PARALLEL / SEQUENTIAL + cluster_counts=2, # 2 clusters + bottom_border=5, # 5–8 tasks per cluster + top_border=8 +) +print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") +``` + +--- + +### 2) Resources (Contractors) + +**Important:** the synthetic graph uses the following standard job types: +`driver`, `fitter`, `manager`, `handyman`, `electrician`, `engineer`. + +A contractor must include workers for every required job type. +The `workers` dictionary is keyed by the resource type name (`req.kind`). + +```python +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker + +# Define several workers for each required type +workers = [ + Worker(id="w_driver", name="driver", count=20), + Worker(id="w_fitter", name="fitter", count=20), + Worker(id="w_manager", name="manager", count=10), + Worker(id="w_handyman", name="handyman", count=20), + Worker(id="w_electrician", name="electrician", count=10), + Worker(id="w_engineer", name="engineer", count=10), +] + +# A single contractor with a complete worker pool +contractors = [ + Contractor( + id="c1", + name="General Contractor", + # Keys are resource type names (match WorkerReq.kind) + workers={w.name: w for w in workers} + ) +] +``` + +Alternative: auto-generate a contractor “from the graph” to ensure resource coverage: + +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod + +contractors = [get_contractor_by_wg( + work_graph, + scaler=1.0, # capacity multiplier (>= 1.0) + method=ContractorGenerationMethod.AVG, # average between min/max needs + contractor_id="c1", + contractor_name="General Contractor" +)] +``` + +--- + +### 3) Choose a Scheduler + +```python +from sampo.scheduler.heft import HEFTScheduler + +# also available: +# from sampo.scheduler.topological import TopologicalScheduler +# from sampo.scheduler.genetic import GeneticScheduler + +scheduler = HEFTScheduler() # fast heuristic for a quick start +``` + +--- + +### 4) Run the Scheduling + +The `schedule(...)` method returns a list of `Schedule` objects. +Take the first one (the best solution): + +```python +best_schedule = scheduler.schedule(work_graph, contractors)[0] +print(f"Projected project duration (makespan): {best_schedule.execution_time}") +``` + +If you need additional details (finish time, timeline, node order), use the extended method: + +```python +best_schedule, finish_time, timeline, node_order = scheduler.schedule_with_cache(work_graph, contractors)[0] +print(f"Makespan: {best_schedule.execution_time}") +``` + +--- + +### View the Schedule (Gantt Chart) + +A reliable way is to get an aggregated representation and visualize it: + +```python +from sampo.utilities.visualization import schedule_gant_chart_fig, VisualizationMode + +merged = best_schedule.merged_stages_datetime_df(offset='2025-01-01') + +fig = schedule_gant_chart_fig( + merged, + visualization=VisualizationMode.ReturnFig + # Specifies what to do with the figure (here: return the object so it can be shown) +) +fig.show() +``` + +- If you want to see only your tasks without the “internal” technical ones, use the table `best_schedule.pure_schedule_df`. +- For a Gantt chart, it is usually better to use the full calendar representation from `best_schedule.merged_stages_datetime_df`. + +--- + +### 5) (Optional) SchedulingPipeline + +The same steps can be performed in a **fluent style** using the `SchedulingPipeline`. +The `finish()` method returns a list of `ScheduledProject`; take `[0]` and read its `project.schedule`. + + +```python +from sampo.pipeline import SchedulingPipeline +from sampo.scheduler.heft import HEFTScheduler + +project = (SchedulingPipeline.create() +.wg(work_graph) +.contractors(contractors) +.schedule(HEFTScheduler()) +.finish()[0]) + +print(f"Project duration: {project.schedule.execution_time}") +``` diff --git a/docs/source/index.rst b/docs/source/index.rst index 32ee396e..6945dbdb 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,5 +1,5 @@ .. SAMPO documentation master file, created by - sphinx-quickstart on Sun Jul 2 18:31:48 2023. + sphinx-quickstart on Sun Jul 2 18:31:48 2025. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. From fd0a9b0917ab1ff20ce3fce98432be598f853fd0 Mon Sep 17 00:00:00 2001 From: NickMur Date: Tue, 14 Oct 2025 14:50:47 +0300 Subject: [PATCH 25/32] Translated it work_graph.md into English. --- docs/source/eng_guidebook/work_graph.md | 174 ++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 docs/source/eng_guidebook/work_graph.md diff --git a/docs/source/eng_guidebook/work_graph.md b/docs/source/eng_guidebook/work_graph.md new file mode 100644 index 00000000..334b2926 --- /dev/null +++ b/docs/source/eng_guidebook/work_graph.md @@ -0,0 +1,174 @@ +# WorkGraph + +## Terms + +- **WorkGraph** — a "project map" represented as a directed acyclic graph. Two service nodes — start and end — are added + automatically. +- **GraphNode** — a graph node that contains a task (`WorkUnit`) and links to its parent/child nodes (each link has a + type and a lag). +- **WorkUnit** — the description of a task: its volume and resource requirements (what kinds of workers and how many). + +--- + +## How to Create a WorkGraph + +### Generate Automatically (Quick Start) + +```python +from sampo.generator.base import SimpleSynthetic +from sampo.generator.pipeline import SyntheticGraphType + +ss = SimpleSynthetic() +wg = ss.work_graph( + mode=SyntheticGraphType.GENERAL, # structure type: General / Parallel / Sequential + cluster_counts=10, # number of clusters (groups of tasks) + bottom_border=100, # lower limit of the number of tasks + top_border=200 # upper limit of the number of tasks +) +``` + +* `mode` — the form of the graph (general, parallel, sequential). +* `cluster_counts` — how many groups of tasks the graph contains. +* `bottom_border` / `top_border` — the range for the number of tasks. + +--- + +### Load from CSV + +```python +from sampo.pipeline import SchedulingPipeline +from sampo.pipeline.lag_optimization import LagOptimizationStrategy +from sampo.scheduler.heft import HEFTScheduler + +project = ( + SchedulingPipeline.create() + .wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) + .lag_optimize(LagOptimizationStrategy.TRUE) # AUTO / NONE / TRUE + .schedule(HEFTScheduler()) + .finish()[0] +) + +wg = project.wg # ready-to-use WorkGraph +schedule = project.schedule +``` + +* `all_connections=True` — try to reconstruct missing dependencies if they are absent in the input data. +* `sep=';'` — column separator in the CSV file. +* `LagOptimizationStrategy` — specifies how lags are processed (you can rely on the "AUTO" option to choose + automatically). + +> If you use `contractor` autogeneration through `get_contractor_by_wg`, **resource columns are optional** — +> only mandatory fields and dependencies are required. + +--- + +## CSV Structure + +### Required Columns + +| Field | Description | +|-----------------|---------------------------------------------------------------| +| `activity_id` | Unique identifier of the task | +| `activity_name` | Human-readable task name | +| `granular_name` | Short task code or label | +| `volume` | Work volume (numeric value) | +| `measurement` | Unit of measurement (e.g., `unit`, `m3`, `pcs`) | +| `priority` | Integer for prioritization (set to `0` for all if not needed) | + +### Dependencies + +Three synchronized lists (their lengths must match): + +* `predecessor_ids` — predecessor IDs separated by commas +* `connection_types` — connection types (FS, SS, FF, etc.) +* `lags` — time delays (numeric values) + +> An empty cell means **three consecutive separators with no spaces** (`;;;`). + +> Dependency example: +> `predecessor_ids="A,B"`, `connection_types="FS,SS"`, `lags="0,3"` + +### Resources (Optional) + +| Field | Format | +|--------------|-------------------------------------------------------| +| `min_req` | JSON dictionary of minimally required resources | +| `max_req` | JSON dictionary of maximum allowed resources | +| `req_volume` | JSON dictionary of norms/volumes for time calculation | + +* Use **valid JSON** with double quotes: + `{"welder": 2, "driver": 1}` +* If these columns are missing, the scheduler automatically generates resources (for example, using + `get_contractor_by_wg`). +* Keys must match `Worker.name`. + +--- + +### Minimal CSV (Without Resources) + +``` +activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags +A;Task A;A;1.0;unit;0;;; +B;Task B;B;1.0;unit;0;A;FS;0 +C;Task C;C;1.0;unit;0;B;FS;0 +``` + +### CSV with Resources + +``` +activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags;min_req;max_req;req_volume +A;Site prep;A;1.0;unit;0;;;;"{""driver"":1,""handyman"":1}";"{""driver"":2,""handyman"":2}";"{""driver"":8,""handyman"":8}" +B;Foundation;B;1.0;unit;0;A;FS;0;"{""fitter"":2,""engineer"":1}";"{""fitter"":4,""engineer"":2}";"{""fitter"":12,""engineer"":12}" +``` + +--- + +### ### Auto-generate a `Contractor` for a CSV-based `WorkGraph` + +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod +from sampo.scheduler.heft import HEFTScheduler + +wg = project.wg + +contractors = [get_contractor_by_wg( + wg, + scaler=1.0, + method=ContractorGenerationMethod.AVG, + contractor_id="c_csv", + contractor_name="CSV Contractor" +)] + +scheduler = HEFTScheduler() +best_schedule, *_ = scheduler.schedule_with_cache(wg, contractors)[0] +print(f"Makespan: {best_schedule.execution_time}") +``` + +--- + +### Create a `WorkGraph` with Python + +```python +from sampo.schemas.works import WorkUnit +from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType +from sampo.schemas.requirements import WorkerReq +from sampo.schemas.time import Time + +# Example chain: A -> B -> C (FS, zero lags) +wu_a = WorkUnit( + id='A', name='Task A', + worker_reqs=[WorkerReq(kind='driver', volume=Time(10), min_count=2, max_count=4)], + volume=1.0, is_service_unit=False +) +wu_b = WorkUnit(id='B', name='Task B', worker_reqs=[], volume=1.0, is_service_unit=False) +wu_c = WorkUnit(id='C', name='Task C', worker_reqs=[], volume=1.0, is_service_unit=False) + +n_a = GraphNode(wu_a, []) +n_b = GraphNode(wu_b, [(n_a, 0, EdgeType.FinishStart)]) # FS +n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS + +wg = WorkGraph.from_nodes([n_a, n_b, n_c]) +``` + +* Mandatory `WorkUnit` fields: `id`, `name`. +* For export, it is also useful to specify `volume`, `measurement`, `priority`. From 6e9d7cd42b8a94ab13f46edbf0692151d414770c Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 15 Oct 2025 18:00:57 +0300 Subject: [PATCH 26/32] Translated it contractors.md into English. --- docs/source/eng_guidebook/contractors.md | 127 +++++++++++++++++++++++ docs/source/guidebook/contractors.md | 6 +- 2 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 docs/source/eng_guidebook/contractors.md diff --git a/docs/source/eng_guidebook/contractors.md b/docs/source/eng_guidebook/contractors.md new file mode 100644 index 00000000..f5a56493 --- /dev/null +++ b/docs/source/eng_guidebook/contractors.md @@ -0,0 +1,127 @@ +# Contractor + +## Terms + +- **Worker Requirement (`WorkerReq`)** — defines who and how many people are needed for a task: + profession (`kind`), minimum/maximum number of workers (`min_count…max_count`), and work volume or productivity rate ( + `volume`). +- **Worker** — represents human resources: specialization (`name`), quantity (`count`), productivity (`productivity`), + owner (`contractor_id`), and optionally cost per unit (`cost_one_unit`). +- **Equipment** — a non-human resource: its type and quantity. Tasks use it the same way as workers. +- **Contractor** — a resource provider, essentially a dictionary of available workers and equipment for the scheduler. + +--- + +## Available Professions: + +- 'driver' +- 'fitter' +- 'handyman' +- 'electrician' +- 'manager' +- 'engineer' + +## How to Define a Contractor + +### Option 1. Manually + +```python +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker +from sampo.schemas.interval import IntervalGaussian + +# Example: a contractor with two professions (drivers and fitters) +contractor = Contractor( + workers={ + # dictionary key 'driver' must match Worker.name='driver' + 'driver': Worker( + id='w1', + name='driver', # profession (must match WorkerReq.kind) + count=8, # number of available workers + productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5) # average productivity with deviation + # cost_one_unit=... optional: cost per unit if cost calculation is required + # contractor_id='c1' usually matches Contractor.id (if set manually) + ), + 'fitter': Worker( + id='w2', + name='fitter', + count=6, + productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6) + ), + }, + id='c1', + name='Contractor A' +) +``` + +**Notes:** + +* Dictionary keys in `workers` **must match** each `Worker.name`. +* The field `WorkerReq.kind` from tasks must match `Worker.name`; otherwise, the scheduler cannot assign workers. +* The `contractor_id` of each worker usually matches `Contractor.id`. +* `IntervalGaussian(μ, σ, low, high)` — defines **average productivity (μ)** with standard deviation (σ) and range + limits `low…high`. + +### Option 2. Quick resource package generator + +```python +from sampo.generator.base import SimpleSynthetic + +ss = SimpleSynthetic() +contractor = ss.contractor(pack_worker_count=10) +# pack_worker_count: A scaling factor for the number of workers in each profession +``` + +--- + +### Option 3. Contractor “from Graph” (based on task requirements) + +```python +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod + +# wg — your WorkGraph with tasks and their WorkerReq +contractor = get_contractor_by_wg( + wg, + scaler=1.0, # scale resource capacities (e.g., 1.5 = +50%) + method=ContractorGenerationMethod.AVG # aggregate requirements by average +) +``` + +**How it works:** + +* Analyzes all `WorkerReq` in the project. +* Aggregates or averages resource needs by job type. +* Builds a contractor with an appropriate number of workers per profession. +* The `scaler` parameter lets you quickly increase or decrease available resources without editing them manually. + +--- + +## A Few More Mini-Examples + +Two contractors defined separately: + +```python +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker + +contractor_a = Contractor( + id='ca', name='Team A', + workers={'welder': Worker(id='wa', name='welder', count=4)} +) + +contractor_b = Contractor( + id='cb', name='Team B', + workers={'driver': Worker(id='wb', name='driver', count=6)} +) + +contractors = [contractor_a, contractor_b] +``` + +Quickly increase resources for an existing one: + +```python +# Previously there were 6 drivers; now there will be 10 +contractor.workers['driver'].count = 10 +``` + +--- \ No newline at end of file diff --git a/docs/source/guidebook/contractors.md b/docs/source/guidebook/contractors.md index afedeeda..19af2a7e 100644 --- a/docs/source/guidebook/contractors.md +++ b/docs/source/guidebook/contractors.md @@ -10,7 +10,7 @@ - Подрядчик (Contractor) — поставщик ресурсов. По сути — словарь доступных «людей» и «техники» для планировщика. --- -## Варианты проффесий: +## Варианты профессий: - 'driver' - 'fitter' - 'handyman' @@ -36,8 +36,8 @@ contractor = Contractor( name='driver', # профессия (должна совпадать с WorkerReq.kind) count=8, # сколько таких людей доступно productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5) # «средняя скорость» с разбросом - # cost_one_unit=..., # при расчёте стоимости — укажите цену за единицу (опционально) - # contractor_id='c1', # как правило, совпадает с Contractor.id (если задаёте вручную) + # cost_one_unit=... при расчёте стоимости — укажите цену за единицу (опционально) + # contractor_id='c1' как правило, совпадает с Contractor.id (если задаёте вручную) ), 'fitter': Worker( id='w2', From 4edb2b76fd63ac42e379aaeea455dfa544a7e361 Mon Sep 17 00:00:00 2001 From: NickMur Date: Wed, 15 Oct 2025 19:08:34 +0300 Subject: [PATCH 27/32] Translated it connections.md into English. --- docs/source/eng_guidebook/connections.md | 74 ++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 docs/source/eng_guidebook/connections.md diff --git a/docs/source/eng_guidebook/connections.md b/docs/source/eng_guidebook/connections.md new file mode 100644 index 00000000..45dc0aa4 --- /dev/null +++ b/docs/source/eng_guidebook/connections.md @@ -0,0 +1,74 @@ +# Task Dependencies + +## Terms + +- **Dependency** — an arrow between tasks that defines when the next one can start. +- **Lag** — the amount of time that must pass after one task before starting the next. +- **IFS (Interruption-Free Sequence)** — a sequence of consecutive tasks performed without breaks by the same crew. + +> **Why it matters (IFS):** It models a continuous block of work, ensuring that tasks performed by the same crew follow +> one another without any interruptions or delays. This is crucial for processes where stopping between steps is +> technologically undesirable or inefficient. + +--- + +## Dependency Types + +| Type | How to read | Meaning (short) | +|------|----------------------------|--------------------------------------------------------------| +| FS | Finish → Start | B can start after A finishes (+ lag, if any) | +| FFS | Finish → Start (with lag) | Same as FS, but with a mandatory pause | +| IFS | Continuous: Finish → Start | B starts immediately after A with no break, by the same crew | +| SS | Start ↔ Start | A and B can start together | +| FF | Finish ↔ Finish | B must finish no earlier than A | + +## When to Use Each Type + +- Regular sequence of actions: **FS**. +- Technological pause required: **FFS** (or **FS** with a lag). +- Continuous work by the same crew without interruption: **IFS**. +- Simultaneous start of tasks: **SS**. +- Tasks must finish at the same time: **FF**. + +--- + +## Mini-Examples + +1) **FS + lag** + A: “Pour concrete” (finishes on Day 2) → lag 3 days → B: “Wall masonry” (not earlier than Day 5). + +2) **IFS** + A: “Drilling” ⇒ B: “Anchoring” ⇒ C: “Post installation” (same crew, no breaks). + +3) **SS** + A: “Server startup” ≡ B: “Monitoring launch” (simultaneous start). + +4) **FF** + A: “Main configuration” || B: “Documentation” (must finish at the same time). + +5) **FFS** + A: “Apply primer” → lag 4 hours → B: “Painting”. + +Legend: → (FS/FFS), ⇒ (IFS), ≡ (SS), || (FF). + +--- + +## Example of a CSV File with Dependencies + +Suppose we have a file named `predecessors.csv`: + +``` +child_id,parent_id,type,lag +B,A,FS,3 +C,B,IFS,0 +D,C,IFS,0 +E,A,SS,0 +``` + +Explanation: + +- **B** starts after **A** + 3 (lag of 3 days). +- **C** and **D** together with **B** form a continuous chain (**IFS**). +- **E** starts together with **A** (**SS**, does not delay the process). + +``` \ No newline at end of file From 934c580353a1f34225c072d5b6901887aeba20b4 Mon Sep 17 00:00:00 2001 From: NickMur Date: Thu, 16 Oct 2025 14:01:32 +0300 Subject: [PATCH 28/32] Translated it algorithms.md into English. --- docs/source/eng_guidebook/algorithms.md | 139 ++++++++++++++++++++++++ docs/source/eng_guidebook/index.md | 14 +++ docs/source/index.rst | 2 +- 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 docs/source/eng_guidebook/algorithms.md create mode 100644 docs/source/eng_guidebook/index.md diff --git a/docs/source/eng_guidebook/algorithms.md b/docs/source/eng_guidebook/algorithms.md new file mode 100644 index 00000000..579014b7 --- /dev/null +++ b/docs/source/eng_guidebook/algorithms.md @@ -0,0 +1,139 @@ +# Scheduling Algorithms + +## How to choose an algorithm + +### Heuristic schedulers (HEFT, HEFTBetween, Topological) + +- Provide a workable schedule quickly. +- HEFT/HEFTBetween compute task order and approximate execution times to reduce project makespan. +- Topological orders tasks by dependencies without complex optimization. +- HEFT builds a plan from scratch over the entire graph, while HEFTBetween inserts new activities into an already + occupied calendar, trying not to reshuffle the previously built plan. + +Imports: + +```python +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.heft import HEFTBetweenScheduler +from sampo.scheduler.topological import TopologicalScheduler +``` + +--- + +### Genetic scheduler + +- Tries many different combinations and often finds a schedule with a shorter project completion time (minimizing project makespan). + Works slower than simpler algorithms. +- Key parameters: + - `number_of_generation` — how many times to improve the solutions (more → higher chance of improvement, but slower). + - `size_of_population` — how many alternatives to keep simultaneously (more → more ideas, but slower and more memory-consuming). + - `mutate_order` — how often to change the task order (while preserving dependencies) + (higher values → wider search, but slower convergence). + - `mutate_resources` — how often to change the allocation of resources/contractors + (higher values → better parallelism, but may cause more “conflicts” if resources are scarce). + - Additionally: `work_estimator`, `seed` (for reproducibility). + +Example: + +```python +from sampo.scheduler.genetic import GeneticScheduler + +scheduler = GeneticScheduler( + number_of_generation=50, + size_of_population=100, + mutate_order=0.1, + mutate_resources=0.1 +) +``` + +--- + +## Multi-Agent Scheduling + +Splits a project into parts (blocks), schedules them using different strategies, and assembles a global plan. +Useful for large projects and for combining strategies (`sampo.scheduler.multi_agency`). + +### “Auction” without block partitioning + +```python +from uuid import uuid4 +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.topological import TopologicalScheduler +from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager +from sampo.schemas.contractor import Contractor +from sampo.schemas.resources import Worker +from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod + +# 1) Small work graph +ss = SimpleSynthetic(231) +wg = ss.work_graph(bottom_border=30, top_border=40) + +# 2) Contractor with the required worker types +kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} +cid = str(uuid4()) +workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} +contractors = [get_contractor_by_wg( + wg, + scaler=1.0, # increase if needed, e.g., 1.2 or 1.5 + method=ContractorGenerationMethod.AVG, +)] + +# 3) Two agents with different approaches +agents = [ + Agent("HEFT", HEFTScheduler(), contractors), + Agent("Topological", TopologicalScheduler(), contractors), +] +manager = StochasticManager(agents) + +# 4) “Auction”: agents propose schedules; pick the best by finish time +start, end, schedule, winner = manager.run_auction(wg) +print("Winning agent:", winner.name, "Makespan:", end - start) +``` + +### With Block Partitioning + +```python +from random import Random +from sampo.generator.base import SimpleSynthetic +from sampo.scheduler.heft import HEFTScheduler +from sampo.scheduler.topological import TopologicalScheduler +from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager +from sampo.scheduler.multi_agency.block_generator import generate_blocks, SyntheticBlockGraphType + +# 1) Build a "block graph" (each block is its own small WorkGraph) +seed = 231 +rand = Random(seed) +bg = generate_blocks( + SyntheticBlockGraphType.RANDOM, + n_blocks=4, + type_prop=[1, 1, 1], + count_supplier=lambda i: (10, 15), + edge_prob=0.3, + rand=rand +) + +# 2) Contractors for agents +ss = SimpleSynthetic(rand) +contractor_a = ss.contractor(40) +contractor_b = ss.contractor(40) + +# 3) Agents with different algorithms +agents = [ + Agent("HEFT", HEFTScheduler(), [contractor_a]), + Agent("Topo", TopologicalScheduler(), [contractor_b]), +] +manager = StochasticManager(agents) + +# 4) Schedule blocks in order, respecting inter-block dependencies +scheduled_blocks = manager.manage_blocks(bg) + +# 5) Summary: who took which block and when it ran +print("Scheduled blocks:") +for block_id, sblock in scheduled_blocks.items(): + print(f"Block {block_id}: agent={sblock.agent.name}, " + f"start={sblock.start_time}, end={sblock.end_time}, duration={sblock.duration}") + +project_finish = max(sb.end_time for sb in scheduled_blocks.values()) +print("Project finish time:", project_finish) +``` diff --git a/docs/source/eng_guidebook/index.md b/docs/source/eng_guidebook/index.md new file mode 100644 index 00000000..c3cc416b --- /dev/null +++ b/docs/source/eng_guidebook/index.md @@ -0,0 +1,14 @@ +# Guidebook + +```{toctree} +:maxdepth: 2 +:numbered: +caption: Guidebook SAMPO +intro +quickstart +work_graph +connections +contractors +algorithms +``` + diff --git a/docs/source/index.rst b/docs/source/index.rst index 6945dbdb..68cbbec1 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -10,7 +10,7 @@ Welcome to SAMPO's documentation! :maxdepth: 2 :caption: Contents: - guidebook/index + eng_guidebook/index autoapi/sampo/index From f75b905f08062294851aa474b0070e5bf11ad58b Mon Sep 17 00:00:00 2001 From: NickMur Date: Thu, 16 Oct 2025 14:43:57 +0300 Subject: [PATCH 29/32] delete old version --- docs/source/guidebook/algorithms.md | 138 --------------------- docs/source/guidebook/connections.md | 72 ----------- docs/source/guidebook/contractors.md | 124 ------------------- docs/source/guidebook/index.md | 14 --- docs/source/guidebook/intro.md | 34 ------ docs/source/guidebook/quickstart.md | 164 ------------------------- docs/source/guidebook/work_graph.md | 172 --------------------------- 7 files changed, 718 deletions(-) delete mode 100644 docs/source/guidebook/algorithms.md delete mode 100644 docs/source/guidebook/connections.md delete mode 100644 docs/source/guidebook/contractors.md delete mode 100644 docs/source/guidebook/index.md delete mode 100644 docs/source/guidebook/intro.md delete mode 100644 docs/source/guidebook/quickstart.md delete mode 100644 docs/source/guidebook/work_graph.md diff --git a/docs/source/guidebook/algorithms.md b/docs/source/guidebook/algorithms.md deleted file mode 100644 index 4731a731..00000000 --- a/docs/source/guidebook/algorithms.md +++ /dev/null @@ -1,138 +0,0 @@ -# Scheduling Algorithms - -## How to choose an algorithm - -### Heuristic schedulers (HEFT, HEFTBetween, Topological) - -- Quickly produce a workable plan. -- HEFT/HEFTBetween compute the task order and approximate execution time to finish sooner. -- Topological simply orders tasks by dependencies, without complex optimization. -- HEFT builds a plan from scratch over the entire graph, while HEFTBetween inserts new tasks into an already occupied - calendar, trying not to rework the previously built plan. - -Imports: - -```python -from sampo.scheduler.heft import HEFTScheduler -from sampo.scheduler.heft import HEFTBetweenScheduler -from sampo.scheduler.topological import TopologicalScheduler -``` - ---- - -### Genetic scheduler - -- Tries many different variants and often finds a plan with a shorter project completion time. Runs longer than the - simple ones. -- Main settings: - - number_of_generation — how many times to improve the solutions (more — higher chance to improve, but longer). - - size_of_population — how many variants to keep simultaneously (more — more ideas, but slower and more memory). - - mutate_order — how often to change the task order (preserving dependencies). Higher — broader search, but may - converge more slowly. - - mutate_resources — how often to change the allocation of resources/contractors. Higher — greater chance to - parallelize, but with resource scarcity may “conflict” more often. - - Additionally: work_estimator, seed (for reproducibility). - -Example: - -```python -from sampo.scheduler.genetic import GeneticScheduler - -scheduler = GeneticScheduler( - number_of_generation=50, - size_of_population=100, - mutate_order=0.1, - mutate_resources=0.1 -) -``` - ---- - -## Multi-agent scheduling - -Splits the project into parts (blocks), plans them in different ways, and assembles the overall plan. Useful on large -projects and when combining strategies (sampo.scheduler.multi_agency). - -### “Auction” without splitting into blocks - -```python -from uuid import uuid4 -from sampo.generator.base import SimpleSynthetic -from sampo.scheduler.heft import HEFTScheduler -from sampo.scheduler.topological import TopologicalScheduler -from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager -from sampo.schemas.contractor import Contractor -from sampo.schemas.resources import Worker -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod - -# 1) Small work graph -ss = SimpleSynthetic(231) -wg = ss.work_graph(bottom_border=30, top_border=40) - -# 2) A contractor with the required worker types -kinds = {req.kind for node in wg.nodes for req in node.work_unit.worker_reqs} -cid = str(uuid4()) -workers = {k: Worker(str(uuid4()), k, 50, contractor_id=cid) for k in kinds} -contractors = [get_contractor_by_wg( - wg, - scaler=1.0, # increase if needed, e.g., 1.2 or 1.5 - method=ContractorGenerationMethod.AVG, -)] -# 3) Two agents with different approaches -agents = [ - Agent("HEFT", HEFTScheduler(), contractors), - Agent("Topological", TopologicalScheduler(), contractors), -] -manager = StochasticManager(agents) - -# 4) “Auction”: agents propose their plans, choose the best by completion time -start, end, schedule, winner = manager.run_auction(wg) -print("Winner agent:", winner.name, "Completion time:", end - start) -``` - -### With block partitioning - -```python -from random import Random -from sampo.generator.base import SimpleSynthetic -from sampo.scheduler.heft import HEFTScheduler -from sampo.scheduler.topological import TopologicalScheduler -from sampo.scheduler.multi_agency.multi_agency import Agent, StochasticManager -from sampo.scheduler.multi_agency.block_generator import generate_blocks, SyntheticBlockGraphType - -# 1) Build a “block graph” (each block is its own small WorkGraph) -seed = 231 -rand = Random(seed) -bg = generate_blocks( - SyntheticBlockGraphType.RANDOM, - n_blocks=4, - type_prop=[1, 1, 1], - count_supplier=lambda i: (10, 15), - edge_prob=0.3, - rand=rand -) - -# 2) Contractors for the agents -ss = SimpleSynthetic(rand) -contractor_a = ss.contractor(40) -contractor_b = ss.contractor(40) - -# 3) Agents with different algorithms -agents = [ - Agent("HEFT", HEFTScheduler(), [contractor_a]), - Agent("Topo", TopologicalScheduler(), [contractor_b]), -] -manager = StochasticManager(agents) - -# 4) Schedule the blocks in order, taking dependencies between them into account -scheduled_blocks = manager.manage_blocks(bg) - -# 5) Results: who took which block and when it was executed -print("Scheduled blocks:") -for block_id, sblock in scheduled_blocks.items(): - print( - f"Block {block_id}: agent={sblock.agent.name}, start={sblock.start_time}, end={sblock.end_time}, duration={sblock.duration}") - -project_finish = max(sb.end_time for sb in scheduled_blocks.values()) -print("Project completion time:", project_finish) -``` diff --git a/docs/source/guidebook/connections.md b/docs/source/guidebook/connections.md deleted file mode 100644 index dfa8d7bc..00000000 --- a/docs/source/guidebook/connections.md +++ /dev/null @@ -1,72 +0,0 @@ -# Connections between tasks - -## Terms - -- Connections — an arrow between tasks that indicates when the next one can start. -- Lag — how long you need to wait after one task before starting the next. -- Uninterrupted chain (IFS) — several tasks in a row without a break, performed by the same crew. - -> Why this is needed (IFS): to avoid starting the next task too early and to observe technological pauses (drying, cooling, etc.). - ---- - -## Connections types - -| Type | How to read | Short meaning | -|------|---------------------------|---------------------------------------------------------| -| FS | Finish → Start | B can start after A finishes (+ lag, if any) | -| FFS | Finish → Start (with lag) | Same, but with a mandatory lag | -| IFS | Uninterrupted: Finish → Start | B goes right after A without a break by the same crew | -| SS | Start ↔ Start | A and B can start together | -| FF | Finish ↔ Finish | B must finish no earlier than A | - ---- - -## When to use which type - -- Ordinary sequence of actions: FS. -- Need to observe a technological pause: FFS (or FS with a lag). -- Need uninterrupted work by the same crew without a break: IFS. -- Common simultaneous start: SS. -- Want to “converge” to a single finish time: FF. - ---- - -## Mini examples - -1) FS + lag - A: “Pour concrete” (finish Day 2) → lag 3 days → B: “Bricklaying” (no earlier than Day 5). - -2) IFS - A: “Drilling” ⇒ B: “Anchoring” ⇒ C: “Stand installation” (one crew, without a break). - -3) SS - A: “Server launch” ≡ B: “Start of monitoring” (simultaneously). - -4) FF - A: “Primary configuration” || B: “Documentation” (finish by the same deadline). - -5) FFS - A: “Apply primer” → lag 4 hours → B: “Painting”. - -Legend: → (FS/FFS), ⇒ (IFS), ≡ (SS), || (FF). - ---- - -## Example file with dependencies (CSV) - -Let this be the file predecessors.csv: - -``` -child_id,parent_id,type,lag -B,A,FS,3 -C,B,IFS,0 -D,C,IFS,0 -E,A,SS,0 -``` - -What’s here: - -- B after A + 3 (lag 3). -- C and D together with B form an uninterrupted chain (IFS). -- E starts together with A (SS, does not delay). \ No newline at end of file diff --git a/docs/source/guidebook/contractors.md b/docs/source/guidebook/contractors.md deleted file mode 100644 index 19af2a7e..00000000 --- a/docs/source/guidebook/contractors.md +++ /dev/null @@ -1,124 +0,0 @@ -# Contractor - -## Термины - -- Требование к людям (WorkerReq) — кого и сколько нужно для задачи: профессия (`kind`), минимум/максимум людей ( - `min_count…max_count`), и объём/норма (`volume`). -- Работник (Worker) — кадровые ресурсы: специализация (`name`), количество (`count`), производительность ( - `productivity`), владелец (`contractor_id`), при необходимости — стоимость (`cost_one_unit`). -- Техника (Equipment) — тоже ресурс, только не люди: тип и количество. Задачи используют её так же, как и людей. -- Подрядчик (Contractor) — поставщик ресурсов. По сути — словарь доступных «людей» и «техники» для планировщика. - ---- -## Варианты профессий: -- 'driver' -- 'fitter' -- 'handyman' -- 'electrician' -- 'manager' -- 'engineer' - -## Как задать подрядчика - -### Вариант 1. Вручную - -```python -from sampo.schemas.contractor import Contractor -from sampo.schemas.resources import Worker -from sampo.schemas.interval import IntervalGaussian - -# Пример: подрядчик с двумя профессиями (водители и слесари) -contractor = Contractor( - workers={ - # ключ словаря 'driver' совпадает с Worker.name='driver' - 'driver': Worker( - id='w1', - name='driver', # профессия (должна совпадать с WorkerReq.kind) - count=8, # сколько таких людей доступно - productivity=IntervalGaussian(1.0, 0.1, 0.5, 1.5) # «средняя скорость» с разбросом - # cost_one_unit=... при расчёте стоимости — укажите цену за единицу (опционально) - # contractor_id='c1' как правило, совпадает с Contractor.id (если задаёте вручную) - ), - 'fitter': Worker( - id='w2', - name='fitter', - count=6, - productivity=IntervalGaussian(1.2, 0.1, 0.8, 1.6) - ), - }, - id='c1', - name='Contractor A' -) -``` - -Подсказки: - -- Ключи словаря `workers` должны совпадать с `Worker.name`. -- `WorkerReq.kind` из задач должен совпадать с `Worker.name`, иначе бригада не подберётся. -- `contractor_id` у работников обычно совпадает с `Contractor.id`. -- `IntervalGaussian(μ, σ, low, high)` — «средняя производительность μ», с разбросом ±σ и ограничениями `low…high`. - ---- - -### Вариант 2. Быстрый генератор «пакета» ресурсов - -```python -from sampo.generator.base import SimpleSynthetic - -ss = SimpleSynthetic() -contractor = ss.contractor(pack_worker_count=10) -``` - ---- - -### Вариант 3. Подрядчик «по графу» (из требований задач) - -```python -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod - -# wg — ваш WorkGraph с задачами и их WorkerReq -contractor = get_contractor_by_wg( - wg, - scaler=1.0, # можно умножить ресурсы (1.5 = +50%) - method=ContractorGenerationMethod.AVG # агрегировать требования «в среднем» -) -``` - -Как это работает: - -- Смотрит на все `WorkerReq` в проекте. -- Складывает/усредняет потребности по профессиям. -- Собирает подрядчика с подходящим количеством людей/типов. -- `scaler` позволяет быстро «дать больше/меньше» ресурсов, не переписывая всё вручную. - ---- - -## Ещё пара мини‑примеров - -Два подрядчика раздельно: - -```python -from sampo.schemas.contractor import Contractor -from sampo.schemas.resources import Worker - -contractor_a = Contractor( - id='ca', name='Team A', - workers={'welder': Worker(id='wa', name='welder', count=4)} -) - -contractor_b = Contractor( - id='cb', name='Team B', - workers={'driver': Worker(id='wb', name='driver', count=6)} -) - -contractors = [contractor_a, contractor_b] -``` - -Быстро «нарастить» ресурсы у существующего: - -```python -# Было 6 водителей, станет 10 -contractor.workers['driver'].count = 10 -``` - ---- \ No newline at end of file diff --git a/docs/source/guidebook/index.md b/docs/source/guidebook/index.md deleted file mode 100644 index 7c1b6632..00000000 --- a/docs/source/guidebook/index.md +++ /dev/null @@ -1,14 +0,0 @@ -# Руководство - -```{toctree} -:maxdepth: 2 -:numbered: -caption: Руководство SAMPO -intro -quickstart -work_graph -connections -contractors -algorithms -``` - diff --git a/docs/source/guidebook/intro.md b/docs/source/guidebook/intro.md deleted file mode 100644 index 2ede5fcf..00000000 --- a/docs/source/guidebook/intro.md +++ /dev/null @@ -1,34 +0,0 @@ -# Introduction - -## Who is this guide for? - -The guide is organized to help users with different levels of expertise: - -* Introductory level — what SAMPO is, how it can help, and how to get started quickly. -* Advanced level — how to configure SAMPO for your tasks. - -## What is SAMPO? - -SAMPO is a framework for automated optimization of business process scheduling. It makes it possible to effectively -leverage accumulated corporate expertise based on retrospective data to automatically build schedules for executing -business processes that are optimal with respect to the required target metrics. - -## Key features and benefits - -SAMPO is based on metaheuristic, genetic, and neural network algorithms that make it possible to analyze retrospective -production data and solve the problem of multi-criteria optimization of planning, taking accumulated experience into -account. A distinguishing feature of the framework is its ability to efficiently solve the scheduling problem over long -time horizons, under uncertainty and with incomplete retrospective data. - -By virtue of its modular structure, SAMPO supports the integration of algorithms and models that take into account the -specifics of a particular domain, so the framework can be applied to industrial problems across various sectors: mining -and manufacturing, construction, transportation, and energy. - -## Openness and documentation - -License: **BSD 3-Clause**, detailed documentation, examples in **Jupyter Notebook**. - -> BSD 3-Clause is a permissive license that allows using, modifying, and distributing the code (including in commercial -> projects) provided that copyright notices and disclaimers are retained. - ---- \ No newline at end of file diff --git a/docs/source/guidebook/quickstart.md b/docs/source/guidebook/quickstart.md deleted file mode 100644 index 16476bd1..00000000 --- a/docs/source/guidebook/quickstart.md +++ /dev/null @@ -1,164 +0,0 @@ -# Quick Start - -We will show installation, preparation of a simple example, running the scheduler, and viewing the result. - -## Installation - -SAMPO is available as a Python package (Python 3.10.x required): - -```bash -pip install sampo -``` - -## First plan in a few steps - -We will create the simplest project and lay it out: - -1) Work graph — create a WorkGraph. For a quick start, we will generate a synthetic one. -2) Resources — describe a list of Contractors with workers. -3) Algorithm — choose a scheduler (heuristic/genetic). -4) Run — get a Schedule and look at the result. - ---- - -### 1) Generating a WorkGraph (quick way — generator) - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.generator.pipeline import SyntheticGraphType -from sampo.schemas.graph import WorkGraph - -# Initialize the synthetic generator -synthetic = SimpleSynthetic() - -# Generate a small graph: ~2 clusters with 5–8 tasks each -work_graph: WorkGraph = synthetic.work_graph( - mode=SyntheticGraphType.GENERAL, # structure type: GENERAL / PARALLEL / SEQUENTIAL - cluster_counts=2, # 2 clusters - bottom_border=5, # 5–8 tasks per cluster - top_border=8 -) -print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") -``` - ---- - -### 2) Resources (Contractors) - -Important: the synthetic graph uses typical professions `driver`, `fitter`, `manager`, `handyman`, `electrician`, -`engineer`. -The contractor must include workers for each required type, and the `workers` dictionary is keyed by the resource kind -name (`req.kind`). - -```python -from sampo.schemas.contractor import Contractor -from sampo.schemas.resources import Worker - -# Specify several workers of each required type -workers = [ - Worker(id="w_driver", name="driver", count=20), - Worker(id="w_fitter", name="fitter", count=20), - Worker(id="w_manager", name="manager", count=10), - Worker(id="w_handyman", name="handyman", count=20), - Worker(id="w_electrician", name="electrician", count=10), - Worker(id="w_engineer", name="engineer", count=10), -] - -# One contractor with a complete pool of workers -contractors = [ - Contractor( - id="c1", - name="General Contractor", - # Keys are resource kind names (match WorkerReq.kind) - workers={w.name: w for w in workers} - ) -] -``` - -Alternative: auto-generate a contractor “based on the graph” so that resources exactly cover the requirements: - -```python -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod - -contractors = [get_contractor_by_wg( - work_graph, - scaler=1.0, # capacity multiplier (>= 1.0) - method=ContractorGenerationMethod.AVG, # averaging between min/max needs - contractor_id="c1", - contractor_name="General Contractor" -)] -``` - ---- - -### 3) Choosing a scheduler - -```python -from sampo.scheduler.heft import HEFTScheduler - -# also available: -# from sampo.scheduler.topological import TopologicalScheduler -# from sampo.scheduler.genetic import GeneticScheduler - -scheduler = HEFTScheduler() # fast heuristic to get started -``` - ---- - -### 4) Running the scheduling - -The schedule(...) method returns a list of Schedule objects. Take the first (best) solution: - -```python -best_schedule = scheduler.schedule(work_graph, contractors)[0] -print(f"Projected project duration (makespan): {best_schedule.execution_time}") -``` - -If you need additional information (finish time, timeline, node order), use the extended method: - -```python -best_schedule, finish_time, timeline, node_order = scheduler.schedule_with_cache(work_graph, contractors)[0] -print(f"Makespan: {best_schedule.execution_time}") -``` - ---- - -### Viewing the schedule (Gantt chart) - -A reliable way is to obtain an aggregated representation and visualize it: - -```python -from sampo.utilities.visualization import schedule_gant_chart_fig, VisualizationMode - -merged = best_schedule.merged_stages_datetime_df(offset='2025-01-01') - -fig = schedule_gant_chart_fig( - merged, - visualization=VisualizationMode.ReturnFig, - color_type='contractor' # you can change the coloring if needed -) -fig.show() -``` - -- If you want to see only your tasks without “internal” technical ones, use the table best_schedule.pure_schedule_df. -- For a Gantt chart, it is common to use the full calendar representation best_schedule.merged_stages_datetime_df. - ---- - -### 5) (Optional) SchedulingPipeline - -An equivalent of the same steps in a “fluent” style. finish() returns a list of ScheduledProject; take [0] and read -project.schedule. - -```python -from sampo.pipeline import SchedulingPipeline -from sampo.scheduler.heft import HEFTScheduler - -project = (SchedulingPipeline.create() -.wg(work_graph) -.contractors(contractors) -.schedule(HEFTScheduler()) -.finish()[0]) - -print(f"Project duration: {project.schedule.execution_time}") -``` \ No newline at end of file diff --git a/docs/source/guidebook/work_graph.md b/docs/source/guidebook/work_graph.md deleted file mode 100644 index 47fb6dbf..00000000 --- a/docs/source/guidebook/work_graph.md +++ /dev/null @@ -1,172 +0,0 @@ -# WorkGraph - -## Terms - -- WorkGraph — the “project map” in the form of a directed acyclic graph. Two service points — start and finish — are - added automatically. -- GraphNode — a graph node: contains a task (WorkUnit) and references to links with parents/children (a link has a type - and a lag). -- WorkUnit — a description of the work itself: volume and resource requirements (which people/how many). - ---- - -## How to build a WorkGraph - -### A) Generate automatically (quick start) - -```python -from sampo.generator.base import SimpleSynthetic -from sampo.generator.pipeline import SyntheticGraphType - -ss = SimpleSynthetic() -wg = ss.work_graph( - mode=SyntheticGraphType.GENERAL, # structure type: General / Parallel / Sequential - cluster_counts=10, # number of clusters (groups of tasks) - bottom_border=100, # lower bound for the number of tasks - top_border=200 # upper bound for the number of tasks -) -``` - -* mode — the graph shape (general, parallel, sequential). -* cluster_counts — how many groups of tasks are in the graph. -* bottom_border / top_border — the range of the number of tasks. - ---- - -### B) Load from CSV - -```python -from sampo.pipeline import SchedulingPipeline -from sampo.pipeline.lag_optimization import LagOptimizationStrategy -from sampo.scheduler.heft import HEFTScheduler - -project = ( - SchedulingPipeline.create() - .wg(wg='tests/parser/test_wg.csv', sep=';', all_connections=True) - .lag_optimize(LagOptimizationStrategy.TRUE) # AUTO / NONE / TRUE - .schedule(HEFTScheduler()) - .finish()[0] -) - -wg = project.wg # ready WorkGraph -schedule = project.schedule -``` - -* all_connections=True — attempt to “complete” links if something is missing in the input data. -* sep=';' — column delimiter in the CSV. -* LagOptimizationStrategy — how to handle technological lags (you can leave the choice to “AUTO”). - -> If you use auto-generation of a contractor via get_contractor_by_wg, you can omit the resource columns. The required -> fields and dependencies are sufficient. - ---- - -## CSV structure - -### Required columns - -| Field | Description | -|---------------|-----------------------------------------------------------| -| activity_id | Unique task identifier | -| activity_name | Human-readable task name | -| granular_name | Short task code/label | -| volume | Work volume (number) | -| measurement | Unit of measure (for example, unit, m3, pcs) | -| priority | Integer for prioritization (if not needed, set 0 for all) | - -### Dependencies - -Three synchronized lists (lengths match): - -* predecessor_ids — predecessor IDs separated by commas -* connection_types — types of links (FS, SS, FF, etc.) -* lags — lags (numbers) - -> An empty cell = simply three delimiters in a row without spaces (;;;). -> -> Example: -> predecessor_ids="A,B", connection_types="FS,SS", lags="0,3" - -### Resources (optional) - -| Field | Format | -|------------|-------------------------------------------------------| -| min_req | JSON dictionary of minimally required resources | -| max_req | JSON dictionary of maximum allowed resources | -| req_volume | JSON dictionary of norms/volumes for time calculation | - -* Use valid JSON with double quotes: - {"welder": 2, "driver": 1} -* If these columns are not present, the scheduler uses resource auto-generation (for example, via get_contractor_by_wg). -* Keys must match Worker.name. - ---- - -### Minimal CSV (without resources) - -``` -activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags -A;Task A;A;1.0;unit;0;;; -B;Task B;B;1.0;unit;0;A;FS;0 -C;Task C;C;1.0;unit;0;B;FS;0 -``` - -### CSV with resources (example) - -``` -activity_id;activity_name;granular_name;volume;measurement;priority;predecessor_ids;connection_types;lags;min_req;max_req;req_volume -A;Site prep;A;1.0;unit;0;;;;"{""driver"":1,""handyman"":1}";"{""driver"":2,""handyman"":2}";"{""driver"":8,""handyman"":8}" -B;Foundation;B;1.0;unit;0;A;FS;0;"{""fitter"":2,""engineer"":1}";"{""fitter"":4,""engineer"":2}";"{""fitter"":12,""engineer"":12}" -``` - ---- - -### Auto-generating a contractor for a CSV graph - -```python -from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod -from sampo.scheduler.heft import HEFTScheduler - -wg = project.wg - -contractors = [get_contractor_by_wg( - wg, - scaler=1.0, - method=ContractorGenerationMethod.AVG, - contractor_id="c_csv", - contractor_name="CSV Contractor" -)] - -scheduler = HEFTScheduler() -best_schedule, *_ = scheduler.schedule_with_cache(wg, contractors)[0] -print(f"Makespan: {best_schedule.execution_time}") -``` - ---- - -### C) Build programmatically from nodes - -```python -from sampo.schemas.works import WorkUnit -from sampo.schemas.graph import GraphNode, WorkGraph, EdgeType -from sampo.schemas.requirements import WorkerReq -from sampo.schemas.time import Time - -# Example chain: A -> B -> C (FS, zero lags) -wu_a = WorkUnit( - id='A', name='Task A', - worker_reqs=[WorkerReq(kind='driver', volume=Time(10), min_count=2, max_count=4)], - volume=1.0, is_service_unit=False -) -wu_b = WorkUnit(id='B', name='Task B', worker_reqs=[], volume=1.0, is_service_unit=False) -wu_c = WorkUnit(id='C', name='Task C', worker_reqs=[], volume=1.0, is_service_unit=False) - -n_a = GraphNode(wu_a, []) -n_b = GraphNode(wu_b, [(n_a, 0, EdgeType.FinishStart)]) # FS -n_c = GraphNode(wu_c, [(n_b, 0, EdgeType.FinishStart)]) # FS - -wg = WorkGraph.from_nodes([n_a, n_b, n_c]) -``` - -* Required WorkUnit fields: id, name. -* For export, it is also useful to specify volume, measurement, priority. \ No newline at end of file From a8e5dea0456822120fe8414a6061bdf13115d89f Mon Sep 17 00:00:00 2001 From: NickMur Date: Fri, 17 Oct 2025 14:08:03 +0300 Subject: [PATCH 30/32] Little fix --- docs/source/eng_guidebook/connections.md | 23 ++++++++++++----------- requirements.txt | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/source/eng_guidebook/connections.md b/docs/source/eng_guidebook/connections.md index 45dc0aa4..f59cb8ce 100644 --- a/docs/source/eng_guidebook/connections.md +++ b/docs/source/eng_guidebook/connections.md @@ -4,9 +4,10 @@ - **Dependency** — an arrow between tasks that defines when the next one can start. - **Lag** — the amount of time that must pass after one task before starting the next. -- **IFS (Interruption-Free Sequence)** — a sequence of consecutive tasks performed without breaks by the same crew. +- **IFS (Interruption-Free Sequence)** — a sequence of consecutive tasks performed without breaks by the same workers. -> **Why it matters (IFS):** It models a continuous block of work, ensuring that tasks performed by the same crew follow +> **Why it matters (IFS):** It models a continuous block of work, ensuring that tasks performed by the same workers +> follow > one another without any interruptions or delays. This is crucial for processes where stopping between steps is > technologically undesirable or inefficient. @@ -14,19 +15,19 @@ ## Dependency Types -| Type | How to read | Meaning (short) | -|------|----------------------------|--------------------------------------------------------------| -| FS | Finish → Start | B can start after A finishes (+ lag, if any) | -| FFS | Finish → Start (with lag) | Same as FS, but with a mandatory pause | -| IFS | Continuous: Finish → Start | B starts immediately after A with no break, by the same crew | -| SS | Start ↔ Start | A and B can start together | -| FF | Finish ↔ Finish | B must finish no earlier than A | +| Type | How to read | Meaning (short) | +|------|----------------------------|-----------------------------------------------------------------| +| FS | Finish → Start | B can start after A finishes (+ lag, if any) | +| FFS | Finish → Start (with lag) | Same as FS, but with a mandatory pause | +| IFS | Continuous: Finish → Start | B starts immediately after A with no break, by the same workers | +| SS | Start ↔ Start | A and B can start together | +| FF | Finish ↔ Finish | B must finish no earlier than A | ## When to Use Each Type - Regular sequence of actions: **FS**. - Technological pause required: **FFS** (or **FS** with a lag). -- Continuous work by the same crew without interruption: **IFS**. +- Continuous work by the same workers without interruption: **IFS**. - Simultaneous start of tasks: **SS**. - Tasks must finish at the same time: **FF**. @@ -38,7 +39,7 @@ A: “Pour concrete” (finishes on Day 2) → lag 3 days → B: “Wall masonry” (not earlier than Day 5). 2) **IFS** - A: “Drilling” ⇒ B: “Anchoring” ⇒ C: “Post installation” (same crew, no breaks). + A: “Drilling” ⇒ B: “Anchoring” ⇒ C: “Post installation” (same workers, no breaks). 3) **SS** A: “Server startup” ≡ B: “Monitoring launch” (simultaneous start). diff --git a/requirements.txt b/requirements.txt index c39d5c7b..f27584ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,7 +30,7 @@ sphinx>=4.5.0 blurb sphinx-lint==0.6.7 sphinxext-opengraph==0.7.5 -sphinx-autoapi==2.1.1 +sphinx-autoapi==3.1.0 furo>=2023.5.20 # The theme used by the documentation is stored separately, so we need From 43d48292251116b73481c71f5349c8d42e706ade Mon Sep 17 00:00:00 2001 From: NickMur Date: Mon, 20 Oct 2025 16:24:59 +0300 Subject: [PATCH 31/32] Little fix (add myst-parser; fix bug with astroid) --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index f27584ea..12c8e82d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,16 +26,16 @@ pytest-xdist~=3.1.0 # Sphinx version is pinned so that new versions that introduce new warnings # won't suddenly cause build failures. Updating the version is fine as long # as no warnings are raised by doing so. -sphinx>=4.5.0 +sphinx==7.4.7 blurb sphinx-lint==0.6.7 sphinxext-opengraph==0.7.5 sphinx-autoapi==3.1.0 +myst-parser==4.0.1 furo>=2023.5.20 # The theme used by the documentation is stored separately, so we need # to install that as well. python-docs-theme>=2022.1 sphinx-rtd-theme - - +astroid<4 From 1f08e70d91ed02ff044ddbbc708dd3b2a7b98eab Mon Sep 17 00:00:00 2001 From: NickMur Date: Tue, 21 Oct 2025 14:37:27 +0300 Subject: [PATCH 32/32] Little fix --- docs/source/eng_guidebook/contractors.md | 6 +++--- docs/source/eng_guidebook/quickstart.md | 10 +++++----- docs/source/eng_guidebook/work_graph.md | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/source/eng_guidebook/contractors.md b/docs/source/eng_guidebook/contractors.md index f5a56493..dfd53770 100644 --- a/docs/source/eng_guidebook/contractors.md +++ b/docs/source/eng_guidebook/contractors.md @@ -23,7 +23,7 @@ ## How to Define a Contractor -### Option 1. Manually +### Manually ```python from sampo.schemas.contractor import Contractor @@ -62,7 +62,7 @@ contractor = Contractor( * `IntervalGaussian(μ, σ, low, high)` — defines **average productivity (μ)** with standard deviation (σ) and range limits `low…high`. -### Option 2. Quick resource package generator +### Quick resource package generator ```python from sampo.generator.base import SimpleSynthetic @@ -74,7 +74,7 @@ contractor = ss.contractor(pack_worker_count=10) --- -### Option 3. Contractor “from Graph” (based on task requirements) +### Contractor “from Graph” (based on task requirements) ```python from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod diff --git a/docs/source/eng_guidebook/quickstart.md b/docs/source/eng_guidebook/quickstart.md index fa8ebb6d..56b18cf8 100644 --- a/docs/source/eng_guidebook/quickstart.md +++ b/docs/source/eng_guidebook/quickstart.md @@ -22,7 +22,7 @@ Let's create a simple project and define it step by step: --- -### 1) Create a WorkGraph (quick method — synthetic generator) +### Create a WorkGraph (quick method — synthetic generator) ```python from sampo.generator.base import SimpleSynthetic @@ -44,7 +44,7 @@ print(f"Generated a WorkGraph with {len(work_graph.nodes)} tasks.") --- -### 2) Resources (Contractors) +### Resources (Contractors) **Important:** the synthetic graph uses the following standard job types: `driver`, `fitter`, `manager`, `handyman`, `electrician`, `engineer`. @@ -93,7 +93,7 @@ contractors = [get_contractor_by_wg( --- -### 3) Choose a Scheduler +### Choose a Scheduler ```python from sampo.scheduler.heft import HEFTScheduler @@ -107,7 +107,7 @@ scheduler = HEFTScheduler() # fast heuristic for a quick start --- -### 4) Run the Scheduling +### Run the Scheduling The `schedule(...)` method returns a list of `Schedule` objects. Take the first one (the best solution): @@ -148,7 +148,7 @@ fig.show() --- -### 5) (Optional) SchedulingPipeline +### (Optional) SchedulingPipeline The same steps can be performed in a **fluent style** using the `SchedulingPipeline`. The `finish()` method returns a list of `ScheduledProject`; take `[0]` and read its `project.schedule`. diff --git a/docs/source/eng_guidebook/work_graph.md b/docs/source/eng_guidebook/work_graph.md index 334b2926..efa55a66 100644 --- a/docs/source/eng_guidebook/work_graph.md +++ b/docs/source/eng_guidebook/work_graph.md @@ -123,7 +123,7 @@ B;Foundation;B;1.0;unit;0;A;FS;0;"{""fitter"":2,""engineer"":1}";"{""fitter"":4, --- -### ### Auto-generate a `Contractor` for a CSV-based `WorkGraph` +### Auto-generate a `Contractor` for a CSV-based `WorkGraph` ```python from sampo.generator.environment.contractor_by_wg import get_contractor_by_wg, ContractorGenerationMethod