Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/dev' into examples/custom_event
Browse files Browse the repository at this point in the history
  • Loading branch information
kjjarvis committed Oct 6, 2023
2 parents 112d257 + 707f9fd commit 9ba7937
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 201 deletions.
129 changes: 49 additions & 80 deletions examples/04_New Models.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -832,23 +832,23 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"## Matrix Models"
"## Matrix Data Access Feature"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"At this point in the notebook, we have seen a few different model types. In all of these models, we have used dictionaries to represent the states. For example, in the implementation of `ThrownObject_ST` above, see how `dx` is defined with a StateContainer dictionary. While all models can be constructed in this way, Matrix Models provide an advanced feature that is applicable and useful for some use-cases. \n",
"In the above models, we have used dictionaries to represent the states. For example, in the implementation of `ThrownObject_ST` above, see how `dx` is defined with a StateContainer dictionary. While all models can be constructed using dictionaries in this way, some dynamical systems allow for the state of the system to be represented with a matrix. For such use-cases, ProgPy has an advanced *matrix data access feature* that provides a more efficient way to define these models.\n",
"\n",
"Matrix models are a particular type of model that represent the state of the system using matrices instead of dictionaries. The provided model.StateContainer, InputContainer, and OutputContainer can be treated as dictionaries but use an underlying matrix. This is important for some applications like surrogate and machine-learned models where the state is represented by a tensor. In a matrix model, simulation functions propagate the state using the matrix form, preventing the inefficiency of having to convert to and from dictionaries."
"In ProgPy's implementation, the provided model.StateContainer, InputContainer, and OutputContainers can be treated as dictionaries but use an underlying matrix. This is important for some applications like surrogate and machine-learned models where the state is represented by a tensor. ProgPy's *matrix data access feature* allows the matrices to be used directly. Simulation functions propagate the state using the matrix form, preventing the inefficiency of having to convert to and from dictionaries. Additionally, this implementation is faster than recreating the StateContainer each time, especially when updating inplace."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this example, we'll illustrate how to create a matrix model. We'll continue with our ThrownObject system, and create a model to simulate this using matrix notation (instead of dictionary notation as in the standard model, seen above in `ThrownObject_ST`). The implementation of the model is comparable to a standard model, except that it uses matrix operations within each function, as seen below. "
"In this example, we'll illustrate how to use the matrix data access feature. We'll continue with our ThrownObject system, and create a model to simulate this using matrix notation (instead of dictionary notation as in the standard model, seen above in `ThrownObject_ST`). The implementation of the model is comparable to a standard model, except that it uses matrix operations within each function, as seen below. "
]
},
{
Expand All @@ -872,76 +872,11 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"We'll start by defining our matrix model class. Similarly to the standard models above, we'll define keys for the inputs, states, outputs, and events, as well as default parameters. We'll also include methods for initialization, output, threshold met, and event state. All of this is the same as the standard implementation. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class ThrownObject_MM(PrognosticsModel):\n",
" # Define the model properties, this is exactly the same as for a regular model.\n",
"\n",
" inputs = [] # no inputs, no way to control\n",
" states = [\n",
" 'x', # Position (m) \n",
" 'v' # Velocity (m/s)\n",
" ]\n",
" outputs = [\n",
" 'x' # Position (m)\n",
" ]\n",
" events = [\n",
" 'falling', # Event- object is falling\n",
" 'impact' # Event- object has impacted ground\n",
" ]\n",
"To use the matrix data access feature, we'll subclass from our state-transition model defined above, `ThrownObject_ST`. Our new model will therefore inherit the default parameters and methods for initialization, output, threshold met, and event state. \n",
"\n",
" is_vectorized = True\n",
"\n",
" # The Default parameters. Overwritten by passing parameters dictionary into constructor\n",
" default_parameters = {\n",
" 'thrower_height': 1.83, # m\n",
" 'throwing_speed': 40, # m/s\n",
" 'g': -9.81, # Acceleration due to gravity in m/s^2\n",
" 'process_noise': 0.0 # amount of noise in each step\n",
" }\n",
"\n",
" # Add initialization \n",
" def initialize(self, u = None, z = None):\n",
" # Note: states are returned using StateContainer\n",
" return self.StateContainer({\n",
" 'x': self.parameters['thrower_height'], \n",
" 'v': self.parameters['throwing_speed']}) \n",
"\n",
" def output(self, x):\n",
" # Note- states can still be accessed a dictionary\n",
" return self.OutputContainer({'x': x['x']})\n",
"\n",
" # This is actually optional. Leaving thresholds_met empty will use the event state to define thresholds.\n",
" # Threshold = Event State == 0. However, this implementation is more efficient, so we included it\n",
" def threshold_met(self, x):\n",
" return {\n",
" 'falling': x['v'] < 0,\n",
" 'impact': x['x'] <= 0\n",
" }\n",
"\n",
" def event_state(self, x): \n",
" x_max = x['x'] + np.square(x['v'])/(-self.parameters['g']*2) # Use speed and position to estimate maximum height\n",
" x_max = np.where(x['v'] > 0, x['x'], x_max) # 1 until falling begins\n",
" return {\n",
" 'falling': np.maximum(x['v']/self.parameters['throwing_speed'],0), # Throwing speed is max speed\n",
" 'impact': np.maximum(x['x']/x_max,0) # then it's fraction of height\n",
" }"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Next, we need to define how the state transitions, and we'll use matrices to do so. Since we are working with a discrete version of the system now, we'll define the `next_state` method. \n",
"To use the matrix data access feature, we'll use matrices to define how the state transitions. Since we are working with a discrete version of the system now, we'll define the `next_state` method, and this will override the `dx` method in the parent class. \n",
"\n",
"In the following, we will use the matrix version for each variable, accessed with `.matrix`. Both `x.matrix` and `u.matrix` are column vectors, and `u.matrix` is in the same order as model.inputs."
"In the following, we will use the matrix version for each variable, accessed with `.matrix`. We implement this within `next_state`, but this feature can also be used in other functions. Here, both `x.matrix` and `u.matrix` are column vectors, and `u.matrix` is in the same order as model.inputs."
]
},
{
Expand All @@ -950,7 +885,7 @@
"metadata": {},
"outputs": [],
"source": [
"class ThrownObject_MM(ThrownObject_MM):\n",
"class ThrownObject_MM(ThrownObject_ST):\n",
"\n",
" def next_state(self, x, u, dt):\n",
"\n",
Expand All @@ -965,7 +900,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Our matrix model is now specified. Let's try simulating with it."
"Our model is now specified. Let's try simulating with it."
]
},
{
Expand All @@ -988,7 +923,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, let's simulate to threshold. "
"Now, let's simulate to threshold. We'll also time the simulation so we can compare with the non-matrix state-transition model below. "
]
},
{
Expand All @@ -997,26 +932,58 @@
"metadata": {},
"outputs": [],
"source": [
"import time \n",
"\n",
"tic_matrix = time.perf_counter()\n",
"# Simulate to threshold \n",
"m_matrix.simulate_to_threshold(\n",
" print = True, \n",
" threshold_keys = 'impact', \n",
" dt = 0.1, \n",
" save_freq = 1)"
" save_freq = 1)\n",
"toc_matrix = time.perf_counter()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our matrix model was successful in simulating the thrown object's behavior throughout time. \n",
"Our matrix notation was successful in simulating the thrown object's behavior throughout time. \n",
"\n",
"Finally, let's simulate the non-matrix version to compare computation time. \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"tic_st = time.perf_counter()\n",
"m_st.simulate_to_threshold(\n",
" print = True, \n",
" threshold_keys = 'impact', \n",
" dt = 0.1, \n",
" save_freq = 1)\n",
"toc_st = time.perf_counter()\n",
"\n",
"print(f'Matrix execution: {(toc_matrix-tic_matrix)*1000:0.4f} milliseconds')\n",
"print(f'Non-matrix execution: {(toc_st-tic_st)*1000:0.4f} milliseconds')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As illustrated here, matrix models are an advanced feature that represent the state of a system using matrices. They can provide efficiency for use-cases where the state is easily represented by a tensor and operations are defined by matrices."
"As we can see, for this system, using the matrix data access feature is computationally faster than a standard state-transition matrix that uses dictionaries. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As illustrated here, the matrix data access feature is an advanced capability that represents the state of a system using matrices. This can provide efficiency for use-cases where the state is easily represented by a tensor and operations are defined by matrices."
]
},
{
Expand All @@ -1030,7 +997,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In real-world, physical systems, there are often constraints on what values the states can take. For example, in the case of a thrown object, if we define our reference frame with the ground at a position of $x=0$, then the position of the object should only be greater than or equal to 0, and should never take on negative values. In ProgPy, we can enforce constraints on the range of each state for a state-transition model using the [state limits](https://nasa.github.io/progpy/prog_models_guide.html#state-limits) attribute. "
"In real-world physical systems, there are often constraints on what values the states can take. For example, in the case of a thrown object, if we define our reference frame with the ground at a position of $x=0$, then the position of the object should only be greater than or equal to 0, and should never take on negative values. In ProgPy, we can enforce constraints on the range of each state for a state-transition model using the [state limits](https://nasa.github.io/progpy/prog_models_guide.html#state-limits) attribute. "
]
},
{
Expand Down Expand Up @@ -1190,7 +1157,9 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"In conclusion, setting appropriate [state limits](https://nasa.github.io/progpy/prog_models_guide.html#state-limits) is crucial in creating realistic and accurate state-transition models. It ensures that the model's behavior stays within the constraints of the physical system. The limits should be set based on the physical or practical constraints of the system being modeled. "
"In conclusion, setting appropriate [state limits](https://nasa.github.io/progpy/prog_models_guide.html#state-limits) is crucial in creating realistic and accurate state-transition models. It ensures that the model's behavior stays within the constraints of the physical system. The limits should be set based on the physical or practical constraints of the system being modeled. \n",
"\n",
"As a final note, state limits are especially important for state estimation (to be discussed in the State Estimation section), as it will force the state estimator to only consider states that are possible or feasible. State estimation will be described in more detail in section 08. State Estimation. "
]
},
{
Expand Down
30 changes: 30 additions & 0 deletions examples/05_Data_Driven.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Using Data-Driven Models"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.11.0 64-bit",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.11.0"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Loading

0 comments on commit 9ba7937

Please sign in to comment.