Main Content

Customizing Model Inventory: Risk Tiering

This example shows how to customize the model inventory to hold information specific to your organization.

You can customize the model data entry and the model summary table. You can also add new filters to the Inventory Browser app to make it easy to find models with a particular value of a custom attribute.

This example uses a simple tree-based approach to risk tiering. The model is from [1]. This model is discussed with other examples in [2].

Add Custom Data to Inventory Browser

The inventory data is likely stored outside of MATLAB®, for example, in a database. Features of the Inventory Browser such as the model entry form do not access this external resource directly. Instead, the features interact with the data through a client. Modelscape™ supports database-backed clients. Modelscape also supports in-memory clients for test use.

To add new model-specific data to the Inventory Browser as references, follow these steps:

  1. Create a new type for the reference.

  2. Create the reference.

  3. Associate the reference to a model.

Create a new reference type called RiskTieringData with these attributes:

  • RiskPriceValueUse — Indication of whether the model measures risk, price, or value, specified as "Unset", "True", or "False".

  • CriticalUse — Indication of whether the model is used for critical business decisions, regulatory reporting, or similar, specified as "Unset", "True", or "False".

  • Exposure — Exposure level of the model, specified as "Unset", "High", "Medium", or "Low".

  • Override — Risk tier level override, specified as "Unset", "High", "Medium", or "Low".

  • RiskTier — Final risk tier, returned as "High", "Medium", or "Low". The software calculates this value using the other attribute values.

To learn how to construct a client with a model and this reference type, and then attach a reference to this model, contact MathWorks Consulting Services.

Open an Inventory session with this client.

app = mrm.inventory.InventoryApp(client);
app.open

Customize Model Data Entry

Customize the Inventory Browser model entry by adding new tabs next to the Details tab in the default view and by changing the layout and contents of the Details tab. Implement these customizations as subclasses of mrm.inventory.model.FormCustomization anywhere on your MATLAB path and register them in extension point configuration files. Implement these methods for each subclass:

  • The constructor, which must take two inputs: the parent mrm.inventory.model.Form object and the client that the Form object carries. To assign these to the Form and Client properties, leave this operation to the base class constructor and do not implement this method.

  • Use populateCustomContents(this), which you can use to set up the additional tab and any controls such as drop-downs.

  • onModelSet(this), which obtains the required references through the client and sets these values to the controls. You can obtain the identifier of the model that the forms display from the GUIDEdit property of the parent form.

  • onSubmit(this), which must read the values carried by the drop-downs and other controls. This method must also use the client to update the relevant references associated to the model.

You can customize the Details tab by modifying the layout grid, stored as the DetailsLayout property, of the parent mrm.inventory.model.Form object. Here are some examples of possible customizations.

  • Replace controls with new custom controls by hiding the existing control by setting the Visible property and creating a new control in the same location in the grid.

  • Add new controls to the form by resizing the DetailsLayout grid.

  • Reorganize controls by using Layout.Row properties.

Finally, you can overwrite the labels of the Details tab controls in a customization class. See mrm.inventory.model.Form for the names of properties defining the labels.

Apply these customizations to implement a risk tiering form. For the form layout, use populateCustomContents to create dropdowns for RiskPriceValueUse, CriticalUse, Exposure and Override, and a read-only label to display the resulting RiskTier. Specify a button to recalculate the risk tier after all the required inputs are considered and set. Finally, define a mechanism for displaying whether the risk tier stored in the inventory synchronized with the chosen inputs. If the risk tier and inputs are not synchronized, the software adds the comment "(Stale)" to the risk tier. The software stores the new tiering data in the inventory only when the user clicks the Update button.

classdef RiskTieringCustomTab < mrm.inventory.model.FormCustomization
%Implements a custom tab for calculating the risk tier

