AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79543775
Accepted
Javier
Javier
Asked: 2025-03-30 03:13:10 +0800 CST2025-03-30 03:13:10 +0800 CST 2025-03-30 03:13:10 +0800 CST

Problemas com Docplex e retorno de chamada

  • 772

Estou tentando implementar um callback personalizado que resolva um subproblema durante o processo de otimização. Eu quero:

  1. Obtenha os valores de certas variáveis ​​da solução do modelo principal.

  2. Resolva um subproblema (um problema de otimização separado) com base nesses valores.

  3. Se a solução do subproblema sugerir um corte, adicione-o ao modelo principal como uma nova restrição.

Vou compartilhar meu código aqui, eu realmente queria fazer isso em python, mas estou muito perdido agora, também tenho tentado xpress e similares, mas a documentação é inútil, qualquer ajuda seria muito apreciada.

from docplex.mp.model import Model
from cplex.callbacks import UserCutCallback
from docplex.mp.callbacks.cb_mixin import ConstraintCallbackMixin
import random

class CustomCutCallback(ConstraintCallbackMixin, UserCutCallback):
    def __init__(self, env):
        UserCutCallback.__init__(self, env)
        ConstraintCallbackMixin.__init__(self)
        self.eps = 1e-6
        self.nb_cuts = 0
        self.cts = []

    def add_cut_constraint(self, cut):
        self.register_constraint(cut)

    def __call__(self, context):
        """
        This method is invoked by CPLEX during optimization.
        It receives the 'context' to interact with the model and variables.
        """
        print("Callback called with context")
        m = context.model  # The main model from the context
        m_sol = m.solution

        x_values = {var: m_sol.get_value(var) for var in m.variables if var.name.startswith('x')}
        w_values = {var: m_sol.get_value(var) for var in m.variables if var.name.startswith('w')}
        m2_cuts = self.solve_subproblem(x_values, w_values, m, context)

    def solve_subproblem(self, x_values, w_values, m, context):
        """
        Solves the subproblem and generates cuts if necessary.
        """
        m2 = Model(name='subproblem')

        client_range = range(len(x_values))
        deposit_range = range(len(w_values))
        plant_range = range(len(w_values[0]))

        alpha = m2.continuous_var_matrix(client_range, deposit_range, name='alpha', lb=0)
        beta = m2.continuous_var_matrix(deposit_range, plant_range, name='beta', lb=0)

        m2.add_constraints(alpha[i, j] + (x_values.get((i, j), 0) * beta[j, k]) <= x_values.get((i, j), 0) * w_values.get((j, k), 0)
                           for i in client_range for j in deposit_range for k in plant_range)

        m2.maximize(m2.sum(alpha[i, j] * x_values.get((i, j), 0) for i in client_range for j in deposit_range) + 
                    m2.sum(beta[j, k] * w_values.get((j, k), 0) for j in deposit_range for k in plant_range))

        m2.solve()
        print(m2.solution)

        # Here, you perform an evaluation of the cut values
        for i in client_range:
            for j in deposit_range:
                for k in plant_range:
                    if  sum(m2.solution.get_value(alpha[i, j]) * x_values.get((i, j), 0) for i in client_range for j in deposit_range) + \
                    sum(m2.solution.get_value(beta[j, k]) * w_values.get((j, k), 0) for j in deposit_range for k in plant_range) > \
                    sum(transport_cost_deposit_client[j][i] * d[i] * x[i, j] for i in client_range for j in deposit_range) + \
                    sum(transport_cost_plant_deposit[j][k] * w[j, k] for j in deposit_range for k in plant_range):
                        m.add_constraint(sum(m2.solution.get_value(alpha[i, j]) * x[i, j] for i in client_range for j in deposit_range) + \
                                         sum(m2.solution.get_value(beta[j, k]) * w[j, k] for j in deposit_range for k in plant_range))


