

# Graph optimization problems and their applications

Graph optimization problems are a fundamental area of study in operations research and computer science. These problems involve finding optimal solutions to problems that can be represented as graphs. Chordal graphs are a special class of graphs that have many useful properties, making them a popular choice for formulating optimization problems.

Applications of graph optimization problems include network design, workflow scheduling, and facility location. In network design, the goal is to design a network with the minimum cost while satisfying certain constraints. In workflow scheduling, the goal is to schedule tasks in a workflow to minimize the total completion time while satisfying constraints on the order of tasks. In facility location, the goal is to locate facilities in a network to minimize the total cost of serving customers while satisfying constraints on the capacity of facilities.

For example, consider a network design problem where you need to connect a set of cities with roads. The goal is to minimize the total cost of constructing roads while satisfying constraints on the capacity of roads and the connectivity of the cities. This problem can be represented as a graph optimization problem with vertices representing cities and edges representing roads.

## Exercise
Instructions:

Consider a network design problem where you need to connect a set of cities with roads. The goal is to minimize the total cost of constructing roads while satisfying constraints on the capacity of roads and the connectivity of the cities.

1. Write down the objective function of this problem.
2. Write down the constraints of this problem.
3. Formulate this problem as a graph optimization problem with vertices representing cities and edges representing roads.

### Solution
1. Objective function: Minimize the total cost of constructing roads.
2. Constraints:
   - The capacity of each road must not exceed a certain value.
   - The cities must be connected.
3. Graph optimization problem: Vertices represent cities, and edges represent roads.

# Linear programming fundamentals

Linear programming (LP) is a mathematical optimization technique that can be used to solve optimization problems with linear constraints and a linear objective function. Linear programming is widely used in solving optimization problems in various fields, including operations research, economics, and engineering.

The main components of a linear programming problem are:

- Objective function: The function that we want to minimize or maximize.
- Constraints: The conditions that the solution must satisfy.
- Variables: The unknowns in the problem.

For example, consider the following linear programming problem:

Minimize:

$$
\min z = 3x_1 + 2x_2
$$

Subject to:

$$
\begin{aligned}
2x_1 + x_2 &\le 8 \\
x_1 + 2x_2 &\le 10 \\
x_1, x_2 &\ge 0
\end{aligned}
$$

## Exercise
Instructions:

Solve the linear programming problem given in the example.

### Solution
The linear programming problem can be solved using the simplex method. The solution is:

$$
(x_1, x_2) = (2, 6)
$$

The optimal value of the objective function is:

$$
z = 3(2) + 2(6) = 12
$$

# Introduction to Pyomo and its capabilities

Pyomo is an open-source Python library for mathematical modeling and optimization. It provides a high-level interface for formulating optimization problems and solving them using various solvers. Pyomo supports a wide range of optimization techniques, including linear programming, mixed-integer programming, and nonlinear programming.

To use Pyomo, you need to install it using pip:

```
pip install pyomo
```

Once installed, you can use Pyomo to formulate optimization problems and solve them using various solvers. Pyomo provides a simple and intuitive syntax for defining optimization problems, making it easy to work with even for those who are not familiar with optimization techniques.

For example, consider the following linear programming problem:

Minimize:

$$
\min z = 3x_1 + 2x_2
$$

Subject to:

$$
\begin{aligned}
2x_1 + x_2 &\le 8 \\
x_1 + 2x_2 &\le 10 \\
x_1, x_2 &\ge 0
\end{aligned}
$$

You can formulate this problem using Pyomo as follows:

```python
from pyomo.environ import *

model = ConcreteModel()

model.x1 = Var(within=NonNegativeReals)
model.x2 = Var(within=NonNegativeReals)

model.obj = Objective(expr=3*model.x1 + 2*model.x2, sense=minimize)

model.c1 = Constraint(expr=2*model.x1 + model.x2 <= 8)
model.c2 = Constraint(expr=model.x1 + 2*model.x2 <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("x1:", model.x1())
print("x2:", model.x2())
```

