コード例 #1
0
 def test_exp_ops(self):
     """Test exponentiation: Numeric"""
     parser = Parser(trappy.BareTrace())
     eqn = "3**3 * 2**4"
     self.assertEquals(parser.solve(eqn), 432)
     eqn = "3**(4/2)"
     self.assertEquals(parser.solve(eqn), 9)
コード例 #2
0
    def test_mul_ops(self):
        """Test Mult and Division: Numeric"""

        parser = Parser(trappy.Run())
        eqn = "(10 * 2 / 10)"
        self.assertEquals(parser.solve(eqn), 2)
        eqn = "-2 * 2 + 2 * 10 / 10"
        self.assertEquals(parser.solve(eqn), -2)
コード例 #3
0
    def test_funcparams_mul(self):
        """Test Mult and Division: Data"""

        thermal_zone_id = 0
        parser = Parser(trappy.FTrace())
        eqn = "trappy.thermal.Thermal:temp * 10.0"
        series = parser.data.thermal.data_frame["temp"]
        assert_series_equal(parser.solve(eqn)[thermal_zone_id], series * 10.0, check_names=False)
        eqn = "trappy.thermal.Thermal:temp / trappy.thermal.Thermal:temp * 10"
        assert_series_equal(parser.solve(eqn)[thermal_zone_id], series / series * 10, check_names=False)
コード例 #4
0
    def test_bool_ops_scalar(self):
        """Test Logical Operations: Vector"""

        thermal_zone_id=0
        parser = Parser(trappy.FTrace())
        # The equation returns a boolean scalar
        eqn = "(numpy.mean(trappy.thermal.Thermal:temp) > 65000) && (numpy.mean(trappy.cpu_power.CpuOutPower) > 500)"
        self.assertTrue(parser.solve(eqn)[thermal_zone_id])
        eqn = "(numpy.mean(trappy.thermal.Thermal:temp) > 65000) || (numpy.mean(trappy.cpu_power.CpuOutPower) < 500)"
        self.assertTrue(parser.solve(eqn)[thermal_zone_id])
コード例 #5
0
    def test_sum_operator(self):
        """Test Addition And Subtraction: Numeric"""

        parser = Parser(trappy.BareTrace())
        # Simple equation
        eqn = "10 + 2 - 3"
        self.assertEquals(parser.solve(eqn), 9)
        # Equation with bracket and unary ops
        eqn = "(10 + 2) - (-3 + 2)"
        self.assertEquals(parser.solve(eqn), 13)
コード例 #6
0
    def test_super_indexing(self):
        "Test if super-indexing works correctly"""

        trace = trappy.FTrace()
        parser = Parser(trace)
        # The first event has less index values
        sol1 = parser.solve("trappy.thermal.Thermal:temp")
        # The second index has more index values
        sol2 = parser.solve("trappy.pid_controller.PIDController:output")
        # Super Indexing should result in len(sol2) > len(sol1)
        self.assertGreater(len(sol2), len(sol1))
コード例 #7
0
    def test_filtered_parse(self):
        """The Parser can filter a trace"""
        trace = trappy.FTrace()

        prs = Parser(trace, filters={"cdev_state": 3})
        dfr_res = prs.solve("devfreq_out_power:freq")
        self.assertEquals(len(dfr_res), 1)
コード例 #8
0
    def test_var_forward(self):
        """Test Forwarding: Variable"""

        thermal_zone_id = 0
        pvars = {}
        pvars["control_temp"] = 78000
        parser = Parser(trappy.FTrace(), pvars=pvars)
        eqn = "numpy.mean(trappy.thermal.Thermal:temp) < control_temp"
        self.assertTrue(parser.solve(eqn)[thermal_zone_id])
コード例 #9
0
    def test_single_func_call(self):
        """Test Single Function Call"""

        thermal_zone_id = 0
        parser = Parser(trappy.FTrace())
        eqn = "numpy.mean(trappy.thermal.Thermal:temp)"
        self.assertEquals(
            parser.solve(eqn)[thermal_zone_id],
            np.mean(
                parser.data.thermal.data_frame["temp"]))
    def test_parser_with_name(self):
        """Test equation using event name"""

        thermal_zone_id = 0
        parser = Parser(trappy.FTrace())
        # Equation with functions as parameters (Mixed)
        eqn = "numpy.mean(thermal:temp) + 1000"
        self.assertEquals(
            parser.solve(eqn)[thermal_zone_id],
            np.mean(parser.data.thermal.data_frame["temp"]) + 1000)
