Exemple #1
0
    def test_df_nans_not_allowed(self):
        df = pd.DataFrame.from_dict(sample_data)
        df2 = pd.DataFrame.from_dict(sample_data2)

        cu._df_nans_not_allowed('sample_data', df)

        with self.assertRaises(TypeError):
            cu._df_nans_not_allowed('sample_data2', df2)
Exemple #2
0
    def create_pyomo_model(self,
                           impact=None,
                           sensor=None,
                           scenario=None,
                           sensor_budget=None,
                           use_sensor_cost=False,
                           use_scenario_probability=False,
                           impact_col_name='Impact'):
        """
        Returns the Pyomo model. 
        
        See :py:meth:`ImpactFormulation.solve` for more information on parameters.
        
        Returns
        -------
        Pyomo ConcreteModel ready to be solved
        """
        # reset the internal model and data attributes
        # BLN: Why do we reset these when they will be overwritten at the
        # end of this method anyway?
        self._model = None
        self._impact = None
        self._sensor = None
        self._scenario = None

        # validate the pandas dataframe input
        cu._df_columns_required(
            'impact', impact, {
                'Scenario': np.object,
                'Sensor': np.object,
                impact_col_name: [np.float64, np.int64]
            })
        cu._df_nans_not_allowed('impact', impact)
        if sensor is not None:
            cu._df_columns_required('sensor', sensor, {'Sensor': np.object})
            cu._df_nans_not_allowed('sensor', sensor)

            sensor = sensor.set_index('Sensor')
            assert (sensor.index.names[0] == 'Sensor')

        cu._df_columns_required('scenario', scenario, {
            'Scenario': np.object,
            'Undetected Impact': [np.float64, np.int64]
        })
        cu._df_nans_not_allowed('scenario', scenario)

        # validate optional columns in pandas dataframe input
        if use_scenario_probability:
            cu._df_columns_required('scenario', scenario,
                                    {'Probability': np.float64})

        if use_sensor_cost:
            if sensor is None:
                raise ValueError(
                    'ImpactFormulation: use_sensor_cost cannot be '
                    'True if "sensor" DataFrame is not provided.')
            cu._df_columns_required('sensor', sensor,
                                    {'Cost': [np.float64, np.int64]})

        # Notice, setting the index here
        impact = impact.set_index(['Scenario', 'Sensor'])
        assert (impact.index.names[0] == 'Scenario')
        assert (impact.index.names[1] == 'Sensor')

        # Python set will extract the unique Scenario and Sensor values
        scenario_list = sorted(scenario['Scenario'].unique())

        # Always get sensor list from impact DataFrame in case there are
        # sensors in the sensor DataFrame that didn't detect anything and
        # therefore do not appear in the impact DataFrame
        sensor_list = sorted(set(impact.index.get_level_values('Sensor')))

        if use_sensor_cost:
            sensor_cost = sensor['Cost']
        else:
            sensor_cost = pd.Series(data=[1.0] * len(sensor_list),
                                    index=sensor_list)

        # Add in the data for the dummy sensor to account for a scenario that
        # is undetected
        sensor_list.append(dummy_sensor_name)

        df_dummy = pd.DataFrame(scenario_list, columns=['Scenario'])
        df_dummy = df_dummy.set_index(['Scenario'])

        scenario = scenario.set_index(['Scenario'])
        df_dummy[impact_col_name] = scenario['Undetected Impact']
        scenario.reset_index(level=[0], inplace=True)

        df_dummy['Sensor'] = dummy_sensor_name
        df_dummy = df_dummy.reset_index().set_index(['Scenario', 'Sensor'])
        impact = impact.append(df_dummy)
        sensor_cost[dummy_sensor_name] = 0.0

        # Create a list of tuples for all the scenario/sensor pairs where
        # detection has occurred
        scenario_sensor_pairs = impact.index.tolist()

        # Create the (jagged) index set of sensors that were able to detect a
        # particular scenario
        scenario_sensors = dict()
        for (a, i) in scenario_sensor_pairs:
            if a not in scenario_sensors:
                scenario_sensors[a] = list()
            scenario_sensors[a].append(i)

        # create the model container
        model = pe.ConcreteModel()
        model.scenario_sensors = scenario_sensors

        # Pyomo does not create an ordered dummy set when passed a list - do
        # this for now as a workaround
        model.scenario_set = pe.Set(initialize=scenario_list, ordered=True)
        model.sensor_set = pe.Set(initialize=sensor_list, ordered=True)
        model.scenario_sensor_pairs_set = \
            pe.Set(initialize=scenario_sensor_pairs, ordered=True)

        # create mutable parameter that may be changed
        model.sensor_budget = pe.Param(initialize=sensor_budget, mutable=True)

        # x_{a,i} variable indicates which sensor is the first to detect
        # scenario a
        model.x = pe.Var(model.scenario_sensor_pairs_set, bounds=(0, 1))

        # y_i variable indicates if a sensor is installed or not
        model.y = pe.Var(model.sensor_set, within=pe.Binary)

        # objective function minimize the sum impact across all scenarios
        if use_scenario_probability:
            scenario.set_index(['Scenario'], inplace=True)
            model.obj = pe.Objective(expr= \
                sum(float(scenario.at[a, 'Probability']) *
                float(impact[impact_col_name].loc[a, i]) * model.x[a, i]
                for (a, i) in scenario_sensor_pairs))
        else:
            model.obj = pe.Objective(expr= \
                1.0 / float(len(scenario_list)) *
                sum(float(impact[impact_col_name].loc[a, i]) * model.x[a, i]
                for (a, i) in scenario_sensor_pairs))

        # constrain the problem to have only one x value for each scenario
        def limit_x_rule(m, a):
            return sum(m.x[a, i] for i in scenario_sensors[a]) == 1

        model.limit_x = pe.Constraint(model.scenario_set, rule=limit_x_rule)

        # can only detect scenario a with location i if location i is selected
        def detect_only_if_sensor_rule(m, a, i):
            return m.x[a, i] <= model.y[i]
        model.detect_only_if_sensor = \
            pe.Constraint(model.scenario_sensor_pairs_set,
                          rule=detect_only_if_sensor_rule)

        # limit the number of sensors
        model.total_sensor_cost = pe.Expression(expr=sum(
            float(sensor_cost[i]) * model.y[i] for i in sensor_list))
        model.sensor_budget_con = pe.Constraint(
            expr=model.total_sensor_cost <= model.sensor_budget)

        self._model = model
        impact.reset_index(inplace=True)
        self._impact = impact
        self._sensor = sensor
        scenario.reset_index(inplace=True)
        self._scenario = scenario
        self._impact_col_name = impact_col_name
        self._use_sensor_cost = use_sensor_cost
        self._use_scenario_probability = use_scenario_probability

        # Any changes to the model require re-solving
        self._solved = False

        return model
