Insulin pump
One of the most powerful features of Genius is that agents can learn to make decisions under uncertainty. In this example, we will use the Genius SDK to build an agent that acts as an insulin pump for users with Type I diabetes. This will showcase a Genius agent's ability to perform continual parameter learning.
Whenever foods containing carbohydrates are consumed, the glucose molecules in the food will be released into the bloodstream as a source of energy for the body. Individuals with Type I diabetes cannot regulate glucose levels in their bloodstream which may become dangerously high and cause numerous medical complications. The administration of the hormone insulin allows these glucose levels to return to normal levels within the bloodstream.
According to The Cleveland Clinic an insulin pump is
A wearable medical device that supplies a continuous flow of rapid-acting insulin underneath your skin. Most pumps are small, computerized devices that are roughly the size of a juice box or a deck of cards. Insulin pumps are an alternative to multiple daily injection (MDI) insulin therapy (syringe or pen injections) for people with diabetes who require insulin to manage the condition.
We can simulate diabetic users with the OpenAI gym environment called SimGlucose which is based on the FDA-approved UVa/Padova Simulator (2008 version). It models 30 virtual patients (10 adolescents, 10 adults, and 10 children) who consume meals at random times, causing their blood glucose levels to rise. Besides the patient's age, our agent will only be able to observe the user's CGM reading, which is a noisy signal of their blood glucose levels. Minute-by-minute, the agent must administer the right level of basal insulin to keep the user's blood glucose levels in a healthy range.
For Type I diabetes in general, the healthy range for blood glucose levels is around 70-80 mg/dL. "Time in range" (TIR) is the percentage of time that the blood glucose level is in the healthy range, and the general target for insulin pumps is 70% TIR. For children, who have a higher risk of hypoglycemia, 70% TIR is quite challenging, and the main goal is to avoid hypoglycemia. In our example, we will only use the "adult" and "adolescent" patients.
Building active inference models for real-world example is more complex and challenging than other toy examples like agent navigation and the multi-armed bandit. As you will see below, there is a significant amount of extra overhead that we need to make the model performant. The model we will be building has the following form when represented as a factor graph:

Each variable and factor in this model will be explained in more detail in the model variables and model factors sections below.
The model file associated with this example is available below:
Imports
In order to build the insulin pump model we will need to allow our Genius agent to interface with the SimGlucose gym environment. The following imports pull in the components we need to create our Genius agent/model with the SDK and build the SimGlucose environment. We will also import matplotlib so that we can create plotting tools to visualize the results.
For reproducibility, please match the following versions:
Building the simulation environment
Here we set up the ModernGlucoseEnv class which specifies the environment. This environment represents a simulated adult or adolescent patient.
POMDP model helper functions
In order to build the POMDP model we need some helper functions to initialize the POMDP factors and a binning function which chunks continuous observations into discrete elements.
Now we have all the pieces we need to build the POMDP Model. In preparation for the next few steps, where we will build the model, let's initialize the POMDPModel class.
Model variables
Actions
The POMDP has a single action variable: basal_insulin which corresponds to the insulin dose to be administered by the insulin pump. For this variable we will restrict the range of the insulin dose to a small range of values commonly used in insulin pumps. The insulin dose will be administered over the range of 0 to 0.06 units per minute which specify the discrete categories for this variable.
POMDP models require that transition factors depend on either another latent state or an action. For this reason, we will also include a dummy action variable called constant which fulfills the action dependency requirement for the transition factor but otherwise has no effect on the model itself.
The following code adds the action variables to the POMDP model.
Observations
There are two observations in this model which are available to the insulin pump Genius agent in 1-minute timestep intervals.
CGM- The continuous glucose monitoring (CGM) reading of the glucose levels. Glucose levels will change over the course of the simulation so that the insulin pump agent can administer the correct insulin levels to keep glucose in the correct range. Since CGM is a continuous variable its values will need to be binned. After binning, the categories range from 40-600 mg/dL at roughly 20 mg/dL increments.patient_age- The age of the patient. This variable will remain constant throughout the simulation. There are two categories: "adult" or "adolescent".
The following code adds the observation variables to the POMDP model.
States
There are three hidden (latent) states of interest in this model:
glucose_utilization- The amount of glucose the patient's body consumes over time. This matters because glucose needs for children and adults are different and it is important that we consider how we expect the levels to change given the type of person we are modeling. There are few ordinal categories for glucose utilization ranging from 0 to 4.uncontrolled_glucose_dynamics- The change in glucose levels in the body over time. By expressing this hidden state variable in our model we can infer how glucose levels will change over time. This change is represented in discrete categories ranging from 0 to 14.latent_patient_age- The patient's age. Even though we directly observe the patient's age we need to include it in the model because it has an effect on glucose dynamics. There are two categories: "adult" or "adolescent".
The following code adds the hidden state variables to the POMDP model.
Model factors
Preferences
The POMDP will have a preference that the CGM stays within the ideal range of 70-180 mg/dL. The figure below shows this relationship.