%   Copyright 2021-2023 The MathWorks, Inc.


    properties (Access = private)
        % Base grid
        Grid(1,1) matlab.ui.container.GridLayout

        % Tree model controls
        UsageDD(1,1) matlab.ui.control.DropDown
        CriticalUseDD(1,1) matlab.ui.control.DropDown
        ExposureLevelDD(1,1) matlab.ui.control.DropDown
        OverrideDD(1,1) matlab.ui.control.DropDown

        CalculateButton(1,1) matlab.ui.control.Button
        RiskTierLabel(1,1) matlab.ui.control.Label

        % Convenience variable for setting the stale status of the tiering
        SavedTieringData struct

        % Cache the reference type for risk tiering data
        RiskTieringReferenceType 
    end

    methods
        function this = RiskTieringCustomTab(form, client)
            this@mrm.inventory.model.FormCustomization(form, client);
            this.RiskTieringReferenceType = this.Client.getReferenceTypeByName("RiskTieringData");
        end

        function populateCustomContent(this)
            parentTab = uitab(this.Form.TabGroup, ...
                              'Title', 'Risk tiering', ...
                              'Tag', 'risktiering_tab');

            % Set up grid
            this.Grid = uigridlayout(parentTab, [6 2]);
            this.Grid.RowHeight = repmat(30, 1, 6);
            this.Grid.ColumnWidth = {'1x', '1x'};

            % Row 1
            uilabel(this.Grid, 'Text', 'Does the model measure risk, price or value?');
            this.UsageDD = uidropdown(this.Grid, ...
                                      'Items', ["Unset"; "True"; "False"], ...
                                      "ItemsData", ["Unset"; "True"; "False"], ...
                                      "ValueChangedFcn", @(~,~)this.setStaleStatus);

            % Row 2
            uilabel(this.Grid, 'Text', 'Is the model usage critical?', ...
                    'Tooltip', 'Includes use for critical business decisions, regulatory purposes or financial reporting?');
            this.CriticalUseDD = uidropdown(this.Grid, ...
                                            'Items', ["Unset"; "True"; "False"], ...
                                            "ItemsData", ["Unset"; "True"; "False"], ...
                                            "ValueChangedFcn", @(~,~)this.setStaleStatus);

            % Row 3
            uilabel(this.Grid, 'Text', 'Exposure');
            this.ExposureLevelDD = uidropdown(this.Grid, ...
                                              'Items', ["Unset"; "High"; "Medium"; "Low"], ...
                                              'ItemsData', ["Unset"; "High"; "Medium"; "Low"], ...
                                              'ValueChangedFcn', @(~,~)this.setStaleStatus);

            % Row 4
            % Tier names 5-7 are 'Low (Stale)' etc, so don't include them in the drop-down.
            uilabel(this.Grid, 'Text', 'Override');
            this.OverrideDD = uidropdown(this.Grid, ...
                                         'Items', ["Unset"; "Low"; "Medium"; "High"], ...
                                         "ItemsData", ["Unset"; "Low"; "Medium"; "High"], ...
                                         "ValueChangedFcn", @(~,~)this.setStaleStatus);

            % Row 5
            this.CalculateButton = uibutton(this.Grid, 'Text', 'Calculate');
            this.CalculateButton.ButtonPushedFcn = @this.onCalculateRiskTier;
            this.CalculateButton.Layout.Row = 5;
            this.CalculateButton.Layout.Column = 2;

            % Row 6
            uilabel(this.Grid, 'Text', 'Risk tier');
            this.RiskTierLabel = uilabel(this.Grid, 'Text', '');
        end

        function onModelSet(this)
            guid = this.Form.GUIDEdit.Value;
            tieringDataForThisModel = this.Client.getReferenceByModelAndType( ...
                guid, this.RiskTieringReferenceType.GUID);
            this.SavedTieringData = tieringDataForThisModel.Attributes;

            this.UsageDD.Value = this.SavedTieringData.RiskPriceValueUse;
            this.CriticalUseDD.Value = this.SavedTieringData.CriticalUse;
            this.ExposureLevelDD.Value = this.SavedTieringData.Exposure;
            this.OverrideDD.Value = this.SavedTieringData.Override;
            this.RiskTierLabel.Text = this.SavedTieringData.RiskTier;
        end

        function onSubmit(this)
            guid = this.Form.GUIDEdit.Value;
            data = containers.Map;
            data("RiskPriceValueUse") = this.UsageDD.Value;
            data("CriticalUse") = this.CriticalUseDD.Value;
            data("Exposure") = this.ExposureLevelDD.Value;
            data("Override") = this.OverrideDD.Value;
            data("RiskTier") = this.RiskTierLabel.Text;

            tieringReference = this.Client.getReferenceByModelAndType( ...
                guid, this.RiskTieringReferenceType.GUID);
            this.Client.updateReference(tieringReference.GUID, "Attributes", ...
                data);
        end
    end

    methods (Access = protected)

        function setStaleStatus(this)
            isStale = this.SavedTieringData.RiskPriceValueUse ~= this.UsageDD.Value || ...
                      this.SavedTieringData.CriticalUse ~= this.CriticalUseDD.Value || ...
                      this.SavedTieringData.Exposure ~= this.ExposureLevelDD.Value || ...
                      this.SavedTieringData.Override ~= this.OverrideDD.Value;

            if isStale && ~contains(this.RiskTierLabel.Text, "Stale") && ...
                    this.RiskTierLabel ~= "Unset"
                this.RiskTierLabel.Text = string(this.RiskTierLabel.Text) + " (Stale)";
            elseif ~isStale && contains(this.RiskTierLabel.Text, "Stale")
                this.RiskTierLabel.Text = extractBefore(this.RiskTierLabel.Text, ...
                                                        " (Stale)");
            end
        end

        function onCalculateRiskTier(this, ~, ~)
            tieringInputs.riskpricevalueflag = this.UsageDD.Value;
            tieringInputs.criticalflag = this.CriticalUseDD.Value;
            tieringInputs.exposurelevel = this.ExposureLevelDD.Value;
            tieringInputs.override =  this.OverrideDD.Value;

            riskTier = mrm.inventory.custom.riskTierTreeSimple(tieringInputs);
            this.RiskTierLabel.Text = riskTier;
        end
    end