## Exercise
Instructions:

Solve the linear programming problem given in the example using Pyomo.

### Solution
```python
from pyomo.environ import *

model = ConcreteModel()

model.x1 = Var(within=NonNegativeReals)
model.x2 = Var(within=NonNegativeReals)

model.obj = Objective(expr=3*model.x1 + 2*model.x2, sense=minimize)

model.c1 = Constraint(expr=2*model.x1 + model.x2 <= 8)
model.c2 = Constraint(expr=model.x1 + 2*model.x2 <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("x1:", model.x1())
print("x2:", model.x2())
```

# Formulating optimization models for chordal graphs



A chordal graph is a graph in which every cycle of length 4 or more can be decomposed into a chain of edges. Chordal graphs have several important properties, such as being triangulated, having a perfect elimination ordering, and having a unique perfect elimination ordering.

For example, consider the following chordal graph:

```
A -- B -- C
|         |
D -- E -- F
```

You can formulate an optimization problem for this graph using Pyomo as follows:

```python
from pyomo.environ import *

model = ConcreteModel()

model.A = Var(within=NonNegativeReals)
model.B = Var(within=NonNegativeReals)
model.C = Var(within=NonNegativeReals)
model.D = Var(within=NonNegativeReals)
model.E = Var(within=NonNegativeReals)
model.F = Var(within=NonNegativeReals)

model.obj = Objective(expr=model.A + model.B + model.C + model.D + model.E + model.F, sense=minimize)

model.c1 = Constraint(expr=model.A + model.B <= 8)
model.c2 = Constraint(expr=model.B + model.C <= 10)
model.c3 = Constraint(expr=model.D + model.E <= 8)
model.c4 = Constraint(expr=model.E + model.F <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("A:", model.A())
print("B:", model.B())
print("C:", model.C())
print("D:", model.D())
print("E:", model.E())
print("F:", model.F())
```

## Exercise
Instructions:

Solve the optimization problem for the chordal graph given in the example using Pyomo.

### Solution
```python
from pyomo.environ import *

model = ConcreteModel()

model.A = Var(within=NonNegativeReals)
model.B = Var(within=NonNegativeReals)
model.C = Var(within=NonNegativeReals)
model.D = Var(within=NonNegativeReals)
model.E = Var(within=NonNegativeReals)
model.F = Var(within=NonNegativeReals)

model.obj = Objective(expr=model.A + model.B + model.C + model.D + model.E + model.F, sense=minimize)

model.c1 = Constraint(expr=model.A + model.B <= 8)
model.c2 = Constraint(expr=model.B + model.C <= 10)
model.c3 = Constraint(expr=model.D + model.E <= 8)
model.c4 = Constraint(expr=model.E + model.F <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("A:", model.A())
print("B:", model.B())
print("C:", model.C())
print("D:", model.D())
print("E:", model.E())
print("F:", model.F())
```

# Solving optimization problems using Pyomo



To use Pyomo, you need to install it using pip:

```
pip install pyomo
```

Once installed, you can use Pyomo to formulate optimization problems and solve them using various solvers. Pyomo provides a simple and intuitive syntax for defining optimization problems, making it easy to work with even for those who are not familiar with optimization techniques.

For example, consider the following linear programming problem:

Minimize:

$$
\min z = 3x_1 + 2x_2
$$

Subject to:

$$
\begin{aligned}
2x_1 + x_2 &\le 8 \\
x_1 + 2x_2 &\le 10 \\
x_1, x_2 &\ge 0
\end{aligned}
$$

You can formulate this problem using Pyomo as follows:

```python
from pyomo.environ import *

model = ConcreteModel()

model.x1 = Var(within=NonNegativeReals)
model.x2 = Var(within=NonNegativeReals)

model.obj = Objective(expr=3*model.x1 + 2*model.x2, sense=minimize)

model.c1 = Constraint(expr=2*model.x1 + model.x2 <= 8)
model.c2 = Constraint(expr=model.x1 + 2*model.x2 <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("x1:", model.x1())
print("x2:", model.x2())
```

## Exercise
Instructions:

Solve the linear programming problem given in the example using Pyomo.

### Solution
```python
from pyomo.environ import *

model = ConcreteModel()

model.x1 = Var(within=NonNegativeReals)
model.x2 = Var(within=NonNegativeReals)

model.obj = Objective(expr=3*model.x1 + 2*model.x2, sense=minimize)

model.c1 = Constraint(expr=2*model.x1 + model.x2 <= 8)
model.c2 = Constraint(expr=model.x1 + 2*model.x2 <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("x1:", model.x1())
print("x2:", model.x2())
```

# Real-world problem: network design



To solve this problem, we will formulate it as a graph optimization problem with vertices representing cities and edges representing roads. We will then use Pyomo to solve the problem using a linear programming solver.

For example, consider a network design problem with the following cities and their distances:

```
A -- B -- C
|         |
D -- E -- F
```

The distances between the cities are as follows:

```
A -- B: 2
B -- C: 3
C -- F: 4
D -- E: 3
E -- F: 2
A -- D: 1
B -- D: 1
C -- E: 1
```

You can formulate this problem using Pyomo as follows:

```python
from pyomo.environ import *

model = ConcreteModel()

model.A = Var(within=NonNegativeReals)
model.B = Var(within=NonNegativeReals)
model.C = Var(within=NonNegativeReals)
model.D = Var(within=NonNegativeReals)
model.E = Var(within=NonNegativeReals)
model.F = Var(within=NonNegativeReals)

model.obj = Objective(expr=model.A + model.B + model.C + model.D + model.E + model.F, sense=minimize)

model.c1 = Constraint(expr=model.A + model.B <= 8)
model.c2 = Constraint(expr=model.B + model.C <= 10)
model.c3 = Constraint(expr=model.D + model.E <= 8)
model.c4 = Constraint(expr=model.E + model.F <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("A:", model.A())
print("B:", model.B())
print("C:", model.C())
print("D:", model.D())
print("E:", model.E())
print("F:", model.F())
```

## Exercise
Instructions:

Solve the network design problem given in the example using Pyomo.

### Solution
```python
from pyomo.environ import *

model = ConcreteModel()

model.A = Var(within=NonNegativeReals)
model.B = Var(within=NonNegativeReals)
model.C = Var(within=NonNegativeReals)
model.D = Var(within=NonNegativeReals)
model.E = Var(within=NonNegativeReals)
model.F = Var(within=NonNegativeReals)

model.obj = Objective(expr=model.A + model.B + model.C + model.D + model.E + model.F, sense=minimize)

model.c1 = Constraint(expr=model.A + model.B <= 8)
model.c2 = Constraint(expr=model.B + model.C <= 10)
model.c3 = Constraint(expr=model.D + model.E <= 8)
model.c4 = Constraint(expr=model.E + model.F <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("A:", model.A())
print("B:", model.B())
print("C:", model.C())
print("D:", model.D())
print("E:", model.E())
print("F:", model.F())
```

# Real-world problem: workflow scheduling



To solve this problem, we will formulate it as a graph optimization problem with vertices representing tasks and edges representing precedence constraints. We will then use Pyomo to solve the problem using a linear programming solver.

For example, consider a workflow scheduling problem with the following tasks and their durations:

```
1 -- 2
|     |
3 -- 4
```

The durations of the tasks are as follows:

```
Task 1: 3
Task 2: 2
Task 3: 4
Task 4: 1
```

You can formulate this problem using Pyomo as follows:

