# Compatibility analysis¶

The compatibility analysis considers an exploration and optimization problem, most often encountered when developing product families. It assumes a product or system consists of various functional modules or components for which multiple variants exist. The goal is to find an optimal configuration, or variant selection for each component, given a set of performance criteria and performance ratings for each variant.

Some variants might be incompatible with each other, which is where the computation of feasible configurations comes into play. Finding a solution therefore becomes a two-step process:

- What are the feasible configurations of variants given their compatibility?
- What is the performance of each configuration, if there are any?

## Modeling the problem¶

The problem can be visualized using a Compatibility Matrix. To this end, we model a
`Graph`

equivalent with the following contents:

- A node for each component variant.
- A variant node may include any number of performance weights, which can later be combined into a performance score per node.
- An edge between variants that are compatible, with a predefined weight to signal this. Here, we'll
use
`"compatibility"`

.

For an absolute minimal example, see the compatibility dataset. In this dataset's info, we find:

Minimal example to illustrate the compatibility analysis.

Contains 6 component variant nodes. They are divided in three node kinds (e.g. components), which correspond to the first character in their node names: A1, B1, B2, C1, C2, C3. For ease of use the "performance" weight of each node is set to it's node name's second character.

Compatibility between nodes is signalled using edges with a "compatibility" kind.

E.g., there are three components for which variants exist. For component `"A"`

we only have a single
variant, for `"B"`

we have two, and for `"C"`

we have three. A configuration would therefore be
represented by `[(A1, B2, C1)]`

. But is that the only feasible one?

## Feasible configurations¶

Using the `ragraph.analysis.compatibility`

module, we can generate
feasible configurations for this problem. First we create a dictionary of elements (A, B, C) to
their respective variant nodes, which we can pass into a
`CompatibilityAnalysis`

:

So currently, there's four feasible configurations. These are feasible since the
`compatibility_method`

with the given arguments checks for edges with an edge kind of
`"compatibility"`

between the given variant nodes. The variants of all elements have to be
compatible with each other element's variant in order to achieve a feasible configuration.

Note that the `configs`

variable has been set with a generator rather than a direct list (note the
`yield`

instead of `get`

in the method's name). Using a generator you could find and parse one
configuration at a time, which is helpful as these problems tend to get computationally expensive to
calculate all at once.

## Configuration performance¶

Generating feasible configurations is one thing, but which configuration is the most performant one? Let's find out! In the example's dataset the performance of each variant is equal to the digit in its name. By default, the scoring method takes the sum of all node weights for each component variant node to calculate a variant's performance. The score for a configuration is the aggregated sum of all variants used.

### Generator approach¶

From which we can conclude that configuration `[(A1, B2, C3)]`

is the most performant with a score
of 6.

Note

Note the ** yield** in

`scored_configs = ca.yield_scored_configurations()`

. This function
returns a generator, as the total number of feasible configurations can rapidly explode for
larger problems.For large problems, you might want to store the best, or a top 5 instead of all configurations.

### List approach¶

If you would want to generate a complete sorted list of all feasible variants, that is possible as well. Although this might take a while for larger problems:

So that's the same result, but pre-cast as a sorted list. If your performance metric is a penalty,
you can set `descending=False`

to get your most performant results first, too.

## Adding constraints¶

Especially in the context of product platforms you might want to add constraints to the element variants to accommodate for different scenario's. For instance, in case of bridges and waterway locks the soil might be different, or the waterway that needs to be crossed exceeds a certain width, which excludes certain variants of being applicable.

This can be modeled by adding constraint nodes, to which we could add applicability edges or
inapplicability edges, depending on what suits the modeling approach best. Let's discuss adding a
constraint in our example that disables `"C3"`

when it's active.

Which shows that we have successfully disabled `"C3"`

in this scenario. Note that disabling *all*
variants of an element just disables the entire element without warning. Make sure to check whether
the `disabled_elements`

property does not hold any
element you would deem essential while trying to satisfy your current constraints:

## Further customization¶

The analysis can be tailored to work with your data model in the following ways:

- Setting a different (in)compatibility method. Please refer to
`ragraph.analysis.compatibility.get_compatibility_method`

for the built-in options. Type:`[Callable[[Graph, Node, Node], bool]]`

. - Setting a different scoring method. Please refer to
`ragraph.analysis.compatibility.get_score_method`

for the built-in options. Type:`[Callable[[Tuple[Node, ...]], float]]`

. - Setting a different applicability method to filter variants. Please refer to
`ragraph.analysis.compatibility.get_applicability_method`

for the built-in options. Type:`[Callable[[Graph, Node, Node], bool]]`

.

All of these method getters return some preset methods that do rudimentary things. Providing your own could be as an inline lambda function that satisfies the call signatures. For instance, some custom methods could look like this:

## Write to CSV¶

If you would like to let the calculations run for a while and generate a
CSV file with all results, please refer to the
`ragraph.analysis.compatibility.CompatibilityAnalysis.write_csv`

method
.

## Design space estimate¶

To get an idea of the maximum number of configurations that would have to be checked of an exhaustive search, take the product of your element variation counts:

Luckily, concept compatibility exploration is invalidated as soon as an incompatible concept combination is encountered.