end

To register the customization, create a resources/extensions.json file anywhere on your MATLAB path, and copy the following content into it:

{
    "mw.modelscape.inventory.form.customizations" : [
        {
            "type": "InventoryFormCustomization",
            "name": "Inventory.RiskTieringCustomization",
            "content": "RiskTieringCustomTab",
            "contentType": "matlab",
           },
        (... other customizations ...)
    ],
}

Here name is used as a tag and callback is the full class name of your customization. Fill in type and callbackType as shown above. List any other form customizations as indicated above.

Customize Model Summary Table

Customize the summary table of model data in the Inventory Browser by omitting columns, including columns corresponding to the custom data and reordering columns. Implement these customizations as a single subclass of mrm.inventory.model.TableCustomization anywhere on your MATLAB path, and register the customization in an extension point configuration file. The class must implement these methods:

  • The constructor, which must accept a single input consisting of the user-visible headers for the default view of the model table. This method must also set the ColumnVisible, ColumnOrdering and AllHeaders properties.

  • process(this,uit,modelIds,client), which takes as inputs the uitable to customize, the model identifiers (IDs), and the client for looking up custom data. This method performs the required customizations.

Customize the summary table so it shows the name and ID of each model from the base product model data, the exposure level, the risk tier, and any possible override from the tiering data. Reorder the columns to demonstrate this capability.

classdef TableCustomizationExample < mrm.inventory.model.TableCustomization
%Example to illustrate addition, removal and reordering of summary table
%column.

%   Copyright 2021-2023 The MathWorks, Inc.


    methods
        function this = TableCustomizationExample(parentHeaders)
            this.ExtraHeaders = ["Risk Tier", "RiskPrice", "Exposure", "Critical", "Tier override"];
            this.AllHeaders = [parentHeaders, this.ExtraHeaders];

            baseVisible = [true, true, false]; % 1-2 of visible columns
            riskTierVisible = [true, false, true, false, true]; % 3-5 of visible columns

            this.ColumnVisible = [baseVisible, riskTierVisible];
            this.ColumnOrdering = [2 1 3 5 4]; % for visible columns only
        end

        function uit = process(this, uit, modelIds, client)
            arguments
                this
                uit matlab.ui.control.Table 
                modelIds(1,:) string
                client 
            end

 
            % Step 1: Read the risk tiering data for all the modelIds from
            % the client.
            tieringDataType = client.getReferenceTypeByName("RiskTieringData");
            tieringData = arrayfun(@(id)client.getReferenceByModelAndType(id, ...
                tieringDataType.GUID), modelIds);

            % Step 2: Arrange this to extraModelTable table with columns
            % corresponding to this.ExtraHeaders
            [tiers, materialUseFlags, exposures, criticalUseFlags, overrides] =  ...
                arrayfun(@(ref)readRiskTierData(ref), tieringData);
            extraModelData = table(tiers', materialUseFlags', exposures', ...
                criticalUseFlags', overrides', 'VariableNames', ...
                ["riskTier", "riskPriceValue", "exposure", "critical", "override"]);

            % Step 3: Concatenate this with uit.Data:
            uit.Data = [uit.Data, extraModelData];
            uit.ColumnName = this.AllHeaders;

            % Step 4: Use the helper methods from TableCustomization base
            % to reset the visibility and the ordering of the columns.
            this.setVisibility(uit);
            this.setOrdering(uit);
        end
    end