コード例 #11
0
    def test_func_forward(self):
        """Test Forwarding: Mixed"""

        thermal_zone_id = 0
        pvars = {}
        pvars["mean"] = np.mean
        pvars["control_temp"] = 78000
        parser = Parser(trappy.FTrace(), pvars=pvars)
        eqn = "mean(trappy.thermal.Thermal:temp) < control_temp"
        self.assertTrue(parser.solve(eqn)[thermal_zone_id])
コード例 #12
0
    def test_bool_ops_vector(self):
        """Test Logical Operations: Vector"""

        thermal_zone_id = 0
        # The equation returns a vector mask
        parser = Parser(trappy.FTrace())
        eqn = "(trappy.thermal.ThermalGovernor:current_temperature > 77000)\
                & (trappy.pid_controller.PIDController:output > 2500)"
        mask = parser.solve(eqn)
        self.assertEquals(len(parser.ref(mask.dropna()[0])), 0)
コード例 #13
0
    def test_func_forward(self):
        """Test Forwarding: Mixed"""

        thermal_zone_id = 0
        pvars = {}
        pvars["mean"] = np.mean
        pvars["control_temp"] = 78000
        parser = Parser(trappy.FTrace(), pvars=pvars)
        eqn = "mean(trappy.thermal.Thermal:temp) < control_temp"
        self.assertTrue(parser.solve(eqn)[thermal_zone_id])
コード例 #14
0
    def test_funcparams_sum(self):
        """Test Addition And Subtraction: Functions"""

        thermal_zone_id = 0
        parser = Parser(trappy.FTrace())
        # Equation with functions as parameters (Mixed)
        eqn = "numpy.mean(trappy.thermal.Thermal:temp) + 1000"
        self.assertEquals(
            parser.solve(eqn)[thermal_zone_id],
            np.mean(
                parser.data.thermal.data_frame["temp"]) +
            1000)
        # Multiple func params
        eqn = "numpy.mean(trappy.thermal.Thermal:temp) + numpy.mean(trappy.thermal.Thermal:temp)"
        self.assertEquals(
            parser.solve(eqn)[thermal_zone_id],
            np.mean(
                parser.data.thermal.data_frame["temp"]) *
            2)
コード例 #15
0
    def test_parser_with_name(self):
        """Test equation using event name"""

        thermal_zone_id = 0
        parser = Parser(trappy.FTrace())
        # Equation with functions as parameters (Mixed)
        eqn = "numpy.mean(thermal:temp) + 1000"
        self.assertEquals(
            parser.solve(eqn)[thermal_zone_id],
            np.mean(
                parser.data.thermal.data_frame["temp"]) + 1000)
コード例 #16
0
    def test_bool_ops_vector(self):
        """Test Logical Operations: Vector"""

        thermal_zone_id = 0
        # The equation returns a vector mask
        parser = Parser(trappy.FTrace())
        eqn = "(trappy.thermal.ThermalGovernor:current_temperature > 77000)\
                & (trappy.pid_controller.PIDController:output > 2500)"

        mask = parser.solve(eqn)
        self.assertEqual(len(parser.ref(mask.dropna()[0])), 0)
コード例 #17
0
    def test_accessors_sum(self):
        """Test Addition And Subtraction: Data"""

        thermal_zone_id = 0
        parser = Parser(trappy.FTrace())
        # Equation with dataframe accessors
        eqn = "trappy.thermal.Thermal:temp + \
trappy.thermal.Thermal:temp"

        assert_series_equal(parser.solve(eqn)[thermal_zone_id],
                            2 * parser.data.thermal.data_frame["temp"],
                            check_names=False)