# Main model
def build_location_model(transport_cost_deposit_client, transport_cost_plant_deposit, p, q, d, **kwargs):
    m = Model(name='location', **kwargs)

    num_deposits = len(transport_cost_deposit_client)
    num_plants = len(transport_cost_plant_deposit[0])
    num_clients = len(d)
    
    deposit_range = range(num_deposits)
    plant_range = range(num_plants)
    client_range = range(num_clients)

    x = m.binary_var_matrix(client_range, deposit_range, name='x')
    w = m.integer_var_matrix(deposit_range, plant_range, name='w', lb=0)
    y = m.binary_var_list(deposit_range, name='y')
    h = m.binary_var_list(plant_range, name='h')

    m.add_constraints(m.sum(x[i, j] for j in deposit_range) == 1 for i in client_range)
    m.add_constraints(m.sum(w[j, k] for k in plant_range) == y[j] for j in deposit_range)
    m.add_constraints(x[i, j] <= y[j] for i in client_range for j in deposit_range)
    m.add_constraint(m.sum(h[k] for k in plant_range) == q)
    m.add_constraint(m.sum(y[j] for j in deposit_range) == p)
    m.add_constraints(m.sum(d[i] * x[i,j] for i in client_range) == m.sum(w[j, k] for k in plant_range) for j in deposit_range)
    m.add_constraints(w[j, k] <= (m.sum(d[i] for i in client_range) * h[k]) for j in deposit_range for k in plant_range)

    transport_cost = m.sum(transport_cost_deposit_client[j][i] * d[i] * x[i, j] for i in client_range for j in deposit_range) + \
                        m.sum(transport_cost_plant_deposit[j][k] * w[j, k] for j in deposit_range for k in plant_range)
    m.minimize(transport_cost)
    m.parameters.preprocessing.presolve = 0

    # Register the callback
    cut_cb = m.register_callback(CustomCutCallback)

    # Configure CPLEX parameters for cuts
    params = m.parameters
    params.mip.cuts.mircut = -1

    m.solve()

    return m



# Test function
def solve_model():
    num_deposits = 10
    num_plants = 4
    num_clients = 20

    TRANSPORT_COST_DEPOSITS_CLIENTS = [
        [random.randint(20, 100) for _ in range(num_clients)] for _ in range(num_deposits)
    ]

    TRANSPORT_COST_PLANTS_DEPOSITS = [
        [random.randint(30, 80) for _ in range(num_plants)] for _ in range(num_deposits)
    ]

    p = 5
    q = 3
    d = [random.randint(5, 20) for _ in range(num_clients)]

    m = build_location_model(TRANSPORT_COST_DEPOSITS_CLIENTS, TRANSPORT_COST_PLANTS_DEPOSITS, p, q, d)
    if m.solution is None:
        print("No valid solution found.")
        return None
    print(m.solution)
    return m


if __name__ == "__main__":
    solve_model()
python
  • 1 1 respostas
  • 30 Views

1 respostas

  • Voted
  1. Best Answer
    Philippe Couronne
    2025-03-30T22:42:40+08:002025-03-30T22:42:40+08:00

    Seu modelo básico ("localização") é inviável. Eu executei seu código com saída emsolve(log_output=True)

    e ele para imediatamente como inviável. Portanto, o retorno de chamada nunca é chamado. Eu sugiro que você modifique o código para começar de um modelo viável e, então, adicione os retornos de chamada.

    Aqui está a saída I da solução:

    C:\python\anaconda202210\envs\docplex_dev38\python.exe C:\OPTIM\PYLAB\stackov\cutcb.py 
    Model: location
     - number of variables: 254
       - binary=214, integer=40, continuous=0
     - number of constraints: 282
       - linear=282
     - parameters:
         parameters.mip.cuts.mircut = -1
         parameters.preprocessing.presolve = 0
     - objective: minimize
     - problem type is: MILP
    Version identifier: 20.1.0.0 | 2020-11-10 | 9bedb6d68
    CPXPARAM_Preprocessing_Presolve                  0
    CPXPARAM_Read_DataCheck                          1
    CPXPARAM_MIP_Cuts_MIRCut                         -1
    Legacy callback                                  UD
    Warning: Control callbacks may disable some MIP features.
    Clique table members: 211.
    MIP emphasis: balance optimality and feasibility.
    MIP search method: traditional branch-and-cut.
    Parallel mode: none, using 1 thread.
    Root relaxation solution time = 0.00 sec. (0.23 ticks)
    
            Nodes                                         Cuts/
       Node  Left     Objective  IInf  Best Integer    Best Bound    ItCnt     Gap         Variable B NodeID Parent  Depth
    
          0     0    infeasible                                          2         
    
    Root node processing (before b&c):
      Real time             =    0.00 sec. (1.57 ticks)
    Sequential b&c:
      Real time             =    0.00 sec. (0.00 ticks)
                              ------------
    Total (root+branch&cut) =    0.00 sec. (1.57 ticks)
    No valid solution found.
    
    Process finished with exit code 0
    
    • 1

relate perguntas

  • Como divido o loop for em 3 quadros de dados individuais?

  • Como verificar se todas as colunas flutuantes em um Pandas DataFrame são aproximadamente iguais ou próximas

  • Como funciona o "load_dataset", já que não está detectando arquivos de exemplo?

  • Por que a comparação de string pandas.eval() retorna False

  • Python tkinter/ ttkboostrap dateentry não funciona quando no estado somente leitura

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve