pyomo icon indicating copy to clipboard operation
pyomo copied to clipboard

ActiveIndexedComponent _active flag redesign

Open jsiirola opened this issue 7 years ago • 5 comments

Currently ActiveIndexedComponents have both an _active flag on the container and on each of the ComponentData sub-objects. I think this is confusing as you can have a situation where the container is “active” but all the contained objects are deactivated.

This strikes me as violating good DB design principles, where the same data is stored in more than one place.

I would like to propose that we remove the _active Flag from the containers and leave it only on the data objects. The active property would stay on the container, but would return False iif all contained data objects were deactivated, otherwise it would return True.

jsiirola avatar Jan 21 '18 20:01 jsiirola

@jsiirola: I think the more important edge case to avoid is having the top container active flag False but one or more children True. I don't know if the current design in the AML protects against that completely, but the redesign I have implemented in the kernel does.

The edge case you bring up is likely rarely ever encountered by a user, and also quite easy to fix (e.g., calling activate() or deactivate() on the container). I disagree that using an O(n) operation to check if something is active is necessary or better. With the current design in the AML, only in the rare case where you arrive at the edge case you describe does an "active objects" iteration take O(n) time (when it could have done no iteration if the top level active flag were accurate). In the typical case, a large container that has been deactivated will add no overhead to an "active object" iteration.

You might consider playing with the constraint_dict and constraint objects in the kernel to see how the "active" semantics have been improved there. In particular, the_ActiveComponentContainerMixin class in pyomo.core.kernel.component_interface keeps track of a count of active children under it. Adding / removing children from the container cause the counter to be updated based on the active status of each child. One can still end up in a state where the container seems "active" at the top-level yet has no active children, but that is an edge case. The pyomo.kernel.pprint function is helpful for viewing the active status in a container, if you decide to play with a small example. E.g.,

import pyomo.kernel as pmo
b = pmo.block()
c = b.c = pmo.constraint_dict()
pmo.pprint(c)
c.deactivate()
pmo.pprint(c)
c[1] = pmo.constraint()
pmo.pprint(c)

ghackebeil avatar Jan 22 '18 04:01 ghackebeil

FWIW, the edge case of having active containers with no active children is pretty common for structural / extension transformations. For example, GDP relaxations work by iterating over Disjunctions and then transforming the individual Disjunct datas referenced by the Disjunction. For well-posed models, all Disjuncts will be relaxed (and therefore deactivated), but the containers will never be marked as inactive.

jsiirola avatar Feb 15 '18 19:02 jsiirola

I do agree with you that it would simplify the design, and it could probably be implemented without O(n) expense everywhere by just having the children of containers just notify the parent of their active status when they are added or removed. This is very close to the what is implemented in the kernel, except the container has an additional degree of freedom to be active or inactive when it is empty.

I think the reason I took the more complicated approach in the kernel was to allow those containers to be used to implement the delayed construction functionality required for abstract models in the AML, where you start with a bunch of "active" but empty things. In the AML, the active status prior to construction indicates whether or not a user wants to construct an object (but perhaps that should change).

ghackebeil avatar Feb 15 '18 19:02 ghackebeil

That's a really good point: what would / should the "active status" of an empty ActiveIndexedComponent object be? For the component_data_objects generator this isn't an issue, but it would be for the component_objects generator.

jsiirola avatar Feb 15 '18 20:02 jsiirola

BWOM: We looked at this issue during the dev call on 2/18/2025 and decided that this is a good idea but would likely require a major release (Pyomo7)

blnicho avatar Feb 18 '25 20:02 blnicho