Course
Optimization is a fundamental tool used across various industries and disciplines to make the best possible decisions within given constraints. Whether it's minimizing costs in a supply chain, maximizing efficiency in energy systems, or finding the optimal parameters in machine learning models, optimization techniques are essential.
Python, known for its simplicity and versatility, offers powerful libraries for optimization problems. Among these, Pyomo stands out as a comprehensive and flexible library that allows users to define and solve complex optimization models seamlessly.
In this tutorial, we will explore Pyomo from the ground up. We'll cover everything from installing and setting up solvers to formulating and solving different optimization problems!
Exploring Feasible Solutions in Linear Programming. Image by Author.
What is Pyomo?
Pyomo is an open-source library for building and solving optimization models using Python. It allows you to define optimization models in a way that's both mathematically rigorous and syntactically intuitive for Python programmers. It supports a wide range of problem types, including:
- Linear programming (LP): LP involves optimizing a linear objective function subject to linear equality and inequality constraints. It's widely used for resource allocation, scheduling, and financial planning problems.
- Nonlinear programming (NLP): NLP deals with optimizing a nonlinear objective function with nonlinear constraints. It’s often used in engineering and economics for more complex systems where relationships are not linear.
- Mixed-integer programming (MIP): MIP involves optimization problems where some variables are restricted to be integers while others can be continuous. This is useful in scenarios like supply chain design or project scheduling, where decisions can be discrete (e.g., on/off).
- Stochastic programming: Stochastic programming addresses optimization problems where some elements are uncertain and modeled as random variables. It's commonly applied in finance and supply chain management to optimize decisions under uncertainty.
- Dynamic optimization: Dynamic optimization focuses on optimizing decision variables over time, typically involving dynamically evolving systems. It is used in fields like process control, robotics, and economics to manage time-dependent processes.
Features of Pyomo
Now that we understand Pyomo better let’s review some of its most important features.
Flexibility and extensibility
Pyomo's flexibility comes from its ability to model complex relationships using standard Python constructs. It integrates with various open-source and commercial solvers, making it easy to solve many optimization problems.
Pythonic syntax
Pyomo models are built on Python and written using standard Python syntax. This makes the learning curve gentle for those familiar with Python and allows you to use Python's extensive libraries within your models.
Strong community and documentation
Pyomo has a robust user community and comprehensive documentation, which includes examples and tutorials to help users at all levels.
Use-cases for Pyomo
Pyomo has a wide range of real-world applications. Here are some of them:
1. Supply chain optimization
Supply chain optimization involves improving logistics, managing inventory levels, and creating efficient production schedules.
This may include minimizing transportation costs, optimizing warehouse locations, or balancing supply and demand.
For example, a company might need to meet customer demand across multiple regions while minimizing shipping costs and maintaining stock levels at each distribution center.
2. Financial modeling
In financial modeling, optimization helps to allocate resources, such as capital, to maximize returns while minimizing risk.
This can involve portfolio optimization, where investors balance risk and reward by selecting a combination of assets subject to constraints like budget limits, regulatory requirements, or risk tolerance.
Financial modeling ensures that financial strategies align with long-term goals while mitigating potential risks.
3. Energy systems
Optimization in energy systems focuses on maximizing power generation, distribution, and consumption efficiency.
This may involve determining the optimal mix of energy sources (e.g., renewable vs. non-renewable) while minimizing fuel costs, meeting emission limits, and adapting to fluctuating demand.
This type of optimization plays a central role in grid management, power plant operations, and reducing environmental impacts.
4. Machine learning and data science
Optimization is central to many machine learning and data science tasks, such as hyperparameter tuning and feature selection.
In hyperparameter tuning, optimization algorithms help find the best model configuration to improve predictive performance.
Feature selection, another critical task, involves identifying the most important features that contribute to a model's accuracy, helping to reduce complexity and improve efficiency.
Now that the context is set, let’s get hands-on and start applying Pyomo to some example modeling problems!
Learn Python From Scratch
Setting Up Pyomo
Before we dive into modeling, we need to set up our environment by installing Pyomo and choosing an appropriate solver.
1. Prerequisites
To use pyomo, you must have Python 3.6 or higher. Pyomo can be installed through pip.
pip install pyomo
This tutorial is created using pyomo version 6.8.0
.
import pyomo
print(pyomo.__version__)
Output:
>>> 6.8.0
2. Choosing and installing the right solver
In optimization, solvers are essential as they are the algorithms that find the optimal solution to the problem you’ve defined. Different solvers are better suited depending on the problem type (e.g., linear, nonlinear, integer). Pyomo is a modeling tool that relies on external solvers to perform the actual computation.
Let’s review some of the most common solvers.
Open-source solvers
1. GLPK (GNU Linear Programming Kit)
GLPK is a popular tool for solving linear programming (LP) and mixed-integer programming (MIP) problems.
It's an excellent choice for basic linear optimization tasks and is widely used in academic and industrial applications.
Installation
- Windows: Download and install GLPK.
- macOS:
brew install glpk
- Linux:
sudo apt-get install glpk-utils
2. CBC (Coin-or Branch and Cut)
CBC is an open-source solver for linear programming (LP) and mixed-integer programming (MIP) problems.
It offers advanced features and better performance, in some instances, compared to GLPK, making it a strong option for more complex optimization tasks.
CBC can be installed via the conda package manager.
conda install -c conda-forge coincbc
3. IPOPT (Interior Point OPTimizer)
IPOPT is a powerful solver designed for large-scale nonlinear programming (NLP) problems.
It is particularly well-suited for handling complex, nonlinear models, making it an excellent choice for problems beyond linear optimization.
IPOPT can be installed via the conda package manager.
!conda install -c conda-forge ipopt
Commercial solvers
1. CPLEX
CPLEX is a state-of-the-art optimization solver that efficiently handles linear programming (LP), mixed-integer programming (MIP), and quadratic programming (QP) problems.
It requires a license from IBM but is available for free to academic users, making it an excellent choice for research and educational purposes.
2. Gurobi
Gurobi is a leading commercial solver known for its speed and efficiency in solving LP, MIP, QP, and nonlinear programming (NLP) problems.
Like CPLEX, it requires a license but offers free access to academic users. Thus, it is an industry-standard tool for advanced optimization.
Open-source vs. commercial solvers
Open-source solvers like GLPK and CBC are free and sufficient for most basic optimization needs. They are excellent choices for smaller-scale projects and educational purposes.
However, commercial solvers such as CPLEX and Gurobi typically offer superior performance, especially for larger, more complex problems. These solvers have advanced features, including enhanced quadratic and nonlinear programming support, and are optimized for large-scale industrial applications.
While open-source solvers can handle many routine optimization tasks, commercial solvers are often a better choice when dealing with more complex, high-performance requirements.
Keep in mind that commercial solvers require a license, though they are available for free to academic users.
Now, let’s see how to configure a solver in Pyomo. I’ll use GLPK in this case.
3. Configuring a solver in Pyomo
First, ensure that the solver's executable is in your system PATH after installation.
Then, create a Python script and add the following:
from pyomo.environ import SolverFactory
solver = SolverFactory('glpk')
To confirm that both Pyomo and your solver are correctly installed, let's solve a simple test problem.
Test problem: simple linear program
Objective: Minimize Z=x+y
Subject to:
- x + 2y ≥ 4
- x - y ≤ 1
- x ≥ 0
- y ≥ 0
This problem is about finding the smallest possible value of Z, which is the sum of two variables, x and y. However, x and y must meet certain conditions.
First, when you add x and twice y, the result must be at least 4. Second, x minus y must be less than or equal to 1. Finally, both x and y must be zero or positive numbers (they can’t be negative).
The goal is to find values of x and y that satisfy these conditions while making Z as small as possible.
Implementing using Pyomo:
import pyomo.environ as pyo
# Create a model
model = pyo.ConcreteModel()
# Define variables
model.x = pyo.Var(within=pyo.NonNegativeReals)
model.y = pyo.Var(within=pyo.NonNegativeReals)
# Define objective
model.obj = pyo.Objective(expr=model.x + model.y, sense=pyo.minimize)
# Define constraints
model.con1 = pyo.Constraint(expr=model.x + 2 * model.y >= 4)
model.con2 = pyo.Constraint(expr=model.x - model.y <= 1)
# Select solver
solver = pyo.SolverFactory('glpk')
# Solve the problem
result = solver.solve(model)
# Display results
print('Status:', result.solver.status)
print('Termination Condition:', result.solver.termination_condition)
print('Optimal x:', pyo.value(model.x))
print('Optimal y:', pyo.value(model.y))
print('Optimal Objective:', pyo.value(model.obj))
If everything is working correctly, the expected output will be:
Status: ok
Termination Condition: optimal
Optimal x: 0.0
Optimal y: 2.0
Optimal Objective: 2.0
Let’s walk through the code above: First, it defines two variables, x and y, that can only take non-negative values. The objective of the model is to minimize the sum of x and y (x + y). The code defines the solver as glpk
to find the optimal values of x and y that satisfy these constraints while minimizing the objective.
After running the code, we find that the optimal values for the variables are x = 0.0 and y = 2.0, which minimize the objective function Z = x + y. Therefore, the minimum value of the objective function is 2.0, which satisfies the given constraints.
Basics of Modeling with Pyomo
Understanding how to define the basic components of an optimization model in Pyomo is necessary to set up and solve optimization problems effectively.
1. Defining variables
Variables represent the decisions that need to be made in an optimization problem. In Pyomo, variables are the quantities the solver will adjust to optimize the objective function while satisfying all constraints.
Scalar variables
A scalar variable is a single variable that is not indexed over any set. To define a scalar variable in Pyomo, you use the Var
class from the pyomo.environ
module.
from pyomo.environ import Var
model.x = Var()
We first import the Var
and create a variable x
using Var()
. This variable has no specified bounds, meaning it can take any real value unless constrained otherwise in the model.
Adding bounds
You can restrict the values that a variable can take by specifying bounds. Bounds are defined as a tuple (lower_bound
, upper_bound
):
from pyomo.environ import Var
model.x = Var(bounds=(0, None))
Specifying domains
Pyomo provides predefined domains that you can use to specify the type of values a variable can take, such as NonNegativeReals
, Integers
, or Binary
:
from pyomo.environ import Var, NonNegativeReals
model.x = Var(domain=NonNegativeReals)
Indexed variables
When dealing with multiple variables that are similar in nature, such as variables representing different time periods or items, it's efficient to use indexed variables. Indexed variables are variables that are defined over a set.
import pyomo.environ as pyo
model.I = pyo.Set(initialize=[1, 2, 3])
model.y = pyo.Var(model.I, domain=pyo.NonNegativeReals)
Suppose you're modeling the production quantities for three products. You can define:
model.Products = pyo.Set(initialize=['A', 'B', 'C'])
model.production = pyo.Var(model.Products, domain=pyo.NonNegativeReals)
Now, model.production['A']
, model.production['B']
, and model.production['C']
represent the production quantities for products A, B, and C, respectively.
2. Defining objectives
The objective function is what we're trying to optimize (minimize or maximize). It defines the goal of the model, such as minimizing costs or maximizing profits, and is typically expressed as a mathematical equation involving the decision variables.
These are defined using the Objective
class:
from pyomo.environ import ConcreteModel, Var, Objective, minimize, maximize, NonNegativeReals
# Create a model
model = ConcreteModel()
# Define variables
model.x = Var(within=NonNegativeReals)
model.y = Var(within=NonNegativeReals)
# Minimization (cost)
model.cost = Objective(expr=2 * model.x + 3 * model.y, sense=minimize)
# When Maximization profit - (can have one objective at a time)
# model.profit = Objective(expr=5 * model.x + 4 * model.y, sense=maximize)
3. Adding constraints
Constraints define the limitations or requirements of the problem:
from pyomo.environ import Constraint
model.con1 = Constraint(expr=model.x + model.y >= 10)
The above example defines a constraint in the Pyomo model using the Constraint
class. The constraint model.con1
specifies that the sum of the variables x and y must be greater than or equal to 10.
4. Parameterizing models
Parameters are fixed values used in the model to represent known quantities or constants that do not change during the optimization process.
They help define the relationships between variables and constraints, providing structure to the model by incorporating real-world data or assumptions:
from pyomo.environ import Param
model.p = Param(initialize=5)
The code above defines a parameter p
in the Pyomo model using the Param
class and initializes it with a fixed value of 5. The parameter p
can now be used in the model to represent a constant value that does not change during the optimization process.
Now, let’s work on an end-to-end optimization problem!
Pyomo End-to-End Example
Let’s see an end-to-end example of solving an optimization problem using Pyomo. We’ll model a real-world scenario where a factory produces two products, and the goal is to maximize profit while considering machine time constraints.
1. Problem statement
A factory produces two products, P1 and P2. The profit per unit is:
- P1: $40
- P2: $50
Machine time available:
- Machine A: 100 hours
- Machine B: 80 hours
- Machine C: 90 hours
Time required per unit:
Product |
Machine A (hours) |
Machine B (hours) |
Machine C (hours) |
P1 |
1 |
2 |
0 |
P2 |
2 |
1 |
3 |
Objective: Maximize profit.
Decision variables:
- x₁: Units of P1 to produce.
- x₂: Units of P2 to produce.
2. Mathematical formulation
Objective function:
Maximize Z = 40x₁ + 50x₂
Constraints:
- Machine A capacity: 1x₁ + 2x₂ ≤ 100
- Machine B capacity: 2x₁ + 1x₂ ≤ 80
- Machine C capacity: 3x₂ ≤ 90
- Non-negativity: x₁, x₂ ≥ 0
3. Implementation
Based on the objective and constraints of the problem, here’s the Python code to model it, again, using GLPK.
# Step 1: Import Libraries
import pyomo.environ as pyo
# Step 2: Create a Concrete Model
model = pyo.ConcreteModel()
# Step 3: Define Decision Variables (Units of P1 and P2 to produce)
model.x1 = pyo.Var(within=pyo.NonNegativeReals)
model.x2 = pyo.Var(within=pyo.NonNegativeReals)
# Step 4: Define the Objective Function (Maximize profit)
model.profit = pyo.Objective(expr=40 * model.x1 + 50 * model.x2, sense=pyo.maximize)
# Step 5: Define Constraints
# Machine A capacity constraint: 1x1 + 2x2 <= 100
model.machine_a = pyo.Constraint(expr=1 * model.x1 + 2 * model.x2 <= 100)
# Machine B capacity constraint: 2x1 + 1x2 <= 80
model.machine_b = pyo.Constraint(expr=2 * model.x1 + 1 * model.x2 <= 80)
# Machine C capacity constraint: 3x2 <= 90
model.machine_c = pyo.Constraint(expr=3 * model.x2 <= 90)
# Step 6: Solve the Model using GLPK solver
solver = pyo.SolverFactory('glpk')
result = solver.solve(model)
# Step 7: Analyze Results
# Display Solver Status and Termination Condition
print('Solver Status:', result.solver.status)
print('Termination Condition:', result.solver.termination_condition)
# Get and display the optimal values for x1, x2, and the maximum profit
x1_opt = pyo.value(model.x1)
x2_opt = pyo.value(model.x2)
profit_opt = pyo.value(model.profit)
print(f'Optimal production of P1 (x1): {x1_opt}')
print(f'Optimal production of P2 (x2): {x2_opt}')
print(f'Maximum Profit: ${profit_opt}')
Output:
>>> Solver Status: ok
>>> Termination Condition: optimal
>>> Optimal production of P1 (x1): 25.0
>>> Optimal production of P2 (x2): 30.0
>>> Maximum Profit: $2500.0
In the code above, we define a linear optimization model to maximize the profit from producing two products (P1 and P2). The objective function is set to maximize profit, with each unit of P1 contributing $40 and each unit of P2 contributing $50.
We impose three constraints representing the machine time limits for Machines A, B, and C.
Finally, we use the GLPK solver to solve the problem.
The final answer is to produce 25 units of P1 and 30 units of P2 where our maximum profit will be $2,500.
Advanced Features in Pyomo
In the previous section, we saw how easy it is to implement an end-to-end optimization problem with Pyomo. However, most real-life problems are not straightforward to solve.
In this section, I present some advanced features you can use to solve more complex scenarios.
1. Nonlinear optimization
Nonlinear optimization minimizes or maximizes a nonlinear objective function subject to nonlinear constraints. Let's look at an example where we minimize the sum of squared differences subject to a circular constraint.
Problem statement
Minimize the objective: Z = (x - 1)² + (y - 2)²
Subject to:
- x² + y² ≤ 4
- x, y ≥ 0
In Pyomo, we can define the decision variables x and y with bounds of 0 to ensure non-negativity. The objective function is written as the sum of squared differences from specific points, and the constraint ensures the solution lies within a circle of radius 2.
In this case, the IPOPT solver is suitable for its nonlinear optimization problem-solving capability:
import pyomo.environ as pyo
model = pyo.ConcreteModel()
# Define variables with lower bounds
model.x = pyo.Var(bounds=(0, None))
model.y = pyo.Var(bounds=(0, None))
# Objective function: minimize (x - 1)² + (y - 2)²
model.obj = pyo.Objective(expr=(model.x - 1)**2 + (model.y - 2)**2, sense=pyo.minimize)
# Constraint: x² + y² ≤ 4 (circle of radius 2)
model.circle = pyo.Constraint(expr=model.x**2 + model.y**2 <= 4)
solver = pyo.SolverFactory('ipopt')
result = solver.solve(model)
print('Optimal x:', pyo.value(model.x))
print('Optimal y:', pyo.value(model.y))
print('Minimum Z:', pyo.value(model.obj))
2. Mixed-integer programming (MIP)
Mixed-integer programming is used when some decision variables are integers (often binary) while others are continuous. It’s valuable for decision-making problems like facility location and production planning.
Problem statement
A company must decide whether to open warehouses in locations A, B, and C. The goal is to minimize the total cost, which includes fixed costs of opening warehouses and transportation costs.
We start by initializing the data, including the fixed costs of opening warehouses, transportation costs, capacity limits, and total demand:
locations = ['A', 'B', 'C']
FixedCost = {'A': 1000, 'B': 1200, 'C': 1500}
TransportCost = {'A': 5, 'B': 4, 'C': 6}
Capacity = {'A': 100, 'B': 80, 'C': 90}
Demand = 150
model = pyo.ConcreteModel()
# Binary variable: 1 if warehouse is open, 0 otherwise
model.y = pyo.Var(locations, domain=pyo.Binary)
# Continuous variable: amount of goods transported
model.x = pyo.Var(locations, domain=pyo.NonNegativeReals)
model.cost = pyo.Objective(
expr=sum(FixedCost[i] * model.y[i] + TransportCost[i] * model.x[i] for i in locations),
sense=pyo.minimize
)
# Demand constraint
model.demand = pyo.Constraint(expr=sum(model.x[i] for i in locations) >= Demand)
# Capacity constraints
def capacity_rule(model, i):
return model.x[i] <= Capacity[i] * model.y[i]
model.capacity = pyo.Constraint(locations, rule=capacity_rule)
solver = pyo.SolverFactory('cbc')
result = solver.solve(model)
for i in locations:
print(f"Warehouse {i}: Open={pyo.value(model.y[i])}, Transported={pyo.value(model.x[i])}")
print('Minimum Total Cost:', pyo.value(model.cost))
The model includes two types of decision variables: a binary variable y
that represents whether a warehouse is open (1 if open, 0 otherwise), and a continuous variable x
that represents the amount of goods transported from each warehouse.
The objective function sums each warehouse's fixed costs and transportation costs and minimizes the total. The constraints ensure that the total goods transported meet the demand and that the capacity of each warehouse is not exceeded if it is open.
3. Handling multiple objectives
Sometimes, optimization problems involve multiple objectives that may conflict, such as maximizing profit while minimizing environmental impact. A common approach is the weighted sum method, where each objective is assigned a weight to balance its importance.
Problem statement
We aim to maximize profit while minimizing environmental impact:
- Profit: Z₁ = 3x + 5y
- Environmental impact: Z₂ = 2x + y
We can combine these objectives using weights w1=0.6
, w2=0.4
, where the total objective becomes a weighted sum:
w1 = 0.6
w2 = 0.4
model.obj = pyo.Objective(
expr=w1 * (3 * model.x + 5 * model.y) - w2 * (2 * model.x + model.y),
sense=pyo.maximize
)
In this combined objective, we maximize profit while minimizing environmental impact by adjusting the weights.
4. Using external data sources
When dealing with large datasets, importing data from external sources such as CSV files is often useful. Pyomo works well with Pandas for reading and using external data.
We can read a CSV file using Pandas and use the data to initialize sets and parameters in our model:
import pandas as pd
data = pd.read_csv('parameters.csv')
# Define set from CSV data
model.I = pyo.Set(initialize=data['index'].unique())
# Define parameter initialized from CSV data
param_dict = data.set_index('index')['value'].to_dict()
model.param = pyo.Param(model.I, initialize=param_dict)
Tips and Best Practices for Using Pyomo
When working with Pyomo, keeping your models efficient, well-documented, and easy to troubleshoot is important.
1. Debugging and troubleshooting
While building optimization models in Pyomo, it's common to encounter issues such as infeasible solutions, solver failures, or incorrect results. Here are some best practices for debugging:
- Check constraints: Review your constraints if your model isn't producing a feasible solution. Tight constraints can make a problem infeasible. Use Pyomo’s
.display()
method to print out the values of variables and constraints to verify they are behaving as expected. - Solver output: Enable detailed solver logs by passing
tee=True
when calling thesolve()
method. This can provide insight into where the solver might struggle, such as unbounded variables or infeasibility. - Test simple models first: When dealing with complex models, test a simplified version. This can help isolate potential issues without the overhead of a fully specified model.
Troubleshooting is much easier if you approach it systematically, analyzing constraints, the objective function, and solver feedback.
2. Modeling efficiency
Optimization problems can become computationally expensive as the size of the model increases. To ensure efficient modeling, consider the following tips:
- Use sparsity: Avoid looping over unnecessary indices when defining constraints or objectives. Leveraging sparsity in your problem reduces computation time.
- Binary vs. continuous variables: Where possible, reduce the number of binary or integer variables. Continuous variables are easier for solvers to handle, leading to faster solutions.
- Constraint formulation: Keep constraints as simple as possible, both in mathematical form and implementation. Avoid unnecessary nonlinearities and break down complex constraints into smaller, manageable ones.
Efficient models solve faster and are easier to debug and maintain.
3. Documentation and maintenance
Maintaining well-documented Pyomo models is a good practice for long-term use and collaboration. Good documentation also makes it easier to revisit and update models over time:
- Use inline comments: Always add comments to explain the purpose of variables, constraints, and the objective function. This is especially important in optimization models where the logic might not be immediately obvious.
- Modularize your code: Break down your model into logical sections or even separate functions. This modular approach can improve readability and make debugging and modifying specific parts of the model easier.
- Track model changes: Keep a version history of your model, especially if it's evolving. Use version control tools like Git to track changes and ensure any updates or improvements can be traced back.
Proper documentation and structured code will make your Pyomo models more accessible to future collaborators and easier to scale or modify as your requirements evolve.
Conclusion
Pyomo is a powerful and flexible tool for building and solving optimization models in Python. Throughout this tutorial, we explored how Pyomo allows users to model various optimization problems, from linear programming to nonlinear and mixed-integer programming.
With its user-friendly syntax and integration with solvers, Pyomo makes formulating and solving real-world optimization problems accessible to both beginners and advanced users.
If you are interested in learning more about solving real-world problems with optimization, check out the Introduction to Optimization in Python free course on DataCamp!
Become a Python Developer
FAQs
What is Pyomo used for?
Pyomo is an open-source Python library used for defining and solving complex optimization problems, including linear, nonlinear, and mixed-integer programming.
How do I install Pyomo and its solvers?
You can install Pyomo using pip install pyomo
. Solvers like GLPK and CBC can be installed via package managers like Homebrew, apt-get, or Conda.
What types of optimization problems can Pyomo solve?
Pyomo supports a wide range of optimization problems, including Linear Programming (LP), Nonlinear Programming (NLP), Mixed-Integer Programming (MIP), and Stochastic Programming.
Can I use Pyomo for real-world applications like supply chain optimization?
Yes, Pyomo is widely used for real-world applications such as supply chain optimization, financial modeling, energy systems optimization, and machine learning tasks.
What is the difference between open-source and commercial solvers in Pyomo?
Open-source solvers like GLPK and CBC are free and sufficient for many problems, while commercial solvers like Gurobi and CPLEX offer enhanced performance and advanced features for more complex, large-scale optimization tasks.
Learn more about Python with these courses!
Course
Introduction to Statistics in Python
Course
Exploratory Data Analysis in Python
tutorial
Optimization in Python: Techniques, Packages, and Best Practices
tutorial
Stochastic Gradient Descent in Python: A Complete Guide for ML Optimization
tutorial
Mastering Bayesian Optimization in Data Science
tutorial
Python Tutorial for Beginners
tutorial
Graph Optimization with NetworkX in Python
tutorial
Using XGBoost in Python Tutorial
Bekhruz Tuychiev
16 min