end

function [tier, materialuseflag, exposure, criticaluseflag, override] = readRiskTierData(ref)
    tier = string(ref.Attributes.RiskTier);
    materialuseflag = string(ref.Attributes.RiskPriceValueUse);
    exposure = string(ref.Attributes.Exposure);
    criticaluseflag = string(ref.Attributes.CriticalUse);
    override = string(ref.Attributes.Override);
end

To register the customization, create a resources/extensions.json file anywhere on your MATLAB path, and copy the following content into it:

{
    "mw.modelscape.inventory.table.customization": {
        "type": "InventoryTableCustomization",
        "name": "Example.Table.Customization",
        "callback": "TableCustomizationExample",
        "callbackType": "matlab"
     }
}

Here name is used as a tag and callback is the full class name of your example customization. Fill in type and callbackType as shown above.

You can use the same extensions.json file for table and form customizations:

{
    "mw.modelscape.inventory.form.customizations" : [
        {
            "type": "InventoryFormCustomization",
            "name": "Inventory.RiskTieringCustomization",
            "content": "RiskTieringCustomTab",
            "contentType": "matlab",
        }
    ],
    "mw.modelscape.inventory.table.customization": {
        "type": "InventoryTableCustomization",
        "name": "Example.Table.Customization",
        "callback": "TableCustomizationExample",
        "callbackType": "matlab"
     }
}

Create Custom Filters

Inventory Browser has an interactive UI for creating filters to limit the list of models in the Models table. These filters make no distinction between data in the default view and columns you add as part of the customization process. You can, for example, construct a filter to show only models with a high risk tier.

Inventory Browser has two filters in the Saved Filters list of the filter editor: Search by Name, which you can use to filter by model name, and Create Custom Filter, which shows a more complex filter, that you can use as a template for creating more complicated queries.

To customize your own filters, implement new filters as subclasses of mrm.inventory.model.filter.FilterDefinition with these properties:

  • Name — Name to display in the Filter dropdown, specified as a string scalar. For example, "Filter by Risk Tier".

  • Serialization Default initialization of the filters, specified as a JSON string.

To understand the format of the serialization JSON file, see mrm.inventory.model.filter.FilterByName for simple (“Primitive”) filters that reference just a single column. See the default serialization in mrm.inventory.model.filter.CustomFilterTemplate for more complex (“Composite”) filters.

To make the filters visible, implement a function called modelFilters in a +mrm/+inventory/+custom/+model/ folder on the MATLAB path. This function must take no inputs. The function must return a row vector of all the filters to include in the Saved Filters list.

The modelFilters output can include a mixture of built-in and custom filters.

function filters = modelFilters()
%Example filter selection customization

%   Copyright 2022-2023 The MathWorks, Inc.

    filters = [ mrm.inventory.model.filter.FilterByName, ...
        mrm.inventory.custom.model.filter.FilterByRiskTier, ...
        mrm.inventory.model.filter.CustomFilterTemplate];
end

In the filter implementation, the variable name in uit.Data can be different from the user-visible header in the inventory. The variable name is riskTier, but the user-visible text is Risk Tier. The implementation does not need to reside in any namespace folder. However, to keep customization code in a single location, use +mrm/+inventory/+custom/+model/+filter.

classdef FilterByRiskTier < mrm.inventory.model.filterDefinition
% Example definition for filtering by risk tier in Modelscape Inventory

%   Copyright 2022-2023 The MathWorks, Inc.

    methods
        function this = FilterByRiskTier()
            this.name = "Filter by Risk Tier";
            this.Serialization = ['{"type":"Primitive","header":"riskTier",', ...
            '"operation":"CONTAINS","value":"","parent":[],"id":"1"}']; 
        end
    end
end

References

[1] Mankotia, S., and A. Joshi. "Measuring model risk: a practitioner’s approach." RMA Journal, July 1 (2013).

[2] Kiritz, Nick, Miles Ravitz, and Mark Levonian. “Model Risk Tiering: An Exploration of Industry Practices and Principles.” Journal of Risk Model Validation 13, no. 2 (2019): 47–77. https://doi.org/10.21314/JRMV.2019.205.