コード例 #18
0
ファイル: Analyzer.py プロジェクト: jrjang/bart
class Analyzer(object):

    """
    :param data: TRAPpy Run Object
    :type data: :mod:`trappy.run.Run`

    :param config: A dictionary of variables, classes
        and functions that can be used in the statements
    :type config: dict
    """

    def __init__(self, data, config, topology=None):
        self._parser = Parser(data, config, topology)

    def assertStatement(self, statement, select=None):
        """Solve the statement for a boolean result

        :param statement: A string representing a valid
            :mod:`trappy.stats.grammar` statement
        :type statement: str

        :param select: If the result represents a boolean
            mask and the data was derived from a TRAPpy event
            with a pivot value. The :code:`select` can be
            used to select a particular pivot value
        :type select: :mod:`pandas.DataFrame` column
        """

        result = self.getStatement(statement, select=select)

        # pylint: disable=no-member
        if not (isinstance(result, bool) or isinstance(result, np.bool_)):
            warnings.warn(
                "solution of {} is not an instance of bool".format(statement))
        return result
        # pylint: enable=no-member

    def getStatement(self, statement, reference=False, select=None):
        """Evaluate the statement"""

        result = self._parser.solve(statement)

        # pylint: disable=no-member
        if np.isscalar(result):
            return result
        # pylint: enable=no-member

        if select is not None and len(result):
            result = result[select]
            if reference:
                result = self._parser.ref(result)

        return result
コード例 #19
0
    def test_windowed_parse(self):
        """Test that the parser can operate on a window of the trace"""
        trace = trappy.FTrace()

        prs = Parser(trace, window=(2, 3))
        dfr_res = prs.solve("thermal:temp")

        self.assertGreater(dfr_res.index[0], 2)
        self.assertLess(dfr_res.index[-1], 3)

        prs = Parser(trace, window=(4, None))
        dfr_res = prs.solve("thermal:temp")

        self.assertGreater(dfr_res.index[0], 4)
        self.assertEquals(dfr_res.index[-1], trace.thermal.data_frame.index[-1])

        prs = Parser(trace, window=(0, 1))
        dfr_res = prs.solve("thermal:temp")

        self.assertEquals(dfr_res.index[0], trace.thermal.data_frame.index[0])
        self.assertLess(dfr_res.index[-1], 1)
コード例 #20
0
    def test_for_parsed_event(self):
        """Test if an added parsed event can be accessed"""

        trace = trappy.FTrace(scope="custom")
        dfr = pandas.DataFrame({"l1_misses": [24, 535,  41],
                                "l2_misses": [155, 11, 200],
                                "cpu":       [ 0,   1,   0]},
                           index=pandas.Series([1.020, 1.342, 1.451], name="Time"))
        trace.add_parsed_event("pmu_counters", dfr)

        p = Parser(trace)
        self.assertTrue(len(p.solve("pmu_counters:cpu")), 3)
コード例 #21
0
    def test_windowed_parse(self):
        """Test that the parser can operate on a window of the trace"""
        trace = trappy.FTrace()

        prs = Parser(trace, window=(2, 3))
        dfr_res = prs.solve("thermal:temp")

        self.assertGreater(dfr_res.index[0], 2)
        self.assertLess(dfr_res.index[-1], 3)

        prs = Parser(trace, window=(4, None))
        dfr_res = prs.solve("thermal:temp")

        self.assertGreater(dfr_res.index[0], 4)
        self.assertEqual(dfr_res.index[-1], trace.thermal.data_frame.index[-1])

        prs = Parser(trace, window=(0, 1))
        dfr_res = prs.solve("thermal:temp")

        self.assertEqual(dfr_res.index[0], trace.thermal.data_frame.index[0])
        self.assertLess(dfr_res.index[-1], 1)
コード例 #22
0
    def test_accessors_sum(self):
        """Test Addition And Subtraction: Data"""

        thermal_zone_id = 0
        parser = Parser(trappy.FTrace())
        # Equation with dataframe accessors
        eqn = "trappy.thermal.Thermal:temp + \
trappy.thermal.Thermal:temp"

        assert_series_equal(
            parser.solve(eqn)[thermal_zone_id],
            2 *
            parser.data.thermal.data_frame["temp"], check_names=False)
コード例 #23
0
    def test_cls_forward(self):
        """Test Forwarding: Classes"""

        cls = trappy.thermal.Thermal
        pvars = {}
        pvars["mean"] = np.mean
        pvars["control_temp"] = 78000
        pvars["therm"] = cls

        thermal_zone_id = 0
        parser = Parser(trappy.FTrace(), pvars=pvars)
        eqn = "mean(therm:temp) < control_temp"
        self.assertTrue(parser.solve(eqn)[thermal_zone_id])
コード例 #24
0
    def test_cls_forward(self):
        """Test Forwarding: Classes"""

        cls = trappy.thermal.Thermal
        pvars = {}
        pvars["mean"] = np.mean
        pvars["control_temp"] = 78000
        pvars["therm"] = cls

        thermal_zone_id = 0
        parser = Parser(trappy.Run(), pvars=pvars)
        eqn = "mean(therm:temp) < control_temp"
        self.assertTrue(parser.solve(eqn)[thermal_zone_id])
