Skip to content

Commit 54f635b

Browse files
authored
Notebook examples (#66)
Add Colab example
1 parent 9b863a0 commit 54f635b

13 files changed

Lines changed: 756 additions & 5 deletions

File tree

‎.mkdocs/theme/breadcrumbs.html‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@
1212
<li class="breadcrumb-item active">{{ page.title }}</li>
1313
{%- endif %}
1414
<li class="wy-breadcrumbs-aside">
15+
<a
16+
href="https://colab.research.google.com/github/PyNumLab/prik/blob/main/examples/notebooks/quickstart.ipynb"
17+
class="prik-notebook-link"
18+
target="_blank"
19+
rel="noopener noreferrer"
20+
title="Run the PRIK quickstart notebook in Google Colab"
21+
aria-label="Run the PRIK quickstart notebook in Google Colab in a new tab"
22+
>
23+
<span aria-hidden="true">&#9654;</span>
24+
<span>Run it in Colab</span>
25+
</a>
1526
<a
1627
href="{{ config.repo_url }}"
1728
class="prik-repository-link"

‎CHANGELOG.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ release tags add a leading `v` to the package version.
77

88
## Unreleased
99

10+
- The `jupyter` extra now accepts IPython 7.0 and newer instead of requiring
11+
8.0. The cell magics use only long-stable IPython APIs, and the higher floor
12+
made `pip install prik[jupyter]` upgrade the IPython that hosted notebook
13+
environments ship, which forced a runtime restart for no benefit. The `qa`
14+
extra installs `prik[jupyter]` rather than repeating that requirement, so
15+
the supported IPython range is stated once.
16+
17+
- Added a runnable `examples/notebooks/quickstart.ipynb` and its guided
18+
tutorial, covering a Fortran cell, a C cell, and reshaping the generated API
19+
by editing its semantic contract in the same session. The home page, Getting
20+
Started, the tutorial, and the README offer it as a Colab run or a direct
21+
download, so the documented workflow can be tried before installing anything.
22+
1023
- Generated contracts now represent a one-level primitive C pointer as
1124
runtime-rank `T[...]` NumPy storage instead of choosing a scalar temporary.
1225
It accepts ranks 0 through 15 with any strides, so a Fortran-ordered array

‎README.md‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ Fortran and C code.
1212
[![codecov](https://codecov.io/gh/PyNumLab/prik/graph/badge.svg?token=QZRRCS5YO6)](https://codecov.io/gh/PyNumLab/prik)
1313
[![DOI](https://zenodo.org/badge/1241799694.svg)](https://doi.org/10.5281/zenodo.21881987)
1414

15+
**Try it without installing anything.** The quickstart notebook compiles a
16+
Fortran cell and a C cell, then reshapes the generated API by editing its
17+
`.pyi` contract.
18+
19+
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/PyNumLab/prik/blob/main/examples/notebooks/quickstart.ipynb)
20+
[Download the notebook](https://pynumlab.github.io/prik/examples/notebooks/quickstart.ipynb)
21+
1522
It preserves modules, derived types, arrays, callbacks, and native behavior
1623
while letting you reshape the resulting Python API through editable `.pyi`
1724
contracts instead of writing low-level binding code.
@@ -455,7 +462,9 @@ python3 -m pip install "prik[jupyter]"
455462

456463
Use `%%fortran` or `%%c` to compile native source in a cell. Add `--pyi` to
457464
review and edit the generated contract before compilation, or use `%%pyi` with
458-
existing native source files. See [IPython and Jupyter
465+
existing native source files. See [Run PRIK in a
466+
Notebook](https://pynumlab.github.io/prik/user/tutorials/notebook-quickstart/)
467+
for the guided version, or [IPython and Jupyter
459468
Notebooks](https://pynumlab.github.io/prik/user/guide/notebooks/) for the
460469
complete workflow.
461470

‎docs/index.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,15 @@ print(item.norm_squared()) # 20.0
272272

273273
Same Fortran source, but a more natural Python API: module procedures become methods.
274274

275+
**Want to run that loop yourself?** The quickstart notebook does exactly this —
276+
compiles a Fortran cell and a C cell, then reshapes the generated API by editing
277+
its `.pyi` contract. It needs no installation.
278+
279+
<p class="prik-notebook-actions">
280+
<a class="prik-primary-cta" href="https://colab.research.google.com/github/PyNumLab/prik/blob/main/examples/notebooks/quickstart.ipynb">▶&nbsp; Run it in Colab</a>
281+
<a class="prik-secondary-cta" href="examples/notebooks/quickstart.ipynb" download="quickstart.ipynb">⬇&nbsp; Download the notebook</a>
282+
</p>
283+
275284
## Why PRIK
276285

277286
- **Natural Python APIs:** Fortran modules become namespaces and derived types

‎docs/stylesheets/site.css‎

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,49 @@
5656
display: none;
5757
}
5858

59+
.wy-breadcrumbs-aside {
60+
display: inline-flex;
61+
align-items: center;
62+
gap: 0.4rem;
63+
}
64+
65+
.prik-notebook-link {
66+
display: inline-flex;
67+
align-items: center;
68+
gap: 0.4rem;
69+
min-height: 2.15rem;
70+
padding: 0.4rem 0.8rem;
71+
border: 1px solid var(--prik-primary);
72+
border-radius: 0.35rem;
73+
background: #fff;
74+
color: var(--prik-primary);
75+
font-size: 0.82rem;
76+
font-weight: 600;
77+
line-height: 1;
78+
text-decoration: none;
79+
white-space: nowrap;
80+
transition:
81+
background-color 120ms ease,
82+
border-color 120ms ease,
83+
color 120ms ease;
84+
}
85+
86+
.prik-notebook-link:visited {
87+
color: var(--prik-primary);
88+
}
89+
90+
.prik-notebook-link:hover,
91+
.prik-notebook-link:focus {
92+
background: var(--prik-primary-bg);
93+
border-color: var(--prik-primary-dark);
94+
color: var(--prik-primary-dark);
95+
}
96+
97+
.prik-notebook-link:focus-visible {
98+
outline: 2px solid #f5b041;
99+
outline-offset: 2px;
100+
}
101+
59102
.prik-repository-link {
60103
display: inline-flex;
61104
align-items: center;
@@ -170,6 +213,49 @@
170213
outline-offset: 2px;
171214
}
172215

216+
.prik-notebook-actions {
217+
display: flex;
218+
flex-wrap: wrap;
219+
gap: 0.75rem;
220+
margin: 1.25rem 0 1.5rem;
221+
}
222+
223+
.prik-secondary-cta {
224+
display: inline-flex;
225+
align-items: center;
226+
min-height: 2.6rem;
227+
padding: 0.65rem 1rem;
228+
border: 1px solid var(--prik-primary);
229+
border-radius: 0.35rem;
230+
background: #fff;
231+
color: var(--prik-primary);
232+
font-weight: 700;
233+
text-decoration: none;
234+
transition:
235+
background-color 120ms ease,
236+
border-color 120ms ease,
237+
box-shadow 120ms ease,
238+
transform 120ms ease;
239+
}
240+
241+
.prik-secondary-cta:visited {
242+
color: var(--prik-primary);
243+
}
244+
245+
.prik-secondary-cta:hover,
246+
.prik-secondary-cta:focus {
247+
background: var(--prik-primary-bg);
248+
border-color: var(--prik-primary-dark);
249+
box-shadow: 0 4px 9px rgb(0 0 0 / 12%);
250+
color: var(--prik-primary-dark);
251+
transform: translateY(-1px);
252+
}
253+
254+
.prik-secondary-cta:focus-visible {
255+
outline: 2px solid #f5b041;
256+
outline-offset: 2px;
257+
}
258+
173259
.prik-faq-item {
174260
max-width: 56rem;
175261
margin: 0.8rem 0;
@@ -442,7 +528,8 @@
442528

443529
@media screen and (max-width: 768px) {
444530
.wy-breadcrumbs-aside {
445-
display: block;
531+
display: flex;
532+
flex-wrap: wrap;
446533
float: none;
447534
margin-top: 0.75rem;
448535
}

‎docs/user/getting-started/index.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ Follow these pages in order:
2525
3. **[Your First Function](first-wrapped-function.md)** — Build the same scalar function from Fortran or C.
2626
4. **[Development Workflow](beginner-workflow.md)** — Repeat the edit → review → build → test loop.
2727

28+
The [quickstart notebook](https://colab.research.google.com/github/PyNumLab/prik/blob/main/examples/notebooks/quickstart.ipynb)
29+
runs this same loop in Colab, with nothing to install.
30+
2831
---
2932

3033
## What You Will Build
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
title: Run PRIK in a Notebook
3+
description: Compile Fortran and C cells and reshape the generated API without leaving the notebook
4+
audience: users
5+
prerequisites: installation, IPython and Jupyter notebooks
6+
related: ../guide/notebooks.md, ../guide/c/pointers-arrays-and-strings.md, pythonic-blas.md
7+
status: maintained
8+
publication: reviewed
9+
---
10+
11+
# Run PRIK in a Notebook
12+
13+
This tutorial compiles Fortran and C in notebook cells, calls them from Python,
14+
and then reshapes the generated API by editing its semantic contract — all in
15+
one session.
16+
17+
<p class="prik-notebook-actions">
18+
<a class="prik-primary-cta" href="https://colab.research.google.com/github/PyNumLab/prik/blob/main/examples/notebooks/quickstart.ipynb">▶&nbsp; Run it in Colab</a>
19+
<a class="prik-secondary-cta" href="../../../examples/notebooks/quickstart.ipynb" download="quickstart.ipynb">⬇&nbsp; Download the notebook</a>
20+
</p>
21+
22+
The notebook runs top to bottom and builds real extension modules, so it needs a
23+
compiler. In Colab the first cell installs one.
24+
25+
## 1. Load the extension
26+
27+
```ipython
28+
%load_ext prik.jupyter
29+
```
30+
31+
## 2. Compile a Fortran cell
32+
33+
`%%fortran` compiles the cell and publishes what it declares. A Fortran module
34+
becomes a notebook name:
35+
36+
```ipython
37+
%%fortran
38+
module geometry
39+
contains
40+
real(8) function circle_area(radius)
41+
real(8), intent(in) :: radius
42+
circle_area = 3.141592653589793d0 * radius**2
43+
end function
44+
end module
45+
```
46+
47+
```python
48+
area = geometry.circle_area(np.float64(2.0))
49+
assert np.isclose(area, np.pi * 4)
50+
print(f"✅ circle_area(2.0) = {area} (expected {np.pi * 4})")
51+
```
52+
53+
```text
54+
✅ circle_area(2.0) = 12.566370614359172 (expected 12.566370614359172)
55+
```
56+
57+
Every result the notebook claims is asserted, so a ✅ means the cell really did
58+
that rather than the page saying so.
59+
60+
## 3. Compile a C cell
61+
62+
`%%c` publishes C functions directly. This one doubles an array in place and
63+
takes the element count the way C usually does:
64+
65+
```ipython
66+
%%c
67+
#include <stddef.h>
68+
69+
void scale(size_t count, double *values) {
70+
for (size_t index = 0; index < count; ++index) {
71+
values[index] *= 2.0;
72+
}
73+
}
74+
```
75+
76+
`double *values` becomes runtime-rank storage, so it accepts a NumPy array of
77+
any rank and writes through it. The count still has to be passed by hand,
78+
though NumPy already knows it:
79+
80+
```python
81+
values = np.array([1.0, 2.0, 3.0])
82+
scale(np.uintp(values.size), values)
83+
assert np.allclose(values, [2.0, 4.0, 6.0])
84+
print(f"✅ scale(count, values) doubled in place: {values} (expected [2. 4. 6.])")
85+
```
86+
87+
```text
88+
✅ scale(count, values) doubled in place: [2. 4. 6.] (expected [2. 4. 6.])
89+
```
90+
91+
## 4. Reshape the API with a contract
92+
93+
`--pyi` compiles nothing. It keeps the source and hands back the semantic
94+
contract it derived, as an editable cell:
95+
96+
```ipython
97+
%%c --pyi
98+
#include <stddef.h>
99+
100+
void scale(size_t count, double *values) {
101+
for (size_t index = 0; index < count; ++index) {
102+
values[index] *= 2.0;
103+
}
104+
}
105+
```
106+
107+
Jupyter and Colab insert the contract below the cell you just ran:
108+
109+
```ipython
110+
%%pyi
111+
112+
# prik: source-sha256=<generated digest>
113+
114+
from prik.contracts import Float64, UInt64
115+
116+
def scale(
117+
count: UInt64,
118+
values: Float64[...]
119+
) -> None: ...
120+
```
121+
122+
Edit it so the count comes from the array. `Arg(0).size` supplies it, and
123+
`Float64[:]` pins the rank to one. Keep the `# prik:` line, then run the cell:
124+
125+
```ipython
126+
%%pyi
127+
128+
# prik: source-sha256=<generated digest>
129+
130+
from prik.contracts import Arg, Float64, native_call
131+
132+
@native_call([Arg(0).size, Arg(0)])
133+
def scale(values: Float64[:]) -> None: ...
134+
```
135+
136+
Same C code, same compiler; only the Python API changed — `count` is gone:
137+
138+
```python
139+
values = np.array([1.0, 2.0, 3.0])
140+
scale(values)
141+
assert np.allclose(values, [2.0, 4.0, 6.0])
142+
print(f"✅ scale(values) doubled in place: {values} (expected [2. 4. 6.])")
143+
```
144+
145+
```text
146+
✅ scale(values) doubled in place: [2. 4. 6.] (expected [2. 4. 6.])
147+
```
148+
149+
## Where to go next
150+
151+
- [IPython and Jupyter Notebooks](../guide/notebooks.md) covers every magic,
152+
its options, and the cell cache.
153+
- [C Pointers, Arrays, and Strings](../guide/c/pointers-arrays-and-strings.md)
154+
explains what `Float64[...]` accepts and how to narrow it.
155+
- [Design a Pythonic BLAS API](pythonic-blas.md) applies the same contract
156+
editing to a real library.

0 commit comments

Comments
 (0)