Pooling Problem with Nonlinear and Setup Costs#

pooling.ipynb Open In Colab Open In Deepnote Open In Kaggle Open In Gradient Open In SageMaker Studio Lab Powered by AMPL

Description: pooling problem with extra nonlinear and setup costs, solved by Knitro with MP2NL

Tags: pooling, minimum-cost flow, blending, nonlinear, setup costs, semi-continuous, AMPLPY, Knitro, MP2NL

Notebook author: Gleb Belov <gleb@ampl.com>

Reference:

  1. Milk pooling and blending. Hands-on optimization with AMPL in Python.

  2. Standard pooling problem in Gurobi, accessed March 12, 2026.

  3. MP2NL meta-driver.

# Install dependencies
%pip install -q amplpy pandas seaborn networkx
# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook

ampl = ampl_notebook(
    modules=["knitro", "mp2nl"],  # modules to install
    license_uuid="default",  # license to use
)  # instantiate AMPL object and register magics

Motivation#

The pooling problem is a challenging problem in the petrochemical refining, wastewater treatment and mining industries. This problem can be regarded as a generalization of the minimum-cost flow problem and the blending problem. It is important because of the significant savings it can generate, so it comes at no surprise that it has been studied extensively.

We take the so-called Q-formulation as the basic model. We solve the standard case first, then add nonlinearities, discrete setup costs, and semi-continuity constraints. The corresponding expressions are written using AMPL logical constraint syntax. To solve the models with Artelys Knitro, we use meta-driver MP2NL.

The Q-formulation in AMPL#

Use %%ampl_eval to evaluate AMPL commands and declarations

%%ampl_eval
# AMPL Model for the Standard Pooling Problem (Q-Formulation)

# Sets
set SOURCES;
set POOLS;
set TARGETS;
set ATTRS;

# Edges for flow variables (defined dynamically based on data)
set S2T within {SOURCES, TARGETS};
set S2P within {SOURCES, POOLS};
set P2T within {POOLS, TARGETS};

# Parameters
param cost {SOURCES};
param supply {SOURCES};
param content {SOURCES, ATTRS};

param price {TARGETS};
param demand {TARGETS};
param min_tol {TARGETS, ATTRS};
param max_tol {TARGETS, ATTRS};

param cap {POOLS};

# Variables
var flow_s2t {(s,t) in S2T} >= 0 <= supply[s];  # Flow from source to target
var flow_p2t {(p,t) in P2T} >= 0 <= cap[p];  # Flow from pool to target
var prop_s2p {S2P} >= 0, <= 1; # Proportion from source to pool

# Defined variables
# They are essentially macros for readability and presolve
var pool_outflow {p in POOLS} =
    sum {t in TARGETS: (p,t) in P2T} flow_p2t[p,t];
var source_pool_flow {(s,p) in S2P} =   # flow_s2p: implicit
    prop_s2p[s,p] * pool_outflow[p];
var source_outflow {s in SOURCES} =
    sum {t in TARGETS: (s,t) in S2T} flow_s2t[s,t] +
    sum {p in POOLS: (s,p) in S2P} source_pool_flow[s, p];
var target_inflow {t in TARGETS} =
    sum {s in SOURCES: (s,t) in S2T} flow_s2t[s,t] +
    sum {p in POOLS: (p,t) in P2T} flow_p2t[p,t];
var target_attr_inflow {t in TARGETS, attr in ATTRS} =
    sum {s in SOURCES: (s,t) in S2T} content[s,attr] * flow_s2t[s,t] +
    sum {p in POOLS: (p,t) in P2T}
        flow_p2t[p,t] *
        sum {s in SOURCES: (s,p) in S2P} content[s,attr] * prop_s2p[s,p];

# Objective Function: Maximize Total Profit
maximize TotalProfit:
  sum {t in TARGETS} price[t] * target_inflow[t]
  - sum {s in SOURCES} cost[s] * source_outflow[s];

# Constraints

# 1. Source capacity
subject to Source_capacity {s in SOURCES}:
  source_outflow[s] <= supply[s];

# 2. Pool capacity
subject to Pool_capacity {p in POOLS}:
  pool_outflow[p] <= cap[p];

# 3. Target demand
subject to Target_demand {t in TARGETS}:
  target_inflow[t] >= demand[t];

# 4. Pool inflow (sum of proportions for each pool must be 1)
subject to Pool_inflow {p in POOLS}:
  sum {s in SOURCES: (s,p) in S2P} prop_s2p[s,p] = 1;

# 5.1 Target (min) tolerances
subject to Target_min_tolerances {t in TARGETS, attr in ATTRS}:
  target_attr_inflow[t, attr] >= min_tol[t,attr] * target_inflow[t];

# 5.2 Target (max) tolerances
subject to Target_max_tolerances {t in TARGETS, attr in ATTRS}:
  target_attr_inflow[t, attr] <= max_tol[t,attr] * target_inflow[t];

Random instance data#

We generate the insatnce parameters randomly.

import random
import math
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

def generate_pooling_data(num_sources=3, num_pools=2, num_targets=3, num_attrs=4,
                          source_cost_range=(10, 50), source_supply_range=(500, 2000),
                          target_price_range=(100, 300), target_demand_range=(100, 1000),
                          pool_cap_range=(500, 2000),
                          s2t_density=0.5, s2p_density=0.8, p2t_density=0.8,
                          content_deviation_percent=0.05, # Max % deviation for source content from ref
                          tolerance_deviation_percent=0.10, # Max % deviation for min_tol from ref
                          tolerance_gap_percent=0.05): # Average % gap for max_tol above min_tol

    sources = [f"s{i+1}" for i in range(num_sources)]
    pools = [f"p{i+1}" for i in range(num_pools)]
    targets = [f"t{i+1}" for i in range(num_targets)]
    attrs = [f"attr{i+1}" for i in range(num_attrs)]

    # Generate diverse reference ranges for attributes
    # This mimics having some low-value attributes (like 'den') and some high-value ones (like 'roz')
    attr_range_templates = [
        {'ref_range': (0.5, 1.0)},   # Low values
        {'ref_range': (2.0, 5.0)},   # Medium-low values
        {'ref_range': (50.0, 70.0)}, # Medium-high values
        {'ref_range': (90.0, 110.0)} # High values
    ]
    
    reference_attr_configs = {}
    for i, attr in enumerate(attrs):
        # Assign a template in a round-robin fashion or randomly
        template = random.choice(attr_range_templates)
        reference_attr_configs[attr] = template

    cost = {s: random.uniform(*source_cost_range) for s in sources}
    supply = {s: random.uniform(*source_supply_range) for s in sources}
    price = {t: random.uniform(*target_price_range) for t in targets}
    demand = {t: random.uniform(*target_demand_range) for t in targets}
    cap = {p: random.uniform(*pool_cap_range) for p in pools}

    # 1. Generate reference content for each attribute
    reference_content_values = {
        attr: random.uniform(*config['ref_range'])
        for attr, config in reference_attr_configs.items()
    }

    # 2. Generate source contents with deviation from reference
    content = {}
    for s in sources:
        for a in attrs:
            ref_val = reference_content_values[a]
            dev_factor = random.uniform(1 - content_deviation_percent, 1 + content_deviation_percent)
            content[s, a] = max(0.0, ref_val * dev_factor) # Ensure non-negative content

    # 3. Generate target tolerances with deviation from reference and controlled gap
    min_tol = {}
    max_tol = {}
    for t in targets:
        for a in attrs:
            ref_val = reference_content_values[a]

            # Min tolerance deviation from reference
            dev_min_factor = random.uniform(1 - tolerance_deviation_percent, 1)
            min_t = max(0.0, ref_val * dev_min_factor)

            # Max tolerance based on min_t and the gap parameter
            gap_factor = random.uniform(1 + tolerance_gap_percent * 0.8, 1 + tolerance_gap_percent * 1.2)
            max_t = min_t * gap_factor
            max_t = max(min_t, max_t) # Ensure max_t is always >= min_t

            min_tol[t, a] = min_t
            max_tol[t, a] = max_t

    s2t = [(s, t) for s in sources for t in targets if random.random() < s2t_density]
    s2p = [(s, p) for s in sources for p in pools if random.random() < s2p_density]
    p2t = [(p, t) for p in pools for t in targets if random.random() < p2t_density]

    return {
        "sources": sources,
        "pools": pools,
        "targets": targets,
        "attrs": attrs,
        "cost": cost,
        "supply": supply,
        "content": content,
        "price": price,
        "demand": demand,
        "min_tol": min_tol,
        "max_tol": max_tol,
        "cap": cap,
        "s2t": s2t,
        "s2p": s2p,
        "p2t": p2t,
    }
# Generate random data with customizable parameters
# You can change these values to generate different problem instances
num_sources_param = 23
num_pools_param = 12
num_targets_param = 16
num_attrs_param = 5

random.seed(42)        # Same seed for reproducibility

data = generate_pooling_data(
    num_sources=num_sources_param,
    num_pools=num_pools_param,
    num_targets=num_targets_param,
    num_attrs=num_attrs_param, # Pass the number of attributes
    s2t_density=0.1, s2p_density=0.2, p2t_density=0.4,
    content_deviation_percent=0.05,  # Source content varies by up to +/- 5% from reference
    tolerance_deviation_percent=0.005, # Min tolerance varies by up to +/- 10% from reference
    tolerance_gap_percent=0.015       # Max tolerance is avg 5% above min tolerance (with variation)
)

Data statistics#

# Display data statistics
print("\n--- Data Statistics ---")
print(f"Number of Sources: {len(data['sources'])}")
print(f"Number of Pools: {len(data['pools'])}")
print(f"Number of Targets: {len(data['targets'])}")
print(f"Number of Attributes: {len(data['attrs'])}")

print("\nCost per Source (Min/Max):", round(min(data['cost'].values()),2), "/", round(max(data['cost'].values()),2))
print("Supply per Source (Min/Max):", round(min(data['supply'].values()),2), "/", round(max(data['supply'].values()),2))
print("Price per Target (Min/Max):", round(min(data['price'].values()),2), "/", round(max(data['price'].values()),2))
print("Demand per Target (Min/Max):", round(min(data['demand'].values()),2), "/", round(max(data['demand'].values()),2))
print("Capacity per Pool (Min/Max):", round(min(data['cap'].values()),2), "/", round(max(data['cap'].values()),2))

print(f"Number of Source-to-Target edges: {len(data['s2t'])}")
print(f"Number of Source-to-Pool edges: {len(data['s2p'])}")
print(f"Number of Pool-to-Target edges: {len(data['p2t'])}")
--- Data Statistics ---
Number of Sources: 23
Number of Pools: 12
Number of Targets: 16
Number of Attributes: 5

Cost per Source (Min/Max): 11.27 / 44.77
Supply per Source (Min/Max): 565.18 / 1977.83
Price per Target (Min/Max): 114.28 / 285.08
Demand per Target (Min/Max): 150.34 / 921.82
Capacity per Pool (Min/Max): 707.5 / 1995.99
Number of Source-to-Target edges: 27
Number of Source-to-Pool edges: 53
Number of Pool-to-Target edges: 76

Contents and tolerances#

print("\n--- Content of Attributes in Sources ---")
content_df = pd.Series(data['content']).unstack()
content_df.index.name = 'Source'
content_df.columns.name = 'Attribute'
display(content_df)

print("\n--- Minimum Tolerances for Attributes in Targets ---")
min_tol_df = pd.Series(data['min_tol']).unstack()
min_tol_df.index.name = 'Target'
min_tol_df.columns.name = 'Attribute'
display(min_tol_df)