コード例 #25
0
class Analyzer(object):
    """
    :param data: TRAPpy Run Object
    :type data: :mod:`trappy.run.Run`

    :param config: A dictionary of variables, classes
        and functions that can be used in the statements
    :type config: dict
    """
    def __init__(self, data, config, topology=None):
        self._parser = Parser(data, config, topology)

    def assertStatement(self, statement, select=None):
        """Solve the statement for a boolean result

        :param statement: A string representing a valid
            :mod:`trappy.stats.grammar` statement
        :type statement: str

        :param select: If the result represents a boolean
            mask and the data was derived from a TRAPpy event
            with a pivot value. The :code:`select` can be
            used to select a particular pivot value
        :type select: :mod:`pandas.DataFrame` column
        """

        result = self.getStatement(statement, select=select)

        # pylint: disable=no-member
        if not (isinstance(result, bool) or isinstance(result, np.bool_)):
            warnings.warn(
                "solution of {} is not an instance of bool".format(statement))
        return result
        # pylint: enable=no-member

    def getStatement(self, statement, reference=False, select=None):
        """Evaluate the statement"""

        result = self._parser.solve(statement)

        # pylint: disable=no-member
        if np.isscalar(result):
            return result
        # pylint: enable=no-member

        if select is not None and len(result):
            result = result[select]
            if reference:
                result = self._parser.ref(result)

        return result
コード例 #26
0
    def test_for_parsed_event(self):
        """Test if an added parsed event can be accessed"""

        trace = trappy.FTrace(scope="custom")
        dfr = pandas.DataFrame(
            {
                "l1_misses": [24, 535, 41],
                "l2_misses": [155, 11, 200],
                "cpu": [0, 1, 0]
            },
            index=pandas.Series([1.020, 1.342, 1.451], name="Time"))
        trace.add_parsed_event("pmu_counters", dfr)

        p = Parser(trace)
        self.assertTrue(len(p.solve("pmu_counters:cpu")), 3)
コード例 #27
0
ファイル: Analyzer.py プロジェクト: sinkap/bart
class Analyzer(object):

    """
        Args:
            data (trappy.Run): A trappy.Run instance
            config (dict): A dictionary of variables, classes
                and functions that can be used in the statements
    """

    def __init__(self, data, config, topology=None):
        self._parser = Parser(data, config, topology)

    def assertStatement(self, statement, select=None):
        """Solve the statement for a boolean result"""

        result = self.getStatement(statement, select=select)

        # pylint: disable=no-member
        if not (isinstance(result, bool) or isinstance(result, np.bool_)):
            warnings.warn(
                "solution of {} is not an instance of bool".format(statement))
        return result
        # pylint: enable=no-member

    def getStatement(self, statement, reference=False, select=None):
        """Evaluate the statement"""

        result = self._parser.solve(statement)

        # pylint: disable=no-member
        if np.isscalar(result):
            return result
        # pylint: enable=no-member

        if select is not None and len(result):
            result = result[select]
            if reference:
                result = self._parser.ref(result)

        return result
コード例 #28
0
class Analyzer(object):
    """
        Args:
            data (trappy.Run): A trappy.Run instance
            config (dict): A dictionary of variables, classes
                and functions that can be used in the statements
    """
    def __init__(self, data, config, topology=None):
        self._parser = Parser(data, config, topology)

    def assertStatement(self, statement, select=None):
        """Solve the statement for a boolean result"""

        result = self.getStatement(statement, select=select)

        # pylint: disable=no-member
        if not (isinstance(result, bool) or isinstance(result, np.bool_)):
            warnings.warn(
                "solution of {} is not an instance of bool".format(statement))
        return result
        # pylint: enable=no-member

    def getStatement(self, statement, reference=False, select=None):
        """Evaluate the statement"""

        result = self._parser.solve(statement)

        # pylint: disable=no-member
        if np.isscalar(result):
            return result
        # pylint: enable=no-member

        if select is not None and len(result):
            result = result[select]
            if reference:
                result = self._parser.ref(result)

        return result