```python
from pyomo.environ import *

model = ConcreteModel()

model.t1 = Var(within=NonNegativeReals)
model.t2 = Var(within=NonNegativeReals)
model.t3 = Var(within=NonNegativeReals)
model.t4 = Var(within=NonNegativeReals)

model.obj = Objective(expr=model.t1 + model.t2 + model.t3 + model.t4, sense=minimize)

model.c1 = Constraint(expr=model.t1 + model.t2 <= 5)
model.c2 = Constraint(expr=model.t3 + model.t4 <= 5)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("t1:", model.t1())
print("t2:", model.t2())
print("t3:", model.t3())
print("t4:", model.t4())
```

## Exercise
Instructions:

Solve the workflow scheduling problem given in the example using Pyomo.

### Solution
```python
from pyomo.environ import *

model = ConcreteModel()

model.t1 = Var(within=NonNegativeReals)
model.t2 = Var(within=NonNegativeReals)
model.t3 = Var(within=NonNegativeReals)
model.t4 = Var(within=NonNegativeReals)

model.obj = Objective(expr=model.t1 + model.t2 + model.t3 + model.t4, sense=minimize)

model.c1 = Constraint(expr=model.t1 + model.t2 <= 5)
model.c2 = Constraint(expr=model.t3 + model.t4 <= 5)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("t1:", model.t1())
print("t2:", model.t2())
print("t3:", model.t3())
print("t4:", model.t4())
```

# Real-world problem: facility location



To solve this problem, we will formulate it as a graph optimization problem with vertices representing customers and facilities and edges representing the cost of serving customers. We will then use Pyomo to solve the problem using a linear programming solver.

For example, consider a facility location problem with the following customers and their demands:

```
A -- B -- C
|         |
D -- E -- F
```

The demands of the customers are as follows:

```
A: 5
B: 3
C: 2
D: 4
E: 1
F: 3
```

The cost of serving a customer from a facility is as follows:

```
A: 1
B: 2
C: 3
D: 4
E: 2
F: 1
```

You can formulate this problem using Pyomo as follows:

```python
from pyomo.environ import *

model = ConcreteModel()

model.A = Var(within=NonNegativeReals)
model.B = Var(within=NonNegativeReals)
model.C = Var(within=NonNegativeReals)
model.D = Var(within=NonNegativeReals)
model.E = Var(within=NonNegativeReals)
model.F = Var(within=NonNegativeReals)

model.obj = Objective(expr=model.A + model.B + model.C + model.D + model.E + model.F, sense=minimize)

model.c1 = Constraint(expr=model.A + model.B <= 8)
model.c2 = Constraint(expr=model.B + model.C <= 10)
model.c3 = Constraint(expr=model.D + model.E <= 8)
model.c4 = Constraint(expr=model.E + model.F <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("A:", model.A())
print("B:", model.B())
print("C:", model.C())
print("D:", model.D())
print("E:", model.E())
print("F:", model.F())
```

## Exercise
Instructions:

Solve the facility location problem given in the example using Pyomo.

### Solution
```python
from pyomo.environ import *

model = ConcreteModel()

model.A = Var(within=NonNegativeReals)
model.B = Var(within=NonNegativeReals)
model.C = Var(within=NonNegativeReals)
model.D = Var(within=NonNegativeReals)
model.E = Var(within=NonNegativeReals)
model.F = Var(within=NonNegativeReals)

model.obj = Objective(expr=model.A + model.B + model.C + model.D + model.E + model.F, sense=minimize)

model.c1 = Constraint(expr=model.A + model.B <= 8)
model.c2 = Constraint(expr=model.B + model.C <= 10)
model.c3 = Constraint(expr=model.D + model.E <= 8)
model.c4 = Constraint(expr=model.E + model.F <= 10)

solver = SolverFactory('glpk')
solver.solve(model)

print("Optimal value:", model.obj())
print("A:", model.A())
print("B:", model.B())
print("C:", model.C())
print("D:", model.D())
print("E:", model.E())
print("F:", model.F())
```

# Advanced optimization techniques for chordal graphs



# Case studies and practical examples



# Conclusion and future directions

