1

I have an implementation where I need to optimize over object variables. I am using the mathematical programming package of docplex. The problem is that all (both) object variables are being considered the same.

    from docplex.mp.model import Model
    
    test = Model()   
    
    class Node:
        def __init__(self, id):
            self.id = id
    
        x = test.integer_var(0, 50)    
    
    n1 = Node(1)
    n2 = Node(2)
    test.add_constraint(n1.x + n2.x <= 12)
    test.add_constraint(2 * n1.x + n2.x <= 16)
    test.maximize(40 * n1.x + 30 * n2.x)
    
    test.solve()
    test.print_solution()

The output is as though there is only one variable:

    objective: 350
      x1=5

The same problem occurs even if I define the variable externally (outside the class) and instantiate it with the class objects:

    class Node:
        def __init__(self, id):
            self.id = id
    
    
    x = test.integer_var(0, 50)
    Node.x = x

However, in a separate test case, when I use variables without docplex, both class and external variables are able to carry distinct values for every object.

class Node:
    def __init__(self, id):
        self.id = id
    cv = 0


ev = 0
Node.ev = ev

o1 = Node(1)
o2 = Node(2)

o1.ev = 5
o2.ev = 10

o1.cv = 6
o2.cv = 7

print(o1.ev, o2.ev)
print(o1.cv, o2.cv)

Output:

5 10
6 7

Process finished with exit code 0

Apologies for the long question. Can someone explain?

1 Answers1

0

You are confused about how to define a class. The x attribute in your first code is a class variable (same as "static" in other languages such as Java or C++). If you want a new variable per object, put it in the constructor.

class Node:
    def __init__(self, id):
        self.id = id
        self.x = test.integer_var(0, 50) 

EDIT: Relevant part of the official tutorial.

In particular, for your last test case, you change different attributes than you think.

class Node:
    def __init__(self, id):
        self.id = id
    cv = 0

This defines an instance attribute id and a class attribute cv.

Node.ev = ev

This adds a new class attribute ev.

o1 = Node(1)
print(o1.ev)

This looks up the ev attribute. o1 has no instance attribute (at this point), so the lookup checks the class. There it finds the class attribute and returns that.

o2 = Node(2)
o1.ev = 5
print(o1.ev, o2.ev)

Here you set a new instance attribute ev for the node o1. This does not overwrite the class attribute with the same name. Therefore o2.ev still refers to the class attribute.

Homer512
  • 9,144
  • 2
  • 8
  • 25
  • Why does it not work with external variables? For normal variables (without docplex), why can both class variables and external variables take distinct values? – Aditya Paul May 28 '22 at 10:24
  • @AdityaPaul I've extended my answer. I hope this clears up your confusion on this part. The key point is that reading a class attribute through an instance is possible. But setting the attribute instead creates a new instance attribute. It doesn't replace the class attribute – Homer512 May 28 '22 at 11:17