print("\n--- Maximum Tolerances for Attributes in Targets ---")
max_tol_df = pd.Series(data['max_tol']).unstack()
max_tol_df.index.name = 'Target'
max_tol_df.columns.name = 'Attribute'
display(max_tol_df)
--- Content of Attributes in Sources ---
Attribute attr1 attr2 attr3 attr4 attr5
Source
s1 0.802453 0.723017 63.034043 3.272937 2.860222
s10 0.806713 0.724535 64.573505 3.491046 2.806537
s11 0.815202 0.685390 62.980816 3.361060 2.864306
s12 0.794332 0.674929 59.218314 3.241103 2.769094
s13 0.805264 0.658262 63.204673 3.253984 2.635094
s14 0.760802 0.680643 62.005420 3.328740 2.750225
s15 0.801336 0.707848 64.285399 3.430093 2.683458
s16 0.796047 0.685949 59.405924 3.458270 2.714112
s17 0.791109 0.717575 59.157296 3.456388 2.794504
s18 0.763132 0.708185 63.778970 3.271139 2.669299
s19 0.801101 0.667470 59.957997 3.391428 2.857385
s2 0.818230 0.710981 60.928171 3.333854 2.660091
s20 0.764312 0.713711 64.121836 3.267272 2.796147
s21 0.801444 0.658786 59.399342 3.490467 2.681635
s22 0.790734 0.691093 64.175602 3.541147 2.661875
s23 0.758466 0.684797 64.558673 3.501212 2.695095
s3 0.758553 0.707761 63.277982 3.564787 2.826393
s4 0.798818 0.665132 62.693599 3.520459 2.756353
s5 0.774171 0.683654 59.822156 3.558528 2.870591
s6 0.831609 0.699234 61.847218 3.272024 2.716504
s7 0.836943 0.713624 60.304515 3.315938 2.772794
s8 0.764581 0.708441 63.874514 3.566863 2.763250
s9 0.768333 0.703445 64.684556 3.290154 2.761864
--- Minimum Tolerances for Attributes in Targets ---
Attribute attr1 attr2 attr3 attr4 attr5
Target
t1 0.796994 0.691634 61.758848 3.390834 2.750603
t10 0.796419 0.689067 61.897053 3.387808 2.749864
t11 0.795625 0.691950 61.840210 3.391158 2.751487
t12 0.795859 0.692122 61.735642 3.399640 2.742663
t13 0.795740 0.690551 61.892522 3.401095 2.743541
t14 0.795804 0.689162 61.716891 3.398695 2.745031
t15 0.797714 0.690932 61.620024 3.393013 2.746481
t16 0.794675 0.689580 61.743178 3.400856 2.753420
t2 0.795465 0.690572 61.772610 3.395773 2.741415
t3 0.797653 0.692181 61.787407 3.388524 2.748255
t4 0.795254 0.690009 61.648698 3.394911 2.753357
t5 0.796693 0.689268 61.764796 3.393069 2.743835
t6 0.795962 0.689969 61.858049 3.398496 2.753303
t7 0.797718 0.692002 61.656305 3.390714 2.749939
t8 0.796231 0.692066 61.804547 3.387879 2.742273
t9 0.794858 0.691365 61.642815 3.398891 2.740967
--- Maximum Tolerances for Attributes in Targets ---
Attribute attr1 attr2 attr3 attr4 attr5
Target
t1 0.810051 0.702680 62.570321 3.432714 2.784616
t10 0.808029 0.698843 63.000875 3.435735 2.793866
t11 0.807845 0.704296 62.925789 3.435159 2.787427
t12 0.806266 0.703677 62.724612 3.445498 2.787116
t13 0.805474 0.699665 62.765080 3.459689 2.787329
t14 0.806684 0.700630 62.646127 3.456683 2.778426
t15 0.808537 0.703300 62.580028 3.449739 2.795658
t16 0.808500 0.698038 62.676706 3.455682 2.798637
t2 0.805250 0.702669 62.534963 3.453868 2.775430
t3 0.809157 0.702854 62.543589 3.442552 2.786451
t4 0.807993 0.699389 62.627248 3.454574 2.786552
t5 0.808944 0.699763 62.555060 3.435187 2.781453
t6 0.808108 0.702322 62.796661 3.450587 2.788053
t7 0.808557 0.703388 62.500413 3.438378 2.797011
t8 0.806985 0.700581 62.853747 3.435314 2.791301
t9 0.806504 0.701988 62.732062 3.442717 2.779931

Data visualization#

# Data Visualization (Histograms)
# Adjust subplot grid if num_attrs_param is large, or just plot one example
num_plots = min(len(data['attrs']), 6) # Plot up to 6 attribute distributions if many attrs
num_rows = math.ceil(num_plots / 3)
fig, axes = plt.subplots(num_rows, 3, figsize=(18, 5 * num_rows))
fig.suptitle('Distribution of Generated Data Parameters', fontsize=16)

# Flatten axes for easier iteration if there are multiple rows
if num_rows > 1: 
    axes = axes.flatten()
else: # Handle case where there's only one row (axes is a 1D array or single object)
    if isinstance(axes, np.ndarray) and axes.ndim == 1:
        pass # already flat
    else:
        axes = [axes] # make it iterable

current_plot_idx = 0

sns.histplot(list(data['cost'].values()), bins=5, kde=True, ax=axes[current_plot_idx], color='skyblue')
axes[current_plot_idx].set_title('Source Costs')
current_plot_idx += 1

sns.histplot(list(data['supply'].values()), bins=5, kde=True, ax=axes[current_plot_idx], color='lightcoral')
axes[current_plot_idx].set_title('Source Supplies')
current_plot_idx += 1

sns.histplot(list(data['price'].values()), bins=5, kde=True, ax=axes[current_plot_idx], color='lightgreen')
axes[current_plot_idx].set_title('Target Prices')
current_plot_idx += 1

sns.histplot(list(data['demand'].values()), bins=5, kde=True, ax=axes[current_plot_idx], color='lightsalmon')
axes[current_plot_idx].set_title('Target Demands')
current_plot_idx += 1

sns.histplot(list(data['cap'].values()), bins=5, kde=True, ax=axes[current_plot_idx], color='mediumpurple')
axes[current_plot_idx].set_title('Pool Capacities')
current_plot_idx += 1

# Plot distribution for one example attribute content
if len(data['attrs']) > 0:
    attr_to_plot = data['attrs'][0]
    attr_contents = [val for (s, a), val in data['content'].items() if a == attr_to_plot]
    sns.histplot(attr_contents, bins=5, kde=True, ax=axes[current_plot_idx], color='gold')
    axes[current_plot_idx].set_title(f'Content for {attr_to_plot} attribute')
    current_plot_idx += 1

# Hide any remaining empty subplots
for i in range(current_plot_idx, len(axes)):
    fig.delaxes(axes[i])

plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()
../_images/f71222ca34ac534091ae9339e4c5482bac14ab9157f6c734353565500fe91827.png

Network visualization#

import networkx as nx
import matplotlib.pyplot as plt

def visualize_pooling_network(data):
    G = nx.DiGraph()

    # Add Source nodes with supply
    for s in data['sources']:
        G.add_node(s, node_type='source', capacity=data['supply'][s])

    # Add Pool nodes with capacity
    for p in data['pools']:
        G.add_node(p, node_type='pool', capacity=data['cap'][p])

    # Add Target nodes with demand
    for t in data['targets']:
        # Demand is a dictionary, might have multiple attributes. Let's sum for simplicity for visualization.
        total_demand = sum(data['demand'].get(t, 0) for t_key in data['demand'] if t_key == t)
        G.add_node(t, node_type='target', capacity=total_demand)

    # Add edges
    for s, t in data['s2t']:
        G.add_edge(s, t, edge_type='s2t')
    for s, p in data['s2p']:
        G.add_edge(s, p, edge_type='s2p')
    for p, t in data['p2t']:
        G.add_edge(p, t, edge_type='p2t')

    # Define node colors and labels
    node_colors = []
    node_labels = {}
    for node in G.nodes():
        if G.nodes[node]['node_type'] == 'source':
            node_colors.append('skyblue')
            node_labels[node] = f"{node}\nSupply: {G.nodes[node]['capacity']:.0f}"
        elif G.nodes[node]['node_type'] == 'pool':
            node_colors.append('lightgreen')
            node_labels[node] = f"{node}\nCapacity: {G.nodes[node]['capacity']:.0f}"
        else: # target
            node_colors.append('salmon')
            node_labels[node] = f"{node}\nDemand: {G.nodes[node]['capacity']:.0f}"

    plt.figure(figsize=(12, 8))
    
    # Use a layered layout to better visualize the tripartite graph
    pos = {}
    source_nodes = [n for n, attr in G.nodes(data=True) if attr['node_type'] == 'source']
    pool_nodes = [n for n, attr in G.nodes(data=True) if attr['node_type'] == 'pool']
    target_nodes = [n for n, attr in G.nodes(data=True) if attr['node_type'] == 'target']
    
    # Position sources, pools, and targets in distinct columns
    for i, node in enumerate(source_nodes):
        pos[node] = (0, i)
    for i, node in enumerate(pool_nodes):
        pos[node] = (1, i)
    for i, node in enumerate(target_nodes):
        pos[node] = (2, i)

    nx.draw(G, pos, with_labels=False, node_color=node_colors, node_size=3000, alpha=0.9, font_size=8, font_weight='bold')
    nx.draw_networkx_labels(G, pos, labels=node_labels, font_size=8)
    
    # No explicit arc capacities in the problem formulation, so we'll just show the arcs.
    # If specific capacities were defined for each s2p, s2t, p2t flow, they could be added here.
    
    plt.title('Pooling Problem Network Visualization with Node Capacities/Demands')
    plt.axis('off')
    # plt.tight_layout()
    plt.show()
# Call the visualization function with the generated data
visualize_pooling_network(data)
../_images/f9f2abba71882a8ef32c5a042589e0ab2da96316c0df23fe81c9362e64f820b6.png

Load data directly from Python data structures using amplpy#

# Set the data
# Sets
ampl.getSet('SOURCES').setValues(data['sources'])
ampl.getSet('POOLS').setValues(data['pools'])
ampl.getSet('TARGETS').setValues(data['targets'])
ampl.getSet('ATTRS').setValues(data['attrs'])

# Edges
ampl.getSet('S2T').setValues(data['s2t'])
ampl.getSet('S2P').setValues(data['s2p'])
ampl.getSet('P2T').setValues(data['p2t'])

# Parameters
ampl.getParameter('cost').setValues(data['cost'])
ampl.getParameter('supply').setValues(data['supply'])
ampl.getParameter('content').setValues(data['content'])

ampl.getParameter('price').setValues(data['price'])
ampl.getParameter('demand').setValues(data['demand'])
ampl.getParameter('min_tol').setValues(data['min_tol'])
ampl.getParameter('max_tol').setValues(data['max_tol'])

ampl.getParameter('cap').setValues(data['cap'])

print("AMPL model loaded and data assigned successfully.")
AMPL model loaded and data assigned successfully.

Solve the standard model#

Solve with Knitro#

ampl.option["solver"] = "knitro"
ampl.option["knitro_options"] = "outlev=1 ms_enable=1"

ampl.snapshot('pooling.run')     # Debugging feature

print("\nSolving the AMPL model...")
ampl.solve()
# ampl.solve(solver="gurobi", gurobi_options="outlev=1 lim:time=30")
Solving the AMPL model...
Artelys Knitro 15.1.0: outlev=1
ms_enable=1

=======================================
          Commercial License
         Artelys Knitro 15.1.0
=======================================

Knitro using up to 8 threads.
Knitro presolve eliminated 0 variables and 0 constraints.

concurrent_evals         0
datacheck                0
feastol                  1e-06
feastol_abs              0.001
findiff_numthreads       1
hessian_no_f             1
hessopt                  1
ms_enable                1
opttol                   1e-06
opttol_abs               0.001
outlev                   1

Problem Characteristics                     |           Presolved
-----------------------
Problem type: QCQP
Objective: maximize / quadratic
Number of variables:                    155 |                           155
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       155 |         0         0       155
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of constraints:                  220 |                           220
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            11        29         0 |        11        29         0
  quadratic:          0       180         0 |         0       180         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:           103      1292           |       103      1292          
  quadratic:        122      4448       336 |       122      4448       336
  total:            155      5040       336 |       155      5040       336

Knitro parallel multistart will run with 8 threads.