コード例 #29
0
class SignalCompare(object):
    """
    :param data: TRAPpy FTrace Object
    :type data: :mod:`trappy.ftrace.FTrace`

    :param sig_a: The first signal
    :type sig_a: str

    :param sig_b: The first signal
    :type sig_b: str

    :param config: A dictionary of variables, classes
        and functions that can be used in the statements
    :type config: dict

    :param method: The method to be used for reindexing data
        This can be one of the standard :mod:`pandas.DataFrame`
        methods (eg. pad, bfill, nearest). The default is pad
        or use the last valid observation.
    :type method: str

    :param limit: The number of indices a value will be propagated
        when reindexing. The default is None
    :type limit: int

    :param fill: Whether to fill the NaNs in the data.
        The default value is True.
    :type fill: bool

    .. note::

        Both the signals must have the same pivots. For example:

            - Signal A has a pivot as :code:`"cpu"` which means that
              the trappy event (:mod:`trappy.base.Base`) has a pivot
              parameter which is equal to :code:`"cpu"`. Then the signal B
              should also have :code:`"cpu"` as it's pivot.

            - Signal A and B can both have undefined or None
              as their pivots
    """
    def __init__(self, data, sig_a, sig_b, **kwargs):

        self._parser = Parser(data,
                              config=kwargs.pop("config", None),
                              **kwargs)
        self._a = sig_a
        self._b = sig_b
        self._pivot_vals, self._pivot = self._get_signal_pivots()

        # Concatenate the indices by doing any operation (say add)
        self._a_data = self._parser.solve(sig_a)
        self._b_data = self._parser.solve(sig_b)

    def _get_signal_pivots(self):
        """Internal function to check pivot conditions and
        return an intersection of pivot on the signals"""

        sig_a_info = self._parser.inspect(self._a)
        sig_b_info = self._parser.inspect(self._b)

        if sig_a_info["pivot"] != sig_b_info["pivot"]:
            raise RuntimeError("The pivot column for both signals" +
                               "should be same (%s,%s)" %
                               (sig_a_info["pivot"], sig_b_info["pivot"]))

        if sig_a_info["pivot"]:
            pivot_vals = set(sig_a_info["pivot_values"]).intersection(
                sig_b_info["pivot_values"])
            pivoted = sig_a_info["pivot"]
        else:
            pivot_vals = [StatConf.GRAMMAR_DEFAULT_PIVOT]
            pivoted = False

        return pivot_vals, pivoted

    def conditional_compare(self, condition, **kwargs):
        """Conditionally compare two signals

        The conditional comparison of signals has two components:

        - **Value Coefficient** :math:`\\alpha_{v}` which measures the difference in values of
          of the two signals when the condition is true:

          .. math::

                \\alpha_{v} = \\frac{area\_under\_curve(S_A\ |\ C(t)\ is\ true)}
                {area\_under\_curve(S_B\ |\ C(t)\ is\ true)} \\\\

                \\alpha_{v} = \\frac{\int S_A(\{t\ |\ C(t)\})dt}{\int S_B(\{t\ |\ C(t)\})dt}

        - **Time Coefficient** :math:`\\alpha_{t}` which measures the time during which the
          condition holds true.

          .. math::

                \\alpha_{t} = \\frac{T_{valid}}{T_{total}}

        :param condition: A condition that returns a truth value and obeys the grammar syntax
            ::

                "event_x:sig_a > event_x:sig_b"

        :type condition: str

        :param method: The method for area calculation. This can
            be any of the integration methods supported in `numpy`
            or `rect`
        :type param: str

        :param step: The step behaviour for area and time
            summation calculation
        :type step: str

        Consider the two signals A and B as follows:

            .. code::

                A = [0, 0, 0, 3, 3, 0, 0, 0]
                B = [0, 0, 2, 2, 2, 2, 1, 1]


            .. code::


                                                     A = xxxx
                3                 *xxxx*xxxx+        B = ----
                                  |         |
                2            *----*----*----+
                             |    |         |
                1            |    |         *----*----+
                             |    |         |
                0  *x-x-*x-x-+xxxx+         +xxxx*xxxx+
                   0    1    2    3    4    5    6    7

        The condition:

        .. math::

            A > B

        is valid between T=3 and T=5. Therefore,

        .. math::

            \\alpha_v=1.5 \\\\
            \\alpha_t=\\frac{2}{7}

        :returns: There are two cases:

            - **Pivoted Signals**
              ::

                    {
                        "pivot_name" : {
                                "pval_1" : (v1,t1),
                                "pval_2" : (v2, t2)
                        }
                    }
            - **Non Pivoted Signals**

              The tuple of :math:`(\\alpha_v, \\alpha_t)`
        """

        if self._pivot:
            result = {self._pivot: {}}

        mask = self._parser.solve(condition)
        step = kwargs.get("step", "post")

        for pivot_val in self._pivot_vals:

            a_piv = self._a_data[pivot_val]
            b_piv = self._b_data[pivot_val]

            area = area_under_curve(a_piv[mask[pivot_val]], **kwargs)
            try:
                area /= area_under_curve(b_piv[mask[pivot_val]], **kwargs)
            except ZeroDivisionError:
                area = float("nan")

            duration = min(a_piv.last_valid_index(), b_piv.last_valid_index())
            duration -= max(a_piv.first_valid_index(),
                            b_piv.first_valid_index())
            duration = interval_sum(mask[pivot_val], step=step) / duration

            if self._pivot:
                result[self._pivot][pivot_val] = area, duration
            else:
                result = area, duration

        return result

    def get_overshoot(self, **kwargs):
        """Special case for :func:`conditional_compare`
        where the condition is:
        ::

            "sig_a > sig_b"

        :param method: The method for area calculation. This can
            be any of the integration methods supported in `numpy`
            or `rect`
        :type param: str

        :param step: The step behaviour for calculation of area
            and time summation
        :type step: str

        .. seealso::

            :func:`conditional_compare`
        """

        condition = " ".join([self._a, ">", self._b])
        return self.conditional_compare(condition, **kwargs)

    def get_undershoot(self, **kwargs):
        """Special case for :func:`conditional_compare`
        where the condition is:
        ::

            "sig_a < sig_b"

        :param method: The method for area calculation. This can
            be any of the integration methods supported in `numpy`
            or `rect`
        :type param: str

        :param step: The step behaviour for calculation of area
            and time summation
        :type step: str

        .. seealso::

            :func:`conditional_compare`
        """

        condition = " ".join([self._a, "<", self._b])
        return self.conditional_compare(condition, **kwargs)
