Configuring object comparison: ComparisonConfig
As seen in the Diffing and the Hash wiki pages, the real power of object comparison is given by the options that you have when performing it. For this reason, we expose options to customise these operations via the ComparisonConfig
object.
Here is an example of the ComparisonConfig object seen from Grasshopper:
There are also "Toolkit-specific" ComparisonConfig
objects that extend the available options when dealing with certain objects, for example Revit's RevitComparisonConfig
gives further options when dealing with Revit objects. More details on it in its dedicated page.
Note for developers
The "default"
comparisonConfig
object inherits from theBaseComparisonConfig
abstract class, which defines all the "basic" options. This abstract class can be extended by the "Toolkit-specific"comparisonConfig
s, so you can include additional options to deal with certain objects in your Toolkit, of whichRevitComparisonConfig
is an example.
If you implement your own Toolkit-specificcomparisonConfig
object, you will need to implement the functions that deal with it too, which are a toolkit-specificDiffing()
method (example in Revit), a toolkit-specificHashString()
method (example in Revit), and any number ofComparisonInclusion()
methods that you might need (example in Revit). More details can be found in the diffing guide for developers.
Description of the ComparisonConfig
options
Let's see the ComparisonConfig
options in detail.
Many of the following examples use the Bar
class as a reference object.
- PropertyExceptions
- PropertiesToConsider
- CustomDataKeysExceptions
- CustomDataKeysToConsider
- TypeExceptions
- NamespaceExceptions
- MaxNesting
- MaxPropertyDifferences
- NumericTolerance
- PropertyNumericTolerance
- SignificantFigures
- PropertySignificantFigures
PropertyExceptions
You can specify one or more names of properties that you want to ignore (not consider, take as exceptions) when comparing objects.
This allows to ignore properties and also sub-properties (i.e., properties of properties) of any object.
This also supports *
wildcard within property names, so you can match multiple properties.
You can specify either the simple name of the property (e.g. StartNode
), or the FullName of the property (e.g. BH.oM.Structure.Elements.Bar.StartNode
) if you want to be more precise and avoid confusion in case you have properties/sub-properties with the same name.
To clarify the above, here are examples using the Bar
class as a reference:
Property name VS Property Full Name - examples
- Specifying the property name
StartNode
would ignore theStartNode
property. It follows that any sub-property ofStartNode
will also be ignored. - Specifying the property Full Name
BH.oM.Structure.Element.Bar.StartNode
would achieve the same result, but it is safer than using only the simple nameStartNode
(and may as well save computation time, like in the case of PropertiesToConsider when Hashing).
To explain why using the property Full Name is safer, consider the example where you are Diffing a mix of objects which include both Bar
s and also GraphLink
s, both of which own a StartNode
property. If you input StartNode
in the PropertyExceptions
, you must be aware that both properties BH.oM.Structure.Elements.Bar.StartNode
and BH.oM.Data.Collections.GraphLink.StartNode
will be treated as exceptions, hence ignored. Specifying the property full name is safer.
Sub-properties examples
- Specifying
StartNode.Position
, would ignore any change in thePosition
property of the start Node, but all the other properties ofStartNode
would still be considered. - Specifying
StartNode.Position.X
, would ignore any change in theX
property of the start Node's Position property, but all the other properties ofStartNode.Position
would still be considered. - Again, you can specify the Full Name even for sub-properties, like
BH.oM.Structure.Elements.Bar.StartNode.Position.X
, and as seen above, this is safer.
Wildcard examples
You can specify *
wildcards within property names, so you can match multiple properties with a single text.
- Specifying
BH.oM.Structure.Elements.Bar.*.Position.Y
would match: BH.oM.Structure.Elements.Bar.StartNode.Position.Y
BH.oM.Structure.Elements.Bar.EndNode.Position.Y
so if this is specified in the PropertyExceptions
, those 2 properties will be ignored.
- Specifying
BH.oM.Structure.Elements.Bar.*.Y
would match: BH.oM.Structure.Elements.Bar.StartNode.Position.Y
BH.oM.Structure.Elements.Bar.EndNode.Position.Y
BH.oM.Structure.Elements.Bar.StartNode.Orientation.Y
BH.oM.Structure.Elements.Bar.EndNode.Orientation.Y
so if this is specified in the PropertyExceptions
, those 4 properties will be ignored.
-
Again, you can specify only the name instead of the Full Name to obtain the same result, i.e.
*.Position.Y
would achieve the same result asBH.oM.Structure.Elements.Bar.*.Position.Y
when the input objects are onlyBar
s, but you incur in the same risks illustrated above if your input objects are of different types (see property name VS property Full Name). -
You can add as many
*
wildcards as you wish, which is especially handy when you have input objects of different types. SpecifyingBH.oM.Structure.*.Start*.*Y
with bothBar
s andBarRelease
s input objects would match all of the following properties: BH.oM.Structure.Elements.Bar.StartNode.Position.Y
BH.oM.Structure.Elements.Bar.StartNode.Orientation.Y
BH.oM.Structure.Elements.Bar.StartNode.Offset.Start.Y
BH.oM.Structure.Constraints.BarRelease.StartRelease.TranslationalStiffnessY
BH.oM.Structure.Constraints.BarRelease.StartRelease.RotationalStiffnessY
BH.oM.Structure.Constraints.BarRelease.StartRelease.TranslationY
BH.oM.Structure.Constraints.BarRelease.StartRelease.RotationY
so if this is specified in the PropertyExceptions
, and both Bar
s and BarRelease
s are in the input objects, all those 7 properties will be ignored.
If instead you only had Bar
s in the input objects, the BH.oM.Structure.*.Start*.*Y
would only match the first 3 properties in the list above.
PropertiesToConsider
The PropertiesToConsider
input allows you to add property names that should be considered in the comparison.
If you add a property name in this field, only the value held in that property will be considered.
If the property name that you specified is not found on the object, then no properties will be considered. Therefore, make sure you input property names that exist on the object.
Like for the PropertyExceptions
option, you can specify the property names as just the Name (e.g. StartNode
), as a Full Name (e.g. BH.oM.Structure.Elements.Bar.StartNode
) and/or using wildcards (e.g. BH.oM.Structure.Elements.*.StartNode
) to get different matching results. See the section on PropertyExceptions
for more details on Full Names and using wildcards.
Note: Hash performance when using
PropertiesToConsider
Using
PropertiesToConsider
can be a resource-intensive operation when calculating an object's Hash (Diffing instead is only slightly affected). To speed up the Hash computation: - use only property Full Names as an input toPropertiesToConsider
; - do not use Wildcards inPropertiesToConsider
; - limit the amount of property names inPropertiesToConsider
.Technical explanation in the details below.
The Hash of an object is calculated by recursively navigating all properties of the object and taking their value. If you specify some
PropertiesToConsider
, the property value is only considered if its name matches a property name in there. Then, the recursion continues, and if the current property has some sub-property, the algorithm checks the sub-property, and so on. When checking a certain property, the algorithm doesn't know the names of all its sub-properties until it gets there.If the property names include Wildcards or are not specified as Full Names, there can be situations where some nested sub-property needs to be considered, but then its parent's siblings must be ignored. When computing the
Hash()
, we are traversing the property tree of the object, but we do not know all the properties during the traversal. For example, say that your input object is aBar
, and you want to consider exclusively properties that match*.Name
. The situation is:One way of solving this could be to "consider" all the properties of the object while doing the Hash, and, at the end, cull away those that do not match any
PropertiesToConsider
. This basically is like saying that we build our knowledge of the object while computing its Hash. However, this can be wasteful for two reasons: - speed: many other operations may be done to the object values being considered when computing the Hash (e.g. numerical approximations); - space: we would need to store in RAM many values that we may never use.For this reason, we instead build the knowledge of the property tree before computing the hash; in other words, we traverse the entire object once and look at the property names, and get the "consequent" PropertiesToConsider, i.e. all the properties of the object that match your wildcard or partial property name, translated to their Full Name form. By using Full Names, it the becomes easy for the Hash algorithm to consider or not a property: just check if the property full name matches any of the
PropertiesToConsider
.The cost of this can be cut by specifying Full Names instead of just the name (i.e.
BH.oM.Structure.Elements.Bar.StartNode
instead ofStartNode
) and avoiding wildcards*
when usingPropertiesToConsider
.
CustomDataKeysExceptions
This works similarly to PropertyExceptions
, but is used only for BHoMObjects CustomData dictionary keys.
Setting a key name in CustomDataKeysExceptions
means that if that key is found on in the CustomData dictionary of an object, it will not be considered.
This option does not support wildcard, unlike PropertyExceptions
.
CustomDataKeysToConsider
Setting a key name in CustomDataKeysToConsider
means that only that dictionary key will be considered amongst any BHoMObject's CustomData. If no matching CustomData key is found on the object, no CustomData entry will be considered.
This option does not support wildcard.
TypeExceptions
You can input any Type here. Any object or property of corresponding types will not be considered.
NamespaceExceptions
You can input the name of any namespace here. An example of a namespace is BH.oM.Structure.Elements
.
Any object or property that belong to the corresponding namespace will not be considered.
This option does not support wildcard.
MaxNesting
This option limits the depth of property discovery when computing Diffing or an object's Hash.
Properties whose Nesting Level is equal to or larger than MaxNesting
will not be considered.
Property Nesting Level definition
The nesting level of a property defines how deep we are in the object property tree.
For example:
Top-level properties are at level 1. Setting MaxNesting
to 1 will make the Hash or Diffing consider only top-level properties. Setting MaxNesting
to 0 will disregard any object property (only the class name will end up in the Hash, and Diffing will not find any differences).
This option is better used as a safety measure to avoid excessive computation time when diffing or computing the hash for objects that may occasionally have one or more deeply nested properties of which we do not care about.
MaxPropertyDifferences
When Diffing, this indicates the maximum number of Property Differences that will be collected and returned. This setting does not affect the Hash calculation (in fact, this option should be moved in DiffingConfig instead).
You can not control what properties are returned and what remain excluded due to this numeric limit. Hence, this option is better used as a safety measure to avoid excessive computation time when:
- we care about finding different objects, but do not care about what properties did change between them, although a better and faster option for this would be to use
DiffingConfig.EnablePropertyDiffing
set tofalse
; - we are okay with finding only the first n differences between objects, whatever those may be.
NumericTolerance
This option sets the Numeric tolerance applied when considering any numerical property of objects.
For example, a Bar
's StartNode.Position.X
property is a numerical property.
When a numerical property is encountered, the function BH.Engine.Base.Round()
is applied to its value, which becomes approximated with the given NumericTolerance
.
Therefore, when Hashing, the property's approximate value will be recorded in the Hash. When Diffing, the property approximate value will be used for the comparison.
If both NumericTolerance
and SignificantFigures
are provided in the ComparisonConfig, both approximations are executed, and the largest approximation among all (the least precise number) is registered.
Round()
detailsThe function
BH.Engine.Base.Round()
will approximate the input value with the given tolerance, which is done by rounding to the nearest tolerance multiplier.Some examples of
Round()
are:
Input number Input Tolerance Result (approximated number) 12 20 20 121 2 122 1.2345 1.1 1.1 0.014 0.01 0.01 0.014 0.02 0.02
PropertyNumericTolerance
This option applies a given Numeric Tolerance to a specific property, therefore considering its value approximated using the given tolerance.
In order to use it, you have to create and input in PropertyNumericTolerance
one or more NamedNumericTolerance
objects, where you set:
- the
Name
of the property you want to target; this supports*
wildcard usage; - the
Tolerance
that you want to apply to the given property.
The approximation will work exactly as per the NumericTolerance
option, only it will target exclusively the properties with the name specified via the NamedNumericTolerance
objects.
If a match is found, this takes precedence over the NumericTolerance
option.
If conflicting values/multiple matches are found among the ComparisonConfig
's numerical precision options, the largest approximation among all (least precise number) is registered.
The Name
field supports wildcard usage. Some examples:
BH.oM.Geometry.Vector
: applies the corresponding tolerance to all numerical properties of Vectors, i.e. X, Y, ZBH.oM.Structure.Elements.*.Position
: applies the corresponding tolerance to all numerical properties of properties namedPosition
under any Structural Element, e.g.Bar.Position.X
,Bar.Position.Y
,Bar.Position.Z
and at the same time alsoNode.Position.X
,Node.Position.Y,
Node.Position.Z
.
SignificantFigures
This option sets the Significant Figures considered for any numerical property of objects.
For example, a Bar
's StartNode.Position.X
property is a numerical property.
When a numerical property is encountered, the function BH.Engine.Base.RoundToSignificantFigures()
is applied to its value, which becomes approximated with the given SignificantFigures
.
Therefore, when Hashing, the property's approximate value will be recorded in the Hash. When Diffing, the property approximate value will be used for the comparison.
If both SignificantFigures
and NumericTolerance
are provided in the ComparisonConfig, both approximations are executed, and the largest approximation among all (the least precise number) is registered.
RoundToSignificantFigures()
detailsThe function
BH.Engine.Base.RoundToSignificantFigures()
will approximate the input value with the given Significant Figures. Some examples:
Input number Input Significant Figures Result (approximated number) 1050.67 1 1000 1050.67 2 1100 1050.67 3 1050 1050.67 4 1051 1050.67 5 1050.7 123456.123 7 123456.1 123456.123 1 100000 0.0000000000000000000123456789 5 1.2346E-20 0.0000000000000000000123456789 99 1.23456789E-20
PropertySignificantFigures
This option applies the approximation with given Significant Figures to a specific property.
In order to use it, you have to create and input in PropertyNumericTolerance
one or more NamedSignificantFigures
objects, where you set:
- the
Name
of the property you want to target; this supports*
wildcard usage; - the
SignificantFigures
that you want to consider when evaluating the given property.
The approximation will work exactly as per the SignificantFigures
option, only it will target exclusively the properties with the name specified via the NamedSignificantFigures
objects.
If a match is found, this takes precedence over the SignificantFigures
option.
If conflicting values/multiple matches are found among the ComparisonConfig
's numerical precision options, the largest approximation among all (least precise number) is registered.
The Name
field supports wildcard usage. Some examples:
BH.oM.Geometry.Vector
: applies the corresponding tolerance to all numerical properties of Vectors, i.e. X, Y, ZBH.oM.Structure.Elements.*.Position
: applies the corresponding tolerance to all numerical properties of properties namedPosition
under any Structural Element, e.g.Bar.Position.X
,Bar.Position.Y
,Bar.Position.Z
and at the same time alsoNode.Position.X
,Node.Position.Y,
Node.Position.Z
.