Return codes description
------------------------
  0:  The final solution satisfies the termination conditions for verifying optimality.
  -100 to -199:  A feasible approximate solution was found.
  -200 to -299:  Knitro terminated at an infeasible point.
  -300 to -301:  The problem was determined to be unbounded.
  -400 to -499:  Knitro terminated because it reached a pre-defined limit.
    -400 to -409:  A feasible point was found.
    -410 to -419:  No feasible point was found.
  -500 to -599:  Knitro terminated with an input error or some non-standard error.
A more detailed description of individual return codes and their corresponding
termination messages is provided at
https://www.artelys.com/app/docs/knitro/3_referenceManual/returnCodes.html

 Solve Thrd Status   Objective   FeasError   Opt Error   Solve Time  Real Time 
 ----- ---- ------ ------------ ----------- ----------- ----------- -----------
     5    7      0  4.16560e+06 3.14060e-11 1.64892e-06 5.15610e-02    0.664054
     6    1      0  4.16560e+06 5.84134e-09 1.12709e-10 5.92010e-02    0.664054
     9    1      0  4.16560e+06 7.02869e-11 3.29499e-06 3.84070e-02    0.664054
    11    4      0  4.15307e+06 9.86802e-11 5.86301e-10 3.38190e-02    0.664054
     7    4      0  4.23238e+06 1.11140e-09 4.26218e-09    0.104792    0.708017
    20    0      0  4.14514e+06 3.85626e-10 9.48260e-08 3.63880e-02    0.708017
    27    1      0  4.16560e+06 9.91918e-11 3.81120e-11 3.28230e-02    0.708017
    29    1      0  4.16560e+06 7.52038e-11 1.12425e-07 4.82080e-02    0.708017
    23    1      0  4.23238e+06 8.69562e-11 1.48566e-10 6.61640e-02    0.742399
    24    6      0  4.23238e+06 8.75818e-07 5.52202e-06 9.33630e-02    0.742399
    12    4      0  4.22041e+06 8.82494e-11 4.99180e-10    0.127042    0.762067
    13    0      0  4.23238e+06 2.78237e-09 1.60785e-06    0.151476    0.762067
    18    2      0  4.23238e+06 4.81671e-08 1.62282e-05    0.115582    0.762067
    25    5      0  4.22805e+06 7.79892e-11 4.62167e-10 7.70020e-02    0.762067
    31    1      0  4.23238e+06 5.85203e-11 8.23955e-10    0.114380    0.762067
    19    6      0  4.22041e+06 9.92486e-11 3.89935e-11    0.133569    0.800481
    16    5      0  4.13532e+06 2.29193e-10 9.89353e-11    0.174785    0.833429
     3    0      0  4.23307e+06 4.15457e-09 1.49846e-06    0.255818    0.863843
     0    3      0  4.15307e+06 8.92942e-09 2.07055e-05    0.268530    0.863843
    14    4      0  4.22806e+06 1.06343e-09 1.86943e-06    0.236256    0.863843
     8    7      0  4.22752e+06 3.99377e-08 2.60411e-06    0.280277    0.905410
     1    5      0  4.22806e+06 3.81493e-07 2.93715e-05    0.326601     1.01695
     4    2      0  4.22806e+06 3.81493e-07 2.93715e-05    0.343142     1.01695
     2    6      0  4.22806e+06 3.81493e-07 2.93715e-05    0.345876     1.01695
    30    5      0  4.23235e+06 9.21485e-14 1.00063e-08    0.290675     1.01695
    28    6      0  4.22876e+06 4.11979e-07 5.18046e-06    0.354302     1.01695
    10    1      0  4.22041e+06 9.50138e-11 7.94588e-07    0.379189     1.13170
    36    4      0  4.23238e+06 7.51470e-11 2.15060e-10 6.17980e-02     1.21465
    26    4      0  4.22752e+06 1.31680e-07 4.87632e-06    0.368114     1.23564
    38    4      0  4.22041e+06 9.85949e-11 6.56479e-11 5.52360e-02     1.23564
    41    0      0  4.16560e+06 9.77707e-11 7.16679e-11 3.74130e-02     1.23564
    45    0      0  4.16560e+06 9.91349e-11 2.08384e-08 4.73410e-02     1.23564
    21    0      0  4.22041e+06 9.76854e-11 7.32921e-11    0.381125     1.28601
    39    4      0  4.23238e+06 1.60557e-11 2.77640e-08 9.01270e-02     1.28601
    42    0      0  4.23235e+06 8.64384e-09 1.32977e-06 6.99580e-02     1.28601
    34    7      0  4.22041e+06 3.92788e-11 4.65083e-06    0.152029     1.32603
    33    0      0  4.15307e+06 5.69433e-13 1.16499e-07    0.152813     1.32603
    17    7      0  4.23305e+06 1.91721e-08 4.01618e-07    0.437182     1.39924
    32    1      0  4.22041e+06 1.21822e-08 1.00550e-06    0.255497     1.39924
    40    7      0  4.23138e+06 9.44800e-14 3.05443e-09    0.192267     1.39924
    46    0      0  4.16560e+06 3.62377e-11 2.66650e-09    0.115504     1.59283
    50    3      0  4.16560e+06 1.38825e-08 3.01559e-07 2.57450e-02     1.59283
    47    7      0  4.16560e+06 1.59702e-10 9.22583e-10    0.185864     1.59283
    49    0      0  4.23238e+06 9.31806e-11 5.10428e-11 4.27970e-02     1.64141
    51    0      0  4.15307e+06 9.34506e-10 1.43591e-07 3.35830e-02     1.64141
    55    7      0  4.14514e+06 1.57652e-14 7.06713e-09 3.24890e-02     1.64141
    48    5      0  4.23238e+06 9.47011e-11 4.56877e-11 9.71920e-02     1.66858
    58    6      0  4.22752e+06 2.02657e-07 5.59106e-06 7.78810e-02     1.74544
    61    6      0  4.23238e+06 2.87482e-08 1.99186e-05 3.79280e-02     1.74544
    60    4      0  4.23238e+06 2.10450e-07 5.86487e-06 5.06160e-02     1.74544
    62    3      0  4.15307e+06 8.17374e-11 4.81416e-07 4.87730e-02     1.74544
    35    5      0  4.23227e+06 1.60026e-07 3.48267e-06    0.380754     1.81122
    59    3      0  4.23234e+06 7.40670e-11 7.84315e-07 8.23170e-02     1.81122
    63    6      0  4.22088e+06 6.28120e-10 2.45894e-08 7.31400e-02     1.81122
    52    3      0  4.22876e+06 6.73792e-06 7.51703e-06    0.196180     1.98960
    15    3      0  4.16560e+06 1.01863e-10 9.93907e-11     1.04730     2.03247
    44    1      0  4.16373e+06 9.55254e-11 5.70179e-10    0.718007     2.06262
    70    4      0  4.15307e+06 9.87711e-10 1.71643e-09 3.82710e-02     2.06262
    65    3      0  4.13490e+06 9.61506e-11 1.98164e-10    0.164255     2.24235
    68    4      0  4.23234e+06 2.65862e-05 7.56912e-06 5.46210e-02     2.24235
    67    1      0  4.23238e+06 8.93721e-11 7.11136e-11    0.122412     2.24235
    71    1      0  4.16560e+06 9.90497e-11 1.01892e-08 7.71180e-02     2.24235
    73    0      0  4.16560e+06 1.58286e-08 2.40691e-05 2.98040e-02     2.24235
    75    1      0  4.16560e+06 9.93623e-11 6.18300e-08 3.89790e-02     2.24235
    22    2      0  4.15307e+06 6.80132e-11 1.67516e-09     1.04604     2.31005
    37    6      0  4.22088e+06 5.47516e-10 1.87489e-10    0.582772     2.31005
    43    4      0  4.15307e+06 9.18874e-11 1.00189e-09    0.504697     2.31005
    64    4      0  4.23238e+06 3.12446e-09 9.70306e-06    0.225141     2.31005
    69    3      0  4.23234e+06 6.40483e-11 2.27976e-09    0.105213     2.31005
    72    4      0  4.16560e+06 9.98170e-11 2.32212e-11 9.08810e-02     2.31005
    66    6      0  4.14512e+06 9.59233e-11 7.49477e-11    0.300217     2.61666
    76    0      0  4.16560e+06 4.00746e-11 3.27201e-06 3.45040e-02     2.61666
    77    7      0  4.23238e+06 8.52936e-11 1.19584e-10 4.77960e-02     2.69434
    79    1      0  4.23238e+06 2.90034e-07 1.28003e-05 6.11930e-02     2.69434
    84    5      0  4.16560e+06 4.12456e-10 6.55087e-06 3.88040e-02     2.69434
    83    7      0  4.23238e+06 9.52980e-11 4.02209e-11 5.03290e-02     2.69434
    85    5      0  4.16560e+06 1.00044e-10 3.55838e-11 4.67210e-02     2.69434
    88    5      0  4.22041e+06 1.65019e-08 2.22784e-07 5.45270e-02     2.69434
    89    6      0  4.16560e+06 8.73115e-10 5.69020e-11 3.47210e-02     2.69434
    82    0      0  4.23235e+06 8.20677e-11 1.36321e-10 6.09460e-02     2.73431
    92    6      0  4.16560e+06 2.97860e-11 5.60658e-06 4.11100e-02     2.73431
    53    0      0  4.22752e+06 1.43700e-09 1.91897e-08    0.628788     2.77365
    91    4      0  4.22876e+06 9.33278e-09 6.59006e-09 6.20970e-02     2.77365
    56    7      0  4.16560e+06 8.97273e-11 2.38128e-09    0.784808     2.86152
    78    6      0  4.13134e+06 8.79083e-11 3.11724e-08    0.198134     2.86152
    80    4      0  4.22752e+06 4.54529e-08 4.16085e-06    0.173601     2.86152
    96    2      0  4.15307e+06 1.02972e-10 7.66989e-10 3.08890e-02     2.86152
    54    5      0  4.16560e+06 8.55778e-11 5.48605e-07    0.714723     2.89951
    81    1      0  4.22041e+06 8.18090e-10 8.72009e-08    0.193241     2.89951
    90    5      0  4.23307e+06 1.06102e-08 7.13089e-09    0.228707     2.96469
    99    0      0  4.22752e+06 1.32219e-07 6.23840e-06    0.131633     2.96469
    87    0      0  4.23235e+06 6.02085e-10 1.44694e-09    0.461309     3.04649
    98    2      0  3.54130e+06 6.66019e-09 8.06140e-07    0.185895     3.04649
    57    2      0  4.14514e+06 1.32590e-07 2.24896e-06     1.11303     3.08547
    86    7      0  4.22752e+06 2.24177e-06 1.66898e-05    0.657243     3.12632
    95    4      0  4.16560e+06 9.98170e-11 9.98170e-11    0.544570     3.20390
    93    1      0  4.23307e+06 7.53744e-10 3.51764e-10    0.650838     3.26399
    97    5      0  4.15307e+06 1.45815e-09 7.57731e-10    0.270789     3.29251
    94    6      0  4.23227e+06 1.46081e-08 2.31986e-08    0.594754     3.33108
    74    3      0  4.15307e+06 1.02737e-08 7.06233e-10     1.31564     3.33108

MULTISTART: Best locally optimal solution is returned.
EXIT: All multi-start solves have terminated.
      100 solve(s) returned satisfactory solutions.

Final Statistics
----------------
Final objective value               =   4.23306594644484e+06
Final feasibility error (abs / rel) =   4.15e-09 / 6.63e-15
Final optimality error  (abs / rel) =   1.50e-06 / 1.49e-08
# of iterations                     =      11450 
# of CG iterations                  =          0 
# of function evaluations           =          0
# of gradient evaluations           =          0
# of Hessian evaluations            =          0
Total program time (secs)           =       3.33126 (    20.763 CPU time)

================================================================================

Knitro 15.1.0: Locally optimal or satisfactory solution.
objective 4233065.946; feasibility error 4.15e-09
11450 iterations; 0 function evaluations

suffix feaserror OUT;
suffix opterror OUT;
suffix numfcevals OUT;
suffix numiters OUT;

Check and display solution#

