Skip to content

Commit

Permalink
1.0.0rc5
Browse files Browse the repository at this point in the history
  • Loading branch information
Mallets committed Jun 8, 2017
1 parent 452cf59 commit 87d65dc
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 4 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Expand Up @@ -64,7 +64,7 @@
# The short X.Y version.
version = '1.0.0'
# The full version, including alpha/beta/rc tags.
release = 'rc4'
release = 'rc5'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
272 changes: 272 additions & 0 deletions docs/tutorial/pipeline.rst
@@ -0,0 +1,272 @@
.. _pipeline:

========
Pipeline
========

Pipelines allow to arbitrarly interconnect generators and resources by chaining them.
Messages belonging to a pipeline automatically flow from a resource to another without the needs of explicitly defining the `hops`.
Please note that a generator (and their messages) belong only to a single pipeline.
If multiple pipelines are needed simultaneously, they need to be merged fisrt. See :ref:`pipeline_branching` for more details.

Single pipeline
===============

Let's start with a simulation where messages are generated by ``Generator #0`` and are served by ``Resource #0`` and ``Resource #1``.

.. code-block:: none
|Generator #0| -> |Resource #0| -> |Resource #1|
The SimPype code would hence be:

.. code-block:: python
import simpype
import random
sim = simpype.Simulation(id = 'single')
gen0 = sim.add_generator(id = 'gen0')
gen0.random['arrival'] = {0: lambda: random.expovariate(1.0)}
res0 = sim.add_resource(id = 'res0')
res0.random['service'] = {0: lambda: random.expovariate(2.0)}
res1 = sim.add_resource(id = 'res1')
res1.random['service'] = {0: lambda: random.expovariate(2.0)}
p0 = sim.add_pipeline(gen0, res0, res1)
sim.run(until = 5)
As it can be noticed in ``sim.log`` file, messages generated by ``Generator #0`` automatically flow through ``Resource #0`` and ``Resource #1`` without the needs of explicitly defining the next hop:

.. code-block:: none
timestamp,message,seq_num,resource,event
0.000000000,gen0,0,res0,pipe.default.in
0.000000000,gen0,0,res0,pipe.default.out
0.044474460,gen0,0,res0,resource.serve
0.044474460,gen0,0,res1,pipe.default.in
0.044474460,gen0,0,res1,pipe.default.out
0.867233916,gen0,1,res0,pipe.default.in
0.867233916,gen0,1,res0,pipe.default.out
1.099185483,gen0,0,res1,resource.serve
1.876512438,gen0,1,res0,resource.serve
1.876512438,gen0,1,res1,pipe.default.in
1.876512438,gen0,1,res1,pipe.default.out
2.873364054,gen0,1,res1,resource.serve
Overlapping pipelines
=====================

Noe let's continue with a simulation scenarios like the following:

.. code-block:: none
|Generator #0| -\ /-> |Resource #1|
)-> |Resource #0| -(
|Generator #1| -/ \-> |Resource #2|
In this scenario we want to reproduce the following interconnection:

.. code-block:: none
|Generator #0| -> |Resource #0| -> |Resource #1|
|Generator #1| -> |Resource #0| -> |Resource #2|
As it can be noticed, there are two dinstinct `paths/pipelines` that overlap at ``Resource #0``.
However, any messages generated by ``Generator #0`` should end to ``Resource #1``.
Similarly, any messages generated by ``Generator #1`` should end to ``Resource #2``.

In this scenario, ``Resource #0`` is hence shared between the two `pipelines`.
The SimPype code would hence be:

.. code-block:: python
import simpype
import random
sim = simpype.Simulation(id = 'overlap')
gen0 = sim.add_generator(id = 'gen0')
gen0.random['arrival'] = {0: lambda: random.expovariate(1.0)}
gen1 = sim.add_generator(id = 'gen1')
gen1.random['arrival'] = {0: lambda: random.expovariate(1.0)}
res0 = sim.add_resource(id = 'res0')
res0.random['service'] = {0: lambda: random.expovariate(4.0)}
res1 = sim.add_resource(id = 'res1')
res1.random['service'] = {0: lambda: random.expovariate(2.0)}
res2 = sim.add_resource(id = 'res2')
res2.random['service'] = {0: lambda: random.expovariate(2.0)}
p0 = sim.add_pipeline(gen0, res0, res1)
p1 = sim.add_pipeline(gen1, res0, res2)
sim.run(until = 2.5)
As it can be noticed in ``sim.log`` file, messages generated by ``Generator #0`` automatically flow through ``Resource #0`` and ``Resource #1`` and messages generated by ``Generator #1`` automatically flow through ``Resource #0`` and ``Resource #2``. Moreover, ``Resource #0`` is shared between the two `pipelines`:

.. code-block:: none
timestamp,message,seq_num,resource,event
0.000000000,gen0,0,res0,pipe.default.in
0.000000000,gen1,0,res0,pipe.default.in
0.000000000,gen0,0,res0,pipe.default.out
0.372608250,gen0,0,res0,resource.serve
0.372608250,gen0,0,res1,pipe.default.in
0.372608250,gen0,0,res1,pipe.default.out
0.372608250,gen1,0,res0,pipe.default.out
0.515112655,gen0,1,res0,pipe.default.in
0.636849329,gen1,0,res0,resource.serve
0.636849329,gen1,0,res2,pipe.default.in
0.636849329,gen1,0,res2,pipe.default.out
0.636849329,gen0,1,res0,pipe.default.out
0.653319564,gen0,1,res0,resource.serve
0.653319564,gen0,1,res1,pipe.default.in
0.684766776,gen1,1,res0,pipe.default.in
0.684766776,gen1,1,res0,pipe.default.out
0.851617505,gen0,0,res1,resource.serve
0.851617505,gen0,1,res1,pipe.default.out
0.921614468,gen1,2,res0,pipe.default.in
0.949578262,gen1,1,res0,resource.serve
0.949578262,gen1,1,res2,pipe.default.in
0.949578262,gen1,2,res0,pipe.default.out
1.052881475,gen1,2,res0,resource.serve
1.052881475,gen1,2,res2,pipe.default.in
1.079748898,gen0,1,res1,resource.serve
1.245866822,gen1,3,res0,pipe.default.in
1.245866822,gen1,3,res0,pipe.default.out
1.352498249,gen1,0,res2,resource.serve
1.352498249,gen1,1,res2,pipe.default.out
1.369990105,gen1,4,res0,pipe.default.in
1.384336838,gen1,1,res2,resource.serve
1.384336838,gen1,2,res2,pipe.default.out
1.385217621,gen1,5,res0,pipe.default.in
1.418331444,gen1,2,res2,resource.serve
1.582122574,gen1,3,res0,resource.serve
1.582122574,gen1,3,res2,pipe.default.in
1.582122574,gen1,3,res2,pipe.default.out
1.582122574,gen1,4,res0,pipe.default.out
2.028251841,gen1,4,res0,resource.serve
2.028251841,gen1,4,res2,pipe.default.in
2.028251841,gen1,5,res0,pipe.default.out
2.148959938,gen1,6,res0,pipe.default.in
.. _pipeline_branching:

Branching pipeline
==================

Now let's continue with a pipeline having a branching point with one generator and three resources:

.. code-block:: none
/-> |Resource #1|
|Generator #0| -> |Resource #0| -(
\-> |Resource #2|
There are two possible options at this stage:

* Serve a copy of the same message to both ``Resource #1`` and ``Resource #2``;
* Either serve a message to ``Resource #1`` or to ``Resource #2``.


Automatic copy
--------------

In case of serving a copy of the same message to both ``Resource #1`` and ``Resource #2``, the SimPype code would hence be:

.. code-block:: python
import simpype
import random
sim = simpype.Simulation(id = 'single')
gen0 = sim.add_generator(id = 'gen0')
gen0.random['arrival'] = {0: lambda: random.expovariate(1.0)}
res0 = sim.add_resource(id = 'res0')
res0.random['service'] = {0: lambda: random.expovariate(2.0)}
res1 = sim.add_resource(id = 'res1')
res1.random['service'] = {0: lambda: random.expovariate(2.0)}
res2 = sim.add_resource(id = 'res2')
res2.random['service'] = {0: lambda: random.expovariate(2.0)}
p0 = sim.add_pipeline(gen0, res0, res1)
p1 = sim.add_pipeline(gen0, res0, res2)
pM = sim.merge_pipeline(p0, p1)
sim.run(until = 5)
Please note the use of :meth:`~simpype.simulation.Simulation.merge_pipeline`. This function merges multiple `pipelines` into a single one, thus creating the branching point.
Withouth calling the :meth:`~simpype.simulation.Simulation.merge_pipeline` function, the only active `pipeline` would have been ``p1``.

