Or-Tools CP-SAT-Solver-Export / Import: Wie greife ich nach dem Laden eines Modells auf vars zu?

Dec 08 2020

Über die Python-Schnittstelle zum CP-CAT-Solver von OR-Tools (Referenz) möchte ich in der Lage sein, ein cp_model zu speichern, es zu einem späteren Zeitpunkt oder aus einem anderen Prozess zu laden und mit ihm weiter zu interagieren.

Ich kann ein Modell in einen Protubuf serialisieren und es dann laden und lösen:

from google.protobuf import text_format
from ortools.sat.python import cp_model

def create_model():
    model = cp_model.CpModel()
    a = model.NewIntVar(0, 10, "var_a")
    b = model.NewIntVar(0, 10, "var_b")

    model.Maximize(a + b)
    return model
    
def clone_model(model):
    new_model = cp_model.CpModel()
    text_format.Parse(str(model), new_model.Proto())
    
    return new_model

def solve_model(model):
    solver = cp_model.CpSolver()
    status = solver.Solve(new_model)

    print(solver.StatusName(status))
    print(solver.ObjectiveValue())

# Works fine
model = create_model()
new_model = clone_model(model)
solve_model(new_model)

(Quelle)

Ich möchte jedoch nach dem Laden weiterhin mit dem Modell interagieren. Zum Beispiel möchte ich in der Lage sein, etwas zu tun wie:

model = create_model()
new_model = clone_model(model)

c = new_model.NewIntVar(0, 5, "var_c")    
new_model.Add(a < c)

Das Problem ist, dass diese letzte Zeile nicht funktioniert, weil sie anicht definiert ist. und ich konnte keine Möglichkeit finden, auf die Variablen des vorhandenen Modells zuzugreifen.

Ich suche nach etwas wie: a = new_model.getExistingVariable("var_a")Damit kann ich nach dem Laden weiterhin mit bereits vorhandenen Variablen im Modell interagieren.

Antworten

2 etov Dec 08 2020 at 23:41

Ein Ansatz, der auf der Grundlage eines Kommentars von @Stradivari zu funktionieren scheint, besteht darin, einfach pickledas Modell zusammen mit seinen Variablen zu verwenden.

Beispielsweise:

from ortools.sat.python import cp_model
import pickle

class ClonableModel:
    def __init__(self):
        self.model = cp_model.CpModel()
        self.vars = {}
        
    def create_model(self):
        self.vars['a'] = self.model.NewIntVar(0, 10, "var_a")
        self.vars['b'] = self.model.NewIntVar(0, 10, "var_b")

        self.model.Maximize(self.vars['a'] + self.vars['b'])
        
    # Also possible to serialize via a file / over network 
    def clone(self):
        return pickle.loads(pickle.dumps(self))
    
    def solve(self):
        solver = cp_model.CpSolver()
        status = solver.Solve(self.model)

        return '%s: %i' % (solver.StatusName(status), solver.ObjectiveValue())

Nun funktioniert folgendes wie erwartet:

model = ClonableModel()
model.create_model()

new_model = model.clone()
new_model.model.NewIntVar(0,5,"c")
new_model.model.Add(new_model.vars['a'] < c)

print('Original model: %s' % model.solve())
print('Cloned model: %s' % new_model.solve())

# Original model: OPTIMAL: 20
# Cloned model: OPTIMAL: 14