示例#1
0
def _build_results(records: Records) -> Results:
    """
    Create results from the records.

    Parameters
    ----------
    records : Records
    """
    ret = Results()

    #
    # 1) Build system size, queue size and busy (server size)
    #    distributions. To do this, we need PMFs. Queue size
    #    PMF and busy PMF can be computed from system size PMF.
    #
    system_size_pmf = list(records.system_size.pmf)
    num_states = len(system_size_pmf)
    p0 = system_size_pmf[0]
    p1 = system_size_pmf[1] if num_states > 1 else 0.0

    queue_size_pmf = [p0 + p1] + system_size_pmf[2:]
    server_size_pmf = [p0, sum(system_size_pmf[1:])]

    ret.system_size = CountableDistribution(system_size_pmf)
    ret.queue_size = CountableDistribution(queue_size_pmf)
    ret.busy = CountableDistribution(server_size_pmf)

    #
    # 2) For future estimations, we need packets and some filters.
    #    Group all of them here.
    #
    all_packets = records.packets
    served_packets = [packet for packet in all_packets if packet.served]
    dropped_packets = [packet for packet in all_packets if packet.dropped]

    #
    # 3) Build scalar statistics.
    #
    ret.loss_prob = len(dropped_packets) / len(all_packets)

    #
    # 4) Build various intervals statistics: departures, waiting times,
    #    response times.
    #
    departure_intervals = _get_departure_intervals(served_packets)
    ret.departures = build_statistics(np.asarray(departure_intervals))
    ret.response_time = build_statistics([
        pkt.departed_at - pkt.created_at for pkt in served_packets
    ])
    ret.wait_time = build_statistics([
        pkt.service_started_at - pkt.created_at for pkt in served_packets
    ])

    return ret
示例#2
0
def test_countable_distribution_with_pmf_props(pmf, string):
    """
    Validate that CountableDistribution can be defined with PMF.
    """
    dist = CountableDistribution(pmf)
    assert dist.truncated_at == len(pmf) - 1
    assert dist.max_value == len(pmf) - 1

    # Validate values:
    for i, prob in enumerate(pmf):
        assert_allclose(dist.pmf(i),
                        prob,
                        err_msg=f"PMF mismatch at i={i} (PMF: {pmf})")

    # Validate CDF values:
    cdf = np.cumsum(pmf)
    for i, prob in enumerate(cdf):
        assert_allclose(dist.cdf(i),
                        prob,
                        err_msg=f"CDF mismatch at i={i} (PMF: {pmf})")

    # Validate points outside the PMF:
    assert_allclose(dist.pmf(-1), 0.0)
    assert_allclose(dist.pmf(len(pmf) + 1), 0.0)
    assert_allclose(dist.cdf(-1), 0.0)
    assert_allclose(dist.cdf(len(pmf) + 1), 1.0)

    # Validate converting to string:
    assert str(dist) == string
示例#3
0
def test_countable_distribution_with_fn_props(fn, precision, truncated_at):
    """
    Validate that CountableDistribution stops when tail probability is less
    then precision.
    """
    dist = CountableDistribution(fn, precision=precision)
    assert dist.truncated_at == truncated_at, str(dist)
示例#4
0
 def system_size(self) -> CountableDistribution:
     props = self._get_system_size_props()
     m1, var = props.get('avg', None), props.get('var', None)
     moments = []
     if m1 is not None:
         moments = [m1, var + m1**2] if var is not None else [m1]
     return CountableDistribution(self.get_system_size_prob,
                                  precision=self._precision,
                                  moments=moments)
示例#5
0
def test_countable_distribution_with_fn_and_max_value_ignores_large_values():
    """
    Validate that if probability is given in functional form, but max_value
    is provided, then any value above it is ignored.
    """
    pmf = [0.2, 0.3, 0.5]
    touched = [False] * 10  # elements after 2 should not be touched!

    def prob(x: int) -> float:
        touched[x] = True
        return pmf[x] if 0 <= x < len(pmf) else 0.0

    dist = CountableDistribution(prob, max_value=2)  # probs given to 0, 1, 2
    assert_allclose(dist.pmf(0), pmf[0])
    assert_allclose(dist.pmf(1), pmf[1])
    assert_allclose(dist.pmf(2), pmf[2])
    assert all(touched[:3])
    assert not any(touched[3:])

    # Now ask for cdf at, say, 4, and make sure no elements above 3 touched:
    assert_allclose(dist.cdf(4), 1.0)
    assert not any(touched[3:]), "touched items >= 3 in CDF call"

    # Also ask for PMF at even larger element, and make sure no items touched:
    assert_allclose(dist.pmf(9), 0.0)
    assert not any(touched[3:]), "touched items >= 3 in PMF call"
示例#6
0
    def queue_size(self) -> CountableDistribution:
        props = self._get_queue_size_props()
        m1, var = props.get('avg', None), props.get('var', None)
        moments = []
        if m1 is not None:
            moments = [m1, var + m1**2] if var is not None else [m1]

        def fn(x: int) -> float:
            if x > 0:
                return self.get_system_size_prob(x + 1)
            if x == 0:
                return self.get_system_size_prob(0) + \
                       self.get_system_size_prob(1)
            return 0.0

        return CountableDistribution(fn,
                                     precision=self._precision,
                                     moments=moments)
示例#7
0
    def __init__(self, records: Optional[Records] = None, 
                 num_stations: Optional[int] = None):
        """
        Create results.

        Parameters
        ----------
        records : Records, optional
        num_stations: int, optional
        """
        self.real_time = 0.0
        self.system_size: List[CountableDistribution] = []
        self.queue_size: List[CountableDistribution] = []
        self.busy: List[CountableDistribution] = []
        self.drop_prob: List[float] = []
        self.delivery_prob: List[float] = []
        self.departures: List[Statistics] = []
        self.arrivals: List[Statistics] = []
        self.wait_time: List[Statistics] = []
        self.response_time: List[Statistics] = []
        self.delivery_delays: List[Statistics] = []

        self._num_stations = records.num_stations if records is not None \
            else num_stations

        if records is None:
            return

        def idiv(x: int, y: int, default: float = 0.0) -> float:
            return x / y if y != 0 else default

        for i in range(records.num_stations):
            #
            # 1) Build system size, queue size and busy (server size)
            #    distributions for each node. To do this, we need PMFs.
            #    Queue size PMF and busy PMF can be computed from system size PMF.
            #
            system_size_pmf = list(records.get_system_size(i).pmf)
            num_states = len(system_size_pmf)
            p0 = system_size_pmf[0]
            p1 = system_size_pmf[1] if num_states > 1 else 0.0

            queue_size_pmf = [p0 + p1] + system_size_pmf[2:]
            server_size_pmf = [p0, sum(system_size_pmf[1:])]

            self.system_size.append(CountableDistribution(system_size_pmf))
            self.queue_size.append(CountableDistribution(queue_size_pmf))
            self.busy.append(CountableDistribution(server_size_pmf))

            #
            # 2) For future estimations, we need packets and some filters.
            #    Group all of them here.
            #
            packets = records.packets
            arrived_packets = [p for p in packets if p.arrived[i] is not None]
            served_packets = [p for p in arrived_packets if p.was_served[i]]
            dropped_here_packets = [
                p for p in arrived_packets
                if p.was_dropped and p.drop_node == i]
            source_packets = [p for p in packets if p.source == i]
            delivered_packets = [p for p in source_packets if p.was_delivered]
            dropped_from_packets = [p for p in source_packets if p.was_dropped]

            #
            # 3) Build scalar statistics.
            #
            num_arrived = len(arrived_packets)
            num_dropped_here = len(dropped_here_packets)
            num_dropped_from = len(dropped_from_packets)
            num_delivered = len(delivered_packets)

            # Drop - packet arrived here (at i), but was not queued and dropped
            p_drop = idiv(num_dropped_here, num_arrived)

            # Delivery - packet was generated here (at i) and was delivered
            # (at some another station j).
            # Here we ignore packets those were generated here, but didn't
            # finish transmission till the simulation end.
            p_delivery = idiv(num_delivered, num_delivered + num_dropped_from,
                              default=1.0)

            # Record 'em!
            self.drop_prob.append(p_drop)
            self.delivery_prob.append(p_delivery)

            #
            # 4) Build various intervals statistics: departures, waiting times,
            #    response times.
            #
            departures = _get_packet_intervals(
                served_packets, i, lambda pkt, node: pkt.service_finished[node]
            )
            arrivals = _get_packet_intervals(
                arrived_packets, i, lambda pkt, node: pkt.arrived[node]
            )
            self.departures.append(build_statistics(departures))
            self.arrivals.append(build_statistics(arrivals))
            self.response_time.append(build_statistics([
                p.service_finished[i] - p.arrived[i] for p in served_packets]))
            self.wait_time.append(build_statistics([
                p.service_started[i] - p.arrived[i] for p in served_packets]))
            self.delivery_delays.append(build_statistics([
                p.delivery_time - p.arrived[i] for p in delivered_packets]))
示例#8
0
         states=[Const(3), Const(7), Const(5)], weights=[1, 4, 5]),
     5.6,
     33.0,
     202.4,
     1281.0,
     '(Mixture: '
     'states=[(Const: value=3), (Const: value=7), (Const: value=5)], '
     'probs=[0.1, 0.4, 0.5])',
     1e-2,
     2e-2,
 ),
 # Countable discrete distributions:
 (
     # Geom(0.25) (number of attempts to get 1 success),
     CountableDistribution(
         lambda k: 0.25 * 0.75**(k - 1) if k > 0 else 0,
         precision=1e-9  # to get 1e-2 precision for m4: 1e-2^4 = 1e-8
     ),
     4,
     28,
     292,
     4060,
     '(Countable: p=[0, 0.25, 0.188, 0.141, 0.105, ...], precision=1e-09)',
     0.01,
     0.01),
 (
     # Geom(0.25) (number of attempts to get 1 success), explicit moments
     CountableDistribution(lambda k: 0.25 * 0.75**(k - 1)
                           if k > 0 else 0,
                           precision=0.001,
                           moments=[4, 28, 292, 4060]),
     4,