Python builder¶
The Python builder allows to build models that can be run with the PySD Model class.
The use of a one-to-one dictionary in translation means that the breadth of functionality is inherently limited. In the case where no direct Python equivalent is available, PySD provides a library of functions such as pulse, step, etc. that are specific to dynamic model behavior.
In addition to translating individual commands between Vensim/XMILE and Python, PySD reworks component identifiers to be Python-safe by replacing spaces with underscores. The translator allows source identifiers to make use of alphanumeric characters, spaces, or special characters. In order to make that possible a namespace is created, which links the original name of the variable with the Python-safe name. The namespace is also available in the PySD model class to allow users working with both original names and Python-safe names.
The resulting Python code from building the model is formatted with black
library and it is written to the same folder where the original model is.
Main builders¶
The ModelBuilder class allows converting the AbstractModel into a PySD model writing the Python code in files that can be loaded later with PySD Model class. Each Abstract level has its own Builder. However, the user is only required to create a ModelBuilder object using the AbstractModel and call the build_model method.
- class pysd.builders.python.python_model_builder.ModelBuilder(abstract_model: AbstractModel)[source]¶
ModelBuilder allows building a PySD Python model from the Abstract Model.
- Parameters:
abstract_model (AbstractModel) – The abstract model to build.
- class pysd.builders.python.python_model_builder.SectionBuilder(abstract_section: AbstractSection)[source]¶
SectionBuilder allows building a section of the PySD model. Each section will be a file unless the model has been set to be split in modules.
- Parameters:
abstract_section (AbstractSection) – The abstract section to build.
- class pysd.builders.python.python_model_builder.ElementBuilder(abstract_element: AbstractElement, section: SectionBuilder)[source]¶
ElementBuilder allows building an element of the PySD model.
- Parameters:
abstract_element (AbstractElement) – The abstract element to build.
section (SectionBuilder) – The section where the element is defined. Necessary to give the acces to the subscripts and namespace.
- class pysd.builders.python.python_model_builder.ComponentBuilder(abstract_component: AbstractComponent, element: ElementBuilder, section: SectionBuilder)[source]¶
ComponentBuilder allows building a component of the PySD model.
- Parameters:
abstract_component (AbstracComponent) – The abstract component to build.
element (ElementBuilder) – The element where the component is defined. Necessary to give the acces to the merging subscripts and other components.
section (SectionBuilder) – The section where the element is defined. Necessary to give the acces to the subscripts and namespace.
Expression builders¶
The translation from Abstract Syntax Tree to Python happens in both ways. The outer expression is visited with its builder, which will split its arguments and visit them with their respective builders. Once the lowest level is reached, it will be translated into Python returning a BuildAST object, this object will include the python expression, its subscripts, its calls to other and its arithmetic order (see Build AST for more info). BuildAST will be returned for each visited argument from the lower lever to the top level, giving the final expression.
- class pysd.builders.python.python_expressions_builder.BuildAST(expression: str, calls: dict, subscripts: dict, order: int)[source]¶
Python expression holder.
- Parameters:
expression (str) – The Python expression.
calls (dict) – The calls to other variables for the dependencies dictionary.
subscripts (dict) – The subscripts dict of the expression.
order (int) – Arithmetic order of the expression. The arithmetic order depends on the last arithmetic operation. If the expression is a number, a call to a function, or is between parenthesis; its order will be 0. If the expression its an exponential of two terms its order will be 1. If the expression is a product or division its order will be 2. If the expression is a sum or substraction its order will be 3. If the expression is a logical comparison its order will be 4.
- reshape(subscripts: SubscriptManager, final_subscripts: dict, final_element: bool = False) None [source]¶
Reshape the object to the desired subscripts. It will modify the expression and lower the order if it is not 0.
- Parameters:
subscripts (SubscriptManager) – The subscripts of the section.
final_subscripts (dict) – The desired final subscripts.
final_element (bool (optional)) – If True the array will be reshaped with the final subscripts to have the shame shape. Otherwise, a length 1 dimension will be included in the position to allow arithmetic operations with other arrays. Default is False.
- lower_order(new_order: int) None [source]¶
Lower the order to maintain the correct order in arithmetic operations. If the requested order is smaller than the current order parenthesis will be added to the expression to lower its order to 0.
- Parameters:
new_order (int) – The required new order of the expression. If 0 it will be assumed that the expression will be passed as an argument of a function and therefore no operations will be done. If order 0 is required, a negative value can be used for new_order.
- class pysd.builders.python.python_expressions_builder.StructureBuilder(value: object, component: object)[source]¶
Main builder for Abstract Syntax Tree structures. All the builders are children of this class, which allows them inheriting the methods.
- reorder(arguments: dict, force: bool = None) dict [source]¶
Reorder the subscripts of the arguments to make them match.
- Parameters:
arguments (dict) – The dictionary of arguments. The keys should br strings of ordered integer numbers starting from 0.
force ('component', 'equal', or None (optional)) – If force is ‘component’ it will force the arguments to have the subscripts of the component definition. If force is ‘equal’ it will force all the arguments to have the same subscripts, includying the floats. If force is None, it will only modify the shape of the arrays adding length 1 dimensions to allow operation between different shape arrays. Default is None.
- Returns:
final_subscripts – The final_subscripts after reordering all the elements.
- Return type:
- get_final_subscripts(arguments: dict) dict [source]¶
Get the final subscripts of a combination of arguments.
- update_object_subscripts(name: str, component_final_subs: dict) None [source]¶
Update the object subscripts. Needed for those objects that use ‘add’ method to load several components at once and mixed definitions are used.
- Parameters:
name (str) – The name of the object in the objects dictionary from the element.
component_final_subs (dict) – The subscripts of the component but with the element subscript ranges as keys. This can differ from the component subscripts when the component is defined with subranges of the final subscript ranges.
- class pysd.builders.python.python_expressions_builder.OperationBuilder(operation: ArithmeticStructure | LogicStructure, component: object)[source]¶
Builder for arithmetic and logical operations.
- class pysd.builders.python.python_expressions_builder.GameBuilder(game_str: GameStructure, component: object)[source]¶
Builder for GAME expressions.
- class pysd.builders.python.python_expressions_builder.CallBuilder(call_str: CallStructure, component: object)[source]¶
Builder for calls to functions, macros and lookups.
- build_not_implemented(arguments: dict) BuildAST [source]¶
Build method for not implemented function calls.
- build_incomplete_call(arguments: dict) BuildAST [source]¶
Build method for incomplete function calls.
- class pysd.builders.python.python_expressions_builder.AllocateAvailableBuilder(allocate_str: AllocateAvailableStructure, component: object)[source]¶
Builder for allocate_available function.
- class pysd.builders.python.python_expressions_builder.AllocateByPriorityBuilder(allocate_str: AllocateByPriorityStructure, component: object)[source]¶
Builder for allocate_by_priority function.
- class pysd.builders.python.python_expressions_builder.ExtLookupBuilder(getlookup_str: GetLookupsStructure, component: object)[source]¶
Builder for External Lookups.
- class pysd.builders.python.python_expressions_builder.ExtDataBuilder(getdata_str: GetDataStructure, component: object)[source]¶
Builder for External Data.
- class pysd.builders.python.python_expressions_builder.ExtConstantBuilder(getconstant_str: GetConstantsStructure, component: object)[source]¶
Builder for External Constants.
- class pysd.builders.python.python_expressions_builder.TabDataBuilder(data_str: DataStructure, component: object)[source]¶
Builder for empty DATA expressions.
- class pysd.builders.python.python_expressions_builder.InitialBuilder(initial_str: InitialStructure, component: object)[source]¶
Builder for Initials.
- class pysd.builders.python.python_expressions_builder.IntegBuilder(integ_str: IntegStructure, component: object)[source]¶
Builder for Integs/Stocks.
- class pysd.builders.python.python_expressions_builder.DelayBuilder(dtype: str, delay_str: DelayStructure | DelayNStructure, component: object)[source]¶
Builder for regular Delays.
- class pysd.builders.python.python_expressions_builder.DelayFixedBuilder(delay_str: DelayFixedStructure, component: object)[source]¶
Builder for Delay Fixed.
- class pysd.builders.python.python_expressions_builder.SmoothBuilder(smooth_str: SmoothStructure | SmoothNStructure, component: object)[source]¶
Builder for Smooths.
- class pysd.builders.python.python_expressions_builder.TrendBuilder(trend_str: TrendStructure, component: object)[source]¶
Builder for Trends.
- class pysd.builders.python.python_expressions_builder.ForecastBuilder(forecast_str: ForecastStructure, component: object)[source]¶
Builder for Forecasts.
- class pysd.builders.python.python_expressions_builder.SampleIfTrueBuilder(sampleiftrue_str: SampleIfTrueStructure, component: object)[source]¶
Builder for Sample If True.
- class pysd.builders.python.python_expressions_builder.LookupsBuilder(lookups_str: LookupsStructure, component: object)[source]¶
Builder for regular Lookups.
- class pysd.builders.python.python_expressions_builder.InlineLookupsBuilder(inlinelookups_str: InlineLookupsStructure, component: object)[source]¶
Builder for inline Lookups.
- class pysd.builders.python.python_expressions_builder.ReferenceBuilder(reference_str: ReferenceStructure, component: object)[source]¶
Builder for references to other variables.
- class pysd.builders.python.python_expressions_builder.NumericBuilder(value: object, component: object)[source]¶
Builder for numeric and nan values.
- class pysd.builders.python.python_expressions_builder.ArrayBuilder(value: object, component: object)[source]¶
Builder for arrays.
- pysd.builders.python.python_expressions_builder.merge_dependencies(*dependencies: dict, inplace: bool = False) dict [source]¶
Merge two dependencies dicts of an element.
- pysd.builders.python.python_expressions_builder.visit_loc(current_subs: dict, original_subs: dict, keep_shape: bool = False) tuple [source]¶
Compares the original subscripts and the current subscripts and returns subindexing information if needed.
- Parameters:
current_subs (dict) – The dictionary of the subscripts that are used in the variable.
original_subs (dict) – The dictionary of the original subscripts of the variable.
keep_shape (bool (optional)) – If True will keep the number of dimensions of the original element and return only loc. Default is False.
- Returns:
loc (list of str or None) – List of the subscripting in each dimensions. If all are full (“:”), None is rerned wich means that array indexing is not needed.
rename (dict) – Dictionary of the dimensions to rename.
final_subs (dict) – Dictionary of the final subscripts of the variable.
reset_coords (bool) – Boolean indicating if the coords need to be reseted.
to_float (bool) – Boolean indicating if the variable should be converted to a float.
- class pysd.builders.python.python_expressions_builder.ASTVisitor(component: object)[source]¶
ASTVisitor allows visiting the Abstract Synatx Tree of a component returning the Python object and generating the neccessary objects.
- Parameters:
component (ComponentBuilder) – The component builder to build.
Supported expressions examples¶
Operators¶
Abstract Syntax |
Python Translation |
---|---|
LogicStructure([‘negative’], (A,)) |
-A |
A |
A |
LogicStructure([‘:NOT:’], (A,)) |
numpy.not(A) |
Abstract Syntax |
Python Translation |
---|---|
ArithmeticStructure([‘^’], (A, B)) |
A**B |
ArithmeticStructure([‘*’], (A, B)) |
A*B |
ArithmeticStructure([‘/’], (A, B)) |
A/B |
CallStructure(‘modulo’, (A, B)) |
pysd.functions.modulo(A, B) |
ArithmeticStructure([‘+’], (A, B)) |
A+B |
ArithmeticStructure([‘-‘], (A, B)) |
A-B |
LogicStructure([‘=’], (A, B)) |
A == B |
LogicStructure([‘<>’], (A, B)) |
A != B |
LogicStructure([‘<’], (A, B)) |
A < B |
LogicStructure([‘>’], (A, B)) |
A > B |
LogicStructure([‘>=’], (A, B)) |
A >= B |
LogicStructure([‘<=’], (A, B)) |
A <= B |
LogicStructure([‘:AND:’], (A, B)) |
numpy.and(A, B) |
LogicStructure([‘:OR:’], (A, B)) |
numpy.or(A, B) |
Functions¶
Abstract Syntax |
Python Translation |
Python comments |
---|---|---|
CallStructure(‘abs’, (A,)) |
numpy.abs(A) |
|
CallStructure(‘min’, (A, B)) |
numpy.minimum(A, B) |
|
CallStructure(‘max’, (A, B)) |
numpy.maximum(A, B) |
|
CallStructure(‘vmin_xmile’, (A,)) |
pysd.functions.vmin(A) |
|
CallStructure(‘vmax_xmile’, (A,)) |
pysd.functions.vmax(A) |
|
CallStructure(‘sqrt’, (A,)) |
numpy.sqrt |
|
CallStructure(‘exp’, (A,)) |
numpy.exp(A) |
|
CallStructure(‘ln’, (A,)) |
numpy.log(A) |
|
CallStructure(‘pi’, (,)) |
numpy.py |
|
CallStructure(‘sin’, (A,)) |
numpy.sin(A) |
|
CallStructure(‘cos’, (A,)) |
numpy.cos(A) |
|
CallStructure(‘tan’, (A,)) |
numpy.tan(A) |
|
CallStructure(‘arcsin’, (A,)) |
numpy.arcsin(A) |
|
CallStructure(‘arccos’, (A,)) |
numpy.arccos(A) |
|
CallStructure(‘arctan’, (A,)) |
numpy.arctan(A) |
|
CallStructure(‘invert_matrix’, (A,)) |
pysd.functions.invert_matrix(A) |
|
CallStructure(‘elmcount’, (A,)) |
len(A) |
|
CallStructure(‘int’, (A,)) |
pysd.functions.integer(A) |
|
CallStructure(‘quantum’, (A, B)) |
pysd.functions.quantum(A, B) |
|
CallStructure(‘modulo’, (A, B)) |
pysd.functions.modulo(A, B) |
|
CallStructure(‘if_then_else’, (A, B)) |
pysd.functions.if_then_else(A, lambda: B, lambda: C) |
|
CallStructure(‘if_then_else’, (A, B)) |
pysd.functions.if_then_else(A, lambda: B, lambda: C) |
|
CallStructure(‘xidz’, (A, B, X)) |
pysd.functions.xidz(A, B, X) |
|
CallStructure(‘zidz’, (A, B)) |
pysd.functions.zidz(A, B) |
|
CallStructure(‘vmin’, (A,)) |
pysd.functions.vmin(A, [‘dim!’]) |
|
CallStructure(‘vmax’, (A,)) |
pysd.functions.vmax(A, [‘dim!’]) |
|
CallStructure(‘sum’, (A,)) |
pysd.functions.sum(A, [‘dim!’]) |
|
CallStructure(‘prod’, (A,)) |
pysd.functions.prod(A, [‘dim!’]) |
|
CallStructure(‘pulse’, (start, width)) |
pysd.functions.pulse(start, width=width) |
|
CallStructure(‘Xpulse’, (start, magnitude)) |
pysd.functions.pulse(start, magnitude=magnitude) |
|
CallStructure(‘Xpulse_train’, (start, interval, magnitude)) |
pysd.functions.pulse(start, repeat_time=interval, magnitude=magnitude) |
|
CallStructure(‘pulse_train’, (start, tbetween, width, end)) |
pysd.functions.pulse(start, repeat_time=tbetween, width=width, end=end) |
|
CallStructure(‘ramp’, (slope, start_time, end_time)) |
pysd.functions.ramp(time, slope, start_time, end_time) |
|
CallStructure(‘ramp’, (slope, start_time)) |
pysd.functions.ramp(time, slope, start_time) |
|
CallStructure(‘step’, (height, step_time)) |
pysd.functions.step(time, height, step_time) |
|
CallStructure(‘get_time_value’, (relativeto, offset, measure)) |
pysd.functions.get_time_value(time, relativeto, offset, measure) |
|
CallStructure(‘vector_select’, (sel_array, exp_array, miss_val, n_action, e_action)) |
pysd.functions.vector_select(sel_array, exp_array, [‘dim!’], miss_val, n_action, e_action) |
|
CallStructure(‘vector_rank’, (vec, direction)) |
vector_rank(vec, direction) |
|
CallStructure(‘vector_reorder’, (vec, svec)) |
vector_reorder(vec, svec) |
|
CallStructure(‘vector_sort_order’, (vec, direction)) |
vector_sort_order(vec, direction) |
|
GameStructure(A) |
A |
|
AllocateAvailableStructure(request, pp, avail) |
allocate_available(request, pp, avail) |
Not all the priority profiles are included. |
AllocateByPriorityStructure(request, priority, size, width, supply) |
allocate_by_priority(request, priority, width, supply) |
|
InitialStructure(value) |
pysd.statefuls.Initial |
|
SampleIfTrueStructure(condition, input, initial_value) |
pysd.statefuls.SampleIfTrue(…) |
|
CallStructure(‘random_0_1’, ()) |
np.random.uniform(0, 1, size=final_shape) |
|
CallStructure(‘random_uniform’, (m, x, s)) |
np.random.uniform(m, x, size=final_shape) |
|
CallStructure(‘random_normal’, (m, x, h, r, s)) |
stats.truncnorm.rvs((m-h)/r, (x-h)/r, loc=h, scale=r, size=final_shape) |
|
CallStructure(‘random_exponential’, (m, x, h, r, s)) |
stats.truncexpon.rvs((x-np.maximum(m, h))/r, loc=np.maximum(m, h), scale=r, size=final_shape) |
Abstract Syntax |
Python Translation |
---|---|
DelayStructure(input, delay_time, initial_value, 1) |
pysd.statefuls.Delay(…) |
DelayStructure(input, delay_time, input, 1) |
pysd.statefuls.Delay(…) |
DelayStructure(input, delay_time, initial_value, 3) |
pysd.statefuls.Delay(…) |
DelayStructure(input, delay_time, input, 3) |
pysd.statefuls.Delay(…) |
DelayNStructure(input, delay_time, initial_value, n) |
pysd.statefuls.DelayN(…) |
DelayNStructure(input, delay_time, input, n) |
pysd.statefuls.DelayN(…) |
DelayFixed(input, delay_time, initial_value) |
pysd.statefuls.DelayFixed(…) |
DelayFixed(input, delay_time, input) |
pysd.statefuls.DelayFixed(…) |
SmoothStructure(input, smth_time, initial_value, 1) |
pysd.statefuls.Smooth(…) |
SmoothStructure(input, smth_time, input, 1) |
pysd.statefuls.Smooth(…) |
SmoothStructure(input, smth_time, initial_value, 3) |
pysd.statefuls.Smooth(…) |
SmoothStructure(input, smth_time, input, 3) |
pysd.statefuls.Smooth(…) |
SmoothNStructure(input, smth_time, initial_value, n) |
pysd.statefuls.SmoothN(…) |
SmoothNStructure(input, smth_time, input, n) |
pysd.statefuls.SmoothN(…) |
ForecastStructure(input, average_time, horizon, initial_trend) |
pysd.statefuls.Forecast(…) |
ForecastStructure(input, average_time, horizon, 0) |
pysd.statefuls.Forecast(…) |
TrendStructure(input, average_time, initial_trend) |
pysd.statefuls.Trend(…) |
TrendStructure(input, average_time, 0) |
pysd.statefuls.Trend(…) |
Abstract Syntax |
Python Translation |
---|---|
GetDataStructure(‘file’, ‘tab’, ‘time_row_or_col’, ‘cell’) |
pysd.external.ExtData(…) |
GetDataStructure(‘file’, ‘tab’, ‘time_row_or_col’, ‘cell’) |
pysd.external.ExtData(…) |
GetLookupsStructure(‘file’, ‘tab’, ‘x_row_or_col’, ‘cell’) |
pysd.external.ExtLookup(…) |
GetLookupsStructure(‘file’, ‘tab’, ‘x_row_or_col’, ‘cell’) |
pysd.external.ExtLookup(…) |
GetConstantsStructure(‘file’, ‘tab’, ‘cell’) |
pysd.external.ExtConstant(…) |
GetConstantsStructure(‘file’, ‘tab’, ‘cell’) |
pysd.external.ExtConstant(…) |
Namespace manager¶
- class pysd.builders.python.namespace.NamespaceManager(parameters: List[str] = [])[source]¶
NamespaceManager object allows includying new elements to the namespace and searching for elements in the namespace. When includying new elements a Python safe name is used to be able to write the equations.
- Parameters:
parameters (list (optional)) – List of the parameters that are used as argument in the Macro. By defaukt it is an empty list.
- add_to_namespace(string: str) None [source]¶
Add a new string to the namespace.
- Parameters:
string (str) – String to add to the namespace.
- Return type:
None
- make_python_identifier(string: str, prefix: str = None, add_to_namespace: bool = False) str [source]¶
Takes an arbitrary string and creates a valid Python identifier.
If the Python identifier created is already in the namespace, but the input string is not (ie, two similar strings resolve to the same Python identifier) or if the identifier is a reserved word in the reserved_words list, or is a Python default reserved word, adds _1, or if _1 is in the namespace, _2, etc.
- Parameters:
string (str) – The text to be converted into a valid Python identifier.
prefix (str or None (optional)) – If given it will be used as a prefix for the output string. Default is None.
add_to_namespace (bool (optional)) – If True it will add the passed string to the namespace and to the cleanspace. Default is False.
- Returns:
identifier – A vaild Python identifier based on the input string.
- Return type:
Examples
>>> make_python_identifier('Capital') 'capital'
>>> make_python_identifier('multiple words') 'multiple_words'
>>> make_python_identifier('multiple spaces') 'multiple_spaces'
When the name is a Python keyword, add ‘_1’ to differentiate it >>> make_python_identifier(‘for’) ‘for_1’
Remove leading and trailing whitespace >>> make_python_identifier(’ whitespace ‘) ‘whitespace’
Remove most special characters outright: >>> make_python_identifier(‘H@t tr!ck’) ‘ht_trck’
add valid string to leading digits >>> make_python_identifier(‘123abc’) ‘nvs_123abc’
already in namespace >>> make_python_identifier(‘Var$’) # namespace={‘Var$’: ‘var’} ‘var’
namespace conflicts >>> make_python_identifier(‘Var@’) # namespace={‘Var$’: ‘var’} ‘var_1’
>>> make_python_identifier('Var$') # namespace={'Var@': 'var', ... 'Var%':'var_1'} 'var_2'
References
- Identifiers must follow the convention outlined here:
https://docs.python.org/2/reference/lexical_analysis.html#identifiers
Subscript manager¶
- class pysd.builders.python.subscripts.SubscriptManager(abstract_subscripts: List[AbstractSubscriptRange], _root: Path)[source]¶
SubscriptManager object allows saving the subscripts included in the Section, searching for elements or keys and simplifying them.
- Parameters:
abstrac_subscripts (list) – List of the AbstractSubscriptRanges comming from the AbstractModel.
_root (pathlib.Path) – Path to the model file. Needed to read subscript ranges from Excel files.
- make_coord_dict(subs: List[str]) dict [source]¶
This is for assisting with the lookup of a particular element.
- Parameters:
subs (list of strings) – Coordinates, either as names of dimensions, or positions within a dimension.
- Returns:
coordinates – Coordinates needed to access the xarray quantities we are interested in.
- Return type:
Examples
>>> sm = SubscriptManager([], Path('')) >>> sm._subscripts = { ... 'Dim1': ['A', 'B', 'C'], ... 'Dim2': ['A', 'B', 'C', 'D']} >>> sm.make_coord_dict(['Dim1', 'D']) {'Dim1': ['A', 'B', 'C'], 'Dim2': ['D']} >>> sm.make_coord_dict(['A']) {'Dim1': ['A']} >>> sm.make_coord_dict(['A', 'B']) {'Dim1': ['A'], 'Dim2': ['B']} >>> sm.make_coord_dict(['A', 'Dim1']) {'Dim2': ['A'], 'Dim1': ['A', 'B', 'C']}
- make_merge_list(subs_list: List[List[str]], element: str = '') List[str] [source]¶
This is for assisting when building xrmerge. From a list of subscript lists returns the final subscript list after merging. Necessary when merging variables with subscripts comming from different definitions.
- Parameters:
- Returns:
dims – Final subscripts after merging.
- Return type:
Examples
>>> sm = SubscriptManager([], Path('')) >>> sm._subscripts = {"upper": ["A", "B"], "all": ["A", "B", "C"]} >>> sm.make_merge_list([['A'], ['B']]) ['upper'] >>> sm.make_merge_list([['A'], ['B'], ['C']]) ['all'] >>> sm.make_merge_list([['upper'], ['C']]) ['all'] >>> sm.make_merge_list([['A'], ['C']]) ['all']
- simplify_subscript_input(coords: dict, merge_subs: List[str] = None) tuple [source]¶
Simplifies the subscripts input to avoid printing the coordinates list when the _subscript_dict can be used. Makes model code more simple.
- Parameters:
- Returns:
final_subs, coords – Final subscripts and the equations to generate the coord dicttionary in the model file.
- Return type:
Examples
>>> sm = SubscriptManager([], Path('')) >>> sm._subscripts = { ... "dim": ["A", "B", "C"], ... "dim2": ["A", "B", "C", "D"]} >>> sm.simplify_subscript_input({"dim": ["A", "B", "C"]}) ({"dim": ["A", "B", "C"]}, "{'dim': _subscript_dict['dim']}" >>> sm.simplify_subscript_input({"dim": ["A", "B", "C"]}, ["dim2"]) ({"dim2": ["A", "B", "C"]}, "{'dim2': _subscript_dict['dim']}" >>> sm.simplify_subscript_input({"dim": ["A", "B"]}) ({"dim": ["A", "B"]}, "{'dim': ['A', 'B']}"