コード例 #30
0
ファイル: signal.py プロジェクト: mdigiorgio/bart
class SignalCompare(object):

    """
    :param data: TRAPpy FTrace Object
    :type data: :mod:`trappy.ftrace.FTrace`

    :param sig_a: The first signal
    :type sig_a: str

    :param sig_b: The first signal
    :type sig_b: str

    :param config: A dictionary of variables, classes
        and functions that can be used in the statements
    :type config: dict

    :param method: The method to be used for reindexing data
        This can be one of the standard :mod:`pandas.DataFrame`
        methods (eg. pad, bfill, nearest). The default is pad
        or use the last valid observation.
    :type method: str

    :param limit: The number of indices a value will be propagated
        when reindexing. The default is None
    :type limit: int

    :param fill: Whether to fill the NaNs in the data.
        The default value is True.
    :type fill: bool

    .. note::

        Both the signals must have the same pivots. For example:

            - Signal A has a pivot as :code:`"cpu"` which means that
              the trappy event (:mod:`trappy.base.Base`) has a pivot
              parameter which is equal to :code:`"cpu"`. Then the signal B
              should also have :code:`"cpu"` as it's pivot.

            - Signal A and B can both have undefined or None
              as their pivots
    """

    def __init__(self, data, sig_a, sig_b, **kwargs):

        self._parser = Parser(
            data,
            config=kwargs.pop(
                "config",
                None),
            **kwargs)
        self._a = sig_a
        self._b = sig_b
        self._pivot_vals, self._pivot = self._get_signal_pivots()

        # Concatenate the indices by doing any operation (say add)
        self._a_data = self._parser.solve(sig_a)
        self._b_data = self._parser.solve(sig_b)

    def _get_signal_pivots(self):
        """Internal function to check pivot conditions and
        return an intersection of pivot on the signals"""

        sig_a_info = self._parser.inspect(self._a)
        sig_b_info = self._parser.inspect(self._b)

        if sig_a_info["pivot"] != sig_b_info["pivot"]:
            raise RuntimeError("The pivot column for both signals" +
                               "should be same (%s,%s)"
                               % (sig_a_info["pivot"], sig_b_info["pivot"]))

        if sig_a_info["pivot"]:
            pivot_vals = set(
                sig_a_info["pivot_values"]).intersection(sig_b_info["pivot_values"])
            pivoted = sig_a_info["pivot"]
        else:
            pivot_vals = [StatConf.GRAMMAR_DEFAULT_PIVOT]
            pivoted = False

        return pivot_vals, pivoted

    def conditional_compare(self, condition, **kwargs):
        """Conditionally compare two signals

        The conditional comparison of signals has two components:

        - **Value Coefficient** :math:`\\alpha_{v}` which measures the difference in values of
          of the two signals when the condition is true:

          .. math::

                \\alpha_{v} = \\frac{area\_under\_curve(S_A\ |\ C(t)\ is\ true)}
                {area\_under\_curve(S_B\ |\ C(t)\ is\ true)} \\\\

                \\alpha_{v} = \\frac{\int S_A(\{t\ |\ C(t)\})dt}{\int S_B(\{t\ |\ C(t)\})dt}

        - **Time Coefficient** :math:`\\alpha_{t}` which measures the time during which the
          condition holds true.

          .. math::

                \\alpha_{t} = \\frac{T_{valid}}{T_{total}}

        :param condition: A condition that returns a truth value and obeys the grammar syntax
            ::

                "event_x:sig_a > event_x:sig_b"

        :type condition: str

        :param method: The method for area calculation. This can
            be any of the integration methods supported in `numpy`
            or `rect`
        :type param: str

        :param step: The step behaviour for area and time
            summation calculation
        :type step: str

        Consider the two signals A and B as follows:

            .. code::

                A = [0, 0, 0, 3, 3, 0, 0, 0]
                B = [0, 0, 2, 2, 2, 2, 1, 1]


            .. code::


                                                     A = xxxx
                3                 *xxxx*xxxx+        B = ----
                                  |         |
                2            *----*----*----+
                             |    |         |
                1            |    |         *----*----+
                             |    |         |
                0  *x-x-*x-x-+xxxx+         +xxxx*xxxx+
                   0    1    2    3    4    5    6    7

        The condition:

        .. math::

            A > B

        is valid between T=3 and T=5. Therefore,

        .. math::

            \\alpha_v=1.5 \\\\
            \\alpha_t=\\frac{2}{7}

        :returns: There are two cases:

            - **Pivoted Signals**
              ::

                    {
                        "pivot_name" : {
                                "pval_1" : (v1,t1),
                                "pval_2" : (v2, t2)
                        }
                    }
            - **Non Pivoted Signals**

              The tuple of :math:`(\\alpha_v, \\alpha_t)`
        """

        if self._pivot:
            result = {self._pivot: {}}

        mask = self._parser.solve(condition)
        step = kwargs.get("step", "post")

        for pivot_val in self._pivot_vals:

            a_piv = self._a_data[pivot_val]
            b_piv = self._b_data[pivot_val]

            area = area_under_curve(a_piv[mask[pivot_val]], **kwargs)
            try:
                area /= area_under_curve(b_piv[mask[pivot_val]], **kwargs)
            except ZeroDivisionError:
                area = float("nan")

            duration = min(a_piv.last_valid_index(), b_piv.last_valid_index())
            duration -= max(a_piv.first_valid_index(),
                            b_piv.first_valid_index())
            duration = interval_sum(mask[pivot_val], step=step) / duration

            if self._pivot:
                result[self._pivot][pivot_val] = area, duration
            else:
                result = area, duration

        return result

    def get_overshoot(self, **kwargs):
        """Special case for :func:`conditional_compare`
        where the condition is:
        ::

            "sig_a > sig_b"

        :param method: The method for area calculation. This can
            be any of the integration methods supported in `numpy`
            or `rect`
        :type param: str

        :param step: The step behaviour for calculation of area
            and time summation
        :type step: str

        .. seealso::

            :func:`conditional_compare`
        """

        condition = " ".join([self._a, ">", self._b])
        return self.conditional_compare(condition, **kwargs)

    def get_undershoot(self, **kwargs):
        """Special case for :func:`conditional_compare`
        where the condition is:
        ::

            "sig_a < sig_b"

        :param method: The method for area calculation. This can
            be any of the integration methods supported in `numpy`
            or `rect`
        :type param: str

        :param step: The step behaviour for calculation of area
            and time summation
        :type step: str

        .. seealso::

            :func:`conditional_compare`
        """

        condition = " ".join([self._a, "<", self._b])
        return self.conditional_compare(condition, **kwargs)