def display_result(ampl):
    # Check solver status
    print(f"AMPL solve result: {ampl.solve_result}, solve code: {ampl.solve_result_num}")
    
    if "solved" in ampl.solve_result or \
            "limit" in ampl.solve_result and 410>ampl.solve_result_num:
        # Retrieve and display results
        # objective_value = ampl.getObjective(next(reversed(ampl.getObjectives()))).value()
        # print(f"\nCurrent objective ({ampl.getObjectives()[-1].getName()}): {objective_value:.2f}")
        ampl.eval('display _objname, _obj;')
    
        print("\n--- Flow from Sources to Targets (flow_s2t) ---")
        flow_s2t_df = ampl.getData('flow_s2t').toPandas().unstack()
        if not flow_s2t_df.empty: # Check if DataFrame is not empty
            df = flow_s2t_df[flow_s2t_df['flow_s2t'] > 1e-6]
            nnz = (df > 1e-6).sum().sum()
            df = df.round(2).replace({np.nan: '-'})
            display(df)
            print(f"Total non-zero values: {nnz}")
        else:
            print("No flow from sources to targets.")

        print("\n--- Flow from Pools to Targets (flow_p2t) ---")
        flow_p2t_df = ampl.getData('flow_p2t').toPandas().unstack()
        if not flow_p2t_df.empty: # Check if DataFrame is not empty
            df = flow_p2t_df[flow_p2t_df['flow_p2t'] > 1e-6]
            nnz = (df > 1e-6).sum().sum()
            df = df.round(2).replace({np.nan: '-'})
            display(df)
            print(f"Total non-zero values: {nnz}")
        else:
            print("No flow from pools to targets.")

        print("\n--- Proportion from Sources to Pools (prop_s2p) ---")
        prop_s2p_df = ampl.getData('prop_s2p').toPandas().unstack()
        if not prop_s2p_df.empty: # Check if DataFrame is not empty
            df = prop_s2p_df[prop_s2p_df['prop_s2p'] > 1e-6]
            nnz = (df > 1e-6).sum().sum()
            df = df.round(2).replace({np.nan: '-'})
            display(df)
            print(f"Total non-zero values: {nnz}")
        else:
            print("No proportion from sources to pools.")

    else:
        print("No optimal or feasible solution found.")
display_result(ampl)
AMPL solve result: solved, solve code: 0
:    _objname      _obj      :=
1   TotalProfit   4233070
;


--- Flow from Sources to Targets (flow_s2t) ---
flow_s2t
index1 t1 t10 t11 t12 t13 t14 t15 t16 t2 t3 t4 t5 t6 t8 t9
index0
s10 - - - - - - - - - - - - - - 71.26
s12 - 72.05 - - 140.67 - 165.93 - - - - - - - -
s15 - - 226.84 - - - - - - - - - - - -
s17 - - 101.34 1558.26 - - - - - - - - - - -
s18 - 0.11 - - - - - - 127.59 - - - - - -
s19 4.21 - - - - 40.47 - 1738.3 - - - - - - -
s20 - - 87.39 - - - - - - - 392.07 - - - -
s21 - - 203.07 - - - - - - - - - - - -
s22 - - - - - - - 978.29 - - - - - - -
s3 - - - - - - - - - - - - - - -
s6 - - - 1189.14 - - - - - - - - - - -
s7 - - - - - - - - - - 415.92 - - - 86.62
s9 - - - - - - - - - - - - - - -
Total non-zero values: 19

--- Flow from Pools to Targets (flow_p2t) ---
flow_p2t
index1 t1 t10 t11 t12 t13 t14 t15 t16 t2 t3 t4 t5 t6 t7 t8 t9
index0
p1 132.17 382.03 - - - - - - - - - - 866.04 - - -
p10 - - - 729.26 - - - - - - - - - - - -
p11 - - 203.87 - - - 136.92 - 358.37 - - 40.82 - - - -
p12 - - 73.71 - - 185.2 - 678.37 353.66 - - - - - 229.78 -
p2 - - - 1004.73 - - - - - - - 4.44 - - - 133.49
p3 - 34.14 - - - - 76.01 493.32 - - - - - - - -
p4 - 60.97 - - - - - - - - 730.41 - 55.78 - - -
p5 - - - 560.99 - - - - - 824.54 - - - 610.46 - -
p6 - - - 707.5 - - - - - - - - - - - -
p7 - - - - - - - 1233.23 - - - 7.04 - - - -
p8 13.97 - - - 567.96 - 354.64 197.55 - - - 103.52 - - 396.03 -
p9 - - - - 72.06 - 36.99 97.87 - - - 3.75 - - 120.31 -
Total non-zero values: 39

--- Proportion from Sources to Pools (prop_s2p) ---
prop_s2p
index1 p1 p10 p11 p12 p2 p3 p4 p5 p6 p7 p8 p9
index0
s1 0.35 - - - - - - - - 0.28 0.14 -
s10 - - - 0.31 - - - - - - 0.24 -
s11 - - 0.5 - - 1.0 - 0.24 - - - -
s13 - - - 0.29 0.51 - - - - - - -
s14 - - - - - - - - - - - -
s15 - - - - - - - - - - 0.23 -
s16 0.18 - - - 0.05 - 0.39 - 0.33 - 0.39 -
s17 - - - - - - - - - - - -
s18 - - - - - - - - - - - -
s19 - - - - - - - - - - - -
s2 - 0.23 - 0.06 - - - 0.39 - - - -
s20 - - - - - - - - - 0.6 - 0.08
s22 - - - 0.13 - - - - - - - -
s23 - - - - - - - 0.01 - - - 0.49
s3 - - - - - - 0.15 - - - - 0.41
s4 0.37 - - 0.08 - - 0.32 - - - - -
s5 - - 0.5 - - - 0.14 0.04 - - - -
s7 - - - 0.12 - - - - - - - -
s8 - 0.77 - - 0.45 - - 0.33 - 0.12 - 0.02
s9 0.1 - - - - - - - 0.67 - - -
Total non-zero values: 40

Nonlinear costs#

To make the model more realistic, we assume the costs are nonlinear. We replace the objective function by a new one.

New objective function#

%%ampl_eval

# Drop the original objective function
drop TotalProfit;

# Objective Function: Maximize Total Profit with Nonlinear Costs
maximize TotalProfitExpCosts:
  sum {t in TARGETS} price[t] * target_inflow[t]
  - sum {s in SOURCES} cost[s] *           # Nonlinear functions of flow
      (sum {t in TARGETS: (s,t) in S2T} exp(flow_s2t[s,t] / supply[s]) +
        sum {p in POOLS: (s,p) in S2P} exp(source_pool_flow[s, p] / supply[s]));

Solve with Knitro#

ampl.option["solver"] = "knitro"
ampl.option["knitro_options"] = "outlev=1 ms_enable=1"

ampl.snapshot('pooling_expcosts.run')     # Debugging feature

print("\nSolving the AMPL model...")

ampl.option["reset_initial_guesses"] = 1  # Do not send the previous solution as an initial guess
                                          # This is done by default to enable iterative approaches

ampl.solve()
# ampl.solve(solver="gurobi", gurobi_options="outlev=1 lim:time=30 dualreductions=0")
# ampl.solve(solver="gurobi", gurobi_options="outlev=1 lim:time=30 alg:optimalitytarget=1")
Solving the AMPL model...
Artelys Knitro 15.1.0: outlev=1
ms_enable=1

=======================================
          Commercial License
         Artelys Knitro 15.1.0
=======================================

Knitro using up to 8 threads.
Knitro presolve eliminated 0 variables and 0 constraints.

concurrent_evals         0
datacheck                0
feastol                  1e-06
feastol_abs              0.001
findiff_numthreads       1
hessian_no_f             1
hessopt                  1
ms_enable                1
opttol                   1e-06
opttol_abs               0.001
outlev                   1

Problem Characteristics                     |           Presolved
-----------------------
Problem type: NLP
Objective: maximize / general
Number of variables:                    155 |                           155
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       155 |         0         0       155
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of constraints:                  220 |                           220
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            11        29         0 |        11        29         0
  quadratic:          0       180         0 |         0       180         0
  nonlinear:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0      1292           |         0      1292          
  quadratic:          0      4448       336 |         0      4448       336
  nonlinear:        155         0       821 |       155         0       821
  total:            155      5040       821 |       155      5040       821

Knitro parallel multistart will run with 8 threads.

Return codes description
------------------------
  0:  The final solution satisfies the termination conditions for verifying optimality.
  -100 to -199:  A feasible approximate solution was found.
  -200 to -299:  Knitro terminated at an infeasible point.
  -300 to -301:  The problem was determined to be unbounded.
  -400 to -499:  Knitro terminated because it reached a pre-defined limit.
    -400 to -409:  A feasible point was found.
    -410 to -419:  No feasible point was found.
  -500 to -599:  Knitro terminated with an input error or some non-standard error.
