Skip to content

Commit

Permalink
Merge pull request #82 from jxx123/fix_warning
Browse files Browse the repository at this point in the history
Fix Random init bg does not work issue
  • Loading branch information
jxx123 authored May 17, 2024
2 parents 72ef682 + 4032e30 commit d4aff7a
Show file tree
Hide file tree
Showing 5 changed files with 938 additions and 933 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name="simglucose",
version="0.2.9",
version="0.2.10",
description="A Type-1 Diabetes Simulator as a Reinforcement Learning Environment in OpenAI gym or rllab (python implementation of UVa/Padova Simulator)",
url="https://github.com/jxx123/simglucose",
author="Jinyu Xie",
Expand Down
99 changes: 52 additions & 47 deletions simglucose/patient/t1dpatient.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,28 @@

logger = logging.getLogger(__name__)

Action = namedtuple("patient_action", ['CHO', 'insulin'])
Observation = namedtuple("observation", ['Gsub'])
Action = namedtuple("patient_action", ["CHO", "insulin"])
Observation = namedtuple("observation", ["Gsub"])

PATIENT_PARA_FILE = pkg_resources.resource_filename(
'simglucose', 'params/vpatient_params.csv')
"simglucose", "params/vpatient_params.csv"
)


class T1DPatient(Patient):
SAMPLE_TIME = 1 # min
EAT_RATE = 5 # g/min CHO

def __init__(self,
params,
init_state=None,
random_init_bg=False,
seed=None,
t0=0):
'''
def __init__(self, params, init_state=None, random_init_bg=False, seed=None, t0=0):
"""
T1DPatient constructor.
Inputs:
- params: a pandas sequence
- init_state: customized initial state.
If not specified, load the default initial state in
params.iloc[2:15]
- t0: simulation start time, it is 0 by default
'''
"""
self._params = params
self._init_state = init_state
self.random_init_bg = random_init_bg
Expand All @@ -43,26 +39,26 @@ def __init__(self,

@classmethod
def withID(cls, patient_id, **kwargs):
'''
"""
Construct patient by patient_id
id are integers from 1 to 30.
1 - 10: adolescent#001 - adolescent#010
11 - 20: adult#001 - adult#001
21 - 30: child#001 - child#010
'''
"""
patient_params = pd.read_csv(PATIENT_PARA_FILE)
params = patient_params.iloc[patient_id - 1, :]
return cls(params, **kwargs)

@classmethod
def withName(cls, name, **kwargs):
'''
"""
Construct patient by name.
Names can be
adolescent#001 - adolescent#010
adult#001 - adult#001
child#001 - child#010
'''
"""
patient_params = pd.read_csv(PATIENT_PARA_FILE)
params = patient_params.loc[patient_params.Name == name].squeeze()
return cls(params, **kwargs)
Expand All @@ -86,33 +82,33 @@ def step(self, action):

# Detect eating or not and update last digestion amount
if action.CHO > 0 and self._last_action.CHO <= 0:
logger.info('t = {}, patient starts eating ...'.format(self.t))
logger.info("t = {}, patient starts eating ...".format(self.t))
self._last_Qsto = self.state[0] + self.state[1] # unit: mg
self._last_foodtaken = 0 # unit: g
self.is_eating = True

if to_eat > 0:
logger.debug('t = {}, patient eats {} g'.format(
self.t, action.CHO))
logger.debug("t = {}, patient eats {} g".format(self.t, action.CHO))

if self.is_eating:
self._last_foodtaken += action.CHO # g

# Detect eating ended
if action.CHO <= 0 and self._last_action.CHO > 0:
logger.info('t = {}, Patient finishes eating!'.format(self.t))
logger.info("t = {}, Patient finishes eating!".format(self.t))
self.is_eating = False

# Update last input
self._last_action = action

# ODE solver
self._odesolver.set_f_params(action, self._params, self._last_Qsto,
self._last_foodtaken)
self._odesolver.set_f_params(
action, self._params, self._last_Qsto, self._last_foodtaken
)
if self._odesolver.successful():
self._odesolver.integrate(self._odesolver.t + self.sample_time)
else:
logger.error('ODE solver failed!!')
logger.error("ODE solver failed!!")
raise

@staticmethod
Expand All @@ -136,8 +132,10 @@ def model(t, x, action, params, last_Qsto, last_foodtaken):
aa = 5 / (2 * Dbar * (1 - params.b))
cc = 5 / (2 * Dbar * params.d)
kgut = params.kmin + (params.kmax - params.kmin) / 2 * (
np.tanh(aa * (qsto - params.b * Dbar)) -
np.tanh(cc * (qsto - params.d * Dbar)) + 2)
np.tanh(aa * (qsto - params.b * Dbar))
- np.tanh(cc * (qsto - params.d * Dbar))
+ 2
)
else:
kgut = params.kmax

Expand All @@ -162,8 +160,7 @@ def model(t, x, action, params, last_Qsto, last_foodtaken):

