FlexCartesian represents a system using Parametric Behaviour Blueprinting stack, as given below. Component titles are clickable, and they refer to the description and API of the component.
flowchart TB
classDef layer fill:#ffffff,stroke:#2c3e50,stroke-width:2px;
classDef ghost fill:transparent,stroke:transparent,color:transparent;
subgraph Analyzers ["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#analyzers'><b>ANALYZERS</b></a>"]
direction LR
gA1["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]:::ghost ~~~ M["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#morris-analyzer'><b>Morris</b></a><br/>trajectories<br/>step<br/>seed<br/>analyze<br/>output"] ~~~ gA2["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]:::ghost
end
subgraph Core ["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#basic-operations'><b>BASIC OPERATIONS</b></a>"]
direction LR
gD1["x"]:::ghost ~~~ DS["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#data-sources'><b>Data Sources</b></a><br/>data"] ~~~ UT["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#utilities'><b>Utilities</b></a><br/>size<br/>to_a<br/>vector_to"] ~~~ FN["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#functions'><b>Functions</b></a><br/>func<br/>function"] ~~~ IT["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#iterators'><b>Iterators</b></a><br/>cartesian"] ~~~ IO["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#io'><b>IO</b></a><br/>output<br/>import<br/>export<br/>visualize"] ~~~ gD2["x"]:::ghost
end
subgraph Cond ["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#conditions'><b>CONDITIONS</b></a>"]
direction LR
gC1["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]:::ghost ~~~ C["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#conditions'><b>Conditions</b></a><br/>cond"] ~~~ gC2["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]:::ghost
end
subgraph Params ["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#parameter-space'><b>PARAMETER SPACE</b></a>"]
direction LR
gP1["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]:::ghost ~~~ PS["<a href='https://github.com/Yuri-Rassokhin/flex-cartesian/blob/main/docs/api/api.md#parameter-space'><b>Parameter Space</b></a><br/>initialize<br/>valid?<br/>levels<br/>dimensionality<br/>values<br/>dimensions<br/>names<br/>raw_size"] ~~~ gP2["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]:::ghost
end
Analyzers ~~~ Core
Core ~~~ Cond
Cond ~~~ Params
class Analyzers,Core,Cond,Params layer;
Parameter space is a space formed as multi-dimensional Cartesian product of the dimensions represented by discrete dimensional values.
Let's suppose we analyze the response latency of an object recognition model running on CPU, and want to capture its dependence on two parameters - number of processes from 1 to 100, and number of concurrent requests from 1 to 100.
These parameters form the parameter space that looks like a square, and the latency later will be a function in a column above this square:
Parameters `requsests` and `processes` form square parameter space
def initialize(dims = nil, path: nil, format: :json, source: nil, uri: nil, dimensions: nil, separator: ',')Create parameter space. A space can be created in two ways.
Firstly, an empty space can be created from the description of dimensions:
dimshash of dimensions (key) and array of dimensional values (value)pathto the file describing dimensionsformatformat of the file describing dimensions, either JSON or YAML
Secondly, a space can be created from a tabular data source. In this approach, dimensions are automatically created from specified columns in the data source, and dimensional values will be filled in from these columns. The resulting space will be empty, but entire the data source will remain available to link the data to behavioural functions, if needed. This method is very powerful - effectively, it allows for the creation of a live behavioural blueprint which evolves in time synchronously with the evolution of the data in the data source.
sourcedata source type, one of:xlsxor:csvurilocal path to the data source filedimensionsarray of symbolic column names in the data source that will become space dimensionsseparatorseparation symbol in the data source file, either colon or semicolon
def valid?(vector)Check if vector is consistent - that is, it has consistent dimensiality, consistent dimensional values, and satisfies conditions in the current space.
def valuesReturn array of arrays of dimensional values.
def dimensialityReturn number of dimensions in the current space.
def dimensionsReturn hash of dimension name (key) referring to array of dimensional values.
def namesReturn array of dimension names.
def levelsReturn array of arrays of dimensional values.
def raw_sizeReturn amount of all vectors in the parameter space, ignoring space conditions.
This is a low-level method; high-level size respects space conditions.
Condition is a logical function defined in parameter space. Condition restricts the space to the subset of vectors that satisfy this condition.
A space can have arbitraty number of conditions, and they apply using logical AND. This means, conditions restrict the space to the subset that satisfies ALL of them.
All layers of the stack higher up respect conditions - that is, when a method applies to space, effectively it applies only to its subset defined by the conditions.
For example, if cartesian iterator iterates over space, it actually iterates over its subset defined by the conditions.
def cond(command = :print, index: nil, &block)Manage conditions in the space.
command:printprints active space conditions,:setadds new conditions as a block,:unsetremoves specific condition by itsindexor all conditions ifindexisn't specifiedindexidentifies condition set in the space; index is assigned automatically, because conditions have no names (unlike functions)blockbody of the condition being added; it must return eithertrueorfalse
def func(command = :print, *names, hide: false, progress: false, title: "Computing function(s)", order: nil, &block)command:printprint the list of defined functions (including their bodies), either all or specified bynames,:addadd new function or functions defined by symbolicnames,:deldelete function or functiosn given bynames:runcalculate all or specified functions in the spacenameslist of function nameshidedo no show specified function(s) being added in theoutput- this is useful for intermediate calculations, irrelevant for the final resultprogressshow or hide progress bar - this is useful for long-running computations of heavy functions on large spacetitlecustom title for the progress barorder: can be:firstor:lastto make the function calculate before or after all other functionsblock: body of the function(s) being added
Plese note that any functions returns nil unless it has been computed.
Also, a function will return nil in the vector that doesn't satisfy space conditions.
def functionReturn function value in a given vector of the parameter space.
As this method is used often (including conditions and custom code), it is intentionally separated from the wrapper method func for the sake of syntax brevity.
vectoris the vectorfunctionsymbol referring to a function defined in the parameter space.substitute= 0 what to return if the function is not defined invector(that is, returnsnil).
This method can enforce substitute value if the functions has not been computed or is undefined in the vector due to space conditions.
def cartesian(dims = nil, lazy: false, progress: false, title: "Iterating over parameter space")dimsiterate over given description of the dimensions, if specified; by default, iterate over the current spacelazywhether or not materialize all vectors of the space in memoryprogressshow or hide progress bartitlecustom title for the progress bar
def data(command, vector: nil, target: nil )Manage tabular data source.
Currently, it only supports access to the data source linked to the space using source flag during creation of the space.
commandcurrently, only:getis supported to fetch data from the data source. For a givenvector, it returns the first line with the dimensional columns corresponding tovector. At the surface,:getresembles MS Excellookup. Given a tabular data, it searches the first line with specified values in specified columns, and then returns the value stored in another (specified) field of this line.vectorfrom the parameter space that is linked to the data sourcetargetwhich column of the line to return
The data method is very powerful for:
- Creation of behavioural blueprints from external tabular sources, such as XLSX or CSV. It allows to extend FlexCartesian modelling capabilities to legacy data sources that hasn't been designed for it.
- Saving and loading the entire blueprtins, including dimensions and computed functions with all their values. This allows for a stateful, cross-session use cases of FlexCartesian.
def sizeReturn amount of of the vectors in the parameter space with respect to conditions.
def to_a(vector = nil, limit: nil)Converts vector from the space to array, or the first limit vectors to arrays, or the entire space to arrays.
def vector_to(v, type)Converts vector from the space to a different type. Currently, only :hash is supported.
def output(separator: " | ", colorize: false, align: true, format: :plain, limit: nil, file: nil)A universal method that prints tabular data, such as function values in the space, or results produced by analyzers. This method is implemented for space, but higher-level objects extend it with their specific parameters.
separatorthe symbol separating columns in the tabular output; it will be validated for such formats as Markdown or CSVcolorizewhether or not make the output colourful; it is automatically disabled if the output goes to filealignwhether or not align content to make the columns look accurateformathow to structure output, one of:plain,:csv, or:markdownlimitfor the number of lines in the output - useful with very large parameter spacesfileoutput to the file, if specified
def import(path, format: :json)For the current space, create its dimensions using their description in the file.
paththe file storing description of the dimensionsformatformat of the description, either:yamlor:json
def export(path, format: :json)For the current space, save its dimensions to file.
paththe file to store dimensionsformatformat of the description, either:yamlor:json
def visualize(x:, y:, func:, output: nil, text: :dark, show_legend: false, show_z_title: true, show_grid: true, equal_axes: true, start_at_zero: true, show_plot_title: false, bg_color: 'transparent', font_color: nil, grid_color: nil, colorscale: 'Bluered')Generate HTML with an interactive visualization of the blueprint. Currently, it only generates 2D-surfaces (that is, visualizes functions of 2 parameters).
Basic parameters:
xsymbolic name of the dimension that will be X-axisysymbolic name of the dimension that will be Y-axisfuncsymbolic name, or an array of symbolic names, of the function(s) that will be visualized
Output parameter:
outputoutput file, defaults to stdout
Visual style parameters:
textshade of textual information on the visualization, either:darkor:light- useful if you intend put the visualization on a customly colored backgoundshow_legendshow/hide color legendshow_z_titleshow/hide the title of Z-axis (the one with function results)show_gridshow/hide coordinate gridequal_axeswhether or not equalize the lengthes of all axes; usually, default equalization looks cool - but you may want to disable it to show true scale of the function results, for instancestart_at_zeroenforce the axes to start from zero value, even if their dimensional values start from a different value; usually it is useful to keep this option enabled for the visual consistencyshow_plot_titleshow title of the chart at the top in the form of a function of parametersbg_colorcustomize background color of the visualizationfont_colorenforce custom font color; in most cases, you don't need it as FlexCartesian will adapt the color itselfgrid_colorenforce custom grid color; in most cases, you don't need it as FlexCartesian will adapt the color itselfcolorscalecustomize color gradient of the visualization
An analyzer is a higher-level concept on top of functions. While value of a function is defined locally in a given vector, an analyzer introduces global calculations, where resulting value depends on wider area of the parameter space, including the entire space, particularly.
Analyzers are instumental in such global calculations as:
- Sensitivity analysis of a given function in the space
- Calculating statistical characteristics of a function - average, median, etc
- Spotting local or global extremal values
- Spotting areas of unusual behaviour of a function
All analyzers share the following methods:
def analyzer(type, **opts)Return new analyzer object attached to the current space.
typeof the analyzer; currently, only:morrisis supportedoptsare analyzer-specific options (see below)
def nameReturn human-readable name of the analyzer, ex.: "Morris sensitivity analysis".
def descriptionReturn extended description of the analyzer, ex.: "Morris method explores the parameter space by changing one parameter at a time across multiple trajectories, and quantifies rate and linearity of its influence on the target function".
def complexityReturn computational complexity of the analyzer in textual form, ex.: "O( dimensions · trajectories )".
def categoryReturn wider category the analyzer belong to, ex.: "Sensitivity analysis".
def urlURL containing description of the method implemented in the analyzer, ex.: "https://en.wikipedia.org/wiki/Morris_method".
The Morris method for global sensitivity analysis determines the influence of each parameter of a given function to the values of this function. Please note that the Morris method has its limitations. Particularly, the method measures non-linearity of a parameter very well - however, it does NOT distinguish between true non-linearity (the parameter itself causes non-linear behaviour of the function) and false non-linearity (the parameter's influence is entangled with the influence of some other parameter, which looks as an unexpected noise coming from the parameter A). Methods for the exploraton of the nature of non-linearity, as well as exploration of the parameter entaglement, are on the roadmap of FlexCartesian.
Specific options required to create Morris analyzer:
trajectoriesnumber of random trajectories in the parameter space. In the context of FlexCartesian, trajectories do respect space conditions. A trajectory tries its best to find a valid next step according to conditions, and give up trying only if there's no option to make any next step at all. This is not a bug, rather a feature of the modelling approach. Please note that too many conditions in the space may restrict trajectories too aggressively - in such case, Morris may provide impractical or misaligned assessments.stepwidth of a step in the trajectory, defaults to 0.1. It must be decimal from (0..1) range, interpreted as a percentage of the number of values in a dimension. Such relativity is critical for the Morris algorithm to treat all dimensions fairly. Otherwise, scarce dimensions would have been investigated disproportionally scarcely.seedcustom random seed, optional
These options remain available as accessors: def trajectories, def step, and def seed upon creation of the analyzer.
def analyze(func:)Run Morris analysis for the given function in the current space.
def output(func:, categorize: true, recommend: true, **opts)Print results of Morris analysis for the func function.
If this analyzer has performed this analysis before, then the results will be reused.
Otherwise, this method will invoke analyze under the hood and store the result in the analyzer for the reuse in future outputs.
Please note that semantics of output is intentionally lazy.
It allows for the output of the same analysis multiple times (in different formats, etc) without the need for recomputation.
Also, it keeps analysis result identical across repeated output.
If you need to update analysis result, just use analyze.
funcsymbolic name of the function in the parameter spacecategorizeshow or hide categorization of the parameters of the function by their influence and linearity. The algorithms distinguishes three categories, for practical convenience:strongif the influence adds at least 10% to the range of the function values;moderateif the parameter adds from 2% to 10%, otherwisenegligible.recommendshow or hide recommended next steps based on categorization of the parametersoptsgeneric options ofoutputas it is defined in parameter space