├── the_model.jpg
├── the_model.slx
├── results_mle2.png
├── results_mrt.png
├── CallingSimFromPython.pptx
├── SECURITY.md
├── plot_results.m
├── LICENSE.md
├── call_sim_the_model.m
├── call_sim_the_model_using_matlab_engine.py
├── README.md
├── call_sim_the_model_using_matlab_runtime.py
└── sim_the_model.m
/the_model.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mathworks/Call-Simulink-from-Python/HEAD/the_model.jpg
--------------------------------------------------------------------------------
/the_model.slx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mathworks/Call-Simulink-from-Python/HEAD/the_model.slx
--------------------------------------------------------------------------------
/results_mle2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mathworks/Call-Simulink-from-Python/HEAD/results_mle2.png
--------------------------------------------------------------------------------
/results_mrt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mathworks/Call-Simulink-from-Python/HEAD/results_mrt.png
--------------------------------------------------------------------------------
/CallingSimFromPython.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mathworks/Call-Simulink-from-Python/HEAD/CallingSimFromPython.pptx
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting Security Vulnerabilities
2 |
3 | If you believe you have discovered a security vulnerability, please report it to
4 | [security@mathworks.com](mailto:security@mathworks.com). Please see
5 | [MathWorks Vulnerability Disclosure Policy for Security Researchers](https://www.mathworks.com/company/aboutus/policies_statements/vulnerability-disclosure-policy.html)
6 | for additional information.
--------------------------------------------------------------------------------
/plot_results.m:
--------------------------------------------------------------------------------
1 | function figHndl = plot_results(res, plotTitle)
2 | %PLOT_RESULTS Plot results from call_sim_the_model
3 |
4 | % By Murali Yeddanapudi, 19-Sep-2022
5 |
6 | figHndl = figure; hold on; cols = colororder;
7 |
8 | plot(res{1}.x1.Time, res{1}.x1.Data, 'Color', cols(1,:), ...
9 | 'DisplayName', 'x1 from 1st sim with default setting');
10 | plot(res{2}.x1.Time, res{2}.x1.Data, 'Color', cols(2,:), ...
11 | 'DisplayName', 'x1 from 2nd sim with limits on dx2');
12 | plot(res{3}.x1.Time, res{3}.x1.Data, 'Color', cols(3,:), ...
13 | 'DisplayName', 'x1 from 3rd sim with input u');
14 | plot(res{4}.x1.Time, res{4}.x1.Data, 'Color', cols(4,:), ...
15 | 'DisplayName', 'x1 from 4th sim with limits on dx2 and input u');
16 | stairs(res{3}.u.Time, res{3}.u.Data, 'Color', cols(5,:), ...
17 | 'DisplayName','input u in 3rd and 4th sims');
18 |
19 | hold off; grid; ylim([-4 3]);
20 | title(plotTitle,'Interpreter','none');
21 | set(get(gca,'Children'),'LineWidth',2);
22 | legend('Location','southeast');
23 |
24 | end
25 |
26 | % To call this MATLAB function from Python make sure to install the correct version of Python
27 | % https://www.mathworks.com/help/matlab/matlab_external/install-supported-python-implementation.html?s_tid=srchtitle_Configure%20Your%20System%20to%20Use%20Python_1
28 | % https://www.mathworks.com/content/dam/mathworks/mathworks-dot-com/support/sysreq/files/python-compatibility.pdf
29 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2023, The MathWorks, Inc.
2 | All rights reserved.
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
5 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
6 | 3. In all cases, the software is, and all modifications and derivatives of the software shall be, licensed to you solely for use in conjunction with MathWorks products and service offerings.
7 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/call_sim_the_model.m:
--------------------------------------------------------------------------------
1 | %% Simulate the_model in deployed mode multiple times and plot the results
2 |
3 | % By Murali Yeddanapudi on 04-Mar-2022
4 |
5 | %% 1st sim: with default parameter values
6 | res{1} = sim_the_model();
7 |
8 | %% 2nd sim: with dx2min and dx2max parameter values
9 | stopTime = nan; % Use deafult value
10 | tunablePrms.dx2min = -3; % Specify new value for dx2min
11 | tunablePrms.dx2max = 4; % Specify new value for dx2max
12 | res{end+1} = sim_the_model('StopTime', stopTime, ...
13 | 'TunableParameters',tunablePrms);
14 |
15 | %% 3rd sim: simulate with a non-zero iinput signal
16 | stopTime = nan; % Use deafult value
17 | tunablePrms = []; % Use default values
18 | % Note that, in the model the input u is sampled at a fixed time interval uST (=1)
19 | % So the time axis for the input values is implicit at 1s (=uST) interval
20 | u = [0 2 zeros(1,3) -2*ones(1,2) 0];
21 | % => u(t) = 2 for t in [1,2), -2 for t in [6,8), 0 otherwise
22 | res{end+1} = sim_the_model(StopTime=stopTime, ...
23 | ExternalInput=u);
24 |
25 | %% 4th sim: with dx2min, dx2max and non-zero input signal
26 | tunablePrms.dx2min = -3; % Specify new value for dx2min
27 | tunablePrms.dx2max = 4; % Specify new value for dx2max
28 | u = [0 2 zeros(1,3) -2*ones(1,2) 0];
29 | res{end+1} = sim_the_model(TunableParameters=tunablePrms, ...
30 | ExternalInput=u);
31 |
32 | %% Plot some results from the simulations
33 | plot_results(res, 'Results from calling sim_the_model in MATLAB');
34 |
35 |
--------------------------------------------------------------------------------
/call_sim_the_model_using_matlab_engine.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Example showing how to simulate a Simulink model (called the_model) with different
4 | parameter and external input signal values using the MATLAB Engine API for Python.
5 |
6 | The example requires:
7 | 1. MATLAB and Simulink products installed and licensed
8 | 2. MATLAB Engine API installed as a Python package
9 | https://www.mathworks.com/help/matlab/matlab_external/install-the-matlab-engine-for-python.html
10 |
11 | @author: Murali Yeddanapudi
12 | Created on Tue Mar 1 2022
13 | """
14 | # Install numpy (For example, pip install numpy)
15 | import numpy as np
16 | import matlab.engine
17 |
18 | mle = matlab.engine.start_matlab(); # start the matlab engine
19 |
20 | # Since we are using MATLAB Engine we do not have to configureForDeployment.
21 | # This allows more flexibility to simulate the model in normal mode, and change
22 | # non-tunable parameters.
23 | configureForDeployment = 0
24 |
25 | # Allocate res list to hold the results from 4 calls to sim_the_model
26 | res = [0]*4;
27 |
28 | ## 1st sim: with default parameter values
29 | res[0] = mle.sim_the_model()
30 |
31 | ## 2nd sim: with new values for dx2min and dx2max parameters
32 | tunableParams = {
33 | 'dx2min': -3.0, # Specify a new value for dx2min
34 | 'dx2max': 4.0 # Specify a new value for dx2max
35 | }
36 | res[1] = mle.sim_the_model('TunableParameters',tunableParams,
37 | 'ConfigureForDeployment',configureForDeployment)
38 |
39 | ## 3rd sim: with a non zero input signal
40 | # Note that, in the model the input u is sampled at a fixed time interval
41 | # uST (=1) which cannot be changed since the model is compiled for deployment.
42 | # So the time axis for the input values is implicit at 1s (=uST) interval
43 | # u = [0 2 zeros(1,3) -2*ones(1,2) 0];
44 | u = np.concatenate([np.zeros(1), 2*np.ones(1), np.zeros(3), -2*np.ones(2), np.zeros(1)])
45 | # => u(t) = 2 for t in [1,2), -2 for t in [6,8), 0 otherwise
46 | externalInput = matlab.double(u.tolist()) # convert numpy array into matlab array
47 | res[2] = mle.sim_the_model('ExternalInput',externalInput,
48 | 'ConfigureForDeployment',configureForDeployment)
49 |
50 | ## 4th sim: with dx2min, dx2max and non-zero input signal
51 | tunableParams = {
52 | 'dx2min': -3.0, # Specify a new value for dx2min
53 | 'dx2max': 4.0 # Specify a new value for dx2max
54 | }
55 | u = np.concatenate([np.zeros(1), 2*np.ones(1), np.zeros(3), -2*np.ones(2), np.zeros(1)])
56 | externalInput = matlab.double(u.tolist()) # convert numpy array into matlab array
57 | res[3] = mle.sim_the_model('TunableParameters',tunableParams,
58 | 'ExternalInput',externalInput,
59 | 'ConfigureForDeployment',configureForDeployment)
60 |
61 | ## callback into MATLAB to plot the results
62 | mle.plot_results(res, "Results from sim_the_model using MATLAB Engine")
63 |
64 | input("Press enter to close the MATLAB figure and exit ...")
65 | mle.quit() # stop the matlab engine
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simulate a Simulink® model from Python®
2 |
3 | This example illustrates two ways to simulate a Simulink model (named the_model) via a wrapper MATLAB® function (named sim_the_model) from Python.
4 |
5 | 
6 |
7 | The first approach, shown in call_sim_the_model_using_matlab_engine.py, uses the [MATLAB Engine API for
8 | Python](https://www.mathworks.com/help/matlab/matlab_external/install-the-matlab-engine-for-python.html) to call the wrapper function sim_the_model.m multiple times passing in different parameters and external input signals.
9 |
10 | 
11 |
12 | The second approach uses [MATLAB Compiler
13 | SDK](https://www.mathworks.com/help/compiler_sdk/gs/create-a-python-application-with-matlab-code.html)™ and [Simulink Compiler](https://www.mathworks.com/help/slcompiler/ug/deploy-from-matlab-command-line.html)™ to first build a Python package around the wrapper function sim_the_model.m as shown in
14 | build_python_package_around_sim_the_model.m. We can then call this package to run the warpper function sim_the_model.m multiple times passing in different parameters and external input signals as show in call_sim_the_model_using_matlab_runtime.py.
15 |
16 | 
17 |
18 | This example includes the following files:
19 |
20 | * the_model.slx: the Simulink model we will simulate in the example;
21 | * sim_the_model.m: the wrapper MATLAB function to simulate a Simulink model with the specified parameter and input signal values;
22 | * call_sim_the_model.m: MATLAB script used to call the sim_them_model multiple times in MATLAB with different inputs and parameters;
23 | * plot_results.m: MATLAB script used by call_sim_the_model to plot the results;
24 | * call_sim_the_model_using_matlab_runtime.py: Python script to call sim_the_model packaged function multiple times and plot the results;
25 | * call_sim_the_model_using_matlab_engine.py: Python script that uses MATLAB Engine API to call sim_the_model.m multiple time and plot the results;
26 | * CallingSimFromPython.pptx: complementary presentation slides describing the demo structure and setup
27 |
28 | The model the_model.slx and the wrapper MATLAB function sim_the_model.m illustrate implementation choices that make data marshaling between Python and sim command in MATLAB relatively straight forward and can be used with any Simulink model. These are:
29 |
30 | * Parameterizing the Simulink model using workspace variables makes it easy run sim with new parameter values passed in from Python.
31 | * Labeling the logged signals in the model with valid identifiers, makes it easy to pack the results into a MATLAB struct and return to Python.
32 | * Extracting the time and data values as numeric vectors from sim command output and returning these to Python makes data marshaling relatively easy.
33 |
34 |
35 |
36 | This example has been tested with MATLAB R2022b and Python 3.8. The following MathWorks products are needed for using this example:
37 |
38 | * [MATLAB](https://www.mathworks.com/products/matlab.html);
39 | * [Simulink](https://www.mathworks.com/products/simulink.html);
40 | * [MATLAB Compiler™](https://www.mathworks.com/products/compiler.html);
41 | * [MATLAB Compiler SDK](https://www.mathworks.com/products/matlab-compiler-sdk.html);
42 | * [Simulink Compiler](https://www.mathworks.com/products/simulink-compiler.html);
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/call_sim_the_model_using_matlab_runtime.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Example showing how call a python package to simulate a Simulink
4 | model (called the_model) with different tunable parameter and
5 | external input signal values.
6 |
7 | To run this script, you need the following prerequisites:
8 | 1. Install the MATLAB Runtime (R2021b or later)
9 | https://www.mathworks.com/products/compiler/matlab-runtime.html
10 |
11 | 2. Install "sim_the_model" python package. See instructions below.
12 |
13 | Notes:
14 | 1. Run build_python_package_around_sim_the_model.m script in MATLAB
15 | (R2021b or later) to create sim_the_model_python_package. This
16 | script requires the following products:
17 | - MATLAB
18 | - Simulink
19 | - MATLAB Compiler
20 | - Simulink Compiler
21 | - MATLAB Compiler SDK
22 |
23 | After your run the script, follow the instructions displayed on the
24 | MATLAB command window to install the sim_the_model_python_package
25 |
26 | 2. sim_the_model_python_package is a wrapper to run sim_the_model.m
27 | MATLAB function using deployed version of the Simulink model
28 | (the_model) and the MATLAB Runtime
29 |
30 | 3. Both MATLAB Runtime, and sim_the_model_python_package (once it is
31 | built) can be distributed freely and do not require licenses.
32 |
33 | By: Murali Yeddanapudi on 01-Mar-2022
34 | """
35 |
36 | import matlab
37 | import numpy as np
38 |
39 | # Specify the path to sim_the_model_python_package. Note that build_python_package_around_sim_the_model.m
40 | # script installs the python package in this location. If you change it, then you need to update the
41 | # code here, or remove it altogether if you install sim_the_model package in a location on the python
42 | # search path.
43 | import sys
44 | sys.path.append(".\\sim_the_model_python_package\\Lib\\site-packages")
45 | import sim_the_model
46 |
47 | # initialize sim_the_model package
48 | mlr = sim_the_model.initialize()
49 |
50 | # Allocate res list to hold the results from 4 calls to sim_the_model
51 | res = [0]*4;
52 |
53 | ## 1st sim: with default parameter values
54 | res[0] = mlr.sim_the_model()
55 |
56 | ## 2nd sim: with new values for dx2min and dx2max parameters
57 | tunableParams = {
58 | 'dx2min': -3.0, # Specify a new value for dx2min
59 | 'dx2max': 4.0 # Specify a new value for dx2max
60 | }
61 | res[1] = mlr.sim_the_model('TunableParameters',tunableParams)
62 |
63 | ## 3rd sim: with a non zero input signal
64 | # Note that, in the model the input u is sampled at a fixed time interval
65 | # uST (=1) which cannot be changed since the model is compiled for deployment.
66 | # So the time axis for the input values is implicit at 1s (=uST) interval
67 | # u = [0 2 zeros(1,3) -2*ones(1,2) 0];
68 | u = np.concatenate([np.zeros(1), 2*np.ones(1), np.zeros(3), -2*np.ones(2), np.zeros(1)])
69 | # => u(t) = 2 for t in [1,2), -2 for t in [6,8), 0 otherwise
70 | externalInput = matlab.double(u.tolist()) # convert numpy array into matlab array
71 | res[2] = mlr.sim_the_model('ExternalInput',externalInput)
72 |
73 | ## 4th sim: with dx2min, dx2max and non-zero input signal
74 | tunableParams = {
75 | 'dx2min': -3.0, # Specify a new value for dx2min
76 | 'dx2max': 4.0 # Specify a new value for dx2max
77 | }
78 | u = np.concatenate([np.zeros(1), 2*np.ones(1), np.zeros(3), -2*np.ones(2), np.zeros(1)])
79 | externalInput = matlab.double(u.tolist()) # convert numpy array into matlab array
80 | res[3] = mlr.sim_the_model('TunableParameters',tunableParams,'ExternalInput',externalInput)
81 |
82 | ## Plot the results
83 | # TODO: Replace this code using plotly
84 | import matplotlib.pyplot as plt
85 | cols = plt.rcParams['axes.prop_cycle'].by_key()['color']
86 | fig, ax = plt.subplots(1,1,sharex=True)
87 | ax.plot(res[0]['x1']['Time'], res[0]['x1']['Data'], color=cols[0], label="x1 from 1st sim with default setting")
88 | ax.plot(res[1]['x1']['Time'], res[1]['x1']['Data'], color=cols[1], label="x1 from 2nd sim with limits on dx2")
89 | ax.plot(res[2]['x1']['Time'], res[2]['x1']['Data'], color=cols[2], label="x1 from 3rd sim with input u")
90 | ax.plot(res[3]['x1']['Time'], res[3]['x1']['Data'], color=cols[3], label="x1 from 4th sim with limits on dx2 and input u")
91 | ax.step(res[3]['u']['Time'], res[3]['u']['Data'], where='post', color=cols[4], label="input u in 3rd and 4th sims")
92 | ax.grid(); ax.set_ylim([-4, 3])
93 | lg = ax.legend(fontsize='x-small'); lg.set_draggable(True)
94 | ax.set_title("Results from sim_the_model using MATLAB Runtime")
95 | plt.show()
96 |
97 | mlr.terminate() # stop the MATLAB Runtime
98 |
--------------------------------------------------------------------------------
/sim_the_model.m:
--------------------------------------------------------------------------------
1 | function res = sim_the_model(args)
2 | % Utility function to simulate a Simulink model (named 'the_model') with
3 | % the specified parameter and input signal values.
4 | %
5 | % Inputs:
6 | % StopTime:
7 | % Simulation stop time, default is nan
8 | % TunableParameters
9 | % A struct where the fields are the tunanle referenced
10 | % workspace variables with the values to use for the
11 | % simulation.
12 | % ExternalInput:
13 | % External Input signal, defualt is empty
14 | % ConfigureForDeployment:
15 | % Sepcify if the simulation input should be configured
16 | % for deployment, default is true
17 | %
18 | % Values of nan or empty for the above inputs indicate that sim should
19 | % run with the default values set in the model.
20 | %
21 | % Outputs:
22 | % res: A structure with the time and data values of the logged signals.
23 |
24 | % By: Murali Yeddanapudi, 20-Feb-2022
25 |
26 | arguments
27 | args.StopTime (1,1) double = nan
28 | args.TunableParameters = []
29 | args.ExternalInput (1,:) {mustBeNumericOrLogical} = []
30 | args.ConfigureForDeployment (1,1) {mustBeNumericOrLogical} = true
31 | args.InputFcn (1,1) {mustBeFunctionHandle} = @emptyFunction
32 | args.OutputFcn (1,1) {mustBeFunctionHandle} = @emptyFunction
33 | args.OutputFcnDecimation (1,1) {mustBeInteger, mustBePositive} = 1
34 | end
35 |
36 | %% Create the SimulationInput object
37 | % Note that the name of the model is hard-coded to 'the_model'
38 | si = Simulink.SimulationInput('the_model');
39 |
40 | %% Load the StopTime into the SimulationInput object
41 | if ~isnan(args.StopTime)
42 | si = si.setModelParameter('StopTime', num2str(args.StopTime));
43 | end
44 |
45 | %% Load the specified tunable parameters into the simulation input object
46 | if isstruct(args.TunableParameters)
47 | tpNames = fieldnames(args.TunableParameters);
48 | for itp = 1:numel(tpNames)
49 | tpn = tpNames{itp};
50 | tpv = args.TunableParameters.(tpn);
51 | si = si.setVariable(tpn, tpv);
52 | end
53 | end
54 |
55 | %% Load the external input into the SimulationInput object
56 | if ~isempty(args.ExternalInput)
57 | % In the model, the external input u is a discrete signal with sample
58 | % time 'uST'. Hence the time points where it is sampled are set, i.e.,
59 | % they are multiples of uST: 0, uST, 2*uST, 3*uST, .. We only specify
60 | % the data values here using the struct with empty time field as
61 | % described in Guy's blog post:
62 | % https://blogs.mathworks.com/simulink/2012/02/09/using-discrete-data-as-an-input-to-your-simulink-model/
63 | uStruct.time = [];
64 | uStruct.signals.dimensions = 1;
65 | % values needs to be column vector
66 | uStruct.signals.values = reshape(args.ExternalInput,numel(args.ExternalInput),1);
67 | si.ExternalInput = uStruct;
68 | end
69 |
70 | %% Configure for deployment
71 | if args.ConfigureForDeployment
72 | si = simulink.compiler.configureForDeployment(si);
73 | elseif ismcc || isdeployed
74 | error("Simulation needs to be configured for deployment");
75 | end
76 |
77 | %% InputFcn
78 | if ~isequal(args.InputFcn, @emptyFunction)
79 | si = simulink.compiler.setExternalInputsFcn(si, args.InputFcn);
80 | end
81 |
82 | %% OutputFcn
83 | prevSimTime = nan;
84 | function locPostStepFcn(simTime)
85 | so = simulink.compiler.getSimulationOutput('the_model');
86 | res = extractResults(so, prevSimTime);
87 | stopRequested = feval(args.OutputFcn, simTime, res);
88 | if stopRequested
89 | simulink.compiler.stopSimulation('the_model');
90 | end
91 | prevSimTime = simTime;
92 | end
93 | if ~isequal(args.OutputFcn, @emptyFunction)
94 | si = simulink.compiler.setPostStepFcn(si, @locPostStepFcn, ...
95 | 'Decimation', args.OutputFcnDecimation);
96 | end
97 |
98 | %% call sim
99 | so = sim(si);
100 |
101 | %% Extract the simulation results
102 | % Package the time and data values of the logged signals into a structure
103 | res = extractResults(so,nan);
104 |
105 | end % sim_the_model_using_matlab_runtime
106 |
107 | function res = extractResults(so, prevSimTime)
108 | % Package the time and data values of the logged signals into a structure
109 | ts = simulink.compiler.internal.extractTimeseriesFromDataset(so.logsout);
110 | for its=1:numel(ts)
111 | if isfinite(prevSimTime)
112 | idx = find(ts{its}.Time > prevSimTime);
113 | res.(ts{its}.Name).Time = ts{its}.Time(idx);
114 | res.(ts{its}.Name).Data = ts{its}.Data(idx);
115 | else
116 | res.(ts{its}.Name).Time = ts{its}.Time;
117 | res.(ts{its}.Name).Data = ts{its}.Data;
118 | end
119 | end
120 | end
121 |
122 | function mustBeFunctionHandle(fh)
123 | if ~isa(fh,'function_handle') && ~ischar(fh) && ~isstring(fh)
124 | throwAsCaller(error("Must be a function handle"));
125 | end
126 | end
127 |
128 | function emptyFunction
129 | end
130 |
--------------------------------------------------------------------------------