import json
import ciw
from rich import print
from json2ciw.datasets import load_jackson_network_model
from json2ciw.engine import CiwConverter, multiple_replications
from json2ciw.results import summarise_results, tidy_to_wide_format
from json2ciw.schema import ProcessModelOpen Jackson Network Model
In this example, the model represents a a classic queuing network problem that can be formulated as an Open Jackson Network.
The JSON for this built-in example can be loaded using json2ciw.datasets.load_jackson_network_model.

Imports
Load JSON
json_network = load_jackson_network_model()
print(json.dumps(json_network, indent=2)){ "name": "Open Jackson Network", "description": "A simple jackson network to convert to ciw", "activities": [ { "name": "Service 1", "type": "activity", "resource": { "name": "Servers 1", "capacity": 1 }, "service_distribution": { "type": "exponential", "parameters": { "mean": 0.1 } }, "arrival_distribution": { "type": "exponential", "parameters": { "rate": 1.0 } } }, { "name": "Service 2", "type": "activity", "resource": { "name": "Servers 2", "capacity": 2 }, "service_distribution": { "type": "exponential", "parameters": { "mean": 0.1 } }, "arrival_distribution": { "type": "exponential", "parameters": { "rate": 4.0 } } }, { "name": "Service 3", "type": "activity", "resource": { "name": "Servers 3", "capacity": 1 }, "service_distribution": { "type": "exponential", "parameters": { "mean": 0.1 } }, "arrival_distribution": { "type": "exponential", "parameters": { "rate": 3.0 } } } ], "transitions": [ { "from": "Service 1", "to": "Exit", "probability": 0.1 }, { "from": "Service 1", "to": "Service 2", "probability": 0.6 }, { "from": "Service 1", "to": "Service 3", "probability": 0.3 }, { "from": "Service 2", "to": "Exit", "probability": 0.6 }, { "from": "Service 2", "to": "Service 1", "probability": 0.1 }, { "from": "Service 2", "to": "Service 3", "probability": 0.3 }, { "from": "Service 3", "to": "Exit", "probability": 0.2 }, { "from": "Service 3", "to": "Service 2", "probability": 0.4 }, { "from": "Service 3", "to": "Service 1", "probability": 0.4 } ] }
Validate with ProcessModel
model_instance = ProcessModel(**json_network)print(model_instance)ProcessModel( name='Open Jackson Network', description='A simple jackson network to convert to ciw', activities=[ Activity( name='Service 1', type='activity', resource=Resource(name='Servers 1', capacity=1), service_distribution=Distribution(type='exponential', parameters={'mean': 0.1}), arrival_distribution=Distribution(type='exponential', parameters={'rate': 1.0}), renege_distribution=None ), Activity( name='Service 2', type='activity', resource=Resource(name='Servers 2', capacity=2), service_distribution=Distribution(type='exponential', parameters={'mean': 0.1}), arrival_distribution=Distribution(type='exponential', parameters={'rate': 4.0}), renege_distribution=None ), Activity( name='Service 3', type='activity', resource=Resource(name='Servers 3', capacity=1), service_distribution=Distribution(type='exponential', parameters={'mean': 0.1}), arrival_distribution=Distribution(type='exponential', parameters={'rate': 3.0}), renege_distribution=None ) ], transitions=[ Transition(source='Service 1', target='Exit', probability=0.1), Transition(source='Service 1', target='Service 2', probability=0.6), Transition(source='Service 1', target='Service 3', probability=0.3), Transition(source='Service 2', target='Exit', probability=0.6), Transition(source='Service 2', target='Service 1', probability=0.1), Transition(source='Service 2', target='Service 3', probability=0.3), Transition(source='Service 3', target='Exit', probability=0.2), Transition(source='Service 3', target='Service 2', probability=0.4), Transition(source='Service 3', target='Service 1', probability=0.4) ] )
model_instance.save_diagram("jackson.mmd")flowchart TD
Arrivals_Service_1("Time between arrivals<br/>Exponential(λ=1.0)")
Arrivals_Service_2("Time between arrivals<br/>Exponential(λ=4.0)")
Arrivals_Service_3("Time between arrivals<br/>Exponential(λ=3.0)")
Service_1["Service 1<br/>Exponential(mean=0.1)"]
Service_2["Service 2<br/>Exponential(mean=0.1)"]
Service_3["Service 3<br/>Exponential(mean=0.1)"]
Resource_Servers_1(("Servers 1<br/>(1)"))
Resource_Servers_2(("Servers 2<br/>(2)"))
Resource_Servers_3(("Servers 3<br/>(1)"))
Exit(["Exit"])
Arrivals_Service_1 --> Service_1
Arrivals_Service_2 --> Service_2
Arrivals_Service_3 --> Service_3
Resource_Servers_1 -.Seize.-> Service_1
Service_1 -.Release.-> Resource_Servers_1
Resource_Servers_2 -.Seize.-> Service_2
Service_2 -.Release.-> Resource_Servers_2
Resource_Servers_3 -.Seize.-> Service_3
Service_3 -.Release.-> Resource_Servers_3
Service_1 -->|10%| Exit
Service_1 -->|60%| Service_2
Service_1 -->|30%| Service_3
Service_2 -->|60%| Exit
Service_2 -->|10%| Service_1
Service_2 -->|30%| Service_3
Service_3 -->|20%| Exit
Service_3 -->|40%| Service_2
Service_3 -->|40%| Service_1
model_instance.display_diagram(include_resources=True)flowchart TD Arrivals_Service_1(“Time between arrivals
Exponential(λ=1.0)”) Arrivals_Service_2(“Time between arrivals
Exponential(λ=4.0)”) Arrivals_Service_3(“Time between arrivals
Exponential(λ=3.0)”) Service_1[“Service 1
Exponential(mean=0.1)”] Service_2[“Service 2
Exponential(mean=0.1)”] Service_3[“Service 3
Exponential(mean=0.1)”] Resource_Servers_1((“Servers 1
(1)”)) Resource_Servers_2((“Servers 2
(2)”)) Resource_Servers_3((“Servers 3
(1)”)) Exit([“Exit”])
Arrivals_Service_1 --> Service_1
Arrivals_Service_2 --> Service_2
Arrivals_Service_3 --> Service_3
Resource_Servers_1 -.Seize.-> Service_1
Service_1 -.Release.-> Resource_Servers_1
Resource_Servers_2 -.Seize.-> Service_2
Service_2 -.Release.-> Resource_Servers_2
Resource_Servers_3 -.Seize.-> Service_3
Service_3 -.Release.-> Resource_Servers_3
Service_1 -->|10%| Exit
Service_1 -->|60%| Service_2
Service_1 -->|30%| Service_3
Service_2 -->|60%| Exit
Service_2 -->|10%| Service_1
Service_2 -->|30%| Service_3
Service_3 -->|20%| Exit
Service_3 -->|40%| Service_2
Service_3 -->|40%| Service_1
model_instance.get_distributions_df()| Activity | Phase | Distribution Type | Parameters | |
|---|---|---|---|---|
| 0 | Service 1 | Arrival | Exponential | rate=1.0 |
| 1 | Service 1 | Service | Exponential | mean=0.1 |
| 2 | Service 2 | Arrival | Exponential | rate=4.0 |
| 3 | Service 2 | Service | Exponential | mean=0.1 |
| 4 | Service 3 | Arrival | Exponential | rate=3.0 |
| 5 | Service 3 | Service | Exponential | mean=0.1 |
model_instance.get_routing_matrix_df()| Service 1 | Service 2 | Service 3 | Exit | |
|---|---|---|---|---|
| Source Activity | ||||
| Service 1 | 0.0 | 0.6 | 0.3 | 0.1 |
| Service 2 | 0.1 | 0.0 | 0.3 | 0.6 |
| Service 3 | 0.4 | 0.4 | 0.0 | 0.2 |
model_instance.get_resources_df()| Resource | Activity | Count | |
|---|---|---|---|
| 0 | Servers 1 | Service 1 | 1 |
| 1 | Servers 2 | Service 2 | 2 |
| 2 | Servers 3 | Service 3 | 1 |
Convert to ciw parameters
adapter = CiwConverter(model_instance)
network_params = adapter.generate_params()
print(network_params){ 'number_of_servers': [1, 2, 1], 'arrival_distributions': [Exponential(rate=1.0), Exponential(rate=4.0), Exponential(rate=3.0)], 'service_distributions': [Exponential(rate=10.0), Exponential(rate=10.0), Exponential(rate=10.0)], 'reneging_time_distributions': [None, None, None], 'routing': [[0.0, 0.6, 0.3], [0.1, 0.0, 0.3], [0.4, 0.4, 0.0]] }
Build and run the ciw model
network = ciw.create_network(**network_params)
sim = ciw.Simulation(network)
sim.simulate_until_max_time(50)
print("Quick simulation run worked!")Quick simulation run worked!
Run the model for multiple replications
df_reps = multiple_replications(
network,
model_instance,
num_reps=5,
runtime=2880,
warmup=1440,
n_jobs=-1,
)
df_reps.head()| rep | node_id | activity_name | resource_name | resource_capacity | n_service | mean_wait | mean_service | utilisation | mean_Lq | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 1 | Service 1 | Servers 1 | 1 | 7235 | 0.100526 | 0.101436 | 49.753370 | 0.505073 |
| 1 | 0 | 2 | Service 2 | Servers 2 | 2 | 14480 | 0.035334 | 0.101033 | 49.936752 | 0.355298 |
| 2 | 0 | 3 | Service 3 | Servers 3 | 1 | 10834 | 0.311941 | 0.101618 | 75.011634 | 2.346920 |
| 3 | 1 | 1 | Service 1 | Servers 1 | 1 | 7221 | 0.105431 | 0.100141 | 49.728757 | 0.528695 |
| 4 | 1 | 2 | Service 2 | Servers 2 | 2 | 14483 | 0.034044 | 0.100673 | 50.070456 | 0.342399 |
Convert to wide format
wide = tidy_to_wide_format(df_reps)
wide.head()| mean_Lq [Service 1] | mean_Lq [Service 2] | mean_Lq [Service 3] | mean_service [Service 1] | mean_service [Service 2] | mean_service [Service 3] | mean_wait [Service 1] | mean_wait [Service 2] | mean_wait [Service 3] | n_service [Service 1] | n_service [Service 2] | n_service [Service 3] | utilisation [Service 1] | utilisation [Service 2] | utilisation [Service 3] | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| rep | |||||||||||||||
| 0 | 0.505073 | 0.355298 | 2.346920 | 0.101436 | 0.101033 | 0.101618 | 0.100526 | 0.035334 | 0.311941 | 7235 | 14480 | 10834 | 49.753370 | 49.936752 | 75.011634 |
| 1 | 0.528695 | 0.342399 | 2.633773 | 0.100141 | 0.100673 | 0.100782 | 0.105431 | 0.034044 | 0.345444 | 7221 | 14483 | 10979 | 49.728757 | 50.070456 | 76.194688 |
| 2 | 0.498286 | 0.343490 | 2.731786 | 0.099994 | 0.098454 | 0.101268 | 0.099478 | 0.034136 | 0.364441 | 7213 | 14490 | 10794 | 49.805521 | 49.213628 | 75.446480 |
| 3 | 0.568632 | 0.361709 | 2.589407 | 0.101253 | 0.099272 | 0.100922 | 0.110862 | 0.035452 | 0.337382 | 7386 | 14692 | 11052 | 51.395484 | 50.568808 | 77.015357 |
| 4 | 0.474180 | 0.314434 | 2.454758 | 0.100743 | 0.097752 | 0.101118 | 0.098716 | 0.031779 | 0.331041 | 6917 | 14248 | 10678 | 49.078224 | 48.828468 | 74.168355 |
Summarise results
summary = summarise_results(df_reps)
summary.round(1)| activity | Metric | Service 1 (Servers 1) | Service 2 (Servers 2) | Service 3 (Servers 3) |
|---|---|---|---|---|
| 0 | Mean completed services | 7194.4 | 14478.6 | 10867.4 |
| 1 | Mean waiting time | 0.1 | 0.0 | 0.3 |
| 2 | Mean service time | 0.1 | 0.1 | 0.1 |
| 3 | Mean utilisation | 50.0 | 49.7 | 75.6 |
| 4 | Mean queue length | 0.5 | 0.3 | 2.6 |