Optimization of Preventative Maintenance Requests background cover

Optimization of Preventative Maintenance Requests

Optimize the timing of preventative maintenance requests to reduce costs and increase efficient operations

EDF

Hosted by

EDF

Problem

The problem is described by a set of NN PMRQ (Preventive Maintenance ReQuest). The PMRQ are endowed with the following properties:
  • cost: {C1,C2,...,CN}\{ C_1, C_2, ..., C_N\}
  • frequency: {f1,f2,...,fN}\{f_1, f_2, ..., f_N\}
  • tolerance: {ϵ1,ϵ2,...,ϵN}\{\epsilon_1, \epsilon_2, ..., \epsilon_N\}
  • offset: {o1,o2,...,oN}\{ o_1, o_2, ..., o_N \}
  • time horizon: TT
  • credit: A dictionary mapping i{j1,j2,...,jN}i \rightarrow \{j_1, j_2 ,..., j_N\} where i,j[1,N]i,j\in [1,N]. The inclusion in the mapping denotes ii credits jj (i.e. the maintenance of jj can be done for free is the maintenance of ii is done at the same time)

Solution

A solution is to be given by the binary matrix SS of size N×TN \times T with the entries
Sit={0,if no maintenance is performed on PMRQ-i at time t1,if a maintenance is performed on PMRQ-i at time tS_{it}= \begin{cases} 0, \quad \text{if no maintenance is performed on PMRQ-i at time } t\\ 1, \quad \text{if a maintenance is performed on PMRQ-i at time } t\\ \end{cases}

Template

You can download a solution template by running the following command in the terminal
aqora template edf-pmrq
cd edf-pmrq
aqora install
You can then fill in src/submission/__init__.py with your solution. You can test your solution by running
aqora test
And finally you can upload your solution by running
aqora upload

Scoring

Each solution is validated and scored according to the following script
class Problem:
    id: int
    cost: np.ndarray
    frequency: np.ndarray
    tolerance: np.ndarray
    offset: np.ndarray
    T: int
    credit: dict
    epsilon: np.ndarray

    def __init__(self, cost, frequency, tolerance, offset, credit, T):
        self.id = id
        self.cost = np.array(cost)
        self.frequency = np.array(frequency)
        self.tolerance = np.array(tolerance)
        self.offset = np.array(offset)
        self.T = T
        self.credit = credit
        self.epsilon = self.calc_epsilon(self.cost, credit)

    def assert_valid(self, df: pd.DataFrame):
        # Iterate over each row in the DataFrame
        for row in df.index:
            # Get the column indices where the row equals 1
            ones_indices = df.loc[row][df.loc[row] == 1].index.tolist()

            # this asserts whether the time between first maintenance and the previous one verifies the frequency and tolerance constraint
            assert (
                ones_indices[0] - self.offset[row] - 1
                <= self.frequency[row] + self.tolerance[row]
            )
            # this asserts whether the time between consecutive maintenance verifies the frequency and tolerance constraint
            diffs = np.diff(ones_indices)
            assert np.all(np.abs(diffs - self.frequency[row]) <= self.tolerance[row])
            # this asserts whether the last maintenance and the time horizon do not violate the frequency and tolerance constraint
            assert (
                self.T - ones_indices[-1] <= self.frequency[row] + self.tolerance[row]
            )

    def calc_cost(self, input: np.ndarray) -> float:
        return np.trace(self.epsilon @ input @ input.T)

    @classmethod
    def calc_epsilon(cls, cost: np.ndarray, credit: dict) -> np.ndarray:
        # Define the epsilon matrix
        epsilon = np.zeros((len(cost), len(cost)))

        # Populate the epsilon matrix with cost values. The value of the coefficient epsilon[i,j] is the cost of j which is free is the maintenance of i is done at the same time as j
        for _, key in enumerate(credit):
            for j in credit[key]:
                epsilon[int(key) - 1, j - 1] = cost[j - 1]

        return epsilon