PuLP is a linear and mixed integer programming modeler that provides an intuitive Python interface for creating, manipulating, and solving mathematical optimization problems.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Supporting functions for combinatorial operations, data structure manipulation, and system utilities that enhance PuLP's modeling capabilities. These utilities provide essential tools for complex optimization problem formulation and data handling.
Functions for creating and manipulating dictionaries and structured data, particularly useful for organizing variables and parameters in large-scale optimization problems.
def makeDict(headers, array, default=None):
"""
Create a dictionary from column headers and data array.
Parameters:
- headers (list): List of header names for dictionary keys
- array (list of lists): Data rows where each row contains values for headers
- default: Default value for missing entries
Returns:
dict: Dictionary with headers as keys and corresponding data as values
Examples:
headers = ['name', 'cost', 'capacity']
data = [['plant1', 100, 500], ['plant2', 120, 600]]
result = makeDict(headers, data)
# Returns: {'name': ['plant1', 'plant2'], 'cost': [100, 120], 'capacity': [500, 600]}
"""
def splitDict(data):
"""
Split a dictionary containing lists into multiple dictionaries.
Parameters:
- data (dict): Dictionary where values are lists of equal length
Returns:
list: List of dictionaries, one for each index position
Examples:
data = {'name': ['A', 'B'], 'cost': [10, 20], 'capacity': [100, 200]}
result = splitDict(data)
# Returns: [{'name': 'A', 'cost': 10, 'capacity': 100},
# {'name': 'B', 'cost': 20, 'capacity': 200}]
"""
def read_table(data, coerce_type, transpose=False):
"""
Read table data into dictionary structure with type coercion.
Parameters:
- data: Input data in various formats (list of lists, nested structure)
- coerce_type: Function or type to apply to data elements
- transpose (bool): Whether to transpose the data structure
Returns:
dict: Processed data dictionary
Examples:
data = [['1', '2'], ['3', '4']]
result = read_table(data, int)
# Converts string data to integers
"""Usage examples:
# Organize problem data
facilities = ['F1', 'F2', 'F3']
costs = [100, 150, 120]
capacities = [500, 600, 400]
# Create structured data dictionary
facility_data = makeDict(['facility', 'cost', 'capacity'],
[facilities, costs, capacities])
# Split into individual facility records
facility_records = splitDict(facility_data)
for record in facility_records:
print(f"Facility {record['facility']}: cost={record['cost']}, capacity={record['capacity']}")
# Process CSV-like data for optimization
raw_data = [
['10', '20', '30'],
['15', '25', '35'],
['12', '22', '32']
]
cost_matrix = read_table(raw_data, float)
# Use in variable creation
plants = list(facility_data['facility'])
production = LpVariable.dicts('prod', plants, 0)
# Create cost constraints using structured data
for i, plant in enumerate(plants):
prob += facility_data['cost'][i] * production[plant] <= facility_data['capacity'][i]Functions for generating combinations and permutations, essential for modeling complex assignment and scheduling problems.
def allcombinations(orgset, k):
"""
Generate all combinations of elements from orgset with up to k items.
Parameters:
- orgset (iterable): Set of elements to combine
- k (int): Maximum number of elements in each combination
Returns:
list: List of all combinations as tuples
Examples:
allcombinations(['A', 'B', 'C'], 2)
# Returns: [(), ('A',), ('B',), ('C',), ('A', 'B'), ('A', 'C'), ('B', 'C')]
"""
def allpermutations(orgset, k):
"""
Generate all permutations of elements from orgset with up to k items.
Parameters:
- orgset (iterable): Set of elements to permute
- k (int): Maximum number of elements in each permutation
Returns:
list: List of all permutations as tuples
Examples:
allpermutations(['A', 'B'], 2)
# Returns: [(), ('A',), ('B',), ('A', 'B'), ('B', 'A')]
"""
def combination(iterable, r):
"""
Alias for itertools.combinations - generate r-length combinations.
Parameters:
- iterable: Elements to combine
- r (int): Length of each combination
Returns:
itertools.combinations: Iterator of r-length combinations
"""
def permutation(iterable, r):
"""
Alias for itertools.permutations - generate r-length permutations.
Parameters:
- iterable: Elements to permute
- r (int): Length of each permutation
Returns:
itertools.permutations: Iterator of r-length permutations
"""Usage examples:
from itertools import combinations, permutations
# Assignment problem - select teams for projects
teams = ['TeamA', 'TeamB', 'TeamC', 'TeamD']
projects = ['Proj1', 'Proj2', 'Proj3']
# All possible assignments of 2 teams to each project
team_combinations = list(combination(teams, 2))
for combo in team_combinations:
for project in projects:
# Create binary variable for each team combination-project pair
var_name = f"assign_{combo[0]}_{combo[1]}_{project}"
assignment_var = LpVariable(var_name, cat=LpBinary)
# Scheduling problem - all possible orderings
tasks = ['TaskA', 'TaskB', 'TaskC']
all_sequences = list(permutation(tasks, len(tasks)))
# Create variables for each possible sequence
sequence_vars = {}
for i, seq in enumerate(all_sequences):
sequence_vars[seq] = LpVariable(f"sequence_{i}", cat=LpBinary)
# Constraint: exactly one sequence must be selected
prob += lpSum(sequence_vars.values()) == 1
# Resource allocation with combinations
resources = ['R1', 'R2', 'R3', 'R4']
max_resources_per_task = 2
# All valid resource combinations for tasks
valid_combinations = list(allcombinations(resources, max_resources_per_task))
for task in tasks:
task_resource_vars = {}
for combo in valid_combinations:
if combo: # Skip empty combination
var_name = f"use_{task}_{'_'.join(combo)}"
task_resource_vars[combo] = LpVariable(var_name, cat=LpBinary)
# Each task must use exactly one resource combination
if task_resource_vars:
prob += lpSum(task_resource_vars.values()) == 1Functions for handling numeric values and type validation in optimization contexts.
def valueOrDefault(x):
"""
Get the value of a variable/expression or return a default within bounds.
Parameters:
- x (LpVariable or LpAffineExpression): Object to evaluate
Returns:
float: Value of x, or a default value if not solved
Note: For variables, returns a value within the variable's bounds.
For expressions, returns the constant term or calculated default.
"""
def isNumber(x):
"""
Check if x is a numeric value (int or float).
Parameters:
- x: Object to check
Returns:
bool: True if x is int or float, False otherwise
"""Usage examples:
# Robust value extraction after solving
variables = [x, y, z]
solution_values = {}
for var in variables:
val = value(var)
if val is not None:
solution_values[var.name] = val
else:
# Use default value within bounds
solution_values[var.name] = valueOrDefault(var)
# Type validation for parameters
def create_cost_constraint(variables, costs, limit):
# Validate that all costs are numeric
if not all(isNumber(cost) for cost in costs):
raise ValueError("All costs must be numeric")
# Create constraint
total_cost = lpDot(variables, costs)
return total_cost <= limit
# Safe arithmetic operations
def safe_multiply(var, coeff):
if isNumber(coeff):
return coeff * var
else:
raise TypeError(f"Coefficient must be numeric, got {type(coeff)}")Functions for performance monitoring and system resource tracking.
def resource_clock():
"""
Return the current resource usage time for performance monitoring.
Returns:
float: Resource time in seconds
Note: Used for tracking solver performance and optimization runtime.
"""Usage examples:
# Performance monitoring for optimization
start_time = resource_clock()
# Build and solve problem
prob = LpProblem("Performance_Test", LpMinimize)
variables = LpVariable.dicts("x", range(1000), 0, 1)
prob += lpSum(variables)
# Add many constraints
for i in range(500):
prob += lpSum(variables[j] for j in range(i, min(i+10, 1000))) <= 5
solve_start = resource_clock()
status = prob.solve()
solve_time = resource_clock() - solve_start
total_time = resource_clock() - start_time
print(f"Model building time: {solve_start - start_time:.3f} seconds")
print(f"Solve time: {solve_time:.3f} seconds")
print(f"Total time: {total_time:.3f} seconds")
# Benchmark different approaches
def benchmark_approach(approach_func, *args):
start = resource_clock()
result = approach_func(*args)
elapsed = resource_clock() - start
return result, elapsed
# Compare different formulation methods
formulation1_result, time1 = benchmark_approach(create_formulation_v1, data)
formulation2_result, time2 = benchmark_approach(create_formulation_v2, data)
print(f"Formulation 1: {time1:.3f}s")
print(f"Formulation 2: {time2:.3f}s")
if time1 < time2:
print("Formulation 1 is faster")
else:
print("Formulation 2 is faster")Complex data manipulation patterns for large-scale optimization problems.
# Multi-dimensional data organization
def organize_transportation_data(supply_points, demand_points, costs, capacities, demands):
"""
Organize transportation problem data into structured format.
"""
# Create cost matrix
cost_data = {}
for i, supply in enumerate(supply_points):
cost_data[supply] = {}
for j, demand in enumerate(demand_points):
cost_data[supply][demand] = costs[i][j]
# Combine with capacity and demand data
supply_data = dict(zip(supply_points, capacities))
demand_data = dict(zip(demand_points, demands))
return cost_data, supply_data, demand_data
# Use organized data in optimization
cost_matrix, supply_capacity, demand_requirement = organize_transportation_data(
['S1', 'S2', 'S3'],
['D1', 'D2', 'D3', 'D4'],
[[10, 15, 20, 25], [12, 18, 22, 28], [8, 14, 16, 30]],
[100, 150, 120],
[80, 90, 100, 70]
)
# Create transportation variables and constraints
transport_vars = LpVariable.matrix("transport",
list(supply_capacity.keys()),
list(demand_requirement.keys()), 0)
# Supply constraints
for supply_point in supply_capacity:
prob += lpSum([transport_vars[supply_point][demand_point]
for demand_point in demand_requirement]) <= supply_capacity[supply_point]
# Demand constraints
for demand_point in demand_requirement:
prob += lpSum([transport_vars[supply_point][demand_point]
for supply_point in supply_capacity]) >= demand_requirement[demand_point]
# Objective function using organized cost data
total_cost = lpSum([cost_matrix[s][d] * transport_vars[s][d]
for s in supply_capacity
for d in demand_requirement])
prob += total_costInstall with Tessl CLI
npx tessl i tessl/pypi-pulp