A more detailed description of individual return codes and their corresponding
termination messages is provided at
https://www.artelys.com/app/docs/knitro/3_referenceManual/returnCodes.html

 Solve Thrd Status   Objective   FeasError   Opt Error   Solve Time  Real Time 
 ----- ---- ------ ------------ ----------- ----------- ----------- -----------
     0    4      0  4.80720e+06 9.99592e-11 1.81192e-11 6.80590e-02    0.651907
    19    7      0  4.75354e+06 2.22045e-16 1.02088e-08 4.39320e-02    0.651907
    23    6      0  4.75354e+06 4.07338e-08 7.62473e-07 3.62700e-02    0.651907
     2    0      0  4.75354e+06 2.22045e-16 6.93993e-08 8.22760e-02    0.678726
     9    0      0  4.75354e+06 4.75779e-11 3.39335e-09 9.46610e-02    0.678726
    11    5      0  4.81082e+06 4.17515e-11 3.09743e-11    0.106092    0.678726
    20    1      0  4.75354e+06 9.79412e-11 8.45795e-11 8.47090e-02    0.678726
    24    2      0  4.75354e+06 9.74865e-11 3.03673e-11 7.37230e-02    0.678726
     3    1      0  4.81082e+06 1.20053e-10 1.66052e-10    0.129090    0.706201
    15    5      0  4.75354e+06 1.52340e-10 1.34908e-10    0.143055    0.706201
     6    5      0  4.71350e+06 2.35093e-06 7.33483e-08    0.160620    0.729097
    12    0      0  4.80720e+06 7.83587e-11 1.59430e-09    0.165097    0.729097
    17    3      0  4.81082e+06 2.13163e-12 3.70304e-09    0.139687    0.729097
     1    6      0  4.81082e+06 4.50200e-11 7.23728e-10    0.202041    0.750151
     4    2      0  4.71867e+06 1.01340e-09 2.82811e-09    0.209758    0.750151
    13    6      0  4.75338e+06 1.35346e-04 8.11988e-07    0.223402    0.750151
    18    0      0  4.80720e+06 7.87708e-11 4.27247e-10    0.176884    0.750151
    22    5      0  4.81082e+06 1.07538e-06 2.94437e-07    0.180621    0.750151
    26    3      0  4.71351e+06 1.00027e-06 1.77360e-08    0.161830    0.750151
    28    2      0  4.75354e+06 4.35784e-09 2.11795e-10    0.141349    0.750151
     8    4      0  4.81082e+06 2.22045e-16 3.25604e-10    0.252682    0.758347
    10    1      0  4.80720e+06 1.59141e-07 4.88408e-07    0.268096    0.758347
    14    2      0  4.80632e+06 1.38215e-04 6.78413e-07    0.224290    0.758347
    30    4      0  4.75352e+06 1.13179e-04 6.95213e-07    0.180911    0.758347
    31    5      0  4.71867e+06 1.10802e-06 2.82488e-07    0.165134    0.758347
    16    4      0  4.81082e+06 5.16112e-05 7.28503e-07    0.230171    0.893743
    29    0      0  4.75354e+06 2.22045e-16 1.71274e-09    0.218443    0.893743
     5    3      0  4.75354e+06 5.91026e-08 6.82484e-08    0.335055     1.16022
     7    7      0  4.80719e+06 1.66983e-09 5.59334e-09    0.354711     1.16022
    27    1      0  4.75354e+06 9.04853e-07 1.40469e-08    0.242038     1.16022
    21    7      0  4.75354e+06 1.12595e-07 2.02873e-07    0.292026     1.18010
    38    4      0  4.75354e+06 9.56106e-11 1.27332e-09 6.05160e-02     1.21353
    49    0      0  4.75354e+06 2.22045e-16 4.16841e-09 4.61000e-02     1.21353
    37    0      0  4.81082e+06 9.52696e-11 1.73692e-07 7.39040e-02     1.27076
    25    6      0  4.75354e+06 3.18283e-05 2.15201e-07    0.370360     1.27076
    36    5      0  4.71867e+06 9.53833e-11 4.24122e-11    0.123659     1.27076
    34    2      0  4.75354e+06 9.87654e-11 3.59103e-11    0.135109     1.27076
    33    1      0  4.75354e+06 3.24576e-11 6.49424e-10    0.140438     1.27076
    41    6      0  4.75354e+06 9.96181e-11 3.13031e-11    0.141039     1.29367
    43    2      0  4.81082e+06 2.22045e-16 1.27893e-07    0.114574     1.29367
    39    4      0  4.81082e+06 9.76002e-11 2.21413e-09    0.183443     1.32359
    42    5      0  4.75354e+06 8.12292e-11 6.08915e-10    0.187649     1.32359
    50    0      0  4.75354e+06 7.11769e-07 4.10453e-07    0.136514     1.32359
    45    6      0  4.75354e+06 2.22045e-16 2.29383e-09    0.182450     1.50494
    47    4      0  4.75354e+06 8.41055e-10 3.62314e-08    0.164165     1.50494
    52    4      0  4.72565e+06 1.33227e-15 9.41115e-09    0.147974     1.50494
    51    6      0  4.75354e+06 7.75877e-06 6.11884e-07    0.161116     1.50494
    40    0      0  4.81082e+06 5.71845e-10 1.01192e-08    0.248018     1.90125
    46    2      0  4.75354e+06 3.33067e-16 8.73314e-09    0.283988     1.95952
    48    5      0  4.72565e+06 3.44183e-05 7.95369e-07    0.273454     1.95952
    60    0      0  4.71350e+06 8.54072e-11 1.52518e-10    0.121599     1.99010
    35    3      0  4.71867e+06 1.72008e-10 2.35573e-07    0.598790     2.01925
    53    0      0  4.71867e+06 9.87654e-11 7.45902e-11    0.211806     2.01925
    69    2      0  4.81082e+06 8.50662e-11 1.46315e-10 5.81400e-02     2.01925
    59    3      0  4.75354e+06 1.65687e-09 1.28134e-07    0.150729     2.04914
    55    7      0  4.75354e+06 1.07269e-07 8.91701e-09    0.210890     2.04914
    32    7      0  4.80720e+06 1.01863e-10 6.28267e-11    0.511325     2.07214
    63    0      0  4.80719e+06 2.28949e-10 1.28667e-09    0.295226     2.07214
    65    1      0  4.80716e+06 2.22045e-16 5.04783e-07    0.231030     2.12495
    67    0      0  4.81082e+06 9.34640e-05 7.81733e-09    0.197416     2.12495
    58    5      0  4.81082e+06 0.00000e+00 1.15632e-09    0.295979     2.26969
    68    3      0  4.75354e+06 2.22045e-16 1.00734e-08    0.204017     2.26969
    66    7      0  4.71351e+06 2.22045e-16 6.80580e-10    0.233070     2.26969
    61    3      0  4.75354e+06 7.97513e-11 1.08810e-10    0.401067     2.39166
    44    1      0  4.81082e+06 1.57431e-04 8.94287e-07    0.893771     2.42085
    62    7      0  4.75354e+06 2.27694e-07 2.84268e-09    0.389235     2.42085
    54    2      0  4.81082e+06 1.00954e-10 7.74523e-11    0.633889     2.60189
    72    6      0  4.75354e+06 1.80512e-09 2.30017e-09    0.121243     2.60189
    70    2      0  4.75354e+06 4.10706e-07 1.76950e-07    0.159584     2.60189
    75    4      0  4.75354e+06 9.93907e-11 4.40235e-11    0.142244     2.60189
    56    4      0  4.81082e+06 5.73102e-05 6.28152e-07    0.807918     2.68523
    76    7      0  4.81082e+06 6.19763e-05 7.78598e-07    0.165890     2.68523
    73    0      0  4.75354e+06 1.18712e-09 8.48623e-08    0.203886     2.68523
    77    6      0  4.75354e+06 1.75569e-08 1.86939e-08    0.193871     2.77721
    82    6      0  4.81082e+06 9.90497e-11 3.99266e-11    0.140216     2.77721
    83    2      0  4.81082e+06 4.44089e-16 3.96217e-09 3.44050e-02     2.77721
    78    2      0  4.70508e+06 2.62882e-04 4.79669e-07    0.232917     2.82938
    79    4      0  4.75354e+06 3.09102e-10 5.21631e-09    0.224003     2.82938
    81    0      0  4.80720e+06 1.33454e-08 5.02885e-10    0.189898     2.82938
    57    6      0  4.72469e+06 7.77156e-16 4.48276e-09    0.719195     2.89416
    80    7      0  4.75354e+06 2.44942e-05 9.31332e-08    0.204140     2.89416
    85    1      0  4.75354e+06 1.15557e-05 1.60133e-07    0.112418     3.05520
    71    1      0  4.75354e+06 3.39787e-09 1.44722e-08    0.410618     3.10640
    86    6      0  4.81082e+06 2.01340e-05 5.23906e-07    0.121734     3.10640
    89    4      0  4.75354e+06 6.25391e-09 2.73332e-08    0.112000     3.10640
    92    4      0  4.75354e+06 2.22045e-16 5.05025e-10 7.12840e-02     3.10640
    87    2      0  4.81082e+06 7.94671e-11 2.20691e-11    0.157702     3.14380
    88    7      0  4.75354e+06 1.82581e-10 7.84120e-09    0.174693     3.14380
    90    1      0  4.80720e+06 9.91633e-11 2.98233e-11    0.172827     3.15998
    74    3      0  4.81082e+06 6.18456e-10 1.55695e-09    0.824703     3.18734
    96    4      0  4.75354e+06 8.69989e-11 2.27972e-10    0.202289     3.23663
    93    2      0  4.71209e+06 1.14937e-10 2.67555e-10    0.272397     3.26144
    97    1      0  4.75354e+06 4.27984e-04 2.28022e-08    0.211368     3.26144
    98    3      0  4.75354e+06 1.33173e-05 2.15118e-08    0.220259     3.27682
    91    6      0  4.81082e+06 1.09139e-10 9.99876e-11    0.421495     3.27682
    99    0      0  4.80720e+06 9.88223e-11 2.08085e-11    0.212799     3.27682
    94    5      0  4.81082e+06 8.88178e-11 3.09220e-10    0.448345     3.27682
    84    0      0  4.81082e+06 9.80833e-11 1.68468e-11    0.449360     3.27689
    64    5      0  4.75354e+06 2.51920e-07 1.29524e-07     1.18829     3.29383
    95    7      0  4.72553e+06 3.55271e-15 2.26469e-07    0.543813     3.37336

MULTISTART: Best locally optimal solution is returned.
EXIT: All multi-start solves have terminated.
      100 solve(s) returned satisfactory solutions.

Final Statistics
----------------
Final objective value               =   4.81082184919915e+06
Final feasibility error (abs / rel) =   1.09e-10 / 1.74e-16
Final optimality error  (abs / rel) =   1.00e-10 / 1.00e-10
# of iterations                     =      13596 
# of CG iterations                  =          0 
# of function evaluations           =      15338
# of gradient evaluations           =      13936
# of Hessian evaluations            =      13616
Total program time (secs)           =       3.37359 (    22.190 CPU time)

================================================================================

Knitro 15.1.0: Locally optimal or satisfactory solution.
objective 4810821.849; feasibility error 1.09e-10
13596 iterations; 15338 function evaluations

Check and display solution#

display_result(ampl)
AMPL solve result: solved, solve code: 0
:        _objname          _obj      :=
1   TotalProfitExpCosts   4810820
;


--- Flow from Sources to Targets (flow_s2t) ---
flow_s2t
index1 t1 t10 t11 t12 t13 t14 t15 t16 t2 t3 t4 t5 t6 t8 t9
index0
s10 - - - - - - - - - - - - - - 76.42
s12 - 92.53 - - 175.66 - 177.17 - - - - - - - -
s15 - - 162.33 - - - - - - - - - - - -
s17 - - 106.02 1553.58 - - - - - - - - - - -
s18 - - - - - - - - 127.31 - - - - - -
s19 7.61 - - - - 40.75 - 1734.62 - - - - - - -
s20 - - 104.6 - - - - - - - 383.47 - - - -
s21 - - 198.11 - - - - - - - - - - - -
s22 - - - - - - - 969.09 - - - - - - -
s3 - - - - - - - - - - - - - - -
s6 - - - 1189.14 - - - - - - - - - - -
s7 - - - - - - - - - - 416.25 - - - 79.85
s9 - - - - - - - - - - 1.02 - - - -
Total non-zero values: 19

--- Flow from Pools to Targets (flow_p2t) ---
flow_p2t
index1 t1 t10 t11 t12 t13 t14 t15 t16 t2 t3 t4 t5 t6 t7 t8 t9
index0
p1 94.66 365.98 - - - - - - - - - - 606.85 - - -
p10 - - - 729.26 - - - - - - - - - - - -
p11 - - 175.29 - - - 60.86 - 358.75 - - 38.87 - - - -
p12 - - 149.87 - - 184.92 - 475.86 353.57 - - - 58.37 - 298.13 -
p2 - - - 1006.1 - - - - - - - 1.46 - - - 135.09
p3 - - - - - - 96.93 642.57 - - - - - - - -
p4 - 90.8 - - - - - - - - 759.46 - 192.74 - - -
p5 - - - 560.99 - - - - - 824.54 - - - 610.46 - -
p6 14.39 - - 554.07 139.04 - - - - - - - - - - -
p7 - - - - - - - 1159.59 - - - 16.82 63.86 - - -
p8 33.69 - - - 803.38 - 382.78 - - - - 102.41 - - 311.43 -
p9 - - - - 11.13 - 52.76 288.04 - - - - - - 136.57 -
Total non-zero values: 40

--- Proportion from Sources to Pools (prop_s2p) ---
prop_s2p
index1 p1 p10 p11 p12 p2 p3 p4 p5 p6 p7 p8 p9
index0
s1 0.45 - - - - - - - - 0.41 0.05 -
s10 - - - 0.31 - - - - - - 0.24 -
s11 - - 0.5 - - 1.0 - 0.19 - - - -
s13 - - - 0.29 0.52 - - - - - - -
s14 - - - - - - - - - - - -
s15 - - - - - - - - - - 0.27 -
s16 0.05 - - - 0.09 - 0.46 - 0.19 - 0.44 -
s17 - - - - - - - - - - - -
s18 - - - - - - - - - - - -
s19 - - - - - - - - - - - -
s2 - 0.17 - 0.06 - - - 0.42 - - - -
s20 - - - - - - - - - 0.49 - 0.28
s22 - - - 0.14 - - - - - - - -
s23 - - - - - - - - - - - -
s3 - - - - - - 0.2 - - - - 0.53
s4 0.46 - - 0.08 - - 0.28 - - - - -
s5 - - 0.5 - - - 0.06 0.1 - - - -
s7 - - - 0.13 - - - - - - - -
s8 - 0.83 - - 0.39 - - 0.29 - 0.1 - 0.19
s9 0.04 - - - - - - - 0.81 - - -
Total non-zero values: 38

Setup costs#

We modify the objective function again to account for any actually used pipes.

Objective function with setup costs#

%%ampl_eval

# Drop the previous objective function
drop TotalProfitExpCosts;

param pipe_setup_cost default 10000;

# Objective Function: Maximize Total Profit with Nonlinear Costs
maximize TotalProfitExpAndSetupCosts:
  sum {t in TARGETS} price[t] * target_inflow[t]
  - sum {s in SOURCES} cost[s] * source_outflow[s]
  - sum {s in SOURCES}              # Pipe setup costs. Note the brackets
      ((sum {t in TARGETS: (s,t) in S2T} if flow_s2t[s,t]>0 then pipe_setup_cost) +
        (sum {p in POOLS: (s,p) in S2P} if source_pool_flow[s, p]>0 then pipe_setup_cost))