Exemple #3
0
    def create_pyomo_model(self, impact, sensor_budget, sensor, scenario):
        """
        Returns the Pyomo model.

        Parameters
        ----------
        impact : pandas DataFrame
            Impact assessment
        sensor_budget : float
            Sensor budget
        sensor : pandas DataFrame
            Sensor characteristics
        scenario : pandas DataFrame
            Scenario characteristics
            
        Returns
        -------
        Pyomo ConcreteModel ready to be solved
        """

        # validate the pandas dataframe input
        cu._df_columns_required('sensor', sensor, {
            'Sensor': np.object,
            'Cost': [np.float64, np.int64]
        })
        cu._df_nans_not_allowed('sensor', sensor)
        cu._df_columns_required('scenario', scenario, {
            'Scenario': np.object,
            'Undetected Impact': [np.float64, np.int64]
        })
        cu._df_nans_not_allowed('scenario', scenario)
        cu._df_columns_required(
            'impact', impact, {
                'Scenario': np.object,
                'Sensor': np.object,
                'Impact': [np.float64, np.int64]
            })
        cu._df_nans_not_allowed('impact', impact)

        # validate optional columns in pandas dataframe input
        if self.use_scenario_probability:
            cu._df_columns_required('scenario', scenario,
                                    {'Probability': np.float64})

        self._sensor_df = sensor
        self._scenario_df = scenario
        self._impact_df = impact

        impact = impact.set_index(['Scenario', 'Sensor'])
        assert (impact.index.names[0] == 'Scenario')
        assert (impact.index.names[1] == 'Sensor')

        sensor = sensor.set_index('Sensor')
        assert (sensor.index.names[0] == 'Sensor')

        # Python set will extract the unique Scenario and Sensor values
        scenario_list = \
            sorted(set(impact.index.get_level_values('Scenario')))
        sensor_list = sorted(set(impact.index.get_level_values('Sensor')))
        if self.use_sensor_cost:
            sensor_cost = sensor['Cost']
        else:
            sensor['Cost'] = 1
            sensor_cost = sensor['Cost']

        # Add in the data for the dummy sensor to account for a scenario that
        # is undetected
        sensor_list.append(dummy_sensor_name)

        df_dummy = pd.DataFrame(scenario_list, columns=['Scenario'])
        df_dummy = df_dummy.set_index(['Scenario'])

        scenario = scenario.set_index(['Scenario'])
        df_dummy['Impact'] = scenario['Undetected Impact']
        scenario.reset_index(level=[0], inplace=True)

        df_dummy['Sensor'] = dummy_sensor_name
        df_dummy = df_dummy.reset_index().set_index(['Scenario', 'Sensor'])
        impact = impact.append(df_dummy)
        sensor_cost[dummy_sensor_name] = 0.0

        # create a list of tuples for all the scenario/sensor pairs where
        # detection has occurred
        scenario_sensor_pairs = impact.index.tolist()

        # create the (jagged) index set of sensors that were able to detect a
        # particular scenario
        scenario_sensors = dict()
        for (a, i) in scenario_sensor_pairs:
            if a not in scenario_sensors:
                scenario_sensors[a] = list()
            scenario_sensors[a].append(i)

        # create the model container
        model = pe.ConcreteModel()
        model._scenario_sensors = scenario_sensors

        # Pyomo does not create an ordered dummy set when passed a list - do
        # this for now as a workaround
        model.scenario_set = pe.Set(initialize=scenario_list, ordered=True)
        model.sensor_set = pe.Set(initialize=sensor_list, ordered=True)
        model.scenario_sensor_pairs_set = \
            pe.Set(initialize=scenario_sensor_pairs, ordered=True)

        # x_{a,i} variable indicates which sensor is the first to detect
        # scenario a
        model.x = pe.Var(model.scenario_sensor_pairs_set, bounds=(0, 1))

        # y_i variable indicates if a sensor is installed or not
        model.y = pe.Var(model.sensor_set, within=pe.Binary)

        # objective function minimize the sum impact across all scenarios
        # in current formulation all scenarios are equally probable
        def obj_rule(m):
            return 1.0 / float(len(scenario_list)) * \
                   sum(float(impact.loc[a, i]) * m.x[a, i]
                       for (a, i) in scenario_sensor_pairs)

        # Modify the objective function to include scenario probabilities
        if self.use_scenario_probability:
            scenario.set_index(['Scenario'], inplace=True)

            def obj_rule(m):
                return sum(
                    float(scenario.loc[a, 'Probability']) *
                    float(impact.loc[a, i]) * m.x[a, i]
                    for (a, i) in scenario_sensor_pairs)

        model.obj = pe.Objective(rule=obj_rule)

        # constrain the problem to have only one x value for each scenario
        def limit_x_rule(m, a):
            return sum(m.x[a, i] for i in scenario_sensors[a]) == 1

        model.limit_x = pe.Constraint(model.scenario_set, rule=limit_x_rule)

        def detect_only_if_sensor_rule(m, a, i):
            return m.x[a, i] <= model.y[i]
        model.detect_only_if_sensor = \
            pe.Constraint(model.scenario_sensor_pairs_set,
                          rule=detect_only_if_sensor_rule)

        model.sensor_budget = \
            pe.Constraint(expr=sum(float(sensor_cost[i]) * model.y[i]
                                   for i in sensor_list) <= sensor_budget)

        self._model = model

        return model