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:
Create a new type for the reference.
Create the reference.
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 theForm
object carries. To assign these to theForm
andClient
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 theGUIDEdit
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
andAllHeaders
properties.process(
this,
uit
,
modelIds
,client),
which takes as inputs theuitable
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.Filter
Definition
with these properties:
Name
— Name to display in the Filter dropdown, specified as a string scalar. For example,"Filter by Risk Tier"
.Serialization
—D
efault 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.