;

Solve with Knitro#

ampl.option["solver"] = "knitro"
ampl.option["knitro_options"] = "outlev=1 ms_enable=1 numthreads=8 convex=0"

ampl.snapshot('pooling_expandsetupcosts.run')     # Debugging feature

print("\nSolving the AMPL model...")

ampl.option["reset_initial_guesses"] = 1  # Do not send the previous solution as an initial guess
                                          # This is done by default to enable iterative approaches

ampl.solve()
# ampl.solve(solver="gurobi", gurobi_options="outlev=1 lim:time=300 dualreductions=0")
Solving the AMPL model...
Artelys Knitro 15.1.0: outlev=1
ms_enable=1
numthreads=8
convex=0

=======================================
          Commercial License
         Artelys Knitro 15.1.0
=======================================

Knitro using up to 8 threads.
Knitro presolve eliminated 0 variables and 0 constraints.

concurrent_evals         0
convex                   0
datacheck                0
feastol                  1e-06
feastol_abs              0.001
findiff_numthreads       1
hessian_no_f             1
hessopt                  1
ms_enable                1
numthreads               8
opttol                   1e-06
opttol_abs               0.001
outlev                   1

Problem Characteristics                     |           Presolved
-----------------------
Problem type: NLP
Objective: maximize / general
Number of variables:                    155 |                           155
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       155 |         0         0       155
                             free     fixed |                free     fixed
                                0         0 |                   0         0
Number of constraints:                  220 |                           220
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            11        29         0 |        11        29         0
  quadratic:          0       180         0 |         0       180         0
  nonlinear:          0         0         0 |         0         0         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:             0      1292           |         0      1292          
  quadratic:          0      4448       336 |         0      4448       336
  nonlinear:        155         0       773 |       155         0       773
  total:            155      5040       773 |       155      5040       773

Knitro parallel multistart will run with 8 threads.

Return codes description
------------------------
  0:  The final solution satisfies the termination conditions for verifying optimality.
  -100 to -199:  A feasible approximate solution was found.
  -200 to -299:  Knitro terminated at an infeasible point.
  -300 to -301:  The problem was determined to be unbounded.
  -400 to -499:  Knitro terminated because it reached a pre-defined limit.
    -400 to -409:  A feasible point was found.
    -410 to -419:  No feasible point was found.
  -500 to -599:  Knitro terminated with an input error or some non-standard error.
A more detailed description of individual return codes and their corresponding
termination messages is provided at
https://www.artelys.com/app/docs/knitro/3_referenceManual/returnCodes.html

 Solve Thrd Status   Objective   FeasError   Opt Error   Solve Time  Real Time 
 ----- ---- ------ ------------ ----------- ----------- ----------- -----------
     3    7      0  2.74130e+06 4.04691e-06 1.63707e-05 3.53140e-02    0.642444
     6    3      0  3.34513e+06 5.03872e-05 1.55964e-05 8.75660e-02    0.661585
    11    0      0  3.35307e+06 7.81313e-11 1.85859e-09 7.48630e-02    0.661585
    14    0      0  3.33134e+06 1.27682e-08 2.42176e-06 7.86360e-02    0.661585
    27    2      0  3.36560e+06 5.09317e-10 1.21141e-08 3.06120e-02    0.661585
    31    0      0  3.36560e+06 5.28235e-09 1.10241e-08 5.11160e-02    0.661585
    18    7      0  3.43238e+06 9.51701e-11 4.28664e-11 9.35830e-02    0.668807
    19    2      0  3.36560e+06 9.46457e-08 5.79029e-07 9.92360e-02    0.668807
    23    2      0  3.36560e+06 9.83107e-11 7.78615e-11 7.71010e-02    0.668807
    30    3      0  3.39560e+06 1.50525e-07 1.32912e-07 5.95640e-02    0.668807
     2    1      0  3.33134e+06 7.60849e-11 7.51825e-08    0.127349    0.691704
     9    3      0  3.36560e+06 7.59610e-09 6.52470e-08    0.134322    0.691704
    12    6      0  3.42752e+06 2.20098e-10 4.46271e-08    0.141241    0.691704
    16    6      0  3.36560e+06 9.39906e-11 5.03927e-08    0.130712    0.691704
    20    5      0  3.36560e+06 1.32705e-06 2.17366e-06    0.113392    0.691704
    10    1      0  3.40560e+06 3.57687e-07 1.27614e-08    0.170523    0.716050
    13    3      0  3.36560e+06 1.52082e-07 3.44953e-07    0.187695    0.716050
    17    0      0  3.43307e+06 5.37621e-08 1.60050e-07    0.134236    0.716050
    25    0      0  2.73078e+06 8.11315e-09 2.44388e-07    0.141741    0.716050
    26    5      0  3.45307e+06 1.16706e-08 1.00566e-07    0.150546    0.716050
     0    0      0  3.36532e+06 1.10504e-10 8.71707e-09    0.178661    0.743383
     5    6      0  3.18808e+06 2.86491e-10 9.50019e-10    0.187270    0.743383
    21    3      0  3.42041e+06 9.81117e-11 7.08477e-11    0.196674    0.743383
    29    2      0  3.36560e+06 8.67431e-11 6.24780e-10    0.151757    0.743383
     8    7      0  3.42876e+06 1.49828e-09 7.66228e-08    0.310798     1.07710
    28    4      0  3.43876e+06 1.41881e-10 6.01410e-11    0.275619     1.07710
     4    5      0  3.42876e+06 1.18903e-08 9.82118e-07    0.377668     1.10922
    24    6      0  3.45752e+06 3.32657e-08 1.72789e-07    0.287464     1.10922
     7    2      0  3.42088e+06 2.02488e-09 8.37155e-08    0.358904     1.13545
    34    5      0  3.36560e+06 2.16858e-11 4.05297e-09    0.137244     1.13545
    33    1      0  3.37560e+06 1.45519e-10 5.25496e-10    0.138687     1.13545
    36    2      0  3.35307e+06 7.98250e-13 8.48083e-08    0.118646     1.13545
    35    0      0  3.36560e+06 9.54685e-11 2.46795e-10    0.164354     1.13545
    42    0      0  3.36560e+06 9.71559e-08 3.40489e-07 9.26730e-02     1.13545
    15    1      0  3.36560e+06 4.44634e-08 6.88703e-07    0.413218     1.18923
    40    1      0  3.38224e+06 2.04705e-09 1.35223e-06    0.139164     1.18923
    47    5      0  3.38560e+06 5.13126e-10 2.63254e-08 8.44860e-02     1.18923
    41    2      0  3.36560e+06 2.05910e-09 5.85713e-09    0.173684     1.27647
    39    5      0  3.40307e+06 1.90441e-07 1.30131e-05    0.191196     1.27647
    45    1      0  3.36560e+06 1.26951e-07 6.41529e-07    0.113698     1.27647
     1    4      0  3.42876e+06 1.86135e-10 2.08454e-09    0.559284     1.42311
    49    1      0  3.36560e+06 9.05800e-11 1.64805e-08 2.57780e-02     1.42311
    37    6      0  3.43307e+06 1.69791e-09 2.52192e-07    0.212153     1.58486
    38    4      0  3.44041e+06 4.06419e-09 4.81859e-08    0.236602     1.58486
    51    3      0  3.38560e+06 4.36557e-10 4.44356e-11 8.04520e-02     1.60178
    60    7      0  3.36560e+06 1.14824e-10 2.84875e-08 5.88680e-02     1.60178
    44    0      0  3.44307e+06 9.03856e-08 4.38136e-06    0.243143     1.63476
    52    1      0  3.36560e+06 9.96181e-11 9.69465e-11    0.140921     1.67009
    50    5      0  3.38134e+06 9.34306e-08 1.77567e-06    0.153298     1.67009
    53    0      0  3.36307e+06 9.86802e-11 2.68402e-09    0.134336     1.67009
    46    2      0  3.33532e+06 9.40048e-11 8.58777e-09    0.358064     1.70238
    55    7      0  3.35307e+06 3.28626e-14 1.43680e-09    0.114396     1.70238
    62    4      0  3.36560e+06 9.76814e-06 1.76247e-05 8.73040e-02     1.70238
    61    7      0  3.36560e+06 8.78515e-11 3.18521e-09    0.121910     1.78844
    32    3      0  3.36560e+06 1.88684e-06 9.78442e-06    0.429023     1.98831
    58    0      0  3.44752e+06 3.33406e-07 2.32579e-06    0.221738     1.98831
    65    7      0  3.43234e+06 8.65475e-09 1.23083e-07 5.27000e-02     2.01409
    67    2      0  3.43752e+06 4.07606e-07 2.22209e-06 7.70830e-02     2.04383
    59    2      0  3.42041e+06 9.85381e-11 8.34190e-11    0.273823     2.08856
    22    7      0  3.42876e+06 3.31566e-07 1.95927e-06    0.863764     2.11685
    43    6      0  3.38309e+06 3.27649e-06 1.51565e-05    0.651742     2.11685
    66    4      0  3.42806e+06 9.52411e-11 3.38705e-11    0.124660     2.11685
    70    2      0  3.36560e+06 5.39148e-07 2.34909e-06    0.104194     2.11685
    72    2      0  3.35307e+06 7.09974e-11 1.37270e-09    0.103400     2.11685
    73    1      0  3.36560e+06 1.44511e-08 1.95829e-07    0.131560     2.11685
    48    4      0  3.42014e+06 1.34485e-09 6.30818e-06    0.436145     2.18233
    75    0      0  3.42041e+06 2.04722e-08 5.97176e-07    0.141132     2.33419
    76    1      0  3.36560e+06 7.78414e-10 4.37673e-09 5.50070e-02     2.33419
    54    3      0  3.43238e+06 1.73627e-08 7.27690e-08    0.501714     2.36566
    56    1      0  3.35307e+06 5.20686e-11 1.00105e-09    0.563763     2.36566
    63    0      0  3.44752e+06 3.39154e-07 2.44713e-06    0.440181     2.36566
    79    0      0  3.34134e+06 9.78844e-11 3.61027e-10 6.17290e-02     2.36566
    69    3      0  3.43752e+06 4.31282e-09 1.04229e-08    0.291310     2.40114
    78    7      0  3.43234e+06 9.89786e-11 3.02020e-09 7.19210e-02     2.40114
    80    1      0  3.36560e+06 7.23446e-09 5.33268e-08    0.130860     2.45381
    82    0      0  3.42806e+06 2.85127e-10 1.09554e-07 8.95730e-02     2.45381
    84    4      0  3.44088e+06 2.70666e-09 1.82559e-10    0.105303     2.45381
    85    6      0  3.18808e+06 1.01494e-10 3.51988e-11    0.148519     2.59222
    68    7      0  3.47752e+06 1.03930e-07 1.95220e-07    0.394193     2.67227
    71    4      0  3.43307e+06 1.09491e-07 9.43463e-06    0.439196     2.69303
    83    5      0  3.36560e+06 1.41162e-06 4.35724e-06    0.231339     2.69303
    92    3      0  3.36560e+06 3.55067e-09 1.35445e-08 4.44460e-02     2.69303
    64    6      0  3.38307e+06 9.02219e-10 1.42130e-10    0.626282     2.73133
    87    0      0  3.36560e+06 8.78106e-09 2.13188e-08    0.199729     2.73133
    93    4      0  3.36560e+06 9.55822e-11 8.59607e-09 8.98820e-02     2.76701
    57    5      0  3.43238e+06 1.77015e-06 1.41027e-05    0.904280     2.80287
    88    4      0  3.36560e+06 9.56106e-11 1.94220e-10    0.165204     2.80287
    86    1      0  3.42752e+06 8.11779e-07 5.09739e-06    0.300188     2.80287
    96    3      0  2.73078e+06 5.14092e-10 2.77567e-08    0.110254     2.80287
    89    6      0  3.36560e+06 1.64377e-06 1.86754e-05    0.225645     2.82259
    77    3      0  3.43041e+06 3.74823e-08 1.06752e-06    0.448914     2.84585
    81    7      0  3.38224e+06 1.92973e-07 5.57229e-06    0.394760     2.84585
    90    5      0  3.43806e+06 2.83535e-10 4.68926e-08    0.198072     2.84585
    94    7      0  3.37309e+06 2.72310e-05 1.41563e-05    0.167731     2.84585
    74    2      0  3.44752e+06 2.41591e-07 2.00572e-06    0.796626     2.89509
    91    0      0  3.39373e+06 4.83669e-09 1.03890e-07    0.282069     2.92198
    99    5      0  3.42041e+06 1.00499e-10 1.39093e-10    0.164768     2.94591
    98    6      0  3.47752e+06 1.86442e-06 4.59599e-06    0.259439     2.99233
    97    4      0  3.33054e+06 1.46997e-09 1.14230e-08    0.308214     2.99233
    95    1      0  3.36560e+06 5.41404e-07 2.23729e-06    0.550739     3.15442