# glucose kinetics
# plus dextrose IV injection input u[2] if needed
dxdt[3] = max(EGPt, 0) + Rat - Uiit - Et - \
params.k1 * x[3] + params.k2 * x[4]
dxdt[3] = max(EGPt, 0) + Rat - Uiit - Et - params.k1 * x[3] + params.k2 * x[4]
dxdt[3] = (x[3] >= 0) * dxdt[3]

Vmt = params.Vm0 + params.Vmx * x[6]
Expand All @@ -173,8 +170,12 @@ def model(t, x, action, params, last_Qsto, last_foodtaken):
dxdt[4] = (x[4] >= 0) * dxdt[4]

# insulin kinetics
dxdt[5] = -(params.m2 + params.m4) * x[5] + params.m1 * x[9] + params.ka1 * \
x[10] + params.ka2 * x[11] # plus insulin IV injection u[3] if needed
dxdt[5] = (
-(params.m2 + params.m4) * x[5]
+ params.m1 * x[9]
+ params.ka1 * x[10]
+ params.ka2 * x[11]
) # plus insulin IV injection u[3] if needed
It = x[5] / params.Vi
dxdt[5] = (x[5] >= 0) * dxdt[5]

Expand All @@ -198,34 +199,33 @@ def model(t, x, action, params, last_Qsto, last_foodtaken):
dxdt[11] = (x[11] >= 0) * dxdt[11]

# subcutaneous glucose
dxdt[12] = (-params.ksc * x[12] + params.ksc * x[3])
dxdt[12] = -params.ksc * x[12] + params.ksc * x[3]
dxdt[12] = (x[12] >= 0) * dxdt[12]

if action.insulin > basal:
logger.debug('t = {}, injecting insulin: {}'.format(
t, action.insulin))
logger.debug("t = {}, injecting insulin: {}".format(t, action.insulin))

return dxdt

@property
def observation(self):
'''
"""
return the observation from patient
for now, only the subcutaneous glucose level is returned
TODO: add heart rate as an observation
'''
"""
GM = self.state[12] # subcutaneous glucose (mg/kg)
Gsub = GM / self._params.Vg
observation = Observation(Gsub=Gsub)
return observation

def _announce_meal(self, meal):
'''
"""
patient announces meal.
The announced meal will be added to self.planned_meal
The meal is consumed in self.EAT_RATE
The function will return the amount to eat at current time
'''
"""
self.planned_meal += meal
if self.planned_meal > 0:
to_eat = min(self.EAT_RATE, self.planned_meal)
Expand All @@ -245,9 +245,9 @@ def seed(self, seed):
self.reset()

def reset(self):
'''
"""
Reset the patient state to default intial state
'''
"""
if self._init_state is None:
self.init_state = self._params.iloc[2:15]
else:
Expand All @@ -257,13 +257,17 @@ def reset(self):
if self.random_init_bg:
# Only randomize glucose related states, x4, x5, and x13
mean = [
1.0 * self.init_state[3], 1.0 * self.init_state[4],
1.0 * self.init_state[12]
1.0 * self.init_state[3],
1.0 * self.init_state[4],
1.0 * self.init_state[12],
]
cov = np.diag([
0.1 * self.init_state[3], 0.1 * self.init_state[4],
0.1 * self.init_state[12]
])
cov = np.diag(
[
0.1 * self.init_state[3],
0.1 * self.init_state[4],
0.1 * self.init_state[12],
]
)
bg_init = self.random_state.multivariate_normal(mean, cov)
self.init_state[3] = 1.0 * bg_init[0]
self.init_state[4] = 1.0 * bg_init[1]
Expand All @@ -273,28 +277,28 @@ def reset(self):
self._last_foodtaken = 0
self.name = self._params.Name

self._odesolver = ode(self.model).set_integrator('dopri5')
self._odesolver = ode(self.model).set_integrator("dopri5")
self._odesolver.set_initial_value(self.init_state, self.t0)

self._last_action = Action(CHO=0, insulin=0)
self.is_eating = False
self.planned_meal = 0


if __name__ == '__main__':
if __name__ == "__main__":
logger.setLevel(logging.INFO)
# create console handler and set level to debug
ch = logging.StreamHandler()
# ch.setLevel(logging.DEBUG)
ch.setLevel(logging.INFO)
# create formatter
formatter = logging.Formatter('%(name)s: %(levelname)s: %(message)s')
formatter = logging.Formatter("%(name)s: %(levelname)s: %(message)s")
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)

p = T1DPatient.withName('adolescent#001')
p = T1DPatient.withName("adolescent#001")
basal = p._params.u2ss * p._params.BW / 6000 # U/min
t = []
CHO = []
Expand All @@ -316,6 +320,7 @@ def reset(self):
p.step(act)

import matplotlib.pyplot as plt

fig, ax = plt.subplots(3, sharex=True)
ax[0].plot(t, BG)
ax[1].plot(t, CHO)
Expand Down
Loading

0 comments on commit d4aff7a

Please sign in to comment.