Extending Category Inheritance to Support Restrictive Constraints

Background

SMTK’s Attribute Category Modeling is based on Categories::Set. A Category::Set represents the category constraint directly associated with an Attribute::ItemDefinition or Attribute::Definition. It is composed of two main parts:

  1. An Inclusion Constraint - this represents a set of categories that must be partially or completely included in the Attribute Resource’s active categories in order for the constraint to be considered satisfied.
  2. An Exclusion Constraint - this represents a set of categories that must be partially or completely excluded from the Attribute Resource’s active categories in order for the constraint to be considered satisfied.

In order to indicate whether all or any of the categories should be include (or excluded), each constraint also includes a combination mode which can be set to Any or All.

In addition, theses two constraints are then combined to form the overall category constraint. The designer could require that both constraints should be satisfied (All) or either constraint should be satisfied (Any). For example we could have the following Categories::Set constraint:

CS(All, IC(Any, {A, B}), EC(All, {C,D})) - The first All indicates that both Inclusion and Exclusion conditions must be met for CS to be satisfied. The Inclusion Constraint (IC) states that either A or B should be present in the set of categories. The Exclusion Constraint (EC) states that both C and D must not be present. So if the active categories were {A, C, Z}, the constraint would pass since IC is satisfied by A being active and EC is satisfied since only C was active. The following would not satisfy the constraint:

  • {E} - fails cause IC failed
  • {A, C, D} - fails cause EC failed

Now if we had specified CS(Any, IC(Any, {A, B}), EC(All, {C,D})) instead we would get the following:

  • {A, C, Z} - passes cause IC passes, EC passes
  • {E} - passes cause EC passes
  • {A, C, D} - passes cause IC passes
  • {} - passes cause EC passes
  • {C, D, E} - fails cause both IC and EC fails

These constraints can be passed down from Definition to ItemDefinition or from parent ItemDefinition to child ItemDefinition as well as in the reverse direction.

For example an Attribute Definition that has no local category constraint but contains:

  • ItemDef 1 with CS(All, IC(All, {A}))
  • ItemDef 2 with CS(All, IC(All, {B}))

The Attribute Definition would “inherit” both CS’s or’d together. this is because it would be relevant if either category A or B is active.

If the Attribute Definition in the above example also had a CS(All, IC(All{D}, EC(All, {E})), it would would “or” its local CS with those it inherited from it’s Items. It would be relevant if any of the following were true:

  • A is active
  • B is active
  • D is active but E is not

In addition, ItemDef1 and ItemDef2 would also “gain” an additional category constraint. For example ItemDef1 would be relevant if any of the following are true:

  • A is active
  • D is active but E is not

This same mechanism is applied when basing one Definition off of another. For example if Attribute Definition D1 has CS(All, IC(All, {A})), and Attribute Definition D2 is based on D1 and has CS(All, IC(All, {B})), it would be relevant if either A or B was active. More importantly, all of the Items that were defined with in D2 would also inherit both CS’s or’d together.

As a result, category inheritance in SMTK is expansive as oppose to restrictive.

Note that at the time the category mechanism was being defined, Attribute Definitions (and Group Items) were not allowed to have category constraints and relied on inheriting the constraints from their Items.

The only way to model making things more restrictive currently is to use the Do Not Inherit mechanism and explicitly set a local category constraint on the Attribute or Item Definition.

Supporting Category Restriction

In terms of extending the current API to capture the ability to say an Attribute or Item Definition would want combine the category constraints it is inheriting with their local one using an “and” requirement would be relatively easy.

We would replace the isOkToInherit methods with inheritConstraint which could be one of the following values:

  • Either/Or/Any - either the inherited category constraint or the local one needs to be satisfied ← what we do now
  • Both/And/All - both must be satisfied
  • LocalOnly - only the local one must be satisfied ← what setIsOkToInherit(false) does today

The execution would get a bit more involved. currently all of the categorySets are simply appended into a set and tested until one returns true (or satisfied). With the new design, we would need to be a bit more careful. First the set of categorySets would be replaced with a stack. Each element of the stack would be a Pair< inheritConstraint, CategorySet>. When performing category inheritance, the base Attribute Definition would push <LocalOnly, localCSofBase> on the stack and pass it children ItemDefinitions. each would then create its own copy of the stack and add its own pair to it (unless its inheritConstraint is LocalOnly and in that case it start its own). If it has children ItemDefinitions (like GroupItem or ValueItem), it would push its current stack down to them and the process repeats.

When evaluating a CategorySetStack, each Pair is tested in order, if the localCS passes and the inheritConstraint is Any or LocalOnly then true is returned, if it fails and inheritConstraint is All then it returns false, else it continues evaluating.

Upward Inheritance

Children CS Stacks would be combined with the Parent’s into a Set to form the Parent’s overall Category Constraint.

Default for Attribute and Item Definition’s inheritConstraint

It might make sense to have the default to be set to All instead of Any - however, in reading Version 5 and below Attribute Files, we could have it be set to Any (or LocalOnly) based on isOkToInherit.
Note that this means we might need to up the XML file format to version 6. We might not need to change the JSON format.

1 Like

@johnt @dcthomp @Aaron @waj334 @rohith @mledesma . FYI - comments welcome!

1. I am not seeing the case quoted below where the attribute is relevant due to item definitions when “D is active but E is not”. It looks like the inclusion constraint is not met (all D,E) and neither of the item inclusion constraints are met either. Can you walk me through the logic please?

2. Looks like a typo slipped in

Looks like “based on A” should be “based on D1”?

While implementing the category mechanism I ran into some inconsistencies with the original implementation that I would like to clarify in the new one:

Effect of Discrete Value Item’s Enum Categories

Original Implementation

The Enum Categories were getting added to the Item Definition’s Categories but were not being added to either the Attribute (or owning Item Definition)'s Categories.

Potential New Implementation

In both the original (and new) implementation, the Enum categories only affected the UI and would not prevent resulting Item’s value from being set to an Enum value that is not supported by the Attribute Resource’s Active Categories. So as a result the Enum’s Categories do not affect Validity or Relevance.

Also - these Enums are assumed to be “And’d” with the Item’s Categories.

Based on the above, the proposed new implementation will not add the Enum’s Categories calculation.

Comments?