MULTISTART: Best locally optimal solution is returned.
EXIT: All multi-start solves have terminated.
      100 solve(s) returned satisfactory solutions.

Final Statistics
----------------
Final objective value               =   3.47752463854000e+06
Final feasibility error (abs / rel) =   1.86e-06 / 2.98e-12
Final optimality error  (abs / rel) =   4.60e-06 / 2.45e-07
# of iterations                     =      13308 
# of CG iterations                  =          0 
# of function evaluations           =      14992
# of gradient evaluations           =      13658
# of Hessian evaluations            =      13333
Total program time (secs)           =       3.15462 (    20.770 CPU time)

================================================================================

Knitro 15.1.0: Locally optimal or satisfactory solution.
objective 3477524.639; feasibility error 1.86e-06
13308 iterations; 14992 function evaluations

Check and display solution#

display_result(ampl)
AMPL solve result: solved, solve code: 0
:            _objname              _obj      :=
1   TotalProfitExpAndSetupCosts   3477520
;


--- Flow from Sources to Targets (flow_s2t) ---
flow_s2t
index1 t1 t10 t11 t12 t13 t14 t15 t16 t2 t3 t4 t5 t6 t8 t9
index0
s10 - - - - - - - - - - - - - - 74.45
s12 - 86.92 - - 87.15 - 153.89 - - - - - - - -
s15 - - 230.15 - - - - - - - - - - - -
s17 - - 101.45 1558.15 - - - - - - - - - - -
s18 - 16.74 - - - - - - 129.39 - - - - - -
s19 38.41 - - - - 38.72 - 1705.84 - - - - - - -
s20 - - 87.43 - - - - - - - 338.98 - - - -
s21 - - 203.93 - - - - - - - - - - - -
s22 - - - - - - - 1035.89 - - - - - - -
s3 - - - - - - - - - - - - - - -
s6 - - - 1189.14 - - - - - - - - - - -
s7 - - - - - - - - - - 460.38 - - - 82.44
s9 - - - - - - - - - - 145.11 - - - -
Total non-zero values: 20

--- Flow from Pools to Targets (flow_p2t) ---
flow_p2t
index1 t1 t10 t11 t12 t13 t14 t15 t16 t2 t3 t4 t5 t6 t7 t8 t9
index0
p1 22.74 445.64 - - - - 199.51 - - - - - 231.46 - - -
p10 - - - 729.26 - - - - - - - - - - - -
p11 - - 204.29 - - - 9.78 - 355.98 - - 39.19 - - - -
p12 - - 68.96 - - 186.94 - 445.12 354.25 - - - 198.44 - 267.01 -
p2 - - - 1008.17 - - - - - - - - - - - 134.48
p3 - - - - - - - 542.58 - - - - 85.53 - - -
p4 - - - - - - - - - - 865.13 - 177.87 - - -
p5 - - - 560.99 - - - - - 824.54 - - - 610.46 - -
p6 - - - 707.5 - - - - - - - - - - - -
p7 - - - - - - - 999.77 - - - 11.98 228.53 - - -
p8 79.16 - - - 488.02 - 405.38 195.57 - - - 108.4 - - 357.14 -
p9 10.03 - - - 45.03 - 1.93 349.7 - - - - - - 121.97 -
Total non-zero values: 39

--- Proportion from Sources to Pools (prop_s2p) ---
prop_s2p
index1 p1 p10 p11 p12 p2 p3 p4 p5 p6 p7 p8 p9
index0
s1 0.4 - - - - - - - - 0.44 0.1 -
s10 - - - 0.32 - - - - - - 0.23 -
s11 - - 0.51 - - 1.0 - 0.25 - - - -
s13 - - - 0.29 0.51 - - - - - - -
s14 - - - - - - - - - - 0.04 -
s15 - - - - - - - - - - 0.23 -
s16 0.08 - - - 0.07 - 0.43 - 0.33 - 0.4 -
s17 - - - - - - - - - - - -
s18 - - - - - - - - - - - -
s19 - - - - - - - - - - - -
s2 - 0.2 - 0.09 - - - 0.38 - - - -
s20 - - - - - - - - - 0.44 - 0.7
s22 - - - 0.09 - - - - - - - -
s23 - - - - - - - 0.02 - - - 0.19
s3 - - - - - - 0.1 - - - - 0.12
s4 0.45 - - 0.11 - - 0.31 - - - - -
s5 0.08 - 0.49 - - - 0.16 0.01 - - - -
s7 - - - 0.09 - - - - - - - -
s8 - 0.8 - - 0.41 - - 0.34 - 0.12 - -
s9 - - - - - - - - 0.67 - - -
Total non-zero values: 40

Solve with MP2NL+Knitro#

While Knitro accepts the if-then-else expressions (they are evaluated by the AMPL Solver Library’s callbacks), they seem not to influence the solution. Let us linearize the model with MP2NL.

ampl.option["solver"] = "mp2nl"
ampl.option["mp2nl_options"] = "solver=knitro outlev=1"
ampl.option["knitro_options"] = ""
# ampl.option["knitro_options"] = \
#   "outlev=1 ms_enable=1 mip_multistart=1 maxtime=300 numthreads=8 mip_numthreads=8 mip_root_nlpalg=5 convex=0"

# ampl.snapshot('pooling_expandsetupcosts.run')     # Debugging feature

print("\nSolving the AMPL model...")

ampl.option["reset_initial_guesses"] = 0  # Do not send the previous solution as an initial guess
                                          # This is done by default to enable iterative approaches

ampl.solve()
# ampl.solve(solver="gurobi", gurobi_options="outlev=1 lim:time=300")
Solving the AMPL model...
MP2NL 0.1:   nl:solver = knitro
  tech:outlev = 1

AMPL MP initial flat model has 155 variables (0 integer, 0 binary);
Objectives: 1 linear; 
Constraints:  106 linear; 181 quadratic;
Algebraic expressions:  80 ifthen;
Logical expressions:  28 conditional (in)equalitie(s); 52 conditional quadratic (in)equalitie(s); 80 not;

AMPL MP final model has 612 variables (80 integer, 160 binary);
Objectives: 1 nonlinear; 
Constraints:  134 linear; 148 quadratic; 232 nonlinear;


Artelys Knitro 15.1.0: 
=======================================
          Commercial License
         Artelys Knitro 15.1.0
=======================================

Knitro changing mip_method from AUTO to 1.
MINLP solver shifted start point to satisfy bounds (2 variables).
concurrent_evals         0
datacheck                0
feastol                  1e-06
feastol_abs              1e-06
findiff_numthreads       1
hessian_no_f             1
hessopt                  1
opttol                   1e-06
opttol_abs               0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 0.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 0.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_heuristic_fixpropagate from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.

Problem Characteristics                     |           Presolved
-----------------------
Problem type: MIQCQP
Objective: maximize / quadratic
Number of variables:                    612 |                           612
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       235 |         0         0       235
                             free     fixed |                free     fixed
                               66       311 |                  66       311
                  cont.    binary   integer |     cont.    binary   integer
                    532        80         0 |       532        80         0
Number of constraints:                  366 |                           366
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            77        57         0 |        77        57         0
  quadratic:          0       232         0 |         0       232         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:           184      1801           |       184      1801          
  quadratic:         63      1608       402 |        63      1608       402
  total:            247      2709       402 |       247      2709       402

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
Read root node initial point:
    Objective value:    3.757575e+06
    Feasibility error:  1.894570e+05
    Feasible:           no
Read MIP primal point:
    Objective value:    4.557575e+06
    Feasibility error:  1.894570e+05
    Feasible:           no, Knitro will try to repair it

Coefficient range:
  linear objective:          [8e+01, 8e+05] |                [8e-01, 1e+02]
  linear constraints:        [2e-04, 2e+04] |                [8e-05, 1e+02]
  quadratic objective:       [1e+01, 4e+01] |                [1e-01, 4e-01]
  quadratic constraints:     [1e+00, 1e+00] |                [6e-03, 1e+00]
  variable bounds:           [1e+00, 1e+04] |                [1e+00, 2e+03]
  constraint bounds:         [1e+00, 2e+04] |                [1e+00, 2e+03]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    1    4.06605e+06          178771.           630.572       0.006
    2    4.06753e+06          177950.           630.282       0.009
    3    4.06331e+06          177119.           629.766       0.011
    4    4.05547e+06          175622.           628.659       0.014
    5    4.10367e+06          174472.           626.963       0.017
    6    4.09160e+06          174136.           319.870       0.023
    7    4.06854e+06          174486.           99.9564       0.025
    8    4.05856e+06          174510.           99.9588       0.026
    9    4.05115e+06          174462.           99.9604       0.027
   10    4.04539e+06          174420.           99.9609       0.029
   11    4.04401e+06          174407.           99.9995       0.031
   12    4.04106e+06          174389.           99.9995       0.033
   13    4.03846e+06          174372.           99.9995       0.035
   14    4.03603e+06          174355.           99.9995       0.037
   15    4.03295e+06          174332.           99.9995       0.039
   16    4.03067e+06          174314.           99.9995       0.041
   17    4.02854e+06          174296.           99.9995       0.043
   18    4.02647e+06          174277.           99.9995       0.045
   19    4.02446e+06          174258.           99.9995       0.047
   20    4.02246e+06          174237.           99.9995       0.049
   30    3.99921e+06          173965.           99.9995       0.069
   40    3.97603e+06          170403.           99.9995       0.089
   50    3.96579e+06          163238.           99.9995       0.131
   60    3.91301e+06          146034.           99.9995       0.146
   70    3.82787e+06          109806.           99.9996       0.165
   80    3.75026e+06          84842.0           99.9995       0.182
   90    3.63921e+06          49122.3           99.9995       0.201
  100    3.53300e+06          32412.9           99.9995       0.219
  110    3.44715e+06          27852.0           99.9999       0.237
  120    3.37961e+06          23950.3           116.653       0.253
  130    3.53130e+06          15573.4           123.867       0.272
  140    3.54588e+06          7204.28           333.161       0.290
  150    4.23085e+06          3555.20           546.089       0.303
  160    4.23075e+06          832.519           455.156       0.317
  170    4.15422e+06          5.58683           60.7852       0.327
  180    4.15853e+06      4.38798e-07       6.78479e-04       0.340
MP2NL 0.1: NLSolver: call "knitro /var/folders/6r/cc3gs1ps1zz2h44w2kq3_96c0000gr/T/nlw2_60543244FD50904A_oqzav8/EFA4B71B4944DF64 -AMPL " failed (code 10).

Check and display solution#

display_result(ampl)
AMPL solve result: failure, solve code: 500
No optimal or feasible solution found.

Minimal flow#

A practical constraint might be a minimal amount of flow whenever a pipe is used. We model this by logical constraints.

Semi-continuous flow as logical constraints#

%%ampl_eval

param min_flow default 50;

# Constraints making pipe flows semi-continuous

subject to MinFlowS2T {(s,t) in S2T}:
  flow_s2t[s,t]<=0 or flow_s2t[s,t]>=min_flow;
subject to MinFlowS2P {(s,p) in S2P}:
  source_pool_flow[s,p]<=0 or source_pool_flow[s,p]>=min_flow;
subject to MinFlowP2T {(p,t) in P2T}:
  flow_p2t[p,t]<=0 or flow_p2t[p,t]>=min_flow;

Solve with MP2NL+Knitro#

ampl.option["solver"] = "mp2nl"
ampl.option["mp2nl_options"] = "solver=knitro outlev=1"
ampl.option["knitro_options"] =\
   "outlev=1 ms_enable=1 mip_multistart=1 maxtime=300 numthreads=8 mip_numthreads=8 mip_root_nlpalg=5 convex=0"


ampl.snapshot('pooling_expandsetupcosts_minflow.run')     # Debugging feature

print("\nSolving the AMPL model...")

ampl.option["reset_initial_guesses"] = 0  # Do not send the previous solution as an initial guess
                                          # This is done by default to enable iterative approaches

ampl.solve()
# ampl.solve(solver="gurobi", gurobi_options="outlev=1 lim:time=300")
Solving the AMPL model...
MP2NL 0.1:   nl:solver = knitro
  tech:outlev = 1

AMPL MP initial flat model has 155 variables (0 integer, 0 binary);
Objectives: 1 linear; 
Constraints:  106 linear; 181 quadratic;
Algebraic expressions:  80 ifthen;
Logical expressions:  208 conditional (in)equalitie(s); 104 conditional quadratic (in)equalitie(s); 80 not; 156 or;

AMPL MP final model has 1000 variables (80 integer, 548 binary);
Objectives: 1 nonlinear; 
Constraints:  470 linear; 148 quadratic; 284 nonlinear;


Artelys Knitro 15.1.0: outlev=1
ms_enable=1
mip_multistart=1
maxtime=300
numthreads=8
mip_numthreads=8
mip_root_nlpalg=5
convex=0

=======================================
          Commercial License
         Artelys Knitro 15.1.0
=======================================

Knitro changing mip_method from AUTO to 1.
MINLP solver shifted start point to satisfy bounds (158 variables).
concurrent_evals         0
convex                   0
datacheck                0
feastol                  1e-06
feastol_abs              1e-06
findiff_numthreads       1
hessian_no_f             1
hessopt                  1
maxtime                  300
mip_multistart           1
mip_numthreads           8
mip_root_nlpalg          5
ms_enable                1
numthreads               8
opttol                   1e-06
opttol_abs               0.001
outlev                   1
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 0.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 0.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_heuristic_fixpropagate from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.
WARNING: Problem marked as non-convex by user.
         The Knitro mixed integer solver is designed for convex problems.
         For non-convex problems it is only a heuristic, and the reported
         bounds and optimality claims cannot be verified.


Problem Characteristics                     |           Presolved
-----------------------
Problem type: MIQCQP
Objective: maximize / quadratic
Number of variables:                   1000 |                          1000
  bounds:         lower     upper     range |     lower     upper     range
                      0         0       467 |         0         0       467
                             free     fixed |                free     fixed
                               66       467 |                  66       467
                  cont.    binary   integer |     cont.    binary   integer
                    688       312         0 |       688       312         0
Number of constraints:                  754 |                           754
                    eq.     ineq.     range |       eq.     ineq.     range
  linear:            77       393         0 |        77       393         0
  quadratic:          0       284         0 |         0       284         0
Number of nonzeros:
              objective  Jacobian   Hessian | objective  Jacobian   Hessian
  linear:           184      2686           |       184      2686          
  quadratic:         63      1712       402 |        63      1712       402
  total:            247      3698       402 |       247      3698       402

Knitro using Branch and Bound method with 8 threads.

Initial points
--------------
Read root node initial point:
    Objective value:    3.757575e+06
    Feasibility error:  1.894570e+05
    Feasible:           no
Read MIP primal point:
    Objective value:    4.557575e+06
    Feasibility error:  1.894570e+05
    Feasible:           no, Knitro will try to repair it

Coefficient range:
  linear objective:          [8e+01, 8e+05] |                [8e-01, 1e+02]
  linear constraints:        [2e-04, 2e+04] |                [8e-05, 1e+02]
  quadratic objective:       [1e+01, 4e+01] |                [1e-01, 4e-01]
  quadratic constraints:     [1e+00, 1e+00] |                [6e-03, 1e+00]
  variable bounds:           [1e+00, 1e+04] |                [1e+00, 2e+03]
  constraint bounds:         [1e+00, 2e+04] |                [1e+00, 2e+03]

Root node relaxation
--------------------

 Iter      Objective      Feasibility        Optimality       Time 
                             error              error        (secs)
 ----      ---------      -----------        ----------      ------
    1    4.13463e+06          185912.           490.643       0.007
    2    4.15639e+06          184941.           488.821       0.008
    3    4.20216e+06          181708.           485.109       0.009
    4    4.24522e+06          176656.           484.329       0.010
    5    4.28361e+06          165709.           479.156       0.011
    6    4.35059e+06          136843.           469.506       0.012
    7    4.41488e+06          85788.3           408.395       0.014
    8    4.47797e+06          71649.9           265.588       0.015
    9    4.50338e+06          48791.8           804.647       0.016
   10    4.24654e+06          26166.3           1826.09       0.018
   11    4.21610e+06          23996.8           1465.26       0.019
   12    4.11275e+06          16435.0           14339.3       0.021
   13    4.11862e+06          12829.1           3454.00       0.022
   14    4.14593e+06          10363.6           7841.79       0.023
   15    4.13260e+06          9369.80           7437.72       0.024
   16    4.12202e+06          7682.23           6411.38       0.025
   17    4.11804e+06          6620.29           5383.05       0.027
   18    4.01769e+06          4783.42           14208.3       0.028
   19    3.97744e+06          4180.46           14861.0       0.029
   20    3.77611e+06          2913.99           13622.6       0.031
   30    3.78721e+06          161.564           5491.55       0.049
   40    4.07927e+06      1.20164e-05           5.60874       0.064

Root node cutting planes
------------------------

 Iter     Cuts      Best solution   Best bound      Gap       Time 
                        value         value                  (secs)
 ----     ----      -------------   ----------      ---      ------
    0        0                             inf                0.080

Tree search
-----------

       Nodes        Best solution   Best bound      Gap       Time 
   Expl  |  Unexpl      value         value                  (secs)
   ---------------  -------------   ----------      ---      ------
      1       1                            inf                0.356
      4       1  3.32508e+06  RFP          inf                1.808
     98       1  3.32508e+06               inf               39.678
    198       1  3.32508e+06               inf               78.578
    200       0  3.32508e+06              -inf               79.417

EXIT: Optimal solution found (assuming convexity).

Final Statistics for MIP
------------------------
Final objective value               =  3.32508019560285192e+06
Final bound value                   =  -inf
Final optimality gap (abs / rel)    =  -inf / inf
# of root cutting plane rounds      =  1
# of restarts                       =  0
# of nodes processed                =  200 (79.283s)
# of strong branching evaluations   =  0 (0.000s)
# of function evaluations           =  0 (0.000s)
# of gradient evaluations           =  0 (0.000s)
# of hessian evaluations            =  0 (0.000s)
# of hessian-vector evaluations     =  0
# of subproblems processed          =  207 (88.400s)
Total program time (secs)           =  79.41748 (109.829 CPU time)
Time spent in evaluations (secs)    =  0.00000

Cuts statistics (gen / add)
---------------------------
Knapsack cuts                       =  0 / 0
Mixed-integer rounding cuts         =  0 / 0
Flow-cover cuts                     =  0 / 0
Probing cuts                        =  464 / 104

Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump                    =  1 / 1 / 0.112s
Rounding heuristic                  =  1 / 0 / 0.000s
MPEC heuristic                      =  1 / 0 / 9.117s
Local search heuristic              =  5 / 0 / 0.049s
Fix-and-propagate heuristics        =  0 / 0 / 0.000s

===========================================================================

MP2NL 0.1: Knitro 15.1.0: Locally optimal or satisfactory solution.
objective 3325080.196; optimality gap 2.26e-313
200 nodes; 207 subproblem solves

suffix relaxbnd OUT;
suffix incumbent OUT;

Check and display solution#

display_result(ampl)
AMPL solve result: solved, solve code: 0
:            _objname              _obj      :=
1   TotalProfitExpAndSetupCosts   3325080
;


--- Flow from Sources to Targets (flow_s2t) ---
flow_s2t
index1 t1 t10 t11 t12 t13 t14 t15 t16 t2 t3 t4 t5 t6 t8 t9
index0
s10 - - - - - - - - - 50.0 - - 50.0 - 60.99
s12 - - - - 50.0 - 145.69 - - - - - - - -
s15 - - 113.79 - - - - - - - - - - - -
s17 - - 77.48 1197.24 - - - - - - - - - - -
s18 - - - - - - - - 105.73 - - - - - -
s19 50.0 - - - - 50.0 - 1632.98 - - - - - - -
s20 - - 96.93 - - - - - - - 460.26 - - - -
s21 - - 194.11 - - - - - - - - - - - -
s22 - - - - - - - 1016.3 - - - - - - -
s3 - - - - - - - - - - - - - - -
s6 - - - 1189.14 - - - - - - - - - - -
s7 - - - - - - - - - - 551.82 - - - 85.42
s9 - - - - - - - - - - 157.82 - - - -
Total non-zero values: 20

--- Flow from Pools to Targets (flow_p2t) ---
flow_p2t
index1 t1 t10 t11 t12 t13 t14 t15 t16 t2 t3 t4 t5 t6 t7 t8 t9
index0
p1 50.0 335.01 - - - - 79.55 - - - - - 458.35 - - -
p10 - - - 579.26 - - - 50.0 - - 50.0 - - 50.0 - -
p11 - - 147.43 - - - - - 335.77 - - 67.71 - - - -
p12 - - 116.47 - - 180.98 - 476.23 298.12 - - - 50.0 - 398.92 -
p2 50.0 50.0 - 647.7 - - 50.0 - 50.0 - - 50.0 50.0 50.0 - 144.96
p3 - 50.0 - - - - 50.0 - - - - - - - - -
p4 - 50.0 - - - - - - - - 941.48 - 51.51 - - -
p5 - - 50.0 - - - 50.0 474.6 - 774.54 - 50.0 - 596.85 - -
p6 - 64.3 50.0 593.2 - - - - - - - - - - - -
p7 - - - - - - - 928.32 50.0 - 50.0 - 211.96 - - -
p8 99.0 - 50.0 - 230.58 - 395.25 515.05 - - 50.0 50.0 50.0 - 191.55 -
p9 - - - - - - - 281.23 - - - - - 50.0 155.65 -
Total non-zero values: 56

--- Proportion from Sources to Pools (prop_s2p) ---
prop_s2p
index1 p1 p10 p11 p12 p2 p3 p4 p5 p6 p7 p8 p9
index0
s1 0.36 - - - - - - - - 0.52 0.06 -
s10 - - - 0.22 - - - - - - 0.27 -
s11 0.05 - 0.57 0.04 - 1.0 - 0.46 - - - -
s13 - - - 0.18 0.43 - - - - - - -
s14 - - - - - - - - - - - -
s15 - - - - - - - - - - 0.3 -
s16 0.39 - - - 0.04 - 0.27 - 0.28 - 0.37 -
s17 - - - - - - - - 0.07 - - -
s18 - - - - - - - - - - - -
s19 0.05 - - - - - - - - - - -
s2 - - - 0.28 - - - 0.31 - - - -
s20 - - - - - - - - - 0.23 - 0.21
s22 - - - 0.11 - - - - - - - -
s23 - - - - - - - 0.03 - - - -
s3 - - - - - - 0.05 - - - - 0.79
s4 0.14 - - 0.14 - - 0.53 - - - - -
s5 - - 0.43 - - - 0.15 0.09 - - - -
s7 - - - 0.03 - - - - - - - -
s8 - 1.0 - - 0.52 - - 0.13 - 0.25 - -
s9 - - - - - - - - 0.65 - - -
Total non-zero values: 40

Conclusion#

Artelys Knitro can be used with AMPL extended logical constraint syntax via the MP2NL meta-driver.