Here we see the individual discrete glucose measurements and the assigned preference level where 0 would be considered a neutral preference (lines added between discrete points to help make the trend easier to see). We focus in this model primarily on preventing hypoglycemia which occurs at dangerously low blood sugar levels.
The following code adds the preference factor to the POMDP model.
Transitions
The transition factors in this model specify how the hidden state variables in our model change over time.
Glucose utilization - This transition models how the
glucose_utilizationhidden state variable changes over time. It is initialized as a discrete Gausian distribution for each column with a unit standard deviation.Uncontrolled glucose dynamics - This transition models how the
uncontrolled glucose dynamicshidden state variable changes over time. It is initialized randomly (while ensuring columns sum to one).Patient age - This transition models how the
patient_agehidden state variable changes over time. It is initialized as an identity matrix.
The following code adds the transition factors to the POMDP model.
Likelihoods
The likelihood factors represent how likely an observation (data) is given the hidden states that influence it. We will initialize the likelihoods with random values which will be learned over the course of the simulation using the available data.
CGM - This likelihood models the relationship between
CGMobservation and theglucose_utilization,uncontrolled_glucose_dynamics, andlatent_patient_agehidden states. It is initialized randomly (while ensuring columns sum to one).Patient age - This likelihood models the relationship between the
patient_ageobservation and theglucose_utilization,uncontrolled_glucose_dynamics, andlatent_patient_agehidden states. It is initialized randomly (while ensuring columns sum to one).
The following code adds the likelihood factors to the POMDP model.
Loading the model to the Genius agent
Now we can load this model we created into a Genius agent. Please be sure to set the correct URL to your agent and API key.
Inference with continual learning
It's time to put our model to the test. We'll run through an episode with each patient, and see how well our model improves over time. First, we set some initial parameters of the simulation.
MAX_EPISODE_LENGTH- Corresponding to eight hours, this is the length of time over which the insulin pump will be active in the simulation.SOJOURN_TIME- This is the total time the agent spends in a particular state.POLICY_LEN- This is the length of the policy the agent will consider. Since the policy length is set at 1, the agent will only lookahead one action into the future.
By default we will learn every 100 episode steps. In general, it is good practice to avoid learning at every time step. Rather, active inference models perform better when learning occurs after some amount of time steps have passed. In this case, learning occurs at 20% intervals of the total episode length.
We will use two metrics of success:
Survival: How many episodes can the agent get through without
riskreaching 100?Time in range (TIR): What percentage of the time does the agent spend in the CGM range of 100-180 mg/dL
We should expect both metrics to improve as the agent learns over time. Additionally, the likelihoods will be learned over time continuously from the observed data.
Everything needed to actually run the agent is inside the section with the comment ACTION LOOP. The remainder of the code shown below is simply for plotting purposes and tracking statistics.
Finally, we can print the summary of the simulation:
Interpreting the results
According to our simulation summary:
In total we ran 18 episodes or trials with a total of 8756 steps over all episodes. Agents survived for an average 486.44/500 steps with an overall survival score of 97.29%. Finally, the optimality score, specifying the time the simulated patient stayed in range, was 38.07%. Let's examine one of the results for more details:

The top panel shows the glucose levels as a function of time steps. Here we have overlaid the continuous glucose signal (green) over the discretized version of the signal (orange) that we fed to the model as observations. The dotted red lines indicate when a meal was eaten. As expected, when meals are eaten, there is a spike in blood sugar. It is this spike that the insulin pump needs to correct to bring glucose down to the appropriate range.
The middle panel shows the insulin released by the pump as a consequence of active inference. We see that the amount of insulin released increases to saturation after each meal which causes glucose levels to eventually decrease into acceptable ranges.
The bottom panel shows the percentage of time across the entire simulation that blood glucose remains in the correct range.
Last updated