{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Preamble"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# used to create block diagrams\n",
"%reload_ext xdiag_magic\n",
"%xdiag_output_format svg\n",
" \n",
"import numpy as np # for multi-dimensional containers"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Introduction\n",
"\n",
"In an earlier section, we briefly covered selection in single-objective problems. There is a fundamental difference between selection in single-objective problems when compared to multi-objective problems. In single-objective problems, we often look for a single solution which has the best objective value, whereas this is not possible in multi-objective problems because they often involve conflicts between multiple objectives. Because multi-objective problems consider more than one objective value, it is often the case that a solution with the best value for one objective will often have degradation in one or more other objective values. This is where a trade-off emerges in the objective space, and it is unlikely that there exists a single solution that can be considered optimal. \n",
"\n",
"Therefore, the solution to a multi-objective optimisation problem is not a single solution vector, but instead an *approximation set*. This is a set of many candidate solutions that present trade-offs between the multiple objectives, where any improvement in one objective value will result in the degradation in one or more of the other objective values. This notion of \"optimum\" solutions is called Pareto-optimality.\n",
"\n",
"Pareto-optimality and other approaches to determining dominance relationships between multiple solutions in a population are important during the selection stage of an optimisation algorithm, highlighted below.\n",
""
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%blockdiag\n",
"{\n",
" orientation = portrait\n",
" Initialisation -> Evaluation -> \"Terminate?\" -> Selection -> Variation -> Evaluation\n",
" Selection [color = '#ffffcc']\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Notation\n",
"\n",
"Let's define a multi-objective function $f(x)$ consisting of two objectives.\n",
"\n",
"$$\n",
"f(x) = (f_{1}(x),f_{2}(x))\\tag{1}\n",
"$$\n",
"\n",
"We have a population $\\mathrm{X}$ of $\\mathrm{N}$ candidate solutions.\n",
"\n",
"$$\n",
"\\mathbf{X} = \\langle \\mathrm{X}_1,\\mathrm{X}_2,\\ldots,\\mathrm{X}_{\\mathrm{N}}\\rangle\n",
"$$\n",
"\n",
"where $\\mathrm{N}$ refers to the number of solutions in the population, $\\mathrm{X}_{n}$ refers to the $n$-th solution in the population, and $x_{dn}$ refers to the $d$-th decision variable of the $n$-th solution in the population.\n",
"\n",
"$$\n",
"\\mathrm{X}_{n} = \\langle x_{1n},x_{2n},\\ldots,x_{\\mathrm{D}n} \\rangle\n",
"$$\n",
"\n",
"\n",
"We will also define the corresponding objective values $\\mathrm{Y}$ which are calculated when evaluating the function in Equation 1.\n",
"\n",
"$$\n",
"\\mathbf{Y} = \\langle \\mathrm{Y}_1,\\mathrm{Y}_2,\\ldots,\\mathrm{Y}_{\\mathrm{N}}\\rangle\n",
"$$\n",
"\n",
"where $\\mathrm{N}$ refers to the number of objective value sets, $\\mathrm{Y}_{n}$ refers to the $n$-th set of objective values in the population, and $y_{mn}$ refers to the $m$-th objective value of the $n$-th set of objective values in the population.\n",
"\n",
"$$\n",
"\\mathrm{Y}_{n} = \\langle y_{1n},y_{2n} \\rangle\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Dominance\n",
"\n",
"When using or designing algorithms to solve multi-objective optimisation problems, we will often encounter the concept of domination. This concept is useful for comparing two solutions to determine whether one is better than the other.\n",
"\n",
"We can now use our notation to define dominance relationships. Let's take two solutions to a two-objective problem: $\\mathrm{X}_1$ and $\\mathrm{X}_2$, with their corresponding objective values $\\mathrm{Y}_{1} = \\langle y_{1,1},y_{2,1} \\rangle$ and $\\mathrm{Y}_{2} = \\langle y_{1,2},y_{2,2} \\rangle$.\n",
"\n",
"**Definition 1**: A solution $\\mathrm{X}_1$ is said to dominate another solution $\\mathrm{X}_2$, if both of the following conditions are satisfied:\n",
"\n",
"1. The objective values of $\\mathrm{X}_1$ are no worse than those of $\\mathrm{X}_2$ in all objectives, i.e. for this two-objective problem $f_{m}(\\mathrm{X}_1) \\leq f_{m}(\\mathrm{X}_2)$ for all $m = (1, 2)$.\n",
"\n",
"2. The objective values of solution $\\mathrm{X}_1$ are strictly better than at least one of those of solution $\\mathrm{X}_2$, i.e. for this two-objective problem $f_{m}(\\mathrm{X}_1) \\lt f_{m}(\\mathrm{X}_2)$ for at least one $m = (1, 2)$.\n",
"\n",
"If any of the two conditions are violated, the solution $\\mathrm{X}_1$ does not dominate the solution $\\mathrm{X}_2$ . Otherwise, we can claim $\\mathrm{X}_1$ dominates $\\mathrm{X}_2$.\n",
"\n",
"**Definition 2**: Two solutions $\\mathrm{X}_1$ and $\\mathrm{X}_2$ are said to be non-dominating with respect to each other if the following conditions are satisfied:\n",
"\n",
"1. The objective values of solution $\\mathrm{X}_1$ are strictly better than at least one of those of solution $\\mathrm{X}_2$, i.e. for this two-objective problem $f_{m}(\\mathrm{X}_1) \\lt f_{m}(\\mathrm{X}_2)$ for at least one $m = (1, 2)$.\n",
"\n",
"1. The objective values of solution $\\mathrm{X}_1$ are strictly worse than at least one of those of solution $\\mathrm{X}_2$, i.e. for this two-objective problem $f_{m}(\\mathrm{X}_1) \\gt f_{m}(\\mathrm{X}_2)$ for at least one $m = (1, 2)$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Selecting Solutions\n",
"\n",
"Generally, one of our goals throughout the optimisation process is to select the *best* solutions. This means that solutions that can be categorised as either \"dominating\" or \"non-dominating\" are solutions that we are interested in. To be clear, we are not interested in solutions that are dominated, because they do not offer any desirable performance with respect to any of the considered objectives.\n",
"\n",
"Let's use Python to demonstrate these dominance relations that are often used for selection. Here, we will assume a minimisation problem, where smaller values are better. We will initialise four sets of solutions by synthetically assigning objective values that will demonstrate our dominance relations.\n",
"\n",
"Our first set $\\mathrm{Y}_1$ and $\\mathrm{Y}_2$ will demonstrate the scenario where $\\mathrm{Y}_1$ dominates $\\mathrm{Y}_2$."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"Y1 = np.array([0, 0.5])\n",
"Y2 = np.array([0.5, 0.5])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our second set $\\mathrm{Y}_3$ and $\\mathrm{Y}_4$ will demonstrate the scenario where $\\mathrm{Y}_3$ is identical to $\\mathrm{Y}_4$."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"Y3 = np.array([0.5, 0.5])\n",
"Y4 = np.array([0.5, 0.5])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our third set $\\mathrm{Y}_5$ and $\\mathrm{Y}_6$ will demonstrate the scenario where $\\mathrm{Y}_5$ and $\\mathrm{Y}_6$ are non-dominated with respect to each other."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"Y5 = np.array([0, 0.5])\n",
"Y6 = np.array([0.5, 0])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our fourth set $\\mathrm{Y}_7$ and $\\mathrm{Y}_8$ will demonstrate the scenario where $\\mathrm{Y}_7$ is dominated by $\\mathrm{Y}_8$."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"Y7 = np.array([0.5, 0.5])\n",
"Y8 = np.array([0, 0.25])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, we will define a function to determine whether a solution dominates another or not."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"def dominates(X1, X2):\n",
" if(np.any(X1 < X2) and np.all(X1 <= X2)):\n",
" return True\n",
" else:\n",
" return False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, let's test it with our four sets of objective values."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dominates(Y1, Y2)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dominates(Y3, Y4)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dominates(Y5, Y6)"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dominates(Y7, Y8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As expected, the only solution pairing that satisfies the criteria for dominance is our first set $\\mathrm{Y}_1$ and $\\mathrm{Y}_2$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, let's define a function to determine whether two solutions are non-dominated."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"def nondominated(X1, X2):\n",
" if(np.any(X1 < X2) and np.any(X1 > X2)):\n",
" return True\n",
" else:\n",
" return False"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Again, we will test it with our four sets of objective values."
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nondominated(Y1, Y2)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nondominated(Y3, Y4)"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nondominated(Y5, Y6)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"False"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"nondominated(Y7, Y8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As expected, the only solution pairing that satisfies the criteria for dominance is our first set $\\mathrm{Y}_5$ and $\\mathrm{Y}_6$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can string these two functions together within a decision structure to determine the dominance relation between any two solutions."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"def dominance_relation(X1, X2):\n",
" if(np.all(X1 == X2)):\n",
" print(\"The solutions are identical.\")\n",
" elif(dominates(X1, X2)):\n",
" print(\"The first solution dominates the second.\")\n",
" elif(nondominated(X1, X2)):\n",
" print(\"The two solations are nondominating\")\n",
" else:\n",
" print(\"The first solution is dominated by the second.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we will test it with our four sets of objective values."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first solution dominates the second.\n"
]
}
],
"source": [
"dominance_relation(Y1, Y2)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The solutions are identical.\n"
]
}
],
"source": [
"dominance_relation(Y3, Y4)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The two solations are nondominating\n"
]
}
],
"source": [
"dominance_relation(Y5, Y6)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first solution is dominated by the second.\n"
]
}
],
"source": [
"dominance_relation(Y7, Y8)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Conclusion\n",
"\n",
"In this section we introduced the concept of Pareto-optimality and looked at dominance relations in more detail, complete with examples in Python. The next challenge we will encounter is when we need to select a subset of solutions from a population that consists of entirely non-dominated solutions. For example, which 100 solutions do we select from a population of 200 non-dominated solutions? We will offer some solutions to this challenge in the later sections."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.7"
},
"nikola": {
"category": "practical-evolutionary-algorithms",
"date": "2019-10-13 20:29:13 UTC+01:00",
"description": "",
"link": "",
"slug": "pareto-optimality-and-dominance-relations",
"tags": "",
"title": "Pareto Optimality and Dominance Relations",
"type": "text"
}
},
"nbformat": 4,
"nbformat_minor": 4
}