As it can be noticed in ``sim.log`` file, messages are automatically copied and served to both ``Resource #1`` and ``Resource #2`` after being served by ``Resource #0``:

.. code-block:: none
timestamp,message,seq_num,resource,event
0.000000000,gen0,0,res0,pipe.default.in
0.000000000,gen0,0,res0,pipe.default.out
0.412762064,gen0,0,res0,resource.serve
0.412762064,gen0,0,res2,pipe.default.in
0.412762064,gen0,0,res1,pipe.default.in
0.412762064,gen0,0,res2,pipe.default.out
0.412762064,gen0,0,res1,pipe.default.out
0.631472230,gen0,0,res1,resource.serve
0.989221320,gen0,0,res2,resource.serve
2.545794865,gen0,1,res0,pipe.default.in
2.545794865,gen0,1,res0,pipe.default.out
2.572402316,gen0,1,res0,resource.serve
2.572402316,gen0,1,res2,pipe.default.in
2.572402316,gen0,1,res1,pipe.default.in
2.572402316,gen0,1,res2,pipe.default.out
2.572402316,gen0,1,res1,pipe.default.out
2.602942195,gen0,1,res1,resource.serve
4.163453623,gen0,2,res0,pipe.default.in
4.163453623,gen0,2,res0,pipe.default.out
4.222865258,gen0,2,res0,resource.serve
4.222865258,gen0,2,res2,pipe.default.in
4.222865258,gen0,2,res1,pipe.default.in
4.222865258,gen0,2,res1,pipe.default.out
4.270038314,gen0,1,res2,resource.serve
4.270038314,gen0,2,res2,pipe.default.out
4.360461106,gen0,2,res2,resource.serve
4.551208266,gen0,2,res1,resource.serve
Next hop selection
------------------

Please refer to :ref:`message_next` in :ref:`message` section to understand how the next hop of the messages can be dynamically changed.

Miscellaneous
=============

:meth:`~simpype.simulation.Simulation.add_pipeline` admits both :class:`~simpype.resource.Resource` and :class:`~simpype.pipeline.Pipeline` objects as arguments as shown in this examples:

.. code-block:: python
import simpype
sim = simpype.Simulation(id = 'single')
gen0 = sim.add_generator(id = 'gen0')
gen1 = sim.add_generator(id = 'gen1')
res0 = sim.add_resource(id = 'res0')
res1 = sim.add_resource(id = 'res1')
res2 = sim.add_resource(id = 'res2')
res3 = sim.add_resource(id = 'res3')
res4 = sim.add_resource(id = 'res4')
res5 = sim.add_resource(id = 'res5')
res6 = sim.add_resource(id = 'res6')
# Only resources
p0 = sim.add_pipeline(res0, res1, res2)
p1 = sim.add_pipeline(res3, res4, res5)
# Mixed pipeline and resources
p2 = sim.add_pipeline(gen0, p0)
p3 = sim.add_pipeline(gen1, p1)
p4 = sim.add_pipeline(p3, res6)
# Only pipelines
# Equivalent to sim.add_pipeline(res0, res1, res2, res3, res4, res5)
p4 = sim.add_pipeline(p0, p1)
Instead, :meth:`~simpype.simulation.Simulation.merge_pipeline` only admits :class:`~simpype.pipeline.Pipeline` objects as arguments.
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -14,7 +14,7 @@
# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
version='1.0.0.rc4',
version='1.0.0.rc5',
description='SimPype is a simulation framework based on Simpy that relies on the concepts of resource and pipe.',
long_description=long_description,
# The project's main homepage.
Expand Down
2 changes: 1 addition & 1 deletion simpype/__init__.py
Expand Up @@ -58,4 +58,4 @@ def compile_toc(entries, section_marker='='):
__all__ = [obj.__name__ for section, objs in toc for obj in objs]

__path__ = extend_path(__path__, __name__)
__version__ = '1.0.0.rc4'
__version__ = '1.0.0.rc5'
2 changes: 1 addition & 1 deletion simpype/build.py
Expand Up @@ -41,7 +41,7 @@ def logger(name, path):
Python Logger from logging module :class:`logging.Logger`
"""
# Create a logger
logger = logging.getLogger(name)
logger = logging.getLogger(path)
logger.setLevel(logging.INFO)
# Create a file handler
# delay = True does not create the logging file if nothing is written
Expand Down

0 comments on commit 87d65dc

Please sign in to comment.