Skip to content

Commit fa067e8

Browse files
michaelosthegeaseyboldt
authored andcommitted
Remove Theano support in favor of PyTensor
1 parent d9a3b2c commit fa067e8

File tree

11 files changed

+85
-91
lines changed

11 files changed

+85
-91
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
python-version: ${{ matrix.python-version }}
2929
- name: Install Dependences
3030
run: |
31-
conda install --yes conda-build conda-verify pytest pytest-cov hypothesis statsmodels aesara c-compiler
31+
conda install --yes conda-build conda-verify pytest pytest-cov hypothesis statsmodels pytensor c-compiler
3232
- name: Build package
3333
run: |
3434
conda build --variants "{python: [${{ matrix.python-version }}]}" ./sunode/conda

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ functions using symbolic differentiation and common subexpression elimination.
1313
In either case the functions are compiled using numba and the resulting
1414
C-function is passed to sunode, so that there is no python overhead.
1515

16-
`sunode` comes with an Aesara wrapper so that parameters of an ODE can be estimated
16+
`sunode` comes with an PyTensor wrapper so that parameters of an ODE can be estimated
1717
using PyMC.
1818

1919
### Installation
@@ -134,7 +134,7 @@ We'll use some time artificial data:
134134
```python
135135
import numpy as np
136136
import sunode
137-
import sunode.wrappers.as_aesara
137+
import sunode.wrappers.as_pytensor
138138
import pymc as pm
139139

140140
times = np.arange(1900,1921,1)
@@ -186,17 +186,17 @@ with pm.Model() as model:
186186
gamma = pm.Deterministic('gamma', freq / speed_ratio / ratio)
187187
delta = pm.Deterministic('delta', freq / speed_ratio / fixed_hares / ratio)
188188

189-
y_hat, _, problem, solver, _, _ = sunode.wrappers.as_aesara.solve_ivp(
189+
y_hat, _, problem, solver, _, _ = sunode.wrappers.as_pytensor.solve_ivp(
190190
y0={
191191
# The initial conditions of the ode. Each variable
192-
# needs to specify a theano or numpy variable and a shape.
192+
# needs to specify a PyTensor or numpy variable and a shape.
193193
# This dict can be nested.
194194
'hares': (hares_start, ()),
195195
'lynx': (lynx_start, ()),
196196
},
197197
params={
198198
# Each parameter of the ode. sunode will only compute derivatives
199-
# with respect to theano variables. The shape needs to be specified
199+
# with respect to PyTensor variables. The shape needs to be specified
200200
# as well. It it infered automatically for numpy variables.
201201
# This dict can be nested.
202202
'alpha': (alpha, ()),

conda/meta.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ test:
4242
- pytest
4343
- hypothesis
4444
- statsmodels
45-
- aesara
45+
- pytensor
4646
commands:
4747
- pytest --pyargs sunode
4848

doc/source/quickstart_pymc.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ We'll use some time artificial data:::
4242

4343
import numpy as np
4444
import sunode
45-
import sunode.wrappers.as_aesara
45+
import sunode.wrappers.as_pytensor
4646
import pymc as pm
4747

4848
times = np.arange(1900,1921,1)
@@ -124,7 +124,7 @@ Now, we define the names, (symbolic) values and shapes of the parameters and ini
124124
We solve the ODE using the ``solve_ivp`` function from sunode::
125125

126126
with model:
127-
from sunode.wrappers.as_aesara import solve_ivp
127+
from sunode.wrappers.as_pytensor import solve_ivp
128128
solution, *_ = solve_ivp(
129129
y0=y0,
130130
params=params,

doc/source/without_pymc.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ current number, and die when eaten by a lynx. We get:
1212
.. math::
1313
\frac{dH}{dt} = \alpha H - \beta LH \\ \frac{dL}{dt} = \delta LH - \gamma L
1414
15-
If we want to solve this ODE without the support of Aesara or PyMC, we need to
15+
If we want to solve this ODE without the support of PyTensor or PyMC, we need to
1616
first declare the parameters and states we are using. We have four parameters
1717
and two states, and each one is a scalar values, so it has shape ()::
1818

@@ -71,7 +71,7 @@ ODE might look like this::
7171
This right-hand-side function is usually only called once to collect the
7272
sympy expressions of the derivatives. Control flow within this function
7373
might behave in unexpected ways if you are new to this concept. It is the
74-
same thing as with theano, pytorch or tensorflow in graph mode. This means
74+
same thing as with PyTensor, pytorch or tensorflow in graph mode. This means
7575
that something like this will **not** work as expected::
7676

7777
value = 1

mypy.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ ignore_missing_imports = True
2020
[mypy-pandas]
2121
ignore_missing_imports = True
2222

23-
[mypy-theano]
23+
[mypy-pytensor]
2424
ignore_missing_imports = True
2525

26-
[mypy-theano.tensor]
26+
[mypy-pytensor.tensor]
2727
ignore_missing_imports = True
2828

2929
[mypy-sympy.printing.pycode]

notebooks/from_sympy.ipynb

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,18 @@
2222
"\n",
2323
"import sunode.symode.paramset\n",
2424
"import sunode.symode.problem\n",
25-
"import sunode.wrappers.as_aesara\n",
25+
"import sunode.wrappers.as_pytensor\n",
2626
"\n",
27-
"import aesara\n",
28-
"import aesara.tensor as aet"
27+
"import pytensor\n",
28+
"import pytensor.tensor as pt"
2929
]
3030
},
3131
{
32+
"attachments": {},
3233
"cell_type": "markdown",
3334
"metadata": {},
3435
"source": [
35-
"### Define ode using sympy and aesara"
36+
"### Define ODE using sympy and Pytensor"
3637
]
3738
},
3839
{
@@ -41,8 +42,8 @@
4142
"metadata": {},
4243
"outputs": [],
4344
"source": [
44-
"b = aet.dvector('b')\n",
45-
"d = aet.dvector('d')\n",
45+
"b = pt.dvector('b')\n",
46+
"d = pt.dvector('d')\n",
4647
"\n",
4748
"def rhs(t, y, params):\n",
4849
" return {\n",
@@ -53,7 +54,7 @@
5354
" }\n",
5455
"\n",
5556
"\n",
56-
"solution, problem, solver = sunode.wrappers.as_aesara.solve_ivp(\n",
57+
"solution, problem, solver = sunode.wrappers.as_pytensor.solve_ivp(\n",
5758
" t0=0,\n",
5859
" y0={\n",
5960
" 'a': (np.arange(3, dtype=float) + d[0] ** 2, 3),\n",
@@ -185,7 +186,7 @@
185186
"outputs": [],
186187
"source": [
187188
"val = (solution ** 2).sum()\n",
188-
"grads = aet.grad(val, [b, d])"
189+
"grads = pt.grad(val, [b, d])"
189190
]
190191
},
191192
{
@@ -194,8 +195,8 @@
194195
"metadata": {},
195196
"outputs": [],
196197
"source": [
197-
"func = aesara.function([b, d], [val] + grads)\n",
198-
"func2 = aesara.function([b, d], [val])"
198+
"func = pytensor.function([b, d], [val] + grads)\n",
199+
"func2 = pytensor.function([b, d], [val])"
199200
]
200201
},
201202
{
@@ -661,10 +662,11 @@
661662
]
662663
},
663664
{
665+
"attachments": {},
664666
"cell_type": "markdown",
665667
"metadata": {},
666668
"source": [
667-
"### Integrate into aesara and check gradients"
669+
"### Integrate into PyTensor and check gradients"
668670
]
669671
},
670672
{
@@ -682,23 +684,23 @@
682684
"metadata": {},
683685
"outputs": [],
684686
"source": [
685-
"params = aet.dvector('params')\n",
686-
"y0 = aet.dvector('y0')\n",
687-
"solve_ode = sunode.wrappers.as_aesara.SolveODEAdjoint(solver, 0, tvals)\n",
687+
"params = pt.dvector('params')\n",
688+
"y0 = pt.dvector('y0')\n",
689+
"solve_ode = sunode.wrappers.as_pytensor.SolveODEAdjoint(solver, 0, tvals)\n",
688690
"solution = solve_ode(y0, params)\n",
689691
"\n",
690692
"loss = (solution ** 2).sum()\n",
691-
"grad_p, grad_y0 = aet.grad(loss, [params, y0])\n",
692-
"func = aesara.function([y0, params], [loss, grad_p, grad_y0])\n",
693+
"grad_p, grad_y0 = pt.grad(loss, [params, y0])\n",
694+
"func = pytensor.function([y0, params], [loss, grad_p, grad_y0])\n",
693695
"\n",
694696
"# Explicit solution\n",
695697
"loss = ((\n",
696698
" ((0.5 * tvals ** 2 * params[1] + tvals * y0[1]) + y0[0]) ** 2\n",
697699
" + (tvals * params[1] + y0[1]) ** 2\n",
698700
")).sum()\n",
699-
"grad_p, grad_y0 = aet.grad(loss, [params, y0])\n",
701+
"grad_p, grad_y0 = pt.grad(loss, [params, y0])\n",
700702
"\n",
701-
"func2 = aesara.function([y0, params], [loss, grad_p, grad_y0])"
703+
"func2 = pytensor.function([y0, params], [loss, grad_p, grad_y0])"
702704
]
703705
},
704706
{
@@ -842,7 +844,7 @@
842844
" params = pm.Normal('params', sigma=10, shape=ode.n_params)\n",
843845
" y0 = pm.Normal('y0', shape=ode.n_states)\n",
844846
" \n",
845-
" solve_ode = sunode.wrappers.as_aesara.SolveODEAdjoint(solver, 0, tvals)\n",
847+
" solve_ode = sunode.wrappers.as_pytensor.SolveODEAdjoint(solver, 0, tvals)\n",
846848
" mu = solve_ode(y0, params)\n",
847849
" error = 0.8 * np.random.randn(len(tvals))\n",
848850
" pm.Normal('y', mu=mu[:, 0], sigma=0.8, observed=tvals ** 2 + tvals + 5 + error)\n",

notebooks/pymc_model.ipynb

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"outputs": [],
2626
"source": [
2727
"import os\n",
28-
"os.environ[\"AESARA_FLAGS\"] = \"floatX=float64\""
28+
"os.environ[\"pytensor_FLAGS\"] = \"floatX=float64\""
2929
]
3030
},
3131
{
@@ -48,8 +48,8 @@
4848
"from scipy.integrate import odeint\n",
4949
"\n",
5050
"import pymc as pm\n",
51-
"import aesara\n",
52-
"import aesara.tensor as aet\n",
51+
"import pytensor\n",
52+
"import pytensor.tensor as pt\n",
5353
"\n",
5454
"# this notebook show DEBUG log messages\n",
5555
"# logging.getLogger('pymc').setLevel(logging.DEBUG)\n",
@@ -150,9 +150,9 @@
150150
"outputs": [],
151151
"source": [
152152
"# To demonstrate that test-value computation works, but also for debugging\n",
153-
"aesara.config.compute_test_value = 'raise'\n",
154-
"aesara.config.exception_verbosity = 'high'\n",
155-
"aesara.config.traceback.limit = 5"
153+
"pytensor.config.compute_test_value = 'raise'\n",
154+
"pytensor.config.exception_verbosity = 'high'\n",
155+
"pytensor.config.traceback.limit = 5"
156156
]
157157
},
158158
{
@@ -176,7 +176,7 @@
176176
"metadata": {},
177177
"outputs": [],
178178
"source": [
179-
"import sunode.wrappers.as_aesara\n",
179+
"import sunode.wrappers.as_pytensor\n",
180180
"\n",
181181
"\n",
182182
"def get_model_sunode():\n",
@@ -187,7 +187,7 @@
187187
" s0 = pm.Normal('red_0', mu=10, sigma=2)\n",
188188
" extra = pm.Normal('extra', shape=n_extra)\n",
189189
"\n",
190-
" y_hat, _, _ = sunode.wrappers.as_aesara.solve_ivp(\n",
190+
" y_hat, _, _ = sunode.wrappers.as_pytensor.solve_ivp(\n",
191191
" y0={\n",
192192
" 'S': (s0, ()), # TODO Infer shape from model?\n",
193193
" 'P': np.array(y0_true[1], dtype='d'),\n",
@@ -196,7 +196,7 @@
196196
" params={\n",
197197
" 'K_S': (K_S, ()),\n",
198198
" 'vmax': (vmax, ()),\n",
199-
" 'tmp': np.zeros(1), # TODO aesara wants at least one fixed param\n",
199+
" 'tmp': np.zeros(1), # TODO pytensor wants at least one fixed param\n",
200200
" 'extra_p': (extra, (n_extra,))\n",
201201
" },\n",
202202
" rhs=reaction_sympy,\n",
@@ -235,7 +235,7 @@
235235
" \n",
236236
" extra = pm.Normal('extra', shape=n_extra)\n",
237237
"\n",
238-
" y_hat, problem, _ = sunode.wrappers.as_aesara.solve_ivp(\n",
238+
" y_hat, problem, _ = sunode.wrappers.as_pytensor.solve_ivp(\n",
239239
" y0={\n",
240240
" 'S': (s0, ()), # TODO Infer shape from model?\n",
241241
" 'P': np.array(y0_true[1], dtype='d'),\n",
@@ -244,7 +244,7 @@
244244
" params={\n",
245245
" 'K_S': (K_S, ()),\n",
246246
" 'vmax': (vmax, ()),\n",
247-
" 'tmp': np.zeros(1), # TODO aesara wants at least one fixed param\n",
247+
" 'tmp': np.zeros(1), # TODO pytensor wants at least one fixed param\n",
248248
" 'extra_p': (extra, (n_extra,))\n",
249249
" },\n",
250250
" rhs=reaction_sympy,\n",
@@ -305,22 +305,22 @@
305305
" \n",
306306
" # create a test function for evaluating the logp value\n",
307307
" print('Compiling f_logpt')\n",
308-
" f_logpt = aesara.function(\n",
308+
" f_logpt = pytensor.function(\n",
309309
" inputs=t_inputs,\n",
310310
" outputs=[pmodel.logpt],\n",
311311
" # with float32, allow downcast because the forward integration is always float64\n",
312-
" allow_input_downcast=(aesara.config.floatX == 'float32')\n",
312+
" allow_input_downcast=(pytensor.config.floatX == 'float32')\n",
313313
" )\n",
314314
" print(f'Test logpt:')\n",
315315
" print(f_logpt(*test_inputs))\n",
316316
" \n",
317317
" # and another test function for evaluating the gradient\n",
318318
" print('Compiling f_logpt')\n",
319-
" f_grad = aesara.function(\n",
319+
" f_grad = pytensor.function(\n",
320320
" inputs=t_inputs,\n",
321-
" outputs=aet.grad(pmodel.logpt, t_inputs),\n",
321+
" outputs=pt.grad(pmodel.logpt, t_inputs),\n",
322322
" # with float32, allow downcast because the forward integration is always float64\n",
323-
" allow_input_downcast=(aesara.config.floatX == 'float32')\n",
323+
" allow_input_downcast=(pytensor.config.floatX == 'float32')\n",
324324
" )\n",
325325
" print(f'Test gradient:')\n",
326326
" print(f_grad(*test_inputs))\n",
@@ -984,7 +984,7 @@
984984
"metadata": {},
985985
"outputs": [],
986986
"source": [
987-
"aesara.printing.pydotprint(aet.grad(model_sunode.logpt, model_sunode.vmax), 'ODE_API_shapes_and_benchmarking.png')\n",
987+
"pytensor.printing.pydotprint(pt.grad(model_sunode.logpt, model_sunode.vmax), 'ODE_API_shapes_and_benchmarking.png')\n",
988988
"IPython.display.Image('ODE_API_shapes_and_benchmarking.png')"
989989
]
990990
},
@@ -1007,7 +1007,7 @@
10071007
"metadata": {},
10081008
"outputs": [],
10091009
"source": [
1010-
"from aesara import d3viz\n",
1010+
"from pytensor import d3viz\n",
10111011
"d3viz.d3viz(model.logpt, 'ODE_API_shapes_and_benchmarking.html')"
10121012
]
10131013
},
@@ -1041,4 +1041,3 @@
10411041
"nbformat": 4,
10421042
"nbformat_minor": 4
10431043
}
1044-

sunode/test_aesara.py renamed to sunode/test_pytensor.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import numpy as np
2-
import aesara
3-
import aesara.tensor as aet
2+
import pytensor
3+
import pytensor.tensor as pt
44

55
import sunode.wrappers
66

@@ -12,7 +12,7 @@ def dydt_dict(t, y, p):
1212
'B': y.B,
1313
'C': y.C,
1414
}
15-
A = aet.dscalar("A")
15+
A = pt.dscalar("A")
1616
A.tag.test_value = np.array(0.9)
1717

1818

@@ -30,7 +30,7 @@ def dydt_dict(t, y, p):
3030
'extra': np.array([0.])
3131
}
3232

33-
solution, *_ = sunode.wrappers.as_aesara.solve_ivp(
33+
solution, *_ = sunode.wrappers.as_pytensor.solve_ivp(
3434
y0=y0,
3535
params=params,
3636
rhs=dydt_dict,
@@ -40,5 +40,5 @@ def dydt_dict(t, y, p):
4040
solver_kwargs=dict(sens_mode="simultaneous")
4141
)
4242

43-
func = aesara.function([A], [solution["A"], solution["B"]])
43+
func = pytensor.function([A], [solution["A"], solution["B"]])
4444
assert func(0.2)[0].shape == time.shape

sunode/wrappers/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from . import as_aesara
2-
from . import as_aesara as as_theano
1+
from . import as_pytensor
2+
from . import as_pytensor as as_aesara
33

4-
__all__ = ['as_theano', 'as_aesara']
4+
__all__ = ['as_aesara', 'as_pytensor']

0 commit comments

Comments
 (0)