diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml
index 9e32dd22..a77b37c6 100644
--- a/.github/DISCUSSION_TEMPLATE/ideas.yml
+++ b/.github/DISCUSSION_TEMPLATE/ideas.yml
@@ -5,7 +5,7 @@ body:
attributes:
label: The idea
description: |
- A model, an interface or a direction you think TorchSim should take,
+ A model, an interface or a direction you think BlochSim should take,
before it is concrete enough to be a feature request.
validations:
required: true
diff --git a/.github/DISCUSSION_TEMPLATE/q-a.yml b/.github/DISCUSSION_TEMPLATE/q-a.yml
index 931eee4c..77687eeb 100644
--- a/.github/DISCUSSION_TEMPLATE/q-a.yml
+++ b/.github/DISCUSSION_TEMPLATE/q-a.yml
@@ -20,5 +20,5 @@ body:
- type: input
id: version
attributes:
- label: TorchSim version
+ label: BlochSim version
placeholder: "0.4.1, or the commit you are on"
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index a9c81265..8807346b 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -5,8 +5,8 @@ body:
- type: markdown
attributes:
value: |
- Questions about how to use TorchSim, and results you are unsure about,
- belong in [Discussions](https://github.com/pulserver/torchsim/discussions).
+ Questions about how to use BlochSim, and results you are unsure about,
+ belong in [Discussions](https://github.com/pulserver/blochsim/discussions).
This form is for behaviour you can pin down.
- type: textarea
@@ -47,7 +47,7 @@ body:
Paste the output of
```sh
- python -c "from importlib.metadata import version; print('torchsim', version('torchsim'))"
+ python -c "from importlib.metadata import version; print('blochsim', version('blochsim'))"
python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.get_device_name() if torch.cuda.is_available() else 'cpu')"
python -VV
```
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 2518bebe..f39206b5 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Question, or a result you are unsure about
- url: https://github.com/pulserver/torchsim/discussions
+ url: https://github.com/pulserver/blochsim/discussions
about: Ask how to model a sequence, or whether a signal you got is expected.
- name: Security report
- url: https://github.com/pulserver/torchsim/security/policy
+ url: https://github.com/pulserver/blochsim/security/policy
about: Report a vulnerability privately rather than in a public issue.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index ce057f39..ce12d8f0 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -1,5 +1,5 @@
name: Feature request
-description: A sequence, a physical effect, an estimator or an interface TorchSim should carry.
+description: A sequence, a physical effect, an estimator or an interface BlochSim should carry.
labels: ["enhancement"]
body:
- type: textarea
@@ -16,7 +16,7 @@ body:
- type: textarea
id: proposal
attributes:
- label: What you would like TorchSim to do
+ label: What you would like BlochSim to do
description: |
If you already know the shape of it -- an operator, a tissue property,
a keyword on an existing simulator -- sketch the call you would like
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 8c97eebb..62ded5fa 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -23,6 +23,6 @@ pytest tests/
- [ ] Comments and docstrings describe the code as it is now -- no
"previously", no "this replaces", no naming of a bug that is fixed.
- [ ] A change to what the kernels compute is in both of them, and arrives
- with a test that pins it against something outside TorchSim.
+ with a test that pins it against something outside BlochSim.
- [ ] The documentation is updated where the change is visible to a caller,
and `bash scripts/build_docs.sh` still builds.
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
index f28c3e4b..a3483bc1 100644
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -10,7 +10,7 @@ Older releases are not patched.
Report privately, not as a public issue:
- open a draft advisory through
- [Security -> Report a vulnerability](https://github.com/pulserver/torchsim/security/advisories/new),
+ [Security -> Report a vulnerability](https://github.com/pulserver/blochsim/security/advisories/new),
which is the preferred route; or
- email **matteo.cencini@gmail.com** if you cannot use GitHub.
@@ -22,7 +22,7 @@ publicly.
## Scope
-TorchSim executes the code you give it: a sequence layout, a cost function and
+BlochSim executes the code you give it: a sequence layout, a cost function and
a signal model are all Python that runs in your process, so a malicious
*script* is out of scope in the same way it is for NumPy or PyTorch.
@@ -36,4 +36,4 @@ merely a bug.
## Not a vulnerability
A simulation that returns wrong physics, diverges, or raises is a
-[bug report](https://github.com/pulserver/torchsim/issues/new/choose).
+[bug report](https://github.com/pulserver/blochsim/issues/new/choose).
diff --git a/.github/skills/add-a-sequence/SKILL.md b/.github/skills/add-a-sequence/SKILL.md
index f0096a5a..db4c4bca 100644
--- a/.github/skills/add-a-sequence/SKILL.md
+++ b/.github/skills/add-a-sequence/SKILL.md
@@ -1,9 +1,9 @@
---
name: add-a-sequence
-description: Write a new simulator, signal model, or sequence operator in TorchSim. Use when asked to add a sequence, a readout module, a physical effect, or a tissue property.
+description: Write a new simulator, signal model, or sequence operator in BlochSim. Use when asked to add a sequence, a readout module, a physical effect, or a tissue property.
---
-# Add a sequence to TorchSim
+# Add a sequence to BlochSim
**There is one base class, `Simulator`, written in one of two ways.** Nothing
downstream ever asks which way.
@@ -62,15 +62,15 @@ and a run reads which properties were passed to decide which terms the kernel
evaluates. Adding an effect is one line in a model's property declaration, plus
the term itself in **both** kernels.
-The shared parameter ABI is `src/torchsim/sequence/_parameters.py`, read by the
+The shared parameter ABI is `src/blochsim/sequence/_parameters.py`, read by the
Python dispatch, the C++ extension and the Triton kernels. A parameter added
there is added in all three or in none.
## What the change has to arrive with
-A test that pins the new physics against something **outside** TorchSim: a
+A test that pins the new physics against something **outside** BlochSim: a
closed form, a published figure, or an isochromat summation written out in the
-test itself. Comparing TorchSim to TorchSim proves the two agree, which was
+test itself. Comparing BlochSim to BlochSim proves the two agree, which was
never in doubt. Put it in `tests/epg/` and state the invariant in the module
docstring.
diff --git a/.github/skills/build-and-test/SKILL.md b/.github/skills/build-and-test/SKILL.md
index cbda28f9..3f851c91 100644
--- a/.github/skills/build-and-test/SKILL.md
+++ b/.github/skills/build-and-test/SKILL.md
@@ -1,9 +1,9 @@
---
name: build-and-test
-description: Compile the C++ kernels and run the TorchSim suite, including the Triton paths on a machine with no GPU. Use when asked to build, install, test, or reproduce a failure in torchsim.
+description: Compile the C++ kernels and run the BlochSim suite, including the Triton paths on a machine with no GPU. Use when asked to build, install, test, or reproduce a failure in blochsim.
---
-# Build and test TorchSim
+# Build and test BlochSim
## Install
@@ -15,7 +15,7 @@ pip install -e ".[dev]" ; echo "exit: $?"
run alone, `pip install -e ".[test]"` is enough and much smaller.
An editable install puts the Python sources on the path, so an edit under
-`src/torchsim` takes effect on the next import. The two C++ extensions are
+`src/blochsim` takes effect on the next import. The two C++ extensions are
compiled artifacts and do not: re-run the install after editing any `.cpp`.
**Read the exit status, not the output.** A failed compile leaves the
@@ -24,7 +24,7 @@ that no longer corresponds to the source. Confirm the extension is the one you
just built:
```sh
-python -c "import torchsim._epg_cpu as k; print(k.__file__)"
+python -c "import blochsim._epg_cpu as k; print(k.__file__)"
```
## Test
diff --git a/.github/skills/build-the-docs/SKILL.md b/.github/skills/build-the-docs/SKILL.md
index 3c121f7b..3cbd9589 100644
--- a/.github/skills/build-the-docs/SKILL.md
+++ b/.github/skills/build-the-docs/SKILL.md
@@ -1,26 +1,26 @@
---
name: build-the-docs
-description: Build the TorchSim documentation, add a figure to an explanation page, or add a gallery example. Use when asked about docs, Sphinx, the gallery, MyST, or Read the Docs for torchsim.
+description: Build the BlochSim documentation, add a figure to an explanation page, or add a gallery example. Use when asked about docs, Sphinx, the gallery, MyST, or Read the Docs for blochsim.
---
-# Build the TorchSim documentation
+# Build the BlochSim documentation
```sh
bash scripts/build_docs.sh # incremental
bash scripts/build_docs.sh --clean # re-execute every example
-PYTHON_BIN=~/envs/torchsim/bin/python bash scripts/build_docs.sh
+PYTHON_BIN=~/envs/blochsim/bin/python bash scripts/build_docs.sh
python -m http.server --directory docs/build/html 8000
```
-The script checks that the interpreter it is given can import TorchSim and the
+The script checks that the interpreter it is given can import BlochSim and the
documentation extensions, then builds into `docs/build/html`. Two things make
it slower than a plain Sphinx run and are the point of it: **sphinx-gallery
executes the examples**, and **the explanation pages' figures are re-rendered**
-by `docs/explanation_figures.py` with the TorchSim in your working tree.
+by `docs/explanation_figures.py` with the BlochSim in your working tree.
An example is executed when the interpreter can import everything it imports:
an environment with the `dev` or `examples` extra runs the whole gallery, one
-with `doc` alone runs what needs nothing but TorchSim. A page already carrying
+with `doc` alone runs what needs nothing but BlochSim. A page already carrying
output executed from the source as it stands is reused rather than re-run, and
the build names each example it did not run and where its output came from. To
build the pages without running any of them, pass `-D plot_gallery=0`.
@@ -28,10 +28,10 @@ build the pages without running any of them, pass `-D plot_gallery=0`.
## Where the published pages are built
`.github/workflows/docs.yml` builds them, and GitHub Pages serves them from
-the `gh-pages` branch at .
+the `gh-pages` branch at .
Its **HTML** job runs on every branch and pull request with the `doc` extra:
-every page is written and the examples needing nothing but TorchSim are
+every page is written and the examples needing nothing but BlochSim are
executed, in a few minutes, and the tree is uploaded as an artifact to read.
Its **Site** job runs on `main`, on a `v*.*.*` tag and on demand. It installs
@@ -43,7 +43,7 @@ Sphinx wrote to `scripts/publish_docs.py`, which places it in the site:
site/index.html a page sending a reader to the newest release
site/versions.json the list the switcher in the sidebar reads
-`TORCHSIM_DOCS_VERSION` is what a build calls itself; it names the directory,
+`BLOCHSIM_DOCS_VERSION` is what a build calls itself; it names the directory,
the switcher's mark and the Binder path prefix, and defaults to `latest`.
## Markdown, and the two places it is not
@@ -90,7 +90,7 @@ executed and where it is published as source.
## Writing
The audience is MR scientists: pulse sequences and physics, not software
-architecture. Describe what TorchSim does and why that is right on its own
+architecture. Describe what BlochSim does and why that is right on its own
terms. These pages are not a changelog — never justify a design by describing
the design it replaced.
@@ -109,7 +109,7 @@ Keep the public sidebar shallow and in this order:
The first four Framework examples are the Course. They teach the public
abstraction in order; the remaining example sections are standalone Tours.
-For a new sequence, teach {class}`torchsim.model.Simulator` as the extension
+For a new sequence, teach {class}`blochsim.model.Simulator` as the extension
point. A state-machine simulator has two complementary inputs: its class-level
handlers interpret commands from an incoming Pulseq/MRD description, and
`layout()` constructs the same sequence offline. Lower-level
diff --git a/.github/skills/cut-a-release/SKILL.md b/.github/skills/cut-a-release/SKILL.md
index 9fd7989c..7b80ec58 100644
--- a/.github/skills/cut-a-release/SKILL.md
+++ b/.github/skills/cut-a-release/SKILL.md
@@ -1,9 +1,9 @@
---
name: cut-a-release
-description: Publish a TorchSim release to PyPI, and diagnose a wheel build. Use when asked about releasing, versioning, cibuildwheel, abi3 wheels, or the packaging setup.
+description: Publish a BlochSim release to PyPI, and diagnose a wheel build. Use when asked about releasing, versioning, cibuildwheel, abi3 wheels, or the packaging setup.
---
-# Cut a TorchSim release
+# Cut a BlochSim release
The version comes from the git tag through setuptools-scm; nothing in the tree
states it. To release:
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index da5c9a8e..ebf07fbd 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -46,7 +46,7 @@ jobs:
--extra-index-url https://pypi.org/simple
# The ``doc`` extra alone: every page is written and the examples
- # needing nothing but TorchSim are executed, which is what a branch
+ # needing nothing but BlochSim are executed, which is what a branch
# wants to know within a few minutes. The Site job below runs the rest.
- name: Build the extensions and install
run: python -m pip install ".[doc]"
@@ -86,7 +86,7 @@ jobs:
# A tag is published under its own name; anything else is the
# development version. It names the directory of the site, the entry
# the switcher marks, and the prefix the notebook links reach through.
- TORCHSIM_DOCS_VERSION: ${{ github.ref_type == 'tag' && github.ref_name || 'latest' }}
+ BLOCHSIM_DOCS_VERSION: ${{ github.ref_type == 'tag' && github.ref_name || 'latest' }}
steps:
- uses: actions/checkout@v7
@@ -125,7 +125,7 @@ jobs:
- uses: actions/upload-artifact@v7
with:
- name: html-${{ env.TORCHSIM_DOCS_VERSION }}
+ name: html-${{ env.BLOCHSIM_DOCS_VERSION }}
path: docs/build/html
retention-days: 7
@@ -141,8 +141,8 @@ jobs:
else
git worktree add --orphan ../pages
fi
- python scripts/publish_docs.py docs/build/html "$TORCHSIM_DOCS_VERSION" ../pages
+ python scripts/publish_docs.py docs/build/html "$BLOCHSIM_DOCS_VERSION" ../pages
git -C ../pages add --all
git -C ../pages commit --quiet \
- -m "Publish $TORCHSIM_DOCS_VERSION from ${{ github.sha }}" || exit 0
+ -m "Publish $BLOCHSIM_DOCS_VERSION from ${{ github.sha }}" || exit 0
git -C ../pages push origin HEAD:gh-pages
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7fd8bf55..aaec7d27 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -67,7 +67,7 @@ jobs:
shell: python
run: |
import importlib.util, pathlib
- spec = importlib.util.find_spec("torchsim")
+ spec = importlib.util.find_spec("blochsim")
root = pathlib.Path(next(iter(spec.submodule_search_locations)))
for kernel in sorted(root.glob("_*_cpu*")):
print(kernel.name, kernel.stat().st_size, "bytes")
@@ -84,6 +84,6 @@ jobs:
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
- slug: pulserver/torchsim
+ slug: pulserver/blochsim
files: coverage.xml
fail_ci_if_error: false
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
index 61a3e254..6ca1ae4f 100644
--- a/.github/workflows/wheels.yml
+++ b/.github/workflows/wheels.yml
@@ -10,8 +10,8 @@ on:
- pyproject.toml
- CMakeLists.txt
- setup.py
- - "src/torchsim/*.cpp"
- - "src/torchsim/*.hpp"
+ - "src/blochsim/*.cpp"
+ - "src/blochsim/*.hpp"
- scripts/check_wheel.py
- .github/workflows/wheels.yml
workflow_dispatch:
@@ -95,7 +95,7 @@ jobs:
runs-on: ubuntu-latest
environment:
name: pypi
- url: https://pypi.org/p/torchsim
+ url: https://pypi.org/p/blochsim
permissions:
# Trusted publishing: no API token is stored anywhere.
id-token: write
diff --git a/.gitignore b/.gitignore
index ba7cdf01..dad77b30 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,7 +21,7 @@ CMakeFiles/
cmake_install.cmake
# Generated at build time
-src/torchsim/_version.py
+src/blochsim/_version.py
# Test and coverage output
.coverage*
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 33dd3476..2df1e55e 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -4,7 +4,7 @@
ci:
autoupdate_schedule: monthly
-exclude: ^src/torchsim/_version\.py$
+exclude: ^src/blochsim/_version\.py$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 026ea234..5f2a5c2f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
## Unreleased
+### Changed
+
+- **The package is `blochsim`.** The distribution, the import name and the
+ repository are `blochsim`; `import torchsim` becomes `import blochsim`, with
+ the same modules and names beneath it.
+
### Added
- **Up to four chemically exchanging pools beside the free water.**
@@ -10,7 +16,7 @@
five pools of BART's Bloch-McConnell simulation, each exchanging with the free
water alone. From two exchanging pools on, a tissue reaches the kernels as the
relaxation-exchange operators of each distinct interval length, formed per
- voxel in double precision (`torchsim.sequence._pools`). The C++ and Triton
+ voxel in double precision (`blochsim.sequence._pools`). The C++ and Triton
kernels apply them in the forward, forward-mode, adjoint and
forward-over-reverse passes, under hard, tabulated and per-voxel pulses and a
transmit array, and the pool properties take their derivatives back through
@@ -88,7 +94,7 @@
Both are table lookups now, `sequence/_calibration.py` is a table, and
`_calibrate.py` -- the same idea for the estimators and the model-based
operator -- is gone, along with the four closures those built for it to time.
- `torchsim._execution.choose` and `per_voxel` take the crossover as a number
+ `blochsim._execution.choose` and `per_voxel` take the crossover as a number
rather than a callable to answer later. Placement is unchanged and stays the
caller's: with no `execution()` block a call runs wherever its tensors are, a
named device is an instruction, and whether a volume is resident or streamed
@@ -104,7 +110,7 @@
of twenty-seven: 7.75 ms against 8.02 ms forward, 41.7 ms against 41.3 ms for
the Jacobian.
- `calibrate` and `TORCHSIM_CALIBRATION` are gone with the probes.
+ `calibrate` and `BLOCHSIM_CALIBRATION` are gone with the probes.
- **How many orders a run keeps is a number, not a compilation.** The Triton
kernels read `state_count` as a mask bound and as the stride between the
@@ -691,10 +697,10 @@
documentation build. *Extended phase graphs* is the physics: dephasing as a
helix, configuration states as its Fourier coefficients, the RF and shift
operators, relaxation reaching only order zero, the phase graph read against
- the echo amplitudes TorchSim computes for the same train, why a low-flip
+ the echo amplitudes BlochSim computes for the same train, why a low-flip
train is not a mono-exponential, how many orders a sequence has to carry,
the order-weighted diffusion b-factors, and the two-pool extensions. *How
- TorchSim runs it* is the realization: events carrying their own action word,
+ BlochSim runs it* is the realization: events carrying their own action word,
one fused kernel per voxel, the terms a declaration switches on, the real
subspace, forward against reverse derivatives, the affine rebinding, the
execution policy, and the closed forms the state machine is held to.
@@ -729,7 +735,7 @@
Nothing was added to the library for it.
-- **`torchsim.recon`: the signal model as an operator.** A physics-based
+- **`blochsim.recon`: the signal model as an operator.** A physics-based
reconstruction writes its forward operator as `P F C M` -- sampling, Fourier
encoding, coil sensitivities, signal model -- and solves for the parameter
maps against k-space directly. Only `M` changes with the sequence, and only
@@ -796,7 +802,7 @@
echo times still on the host. `bound()` adds or replaces tissue, which is
what a fit does with a property measured separately.
-- **`torchsim.ParameterMapping`**, with `Estimator` and `Subspace`. A mapping
+- **`blochsim.ParameterMapping`**, with `Estimator` and `Subspace`. A mapping
problem is stated the way a design problem is: an `Acquisition`, the
properties that are unknown and the range to train each over, the ones
measured separately, and the noise. `train(method)` simulates the training
@@ -859,7 +865,7 @@
grid it can rule out the group holding the match; widening it recovers the
voxel, and a test pins both halves of that.
-- **`torchsim.NonlinearLeastSquares`**, Levenberg-Marquardt stepping every
+- **`blochsim.NonlinearLeastSquares`**, Levenberg-Marquardt stepping every
voxel at once. Where a dictionary spans a grid whose size is the product of
the parameter ranges, a nonlinear fit pays for a third parameter with a third
column of the Jacobian. Voxels do not take turns: each carries its own
@@ -892,7 +898,7 @@
expression. The inversion recovery carries the repetition time as well, which
the usual fully-relaxed expression drops.
-- **`torchsim.LookupTable`**, for a model with a single unknown. Its atoms lie
+- **`blochsim.LookupTable`**, for a model with a single unknown. Its atoms lie
on a curve rather than filling a space, so the nearest one is found by
looking along it, and interpolating between the two nearest removes the grid
spacing from the answer -- which is otherwise what a matched estimate is
@@ -923,7 +929,7 @@
individual readouts agree.
- **Fused kernels for PERK**, forward and adjoint, in Triton on CUDA and in a
- new `torchsim._perk_cpu` extension on the host. The feature matrix is never
+ new `blochsim._perk_cpu` extension on the host. The feature matrix is never
written: a tile of features is formed and consumed into the output
accumulator in registers. On a million voxels of 64 contrasts at a thousand
features that is 3.3x on this card and 2.4-2.9x on this host, agreeing with
@@ -962,7 +968,7 @@
`docs/conf.py` drops sphinx-gallery's code-link pass on interpreters whose
standard library has no `dbm`.
-- **`torchsim.model`**, with `SignalModel`, `StateMachineModel`, `Triggers`
+- **`blochsim.model`**, with `SignalModel`, `StateMachineModel`, `Triggers`
and `AbstractSimulator`. A signal model is written in two pieces: a
state-machine model saying what a voxel holds -- `properties` maps the name
a caller uses to the tissue field it fills, so declaring `b0_hz` or a second
@@ -980,20 +986,20 @@
`AbstractSimulator.from_description` takes a stream someone else assembled,
which is the path a description arriving from a scanner takes.
-- **`torchsim.simulators`**, replacing `torchsim.models`. Each shipped
+- **`blochsim.simulators`**, replacing `blochsim.models`. Each shipped
sequence names its protocol at construction (`MRFSimulator(flip=..., TR=...)`)
and its tissue at the call (`.simulate(T1=..., T2=...)`), so parameter
inference, sequence optimization and a reconstruction pipeline take all of
them the same way. A protocol argument may still be overridden per call.
-- **`torchsim.sequence` operators.** `Operator`, `compose`, `module` and the
+- **`blochsim.sequence` operators.** `Operator`, `compose`, `module` and the
factories `excitation`, `refocusing`, `inversion`, `saturation`, `readout`
and `delay`, with a name registry (`register_operator`, `operator`,
`operator_names`). A preparation, a readout or a shaped pulse is written by
composing these and reaches the fused kernels with no change to them. The
five shipped builders are written over them.
-- **`torchsim.sequence.ideal_rf_definition`**, the hard-pulse RF definition a
+- **`blochsim.sequence.ideal_rf_definition`**, the hard-pulse RF definition a
description built by hand needs.
- **Sequence design: `Acquisition`, `Bounded`, `SequenceDesign` and `crlb`.**
@@ -1153,11 +1159,11 @@
Anything matching the `LeastSquares` protocol -- `A`, `AT`, `y`, `z`,
`gamma`, `max_iter`, `tol` in, the damped step out -- is called directly. A
reconstruction that brings its own conjugate gradients, or wraps a proximal
- solver to carry a regularizer, needs nothing from a dependency TorchSim does
+ solver to carry a regularizer, needs nothing from a dependency BlochSim does
not have. Given nothing, it falls back to deepinv's `least_squares`, which
satisfies the protocol unchanged; one of deepinv's others is that same
function with its argument bound, which is the caller's own composition
- rather than a name TorchSim interprets.
+ rather than a name BlochSim interprets.
- **The API pages follow one subject each.** *Sequences* is the description
and what assembles it -- events, operators, builders. The engine and the
@@ -1178,9 +1184,9 @@
Under a streaming fit that is the difference between simulating the training
set twice and simulating it three times.
-- **`Acquisition` moved to `torchsim.model`**, where both `torchsim.optim` and
- `torchsim.estimators` can reach it without either depending on the other. It
- is still exported from `torchsim` and `torchsim.optim`.
+- **`Acquisition` moved to `blochsim.model`**, where both `blochsim.optim` and
+ `blochsim.estimators` can reach it without either depending on the other. It
+ is still exported from `blochsim` and `blochsim.optim`.
- **`SubspaceBasis` carries a `Subspace`** rather than its own copy of the
basis and singular values, which it still exposes under the same names.
@@ -1245,11 +1251,11 @@
reaching for is `Triggers`, where it decides something. `simulate_subspace`
loses its simulator argument for the same reason.
-- **`torchsim.models` and the seven `*Model` classes.** Use
- `torchsim.simulators` and the `*Simulator` classes, which take the protocol
+- **`blochsim.models` and the seven `*Model` classes.** Use
+ `blochsim.simulators` and the `*Simulator` classes, which take the protocol
at construction. The seven `*_sim` functional wrappers are unchanged.
-- **`torchsim.epg`.** The package held the state-machine operators the
+- **`blochsim.epg`.** The package held the state-machine operators the
simulator's torch loop was written from. That loop is gone: every sequence
now runs on the fused CPU and CUDA kernels, which reach the same operators
through their own code. The operators survive as the tests' parity oracle
@@ -1269,7 +1275,7 @@
there is nothing to select between. A tissue no kernel can take now raises.
- **`slice_profile=` as a tensor of flip-angle scalings**, and
- `torchsim.utils.slice_prof` with it. A slice profile is a Bloch response and
+ `blochsim.utils.slice_prof` with it. A slice profile is a Bloch response and
is worked out from the RF definition; `slice_profile=` now says only where
across the slice to sample it, through `exact_slice_profile(...)`.
@@ -1277,10 +1283,10 @@
functional wrappers, for the same reason. Give the RF definition its
waveform instead.
-- **`torchsim.base`**, with `AbstractModel`, `autocast`, and the
+- **`blochsim.base`**, with `AbstractModel`, `autocast`, and the
`prepare_single_pool` / `prepare_two_pool_bm` / `prepare_two_pool_mt` /
`prepare_three_pool` / `prepare_environmental_parameters` helpers. Write a
- model over `torchsim.model.SignalModel` or `EpgModel` instead; a tissue is
+ model over `blochsim.model.SignalModel` or `EpgModel` instead; a tissue is
built by naming the fields a model exposes.
- **`chunk_size` on the seven `*_sim` wrappers.** It selected a `torch.vmap`
diff --git a/CLAUDE.md b/CLAUDE.md
index b18a83cc..6b6972f0 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,6 +1,6 @@
-# TorchSim, for an agent working on it
+# BlochSim, for an agent working on it
-TorchSim simulates MR signals in PyTorch, differentiably. A **signal model** is
+BlochSim simulates MR signals in PyTorch, differentiably. A **signal model** is
the only thing a sequence has to supply; differentiation, device placement,
parameter estimation, model-based reconstruction and sequence design are all
written once against that interface.
@@ -13,15 +13,15 @@ broken checkout (Windows without `core.symlinks`), not two documents.
| Path | What is in it |
| --- | --- |
-| `src/torchsim/sequence/` | The description an acquisition is assembled from — events, operators, builders — and the dispatch that turns one into a kernel launch. |
-| `src/torchsim/model/` | What a signal model *is*: the physics, the simulator that orders its events, and the binding that resolves a protocol's structure once and rebinds its values per call. |
-| `src/torchsim/_epg_cpu.cpp`, `_perk_cpu.cpp` | The CPU kernels. Every path the GPU has exists here too, and the two agree to float32 round-off. |
-| `src/torchsim/sequence/_epg_triton.py`, `estimators/_perk_triton.py` | The GPU kernels. |
-| `src/torchsim/simulators/`, `estimators/`, `recon/`, `optim/` | The sequences that ship, and what is built on top of them. |
+| `src/blochsim/sequence/` | The description an acquisition is assembled from — events, operators, builders — and the dispatch that turns one into a kernel launch. |
+| `src/blochsim/model/` | What a signal model *is*: the physics, the simulator that orders its events, and the binding that resolves a protocol's structure once and rebinds its values per call. |
+| `src/blochsim/_epg_cpu.cpp`, `_perk_cpu.cpp` | The CPU kernels. Every path the GPU has exists here too, and the two agree to float32 round-off. |
+| `src/blochsim/sequence/_epg_triton.py`, `estimators/_perk_triton.py` | The GPU kernels. |
+| `src/blochsim/simulators/`, `estimators/`, `recon/`, `optim/` | The sequences that ship, and what is built on top of them. |
| `tests/`, `examples/`, `docs/` | Mirrored by subpackage, executed by the gallery, built by Sphinx. |
The shared parameter ABI — read by the Python dispatch, the C++ extension and
-the Triton kernels alike — is `src/torchsim/sequence/_parameters.py`. A
+the Triton kernels alike — is `src/blochsim/sequence/_parameters.py`. A
parameter added there is added in all three places or in none.
## Commands
@@ -54,7 +54,7 @@ reading. Read the exit status of the install, not the last lines of its output:
```sh
pip install -e ".[dev]" ; echo "exit: $?"
-python -c "import torchsim._epg_cpu as k; print(k.__file__)"
+python -c "import blochsim._epg_cpu as k; print(k.__file__)"
```
**Kernel compiles dominate a cold GPU run**, not the arithmetic. A suite that
@@ -123,10 +123,10 @@ what a human decides:
## Changing the physics
A change to what the kernels compute arrives with a test that pins it against
-something **outside** TorchSim: a closed form, a published figure, or an
+something **outside** BlochSim: a closed form, a published figure, or an
isochromat summation written out in the test itself. The `tests/epg` files are
written that way and each states its invariant in its module docstring. A test
-that only compares TorchSim to TorchSim proves the two agree, which was never
+that only compares BlochSim to BlochSim proves the two agree, which was never
in doubt.
Whatever you change in one kernel, change in the other. The C++ and Triton
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c791d3ef..5d80ec2c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,12 +1,12 @@
cmake_minimum_required(VERSION 3.26)
-project(torchsim LANGUAGES CXX)
+project(blochsim LANGUAGES CXX)
# Both kernels are plain CPython extensions against the stable ABI: they call
# no PyTorch API and link no PyTorch library, which is why one wheel per
# platform serves every supported interpreter and why that wheel is a couple of
# megabytes rather than the size of libtorch.
-set(TORCHSIM_ABI3_VERSION 3.10)
+set(BLOCHSIM_ABI3_VERSION 3.10)
find_package(Python 3.10 REQUIRED
COMPONENTS Interpreter Development.Module Development.SABIModule
@@ -19,14 +19,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
-function(torchsim_add_kernel name source)
+function(blochsim_add_kernel name source)
python_add_library(${name} MODULE
- USE_SABI ${TORCHSIM_ABI3_VERSION}
+ USE_SABI ${BLOCHSIM_ABI3_VERSION}
WITH_SOABI
"${source}"
)
# The shared headers sit beside the sources they are shared by.
- target_include_directories(${name} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/torchsim")
+ target_include_directories(${name} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/blochsim")
target_link_libraries(${name} PRIVATE Threads::Threads)
# Nothing the C++ runtime owns crosses the module boundary: the kernel
@@ -51,8 +51,8 @@ function(torchsim_add_kernel name source)
target_compile_options(${name} PRIVATE -O3 -ffp-contract=off)
endif()
- install(TARGETS ${name} DESTINATION torchsim)
+ install(TARGETS ${name} DESTINATION blochsim)
endfunction()
-torchsim_add_kernel(_epg_cpu src/torchsim/_epg_cpu.cpp)
-torchsim_add_kernel(_perk_cpu src/torchsim/_perk_cpu.cpp)
+blochsim_add_kernel(_epg_cpu src/blochsim/_epg_cpu.cpp)
+blochsim_add_kernel(_perk_cpu src/blochsim/_perk_cpu.cpp)
diff --git a/README.md b/README.md
index b53dfd93..f2e33ca2 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,20 @@
-# TorchSim
+
+
+
+
-TorchSim is a differentiable MR signal simulator built on PyTorch, with
+# BlochSim
+
+BlochSim is a differentiable MR signal simulator built on PyTorch, with
closed-form signal models and a fused extended-phase-graph (EPG) state machine
for pulse trains.
-[](https://codecov.io/gh/pulserver/torchsim)
-[](https://github.com/pulserver/torchsim/actions/workflows/test.yml)
-[](https://github.com/pulserver/torchsim/actions/workflows/lint.yml)
-[](https://github.com/pulserver/torchsim/blob/main/LICENSE.txt)
-[](https://pulserver.github.io/torchsim/)
-[](https://pypi.org/project/torchsim)
+[](https://codecov.io/gh/pulserver/blochsim)
+[](https://github.com/pulserver/blochsim/actions/workflows/test.yml)
+[](https://github.com/pulserver/blochsim/actions/workflows/lint.yml)
+[](https://github.com/pulserver/blochsim/blob/main/LICENSE.txt)
+[](https://pulserver.github.io/blochsim/)
+[](https://pypi.org/project/blochsim)
[](https://github.com/astral-sh/ruff)
[](https://python.org)
@@ -28,13 +33,13 @@ for pulse trains.
## Installation
-Install the PyTorch build appropriate for your machine first, then TorchSim:
+Install the PyTorch build appropriate for your machine first, then BlochSim:
```bash
-pip install torchsim
+pip install blochsim
```
-See the [User Guide](https://pulserver.github.io/torchsim/latest/user_guide.html)
+See the [User Guide](https://pulserver.github.io/blochsim/latest/user_guide.html)
for CPU, CUDA, macOS and source-build details.
## Basic usage
@@ -44,7 +49,7 @@ sequence; `simulate` and `jacobian` evaluate it over the tissue you pass:
```python
import numpy as np
-from torchsim.simulators import MRFSimulator
+from blochsim.simulators import MRFSimulator
flip = np.concatenate(
(np.linspace(5.0, 60.0, 300), np.linspace(60.0, 2.0, 300), np.full(280, 2.0))
@@ -58,14 +63,14 @@ signal, jacobian = sequence.jacobian(
)
```
-Functional helpers such as `torchsim.mrf_sim(...)` remain convenient for
+Functional helpers such as `blochsim.mrf_sim(...)` remain convenient for
one-off calls. The class interface is the canonical one for reusable models,
parameter estimation, reconstruction, optimization, Pulseq input and scanner
descriptions.
## Implementing a sequence
-Subclass `torchsim.model.Simulator`. For a state-machine sequence you define:
+Subclass `blochsim.model.Simulator`. For a state-machine sequence you define:
1. the event handlers that say how excitation, refocusing, inversion,
saturation, readout and delay commands are interpreted; and
@@ -77,14 +82,14 @@ That gives offline design and scanner-driven simulation one public sequence
abstraction.
The executable
-[Framework course](https://pulserver.github.io/torchsim/latest/generated/autoexamples/01-framework/index.html)
+[Framework course](https://pulserver.github.io/blochsim/latest/generated/autoexamples/01-framework/index.html)
walks through the complete pattern.
## Development
```bash
-git clone git@github.com:pulserver/torchsim
-cd torchsim
+git clone git@github.com:pulserver/blochsim
+cd blochsim
pip install -e ".[dev]"
pre-commit install
```
@@ -96,6 +101,6 @@ format/lint checks as CI.
## Related projects
The documentation's
-[Related projects](https://pulserver.github.io/torchsim/latest/misc/related.html)
-page places TorchSim among other MR simulators and links to the relevant
+[Related projects](https://pulserver.github.io/blochsim/latest/misc/related.html)
+page places BlochSim among other MR simulators and links to the relevant
packages and literature.
diff --git a/benchmarks/README.md b/benchmarks/README.md
index 985e9765..3b2972fa 100644
--- a/benchmarks/README.md
+++ b/benchmarks/README.md
@@ -18,7 +18,7 @@ bash benchmarks/setup.sh # or: setup.sh python / setup.sh julia
source benchmarks/.env/activate
```
-That builds a virtual environment with PyTorch, TorchSim, epgpy and sycomore
+That builds a virtual environment with PyTorch, BlochSim, epgpy and sycomore
in it, and a Julia with BlochSimulators.jl, KomaMRI.jl and CUDA.jl, all under
`benchmarks/.env`, which is the only thing to delete afterwards. Every piece is
optional: `run_all.py` skips a backend that is not installed and says so.
@@ -28,7 +28,7 @@ Three notes on the awkward ones.
**Which interpreter the environment is built on matters.** `PYTHON` says which,
and the default is whatever `python3` finds first. An interpreter that ships a
C++ runtime of its own -- a conda one does -- loads that runtime ahead of the
-system's, and TorchSim's kernels are compiled against the system's. The
+system's, and BlochSim's kernels are compiled against the system's. The
extension then fails to load with a missing `GLIBCXX` version, every fused
kernel is reported absent, and nothing here can run:
@@ -52,11 +52,11 @@ benchmarks alone.
python benchmarks/run_all.py # the CPU sweep, into results/
python benchmarks/run_all.py --device cuda # the same sweep, on a card
python benchmarks/run_all.py --quick # stop at 1000 tissues
-python benchmarks/run_all.py --backends torchsim,blochsimulators
+python benchmarks/run_all.py --backends blochsim,blochsimulators
python benchmarks/validate.py # do they agree?
python benchmarks/summarize.py # the table, from results/
python benchmarks/make_figures.py # the figures, into figures/
-python benchmarks/anatomy.py [--device cuda] # what TorchSim's runtime is made of
+python benchmarks/anatomy.py [--device cuda] # what BlochSim's runtime is made of
```
`--device` reaches every backend that has a card to be placed on. Sycomore and
@@ -105,7 +105,7 @@ measures what it costs rather than pretending it is the same computation.
**Wall time**, the fastest of the timed runs. What comes before them is a
warm-up given a budget of seconds rather than a count, because what has to be
-warm differs by orders of magnitude: TorchSim resolves the *structure* of a
+warm differs by orders of magnitude: BlochSim resolves the *structure* of a
sequence once and rebinds values on every call afterwards, Julia compiles on
first call, and a card idles at a low clock and takes the better part of a
second of continuous work to reach its boost one. That last is the one that
@@ -140,7 +140,7 @@ placed on a card computed what the same run computes on a CPU.
*every* order in double precision as the reference. Where 32 orders are enough
for the tissue -- everything at brain T2 -- the implementations agree to
**float32 round-off**, and what is left at long T2 is the truncation and not a
-disagreement: it halves as the orders double. TorchSim's two kernels are held
+disagreement: it halves as the orders double. BlochSim's two kernels are held
against each other as well, since they are separate implementations of one
recursion: a dictionary run on the card matches the same dictionary on the CPU
to **6e-06 relative**. `results/validation.json` and `results/validation.txt`
@@ -173,16 +173,16 @@ The measurements committed here were taken on an Intel Core i7-13700H -- six
performance cores, eight efficiency cores, 23 GB -- with an NVIDIA GeForce RTX
4060 Laptop GPU, 8 GB; Python 3.11.5, PyTorch 2.13 with CUDA 13.0, Triton
3.7.1, NumPy 2.4.6, sycomore 2.0.0, epgpy at 82eebbf, Julia 1.12.7,
-BlochSimulators 0.9.0, KomaMRICore 0.13.0, CUDA.jl 5. TorchSim and
+BlochSimulators 0.9.0, KomaMRICore 0.13.0, CUDA.jl 5. BlochSim and
BlochSimulators compute in float32, sycomore and epgpy in float64. The CPU
sweep pins **four threads** for every backend that takes them, which is what
makes the packages comparable to each other rather than to the machine.
At ten thousand tissues, four CPU threads, 32 orders:
-| | forward | against TorchSim |
+| | forward | against BlochSim |
| --- | ---: | ---: |
-| TorchSim | 0.187 s | -- |
+| BlochSim | 0.187 s | -- |
| BlochSimulators.jl, real states | 0.212 s | 1.1x slower |
| BlochSimulators.jl, complex states | 0.520 s | 2.8x slower |
| sycomore | 28.40 s | 152x slower |
@@ -194,8 +194,8 @@ asking:
| | CPU, four threads | RTX 4060 Laptop | what the card buys |
| --- | ---: | ---: | ---: |
| BlochSimulators.jl | 2.276 s | 0.037 s | 61x |
-| TorchSim | 1.903 s | 0.069 s | 28x |
-| TorchSim, Jacobian in T1 and T2 | 9.657 s | 0.381 s | 25x |
+| BlochSim | 1.903 s | 0.069 s | 28x |
+| BlochSim, Jacobian in T1 and T2 | 9.657 s | 0.381 s | 25x |
| BlochSimulators.jl, finite differences | 7.409 s | 0.119 s | 62x |
KomaMRI reaches a thousand tissues, and 64 isochromats each: 7.33 s on four
@@ -208,18 +208,18 @@ BlochSimulators.jl is the comparison that matters -- it is the one other
package built for exactly this workload -- and at ten thousand tissues on four
threads the two are within a tenth of each other. Both take a real path when a
train's pulses share one axis: BlochSimulators reads that off the element type
-of its `RF_train`, TorchSim decides it per run from the phases the description
+of its `RF_train`, BlochSim decides it per run from the phases the description
carries, and both fall back to complex arithmetic when they cannot. The real
path is worth 2.5x in BlochSimulators (0.212 s against 0.520 s) and 4.9x in
-TorchSim, which `anatomy.py` measures on one event stream with only the verdict
+BlochSim, which `anatomy.py` measures on one event stream with only the verdict
changed.
On the card the two separate. BlochSimulators simulates a hundred thousand
-tissues in 37 ms against TorchSim's 69 ms, and takes its finite-difference
-Jacobian in 119 ms against TorchSim's 381 ms. The two lay the same recursion out
+tissues in 37 ms against BlochSim's 69 ms, and takes its finite-difference
+Jacobian in 119 ms against BlochSim's 381 ms. The two lay the same recursion out
differently: BlochSimulators gives each voxel a whole warp and each thread
`states / 32` of the configuration orders, held in registers as an immutable
-`SMatrix`, while TorchSim's Triton kernel takes a tile of voxels by all of
+`SMatrix`, while BlochSim's Triton kernel takes a tile of voxels by all of
their orders per program. Whatever the gap is, it is in the kernel and not in
the dispatch around it: `anatomy.py --device cuda` puts a forced-real verdict at
106.3 ms against 107.2 ms for the automatic one, so the fast path is reached and
@@ -243,15 +243,15 @@ an order of magnitude between one tissue and a hundred, then stops improving.
**A Jacobian costs a few times the forward pass it comes from, on every
package that has one.** At ten thousand tissues on four threads:
BlochSimulators' finite differences 0.847 s, which is 4.0x its own forward pass
-and is what three passes should cost; TorchSim's dual arithmetic 1.088 s for
+and is what three passes should cost; BlochSim's dual arithmetic 1.088 s for
two properties and 0.551 s for one, which is 2.9x its own forward pass per
-property; epgpy's analytic derivative 156 s, 4.5x its own. TorchSim's is linear
+property; epgpy's analytic derivative 156 s, 4.5x its own. BlochSim's is linear
in the number of properties, as forward mode should be, and exact where the
finite differences are not. The reverse pass takes the same lanes: a gradient
through a 500-echo train costs 4.6x its forward pass on the real path, against
the Jacobian's 5.9x.
-**TorchSim pays a structure cost the others do not.** Resolving a
+**BlochSim pays a structure cost the others do not.** Resolving a
500-repetition fingerprinting train -- walking 2 000 events, packing them,
learning the affine rebinding -- takes 1.9 s, once per sequence *shape*; a
500-echo refocused train takes 0.16 s. What it buys is the calls afterwards:
@@ -275,7 +275,7 @@ samples and the fastest of them, but a desktop or datacentre card would
separate the kernels differently and neither ratio above should be read as
architectural.
-**A second and third task.** One 64-echo CPMG train, where TorchSim's
+**A second and third task.** One 64-echo CPMG train, where BlochSim's
real-subspace specialization does apply; one two-pool run, which sycomore
cannot express at all and epgpy can. That pair separates "faster" from "does
more", which no single-task benchmark can.
diff --git a/benchmarks/anatomy.py b/benchmarks/anatomy.py
index 104d2d8d..5851acc8 100644
--- a/benchmarks/anatomy.py
+++ b/benchmarks/anatomy.py
@@ -1,4 +1,4 @@
-"""What TorchSim's own runtime is made of, on one CPU core count.
+"""What BlochSim's own runtime is made of, on one CPU core count.
Four experiments, each isolating one thing the dictionary benchmark cannot:
@@ -40,7 +40,7 @@
import torch
-from torchsim.simulators import FSESimulator, MRFSimulator
+from blochsim.simulators import FSESimulator, MRFSimulator
WARMUP_SECONDS = 2.0
@@ -84,14 +84,14 @@ def _reached(
which kernel a verdict selects, which is not something a caller can ask
for.
"""
- from torchsim.sequence import _builders
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence import _builders
+ from blochsim.sequence._accelerators import (
_pack_events,
_run_packed,
_run_packed_jvp,
real_subspace_axis,
)
- from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
+ from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
description = _builders.fse_description(
echo_train.to(torch.float32) * (torch.pi / 180.0),
@@ -308,7 +308,7 @@ def gradient(s: Any = sequence) -> None:
" few times the plain one. The multiple is the check that the dual and\n"
" adjoint kernels take the same path the plain ones take, rather than\n"
" the arithmetic being cheap where the plumbing is not. Run the whole\n"
- " file again under TORCHSIM_REAL_SCALAR=1 to separate what the real\n"
+ " file again under BLOCHSIM_REAL_SCALAR=1 to separate what the real\n"
" subspace is worth from what the lane kernels on top of it are worth:\n"
" a multiple that does not move is a pass with no laned kernel to take."
)
diff --git a/benchmarks/bench_torchsim.py b/benchmarks/bench_blochsim.py
similarity index 89%
rename from benchmarks/bench_torchsim.py
rename to benchmarks/bench_blochsim.py
index f2a718c9..e8de8692 100644
--- a/benchmarks/bench_torchsim.py
+++ b/benchmarks/bench_blochsim.py
@@ -1,6 +1,6 @@
-"""The fingerprinting dictionary, on TorchSim's fused state machine.
+"""The fingerprinting dictionary, on BlochSim's fused state machine.
-Run as ``python benchmarks/bench_torchsim.py --atoms 10000``. The structure of
+Run as ``python benchmarks/bench_blochsim.py --atoms 10000``. The structure of
the sequence is resolved once before anything is timed, which is what a
dictionary sweep or a design loop does too: the schedule is the same at every
call and only the tissue changes.
@@ -28,7 +28,7 @@
def main() -> None:
"""Time one dictionary, and say what it cost."""
- cli = parser("torchsim")
+ cli = parser("blochsim")
cli.add_argument(
"--diff",
default="T1,T2",
@@ -37,13 +37,13 @@ def main() -> None:
args = cli.parse_args()
diff = tuple(name for name in args.diff.split(",") if name)
if args.threads:
- os.environ["TORCHSIM_NUM_THREADS"] = str(args.threads)
+ os.environ["BLOCHSIM_NUM_THREADS"] = str(args.threads)
import time
import torch
- from torchsim.simulators import MRFSimulator
+ from blochsim.simulators import MRFSimulator
if args.threads:
torch.set_num_threads(args.threads)
@@ -85,7 +85,7 @@ def jacobian() -> torch.Tensor:
seconds, signal = timed(run, repeats=args.repeats, synchronize=synchronize)
measurement = Measurement(
- backend="torchsim",
+ backend="blochsim",
mode=args.mode if args.mode == "forward" else f"jacobian({args.diff})",
atoms=args.atoms,
length=args.length,
@@ -106,7 +106,7 @@ def jacobian() -> torch.Tensor:
else 0.0
),
checksum=complex(signal.reshape(-1).to(torch.complex64).sum().cpu()),
- versions={"torchsim": version("torchsim"), "torch": torch.__version__},
+ versions={"blochsim": version("blochsim"), "torch": torch.__version__},
machine=machine(),
)
report(measurement, args.json)
diff --git a/benchmarks/bench_epgpy.py b/benchmarks/bench_epgpy.py
index 8ff6c4e5..af885e7e 100644
--- a/benchmarks/bench_epgpy.py
+++ b/benchmarks/bench_epgpy.py
@@ -1,6 +1,6 @@
"""The same fingerprinting dictionary, on epgpy's operator-per-event EPG.
-Epgpy is the closest thing to TorchSim in Python: NumPy under the hood, the
+Epgpy is the closest thing to BlochSim in Python: NumPy under the hood, the
dictionary carried in the array shape rather than in a loop, analytic first-
and second-order derivatives, and CuPy for a card. What it does not do is fuse:
each operator is a pass over the whole state matrix, so a train of a thousand
@@ -86,7 +86,7 @@ def run() -> np.ndarray:
checksum=complex(np.asarray(signal).sum()),
versions={"epgpy": release, "numpy": np.__version__},
machine=machine(),
- note="max_nstate matches the orders TorchSim keeps",
+ note="max_nstate matches the orders BlochSim keeps",
)
report(measurement, args.json)
diff --git a/benchmarks/julia/Project.toml b/benchmarks/julia/Project.toml
index 721b5cbd..d3ebd793 100644
--- a/benchmarks/julia/Project.toml
+++ b/benchmarks/julia/Project.toml
@@ -1,4 +1,4 @@
-name = "TorchSimBenchmarks"
+name = "BlochSimBenchmarks"
# The two Julia simulators the dictionary benchmark runs against, and what the
# scripts here need to drive them. Instantiate with:
diff --git a/benchmarks/julia/bench_blochsimulators.jl b/benchmarks/julia/bench_blochsimulators.jl
index 695cfba9..282aa903 100644
--- a/benchmarks/julia/bench_blochsimulators.jl
+++ b/benchmarks/julia/bench_blochsimulators.jl
@@ -12,7 +12,7 @@
# * `FISP2D` requires the number of configuration states to be a multiple of
# 32, so the shared task carries 32 orders rather than 20.
# * A real `RF_train` keeps the configuration states real, which is the same
-# specialization TorchSim applies when a sequence earns it. The train here
+# specialization BlochSim applies when a sequence earns it. The train here
# has no phase, so both packages take their real path.
#
# Run as (from the repository root):
@@ -66,7 +66,7 @@ function main()
baseline = peak_rss_mib()
# Seconds and milliseconds: BlochSimulators takes times in seconds,
- # TorchSim in milliseconds, and the task is stated in milliseconds.
+ # BlochSim in milliseconds, and the task is stated in milliseconds.
TR = 0.010
TE = 0.0
TI = 0.0
@@ -83,7 +83,7 @@ function main()
atoms = length(T1)
end
# A real RF train keeps the configuration states real, which is the same
- # saving TorchSim's real-subspace kernels make. Asking for a complex train
+ # saving BlochSim's real-subspace kernels make. Asking for a complex train
# is how the two halves of the difference are told apart: what the
# specialization buys, and what is left over.
train = options["rf"] == "complex" ? complex.(flip) : flip
diff --git a/benchmarks/make_figures.py b/benchmarks/make_figures.py
index 420fef2b..02f546b0 100644
--- a/benchmarks/make_figures.py
+++ b/benchmarks/make_figures.py
@@ -42,7 +42,7 @@
# Categorical slots in a fixed order: a backend keeps its colour whether or
# not the others are present.
COLORS = {
- "torchsim": "#2a78d6",
+ "blochsim": "#2a78d6",
"sycomore": "#eb6834",
"epgpy": "#1baf7a",
"BlochSimulators.jl": "#eda100",
@@ -177,7 +177,7 @@ def draw(axes: plt.Axes, name: str, points: list[tuple[int, float]]) -> None:
def throughput(records: list[dict], suffix: str) -> None:
"""Tissues a second against dictionary size, one line per backend."""
threaded = [
- r for r in records if not (r["backend"] == "torchsim" and r["threads"] == 1)
+ r for r in records if not (r["backend"] == "blochsim" and r["threads"] == 1)
]
series = curves(threaded, "forward", lambda r: r["atoms"] / r["best"])
figure, axes = plt.subplots(figsize=(7.2, 4.2), dpi=200)
@@ -276,7 +276,7 @@ def memory(records: list[dict], suffix: str) -> None:
r
for r in records
if r.get("device", "cpu") == "cpu"
- and not (r["backend"] == "torchsim" and r["threads"] == 1)
+ and not (r["backend"] == "blochsim" and r["threads"] == 1)
]
series = curves(
threaded, "forward", lambda r: r["peak_rss_mib"] - r["baseline_rss_mib"]
diff --git a/benchmarks/results/anatomy.txt b/benchmarks/results/anatomy.txt
index 62ad8c03..e9c9a588 100644
--- a/benchmarks/results/anatomy.txt
+++ b/benchmarks/results/anatomy.txt
@@ -38,7 +38,7 @@ What the Jacobian costs on each path, same event stream
few times the plain one. The multiple is the check that the dual and
adjoint kernels take the same path the plain ones take, rather than
the arithmetic being cheap where the plumbing is not. Run the whole
- file again under TORCHSIM_REAL_SCALAR=1 to separate what the real
+ file again under BLOCHSIM_REAL_SCALAR=1 to separate what the real
subspace is worth from what the lane kernels on top of it are worth:
a multiple that does not move is a pass with no laned kernel to take.
@@ -84,6 +84,6 @@ What the Jacobian costs on each path, same event stream
few times the plain one. The multiple is the check that the dual and
adjoint kernels take the same path the plain ones take, rather than
the arithmetic being cheap where the plumbing is not. Run the whole
- file again under TORCHSIM_REAL_SCALAR=1 to separate what the real
+ file again under BLOCHSIM_REAL_SCALAR=1 to separate what the real
subspace is worth from what the lane kernels on top of it are worth:
a multiple that does not move is a pass with no laned kernel to take.
diff --git a/benchmarks/results/torchsim-forward-1-cpu-t4.json b/benchmarks/results/blochsim-forward-1-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-1-cpu-t4.json
rename to benchmarks/results/blochsim-forward-1-cpu-t4.json
index e72799d7..b58d5462 100644
--- a/benchmarks/results/torchsim-forward-1-cpu-t4.json
+++ b/benchmarks/results/blochsim-forward-1-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 1,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 708.36328125,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-1-cuda.json b/benchmarks/results/blochsim-forward-1-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-forward-1-cuda.json
rename to benchmarks/results/blochsim-forward-1-cuda.json
index 4e4da2d2..e80c1e87 100644
--- a/benchmarks/results/torchsim-forward-1-cuda.json
+++ b/benchmarks/results/blochsim-forward-1-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 1,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1152.296875,
"peak_device_mib": 54.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-10-cpu-t4.json b/benchmarks/results/blochsim-forward-10-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-10-cpu-t4.json
rename to benchmarks/results/blochsim-forward-10-cpu-t4.json
index d0b05863..d2bce726 100644
--- a/benchmarks/results/torchsim-forward-10-cpu-t4.json
+++ b/benchmarks/results/blochsim-forward-10-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 10,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 707.640625,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-10-cuda.json b/benchmarks/results/blochsim-forward-10-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-forward-10-cuda.json
rename to benchmarks/results/blochsim-forward-10-cuda.json
index f326da1b..c3a6dea0 100644
--- a/benchmarks/results/torchsim-forward-10-cuda.json
+++ b/benchmarks/results/blochsim-forward-10-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 10,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1179.51953125,
"peak_device_mib": 54.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-100-cpu-t4.json b/benchmarks/results/blochsim-forward-100-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-100-cpu-t4.json
rename to benchmarks/results/blochsim-forward-100-cpu-t4.json
index 79f072fa..c9fe0af7 100644
--- a/benchmarks/results/torchsim-forward-100-cpu-t4.json
+++ b/benchmarks/results/blochsim-forward-100-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 100,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 708.12890625,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-100-cuda.json b/benchmarks/results/blochsim-forward-100-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-forward-100-cuda.json
rename to benchmarks/results/blochsim-forward-100-cuda.json
index a3232af7..42ac441f 100644
--- a/benchmarks/results/torchsim-forward-100-cuda.json
+++ b/benchmarks/results/blochsim-forward-100-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 100,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1240.3984375,
"peak_device_mib": 54.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-1000-cpu-t1.json b/benchmarks/results/blochsim-forward-1000-cpu-t1.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-1000-cpu-t1.json
rename to benchmarks/results/blochsim-forward-1000-cpu-t1.json
index f0917469..52115e16 100644
--- a/benchmarks/results/torchsim-forward-1000-cpu-t1.json
+++ b/benchmarks/results/blochsim-forward-1000-cpu-t1.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 1000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 724.49609375,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-1000-cpu-t4.json b/benchmarks/results/blochsim-forward-1000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-1000-cpu-t4.json
rename to benchmarks/results/blochsim-forward-1000-cpu-t4.json
index 02175fc2..b1b5d05d 100644
--- a/benchmarks/results/torchsim-forward-1000-cpu-t4.json
+++ b/benchmarks/results/blochsim-forward-1000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 1000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 720.44921875,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-1000-cuda.json b/benchmarks/results/blochsim-forward-1000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-forward-1000-cuda.json
rename to benchmarks/results/blochsim-forward-1000-cuda.json
index ea95eb79..905b032f 100644
--- a/benchmarks/results/torchsim-forward-1000-cuda.json
+++ b/benchmarks/results/blochsim-forward-1000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 1000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1201.71875,
"peak_device_mib": 54.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-10000-cpu-t1.json b/benchmarks/results/blochsim-forward-10000-cpu-t1.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-10000-cpu-t1.json
rename to benchmarks/results/blochsim-forward-10000-cpu-t1.json
index a03431e5..e937dbdd 100644
--- a/benchmarks/results/torchsim-forward-10000-cpu-t1.json
+++ b/benchmarks/results/blochsim-forward-10000-cpu-t1.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 10000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 822.4609375,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-10000-cpu-t4.json b/benchmarks/results/blochsim-forward-10000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-10000-cpu-t4.json
rename to benchmarks/results/blochsim-forward-10000-cpu-t4.json
index 88bd5f74..1074ba27 100644
--- a/benchmarks/results/torchsim-forward-10000-cpu-t4.json
+++ b/benchmarks/results/blochsim-forward-10000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 10000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 859.69921875,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-10000-cuda.json b/benchmarks/results/blochsim-forward-10000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-forward-10000-cuda.json
rename to benchmarks/results/blochsim-forward-10000-cuda.json
index 62d84918..57090f1b 100644
--- a/benchmarks/results/torchsim-forward-10000-cuda.json
+++ b/benchmarks/results/blochsim-forward-10000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 10000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1194.02734375,
"peak_device_mib": 196.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-100000-cpu-t1.json b/benchmarks/results/blochsim-forward-100000-cpu-t1.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-100000-cpu-t1.json
rename to benchmarks/results/blochsim-forward-100000-cpu-t1.json
index d3526f16..1b2432d6 100644
--- a/benchmarks/results/torchsim-forward-100000-cpu-t1.json
+++ b/benchmarks/results/blochsim-forward-100000-cpu-t1.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 100000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 1860.96875,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-100000-cpu-t4.json b/benchmarks/results/blochsim-forward-100000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-forward-100000-cpu-t4.json
rename to benchmarks/results/blochsim-forward-100000-cpu-t4.json
index 879b18db..62f0d4f8 100644
--- a/benchmarks/results/torchsim-forward-100000-cpu-t4.json
+++ b/benchmarks/results/blochsim-forward-100000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 100000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 1860.58203125,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-forward-100000-cuda.json b/benchmarks/results/blochsim-forward-100000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-forward-100000-cuda.json
rename to benchmarks/results/blochsim-forward-100000-cuda.json
index b847e43e..5a0a3bc9 100644
--- a/benchmarks/results/torchsim-forward-100000-cuda.json
+++ b/benchmarks/results/blochsim-forward-100000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "forward",
"atoms": 100000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1203.59375,
"peak_device_mib": 1586.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-1-cpu-t4.json b/benchmarks/results/blochsim-jacobian-1-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian-1-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian-1-cpu-t4.json
index f9f448d8..0453b864 100644
--- a/benchmarks/results/torchsim-jacobian-1-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian-1-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 1,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 712.3828125,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-1-cuda.json b/benchmarks/results/blochsim-jacobian-1-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian-1-cuda.json
rename to benchmarks/results/blochsim-jacobian-1-cuda.json
index 91ff2ca2..10a91845 100644
--- a/benchmarks/results/torchsim-jacobian-1-cuda.json
+++ b/benchmarks/results/blochsim-jacobian-1-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 1,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1164.125,
"peak_device_mib": 72.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-10-cpu-t4.json b/benchmarks/results/blochsim-jacobian-10-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian-10-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian-10-cpu-t4.json
index b3b71c59..8e119478 100644
--- a/benchmarks/results/torchsim-jacobian-10-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian-10-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 10,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 712.0234375,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-10-cuda.json b/benchmarks/results/blochsim-jacobian-10-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian-10-cuda.json
rename to benchmarks/results/blochsim-jacobian-10-cuda.json
index 15ffc5ca..bf016b43 100644
--- a/benchmarks/results/torchsim-jacobian-10-cuda.json
+++ b/benchmarks/results/blochsim-jacobian-10-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 10,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1197.01953125,
"peak_device_mib": 54.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-100-cpu-t4.json b/benchmarks/results/blochsim-jacobian-100-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian-100-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian-100-cpu-t4.json
index efffb419..e64c464c 100644
--- a/benchmarks/results/torchsim-jacobian-100-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian-100-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 100,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 714.125,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-100-cuda.json b/benchmarks/results/blochsim-jacobian-100-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian-100-cuda.json
rename to benchmarks/results/blochsim-jacobian-100-cuda.json
index 87eb5789..9d39af1b 100644
--- a/benchmarks/results/torchsim-jacobian-100-cuda.json
+++ b/benchmarks/results/blochsim-jacobian-100-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 100,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1252.91796875,
"peak_device_mib": 54.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-1000-cpu-t4.json b/benchmarks/results/blochsim-jacobian-1000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian-1000-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian-1000-cpu-t4.json
index dec50db2..989aa3ca 100644
--- a/benchmarks/results/torchsim-jacobian-1000-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian-1000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 1000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 756.9609375,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-1000-cuda.json b/benchmarks/results/blochsim-jacobian-1000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian-1000-cuda.json
rename to benchmarks/results/blochsim-jacobian-1000-cuda.json
index bcbc4461..c9676ee0 100644
--- a/benchmarks/results/torchsim-jacobian-1000-cuda.json
+++ b/benchmarks/results/blochsim-jacobian-1000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 1000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1206.015625,
"peak_device_mib": 56.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-10000-cpu-t4.json b/benchmarks/results/blochsim-jacobian-10000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian-10000-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian-10000-cpu-t4.json
index d9467761..6e9bc006 100644
--- a/benchmarks/results/torchsim-jacobian-10000-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian-10000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 10000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 1054.765625,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-10000-cuda.json b/benchmarks/results/blochsim-jacobian-10000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian-10000-cuda.json
rename to benchmarks/results/blochsim-jacobian-10000-cuda.json
index 431983f3..e9b06254 100644
--- a/benchmarks/results/torchsim-jacobian-10000-cuda.json
+++ b/benchmarks/results/blochsim-jacobian-10000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 10000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1206.015625,
"peak_device_mib": 512.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-100000-cpu-t4.json b/benchmarks/results/blochsim-jacobian-100000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian-100000-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian-100000-cpu-t4.json
index 8f9758c1..9d3aa230 100644
--- a/benchmarks/results/torchsim-jacobian-100000-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian-100000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 100000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 4147.16796875,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian-100000-cuda.json b/benchmarks/results/blochsim-jacobian-100000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian-100000-cuda.json
rename to benchmarks/results/blochsim-jacobian-100000-cuda.json
index db664941..0cadf2b7 100644
--- a/benchmarks/results/torchsim-jacobian-100000-cuda.json
+++ b/benchmarks/results/blochsim-jacobian-100000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1,T2)",
"atoms": 100000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1207.609375,
"peak_device_mib": 4642.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-1-cpu-t4.json b/benchmarks/results/blochsim-jacobian1-1-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian1-1-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian1-1-cpu-t4.json
index 972e2bbf..8aaf968b 100644
--- a/benchmarks/results/torchsim-jacobian1-1-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian1-1-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 1,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 711.5234375,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-1-cuda.json b/benchmarks/results/blochsim-jacobian1-1-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian1-1-cuda.json
rename to benchmarks/results/blochsim-jacobian1-1-cuda.json
index 4d7c71c7..833debb7 100644
--- a/benchmarks/results/torchsim-jacobian1-1-cuda.json
+++ b/benchmarks/results/blochsim-jacobian1-1-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 1,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1166.8359375,
"peak_device_mib": 122.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-10-cpu-t4.json b/benchmarks/results/blochsim-jacobian1-10-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian1-10-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian1-10-cpu-t4.json
index d9fa1388..3595b884 100644
--- a/benchmarks/results/torchsim-jacobian1-10-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian1-10-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 10,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 712.04296875,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-10-cuda.json b/benchmarks/results/blochsim-jacobian1-10-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian1-10-cuda.json
rename to benchmarks/results/blochsim-jacobian1-10-cuda.json
index 7334a45e..22d6d67d 100644
--- a/benchmarks/results/torchsim-jacobian1-10-cuda.json
+++ b/benchmarks/results/blochsim-jacobian1-10-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 10,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1157.8125,
"peak_device_mib": 54.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-100-cpu-t4.json b/benchmarks/results/blochsim-jacobian1-100-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian1-100-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian1-100-cpu-t4.json
index 661c3671..2870640d 100644
--- a/benchmarks/results/torchsim-jacobian1-100-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian1-100-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 100,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 712.90234375,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-100-cuda.json b/benchmarks/results/blochsim-jacobian1-100-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian1-100-cuda.json
rename to benchmarks/results/blochsim-jacobian1-100-cuda.json
index c1c0bc88..627f69e3 100644
--- a/benchmarks/results/torchsim-jacobian1-100-cuda.json
+++ b/benchmarks/results/blochsim-jacobian1-100-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 100,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1209.2578125,
"peak_device_mib": 72.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-1000-cpu-t4.json b/benchmarks/results/blochsim-jacobian1-1000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian1-1000-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian1-1000-cpu-t4.json
index 0254d8ca..2c087373 100644
--- a/benchmarks/results/torchsim-jacobian1-1000-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian1-1000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 1000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 745.24609375,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-1000-cuda.json b/benchmarks/results/blochsim-jacobian1-1000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian1-1000-cuda.json
rename to benchmarks/results/blochsim-jacobian1-1000-cuda.json
index c76f9cee..027d205a 100644
--- a/benchmarks/results/torchsim-jacobian1-1000-cuda.json
+++ b/benchmarks/results/blochsim-jacobian1-1000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 1000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1210.796875,
"peak_device_mib": 56.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-10000-cpu-t4.json b/benchmarks/results/blochsim-jacobian1-10000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian1-10000-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian1-10000-cpu-t4.json
index c7a57ca7..f1a14f45 100644
--- a/benchmarks/results/torchsim-jacobian1-10000-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian1-10000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 10000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 940.640625,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-10000-cuda.json b/benchmarks/results/blochsim-jacobian1-10000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian1-10000-cuda.json
rename to benchmarks/results/blochsim-jacobian1-10000-cuda.json
index 2dbea12d..43d41a2e 100644
--- a/benchmarks/results/torchsim-jacobian1-10000-cuda.json
+++ b/benchmarks/results/blochsim-jacobian1-10000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 10000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1206.37890625,
"peak_device_mib": 316.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-100000-cpu-t4.json b/benchmarks/results/blochsim-jacobian1-100000-cpu-t4.json
similarity index 92%
rename from benchmarks/results/torchsim-jacobian1-100000-cpu-t4.json
rename to benchmarks/results/blochsim-jacobian1-100000-cpu-t4.json
index 24c6c1a8..e703c445 100644
--- a/benchmarks/results/torchsim-jacobian1-100000-cpu-t4.json
+++ b/benchmarks/results/blochsim-jacobian1-100000-cpu-t4.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 100000,
"length": 500,
@@ -17,7 +17,7 @@
"peak_rss_mib": 3006.68359375,
"peak_device_mib": 0.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/torchsim-jacobian1-100000-cuda.json b/benchmarks/results/blochsim-jacobian1-100000-cuda.json
similarity index 94%
rename from benchmarks/results/torchsim-jacobian1-100000-cuda.json
rename to benchmarks/results/blochsim-jacobian1-100000-cuda.json
index e7e23ce5..e25306a4 100644
--- a/benchmarks/results/torchsim-jacobian1-100000-cuda.json
+++ b/benchmarks/results/blochsim-jacobian1-100000-cuda.json
@@ -1,5 +1,5 @@
{
- "backend": "torchsim",
+ "backend": "blochsim",
"mode": "jacobian(T1)",
"atoms": 100000,
"length": 500,
@@ -29,7 +29,7 @@
"peak_rss_mib": 1209.09375,
"peak_device_mib": 2732.0,
"versions": {
- "torchsim": "0.0.4.dev137",
+ "blochsim": "0.0.4.dev137",
"torch": "2.13.0+cu130"
},
"machine": {
diff --git a/benchmarks/results/epgpy-forward-1.json b/benchmarks/results/epgpy-forward-1.json
index c3d9ef39..4debe88d 100644
--- a/benchmarks/results/epgpy-forward-1.json
+++ b/benchmarks/results/epgpy-forward-1.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 0.02916437999920163,
"median": 0.030261382000389858,
"atoms_per_second": 34.28840249740865,
diff --git a/benchmarks/results/epgpy-forward-10.json b/benchmarks/results/epgpy-forward-10.json
index 71b7008c..3bc1a7b6 100644
--- a/benchmarks/results/epgpy-forward-10.json
+++ b/benchmarks/results/epgpy-forward-10.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 0.05330446900006791,
"median": 0.054138121000505635,
"atoms_per_second": 187.60153112091334,
diff --git a/benchmarks/results/epgpy-forward-100.json b/benchmarks/results/epgpy-forward-100.json
index fe993a1b..cfe39db6 100644
--- a/benchmarks/results/epgpy-forward-100.json
+++ b/benchmarks/results/epgpy-forward-100.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 0.2370688480004901,
"median": 0.27986130800127285,
"atoms_per_second": 421.81839091652085,
diff --git a/benchmarks/results/epgpy-forward-1000.json b/benchmarks/results/epgpy-forward-1000.json
index f07da714..a5de274f 100644
--- a/benchmarks/results/epgpy-forward-1000.json
+++ b/benchmarks/results/epgpy-forward-1000.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 2.510219899000731,
"median": 2.5374647440003173,
"atoms_per_second": 398.3714735103806,
diff --git a/benchmarks/results/epgpy-forward-10000.json b/benchmarks/results/epgpy-forward-10000.json
index b464c4bf..51633db4 100644
--- a/benchmarks/results/epgpy-forward-10000.json
+++ b/benchmarks/results/epgpy-forward-10000.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 34.74142441699951,
"median": 36.0643737399987,
"atoms_per_second": 287.84081734734076,
diff --git a/benchmarks/results/epgpy-jacobian-1.json b/benchmarks/results/epgpy-jacobian-1.json
index 8cd5f126..735f7424 100644
--- a/benchmarks/results/epgpy-jacobian-1.json
+++ b/benchmarks/results/epgpy-jacobian-1.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 0.08616435799922328,
"median": 0.08839201599948865,
"atoms_per_second": 11.605726813504655,
diff --git a/benchmarks/results/epgpy-jacobian-10.json b/benchmarks/results/epgpy-jacobian-10.json
index 5477d3e7..7028732b 100644
--- a/benchmarks/results/epgpy-jacobian-10.json
+++ b/benchmarks/results/epgpy-jacobian-10.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 0.1852214129994536,
"median": 0.18672170799982268,
"atoms_per_second": 53.9894380355985,
diff --git a/benchmarks/results/epgpy-jacobian-100.json b/benchmarks/results/epgpy-jacobian-100.json
index 29a30a84..ec47107b 100644
--- a/benchmarks/results/epgpy-jacobian-100.json
+++ b/benchmarks/results/epgpy-jacobian-100.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 0.9521177429996897,
"median": 0.9590095800012932,
"atoms_per_second": 105.02902685643218,
diff --git a/benchmarks/results/epgpy-jacobian-1000.json b/benchmarks/results/epgpy-jacobian-1000.json
index b365d5ab..36deeb2b 100644
--- a/benchmarks/results/epgpy-jacobian-1000.json
+++ b/benchmarks/results/epgpy-jacobian-1000.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 10.551914194000346,
"median": 12.37560925300022,
"atoms_per_second": 94.76953485544684,
diff --git a/benchmarks/results/epgpy-jacobian-10000.json b/benchmarks/results/epgpy-jacobian-10000.json
index 6bea51fd..3a2ec9e7 100644
--- a/benchmarks/results/epgpy-jacobian-10000.json
+++ b/benchmarks/results/epgpy-jacobian-10000.json
@@ -25,7 +25,7 @@
"processor": "x86_64",
"python": "3.11.5"
},
- "note": "max_nstate matches the orders TorchSim keeps",
+ "note": "max_nstate matches the orders BlochSim keeps",
"best": 155.95141346199853,
"median": 164.54237790599836,
"atoms_per_second": 64.12253520508649,
diff --git a/benchmarks/results/table.md b/benchmarks/results/table.md
index 66c7a5a9..be5695dc 100644
--- a/benchmarks/results/table.md
+++ b/benchmarks/results/table.md
@@ -50,45 +50,45 @@
| sycomore | forward | cpu | 1 | 100 | 0.2687 | 372 | 37 | 3.5 | 0 |
| sycomore | forward | cpu | 1 | 1000 | 2.2262 | 449 | 57 | 23.8 | 0 |
| sycomore | forward | cpu | 1 | 10000 | 28.3982 | 352 | 266 | 233.0 | 0 |
-| torchsim | forward | cpu | 1 | 1000 | 0.0698 | 14,324 | 724 | 117.5 | 0 |
-| torchsim | forward | cpu | 1 | 10000 | 0.7669 | 13,039 | 822 | 215.4 | 0 |
-| torchsim | forward | cpu | 1 | 100000 | 7.1735 | 13,940 | 1861 | 1254.3 | 0 |
-| torchsim | forward | cpu | 4 | 1 | 0.0009 | 1,132 | 708 | 101.6 | 0 |
-| torchsim | forward | cpu | 4 | 10 | 0.0016 | 6,343 | 708 | 101.1 | 0 |
-| torchsim | forward | cpu | 4 | 100 | 0.0037 | 27,010 | 708 | 101.2 | 0 |
-| torchsim | forward | cpu | 4 | 1000 | 0.0233 | 42,972 | 720 | 113.5 | 0 |
-| torchsim | forward | cpu | 4 | 10000 | 0.1866 | 53,578 | 860 | 252.6 | 0 |
-| torchsim | forward | cpu | 4 | 100000 | 1.9028 | 52,554 | 1861 | 1254.0 | 0 |
-| torchsim | forward | cuda | 4 | 1 | 0.0037 | 267 | 1152 | 551.3 | 54 |
-| torchsim | forward | cuda | 4 | 10 | 0.0034 | 2,942 | 1180 | 582.2 | 54 |
-| torchsim | forward | cuda | 4 | 100 | 0.0028 | 35,256 | 1240 | 639.7 | 54 |
-| torchsim | forward | cuda | 4 | 1000 | 0.0033 | 301,734 | 1202 | 600.6 | 54 |
-| torchsim | forward | cuda | 4 | 10000 | 0.0087 | 1,150,290 | 1194 | 592.4 | 196 |
-| torchsim | forward | cuda | 4 | 100000 | 0.0687 | 1,454,714 | 1204 | 602.3 | 1586 |
-| torchsim | jacobian(T1) | cpu | 4 | 1 | 0.0054 | 185 | 712 | 105.2 | 0 |
-| torchsim | jacobian(T1) | cpu | 4 | 10 | 0.0081 | 1,234 | 712 | 105.4 | 0 |
-| torchsim | jacobian(T1) | cpu | 4 | 100 | 0.0117 | 8,520 | 713 | 106.3 | 0 |
-| torchsim | jacobian(T1) | cpu | 4 | 1000 | 0.0534 | 18,738 | 745 | 138.4 | 0 |
-| torchsim | jacobian(T1) | cpu | 4 | 10000 | 0.5511 | 18,146 | 941 | 333.8 | 0 |
-| torchsim | jacobian(T1) | cpu | 4 | 100000 | 5.4812 | 18,244 | 3007 | 2400.2 | 0 |
-| torchsim | jacobian(T1) | cuda | 4 | 1 | 0.0084 | 119 | 1167 | 560.0 | 122 |
-| torchsim | jacobian(T1) | cuda | 4 | 10 | 0.0081 | 1,242 | 1158 | 556.2 | 54 |
-| torchsim | jacobian(T1) | cuda | 4 | 100 | 0.0079 | 12,640 | 1209 | 607.9 | 72 |
-| torchsim | jacobian(T1) | cuda | 4 | 1000 | 0.0080 | 124,412 | 1211 | 609.8 | 56 |
-| torchsim | jacobian(T1) | cuda | 4 | 10000 | 0.0237 | 421,684 | 1206 | 605.2 | 316 |
-| torchsim | jacobian(T1) | cuda | 4 | 100000 | 0.1895 | 527,615 | 1209 | 607.7 | 2732 |
-| torchsim | jacobian(T1,T2) | cpu | 4 | 1 | 0.0101 | 99 | 712 | 105.1 | 0 |
-| torchsim | jacobian(T1,T2) | cpu | 4 | 10 | 0.0117 | 856 | 712 | 105.7 | 0 |
-| torchsim | jacobian(T1,T2) | cpu | 4 | 100 | 0.0229 | 4,358 | 714 | 107.2 | 0 |
-| torchsim | jacobian(T1,T2) | cpu | 4 | 1000 | 0.1285 | 7,783 | 757 | 150.1 | 0 |
-| torchsim | jacobian(T1,T2) | cpu | 4 | 10000 | 1.0879 | 9,192 | 1055 | 448.1 | 0 |
-| torchsim | jacobian(T1,T2) | cpu | 4 | 100000 | 9.6565 | 10,356 | 4147 | 3540.4 | 0 |
-| torchsim | jacobian(T1,T2) | cuda | 4 | 1 | 0.0156 | 64 | 1164 | 563.0 | 72 |
-| torchsim | jacobian(T1,T2) | cuda | 4 | 10 | 0.0156 | 640 | 1197 | 595.8 | 54 |
-| torchsim | jacobian(T1,T2) | cuda | 4 | 100 | 0.0159 | 6,282 | 1253 | 651.3 | 54 |
-| torchsim | jacobian(T1,T2) | cuda | 4 | 1000 | 0.0158 | 63,392 | 1206 | 605.0 | 56 |
-| torchsim | jacobian(T1,T2) | cuda | 4 | 10000 | 0.0459 | 217,764 | 1206 | 605.3 | 512 |
-| torchsim | jacobian(T1,T2) | cuda | 4 | 100000 | 0.3813 | 262,295 | 1208 | 606.0 | 4642 |
+| blochsim | forward | cpu | 1 | 1000 | 0.0698 | 14,324 | 724 | 117.5 | 0 |
+| blochsim | forward | cpu | 1 | 10000 | 0.7669 | 13,039 | 822 | 215.4 | 0 |
+| blochsim | forward | cpu | 1 | 100000 | 7.1735 | 13,940 | 1861 | 1254.3 | 0 |
+| blochsim | forward | cpu | 4 | 1 | 0.0009 | 1,132 | 708 | 101.6 | 0 |
+| blochsim | forward | cpu | 4 | 10 | 0.0016 | 6,343 | 708 | 101.1 | 0 |
+| blochsim | forward | cpu | 4 | 100 | 0.0037 | 27,010 | 708 | 101.2 | 0 |
+| blochsim | forward | cpu | 4 | 1000 | 0.0233 | 42,972 | 720 | 113.5 | 0 |
+| blochsim | forward | cpu | 4 | 10000 | 0.1866 | 53,578 | 860 | 252.6 | 0 |
+| blochsim | forward | cpu | 4 | 100000 | 1.9028 | 52,554 | 1861 | 1254.0 | 0 |
+| blochsim | forward | cuda | 4 | 1 | 0.0037 | 267 | 1152 | 551.3 | 54 |
+| blochsim | forward | cuda | 4 | 10 | 0.0034 | 2,942 | 1180 | 582.2 | 54 |
+| blochsim | forward | cuda | 4 | 100 | 0.0028 | 35,256 | 1240 | 639.7 | 54 |
+| blochsim | forward | cuda | 4 | 1000 | 0.0033 | 301,734 | 1202 | 600.6 | 54 |
+| blochsim | forward | cuda | 4 | 10000 | 0.0087 | 1,150,290 | 1194 | 592.4 | 196 |
+| blochsim | forward | cuda | 4 | 100000 | 0.0687 | 1,454,714 | 1204 | 602.3 | 1586 |
+| blochsim | jacobian(T1) | cpu | 4 | 1 | 0.0054 | 185 | 712 | 105.2 | 0 |
+| blochsim | jacobian(T1) | cpu | 4 | 10 | 0.0081 | 1,234 | 712 | 105.4 | 0 |
+| blochsim | jacobian(T1) | cpu | 4 | 100 | 0.0117 | 8,520 | 713 | 106.3 | 0 |
+| blochsim | jacobian(T1) | cpu | 4 | 1000 | 0.0534 | 18,738 | 745 | 138.4 | 0 |
+| blochsim | jacobian(T1) | cpu | 4 | 10000 | 0.5511 | 18,146 | 941 | 333.8 | 0 |
+| blochsim | jacobian(T1) | cpu | 4 | 100000 | 5.4812 | 18,244 | 3007 | 2400.2 | 0 |
+| blochsim | jacobian(T1) | cuda | 4 | 1 | 0.0084 | 119 | 1167 | 560.0 | 122 |
+| blochsim | jacobian(T1) | cuda | 4 | 10 | 0.0081 | 1,242 | 1158 | 556.2 | 54 |
+| blochsim | jacobian(T1) | cuda | 4 | 100 | 0.0079 | 12,640 | 1209 | 607.9 | 72 |
+| blochsim | jacobian(T1) | cuda | 4 | 1000 | 0.0080 | 124,412 | 1211 | 609.8 | 56 |
+| blochsim | jacobian(T1) | cuda | 4 | 10000 | 0.0237 | 421,684 | 1206 | 605.2 | 316 |
+| blochsim | jacobian(T1) | cuda | 4 | 100000 | 0.1895 | 527,615 | 1209 | 607.7 | 2732 |
+| blochsim | jacobian(T1,T2) | cpu | 4 | 1 | 0.0101 | 99 | 712 | 105.1 | 0 |
+| blochsim | jacobian(T1,T2) | cpu | 4 | 10 | 0.0117 | 856 | 712 | 105.7 | 0 |
+| blochsim | jacobian(T1,T2) | cpu | 4 | 100 | 0.0229 | 4,358 | 714 | 107.2 | 0 |
+| blochsim | jacobian(T1,T2) | cpu | 4 | 1000 | 0.1285 | 7,783 | 757 | 150.1 | 0 |
+| blochsim | jacobian(T1,T2) | cpu | 4 | 10000 | 1.0879 | 9,192 | 1055 | 448.1 | 0 |
+| blochsim | jacobian(T1,T2) | cpu | 4 | 100000 | 9.6565 | 10,356 | 4147 | 3540.4 | 0 |
+| blochsim | jacobian(T1,T2) | cuda | 4 | 1 | 0.0156 | 64 | 1164 | 563.0 | 72 |
+| blochsim | jacobian(T1,T2) | cuda | 4 | 10 | 0.0156 | 640 | 1197 | 595.8 | 54 |
+| blochsim | jacobian(T1,T2) | cuda | 4 | 100 | 0.0159 | 6,282 | 1253 | 651.3 | 54 |
+| blochsim | jacobian(T1,T2) | cuda | 4 | 1000 | 0.0158 | 63,392 | 1206 | 605.0 | 56 |
+| blochsim | jacobian(T1,T2) | cuda | 4 | 10000 | 0.0459 | 217,764 | 1206 | 605.3 | 512 |
+| blochsim | jacobian(T1,T2) | cuda | 4 | 100000 | 0.3813 | 262,295 | 1208 | 606.0 | 4642 |
- BlochSimulators.jl n=1000: float32; real RF train, so the states stay real; max_state is a multiple of 32 by construction
- BlochSimulators.jl n=10000: float32; real RF train, so the states stay real; max_state is a multiple of 32 by construction
@@ -125,16 +125,16 @@
- KomaMRI.jl n=10: isochromat: 64 spins per tissue through one spoiler cycle, 640 spins in all
- KomaMRI.jl n=100: isochromat: 64 spins per tissue through one spoiler cycle, 6400 spins in all
- KomaMRI.jl n=1000: isochromat: 64 spins per tissue through one spoiler cycle, 64000 spins in all
-- epgpy n=1: max_nstate matches the orders TorchSim keeps
-- epgpy n=10: max_nstate matches the orders TorchSim keeps
-- epgpy n=100: max_nstate matches the orders TorchSim keeps
-- epgpy n=1000: max_nstate matches the orders TorchSim keeps
-- epgpy n=10000: max_nstate matches the orders TorchSim keeps
-- epgpy n=1: max_nstate matches the orders TorchSim keeps
-- epgpy n=10: max_nstate matches the orders TorchSim keeps
-- epgpy n=100: max_nstate matches the orders TorchSim keeps
-- epgpy n=1000: max_nstate matches the orders TorchSim keeps
-- epgpy n=10000: max_nstate matches the orders TorchSim keeps
+- epgpy n=1: max_nstate matches the orders BlochSim keeps
+- epgpy n=10: max_nstate matches the orders BlochSim keeps
+- epgpy n=100: max_nstate matches the orders BlochSim keeps
+- epgpy n=1000: max_nstate matches the orders BlochSim keeps
+- epgpy n=10000: max_nstate matches the orders BlochSim keeps
+- epgpy n=1: max_nstate matches the orders BlochSim keeps
+- epgpy n=10: max_nstate matches the orders BlochSim keeps
+- epgpy n=100: max_nstate matches the orders BlochSim keeps
+- epgpy n=1000: max_nstate matches the orders BlochSim keeps
+- epgpy n=10000: max_nstate matches the orders BlochSim keeps
- sycomore n=1: threshold=1e-06; orders reached 11-11
- sycomore n=10: threshold=1e-06; orders reached 11-158
- sycomore n=100: threshold=1e-06; orders reached 11-158
diff --git a/benchmarks/results/validation.json b/benchmarks/results/validation.json
index 8512a5c9..fe98e1c7 100644
--- a/benchmarks/results/validation.json
+++ b/benchmarks/results/validation.json
@@ -77,7 +77,7 @@
"nrmse": 0.03221091974573033
}
],
- "TorchSim": [
+ "BlochSim": [
{
"T1_ms": 200.0,
"T2_ms": 10.0,
diff --git a/benchmarks/results/validation.txt b/benchmarks/results/validation.txt
index 29306340..cbe27d04 100644
--- a/benchmarks/results/validation.txt
+++ b/benchmarks/results/validation.txt
@@ -8,9 +8,9 @@ Reference: sycomore, every order kept, double precision.
32 5.429e-03 3.10e-02
64 2.675e-03 1.53e-02
-TorchSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
+BlochSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
-TorchSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
+BlochSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
BlochSimulators.jl against sycomore, per tissue:
T1 = 200 ms, T2 = 10 ms: max 1.019e-07, NRMSE 4.143e-07
@@ -28,7 +28,7 @@ Per tissue, at 32 orders:
T1 = 3000 ms, T2 = 300 ms: max 3.916e-04, NRMSE 2.288e-03
T1 = 4000 ms, T2 = 2000 ms: max 5.429e-03, NRMSE 3.221e-02
-written to /home/mcencini/pulserver-project/refcode/recon/torchsim/benchmarks/results/validation.json
+written to /home/mcencini/pulserver-project/refcode/recon/blochsim/benchmarks/results/validation.json
MRF-FISP, 500 repetitions, TR = 10.0 ms, inversion prepared.
Reference: sycomore, every order kept, double precision.
@@ -39,9 +39,9 @@ Reference: sycomore, every order kept, double precision.
32 5.429e-03 3.10e-02
64 2.675e-03 1.53e-02
-TorchSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
+BlochSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
-TorchSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
+BlochSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
KomaMRI.jl, 4 spins against sycomore, per tissue:
T1 = 1000 ms, T2 = 80 ms: max 5.246e-02, NRMSE 1.115e-01
@@ -55,7 +55,7 @@ Per tissue, at 32 orders:
T1 = 3000 ms, T2 = 300 ms: max 3.916e-04, NRMSE 2.288e-03
T1 = 4000 ms, T2 = 2000 ms: max 5.429e-03, NRMSE 3.221e-02
-written to /home/mcencini/pulserver-project/refcode/recon/torchsim/benchmarks/results/validation.json
+written to /home/mcencini/pulserver-project/refcode/recon/blochsim/benchmarks/results/validation.json
MRF-FISP, 500 repetitions, TR = 10.0 ms, inversion prepared.
Reference: sycomore, every order kept, double precision.
@@ -66,9 +66,9 @@ Reference: sycomore, every order kept, double precision.
32 5.429e-03 3.10e-02
64 2.675e-03 1.53e-02
-TorchSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
+BlochSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
-TorchSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
+BlochSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
KomaMRI.jl, 8 spins against sycomore, per tissue:
T1 = 1000 ms, T2 = 80 ms: max 3.116e-02, NRMSE 6.823e-02
@@ -82,7 +82,7 @@ Per tissue, at 32 orders:
T1 = 3000 ms, T2 = 300 ms: max 3.916e-04, NRMSE 2.288e-03
T1 = 4000 ms, T2 = 2000 ms: max 5.429e-03, NRMSE 3.221e-02
-written to /home/mcencini/pulserver-project/refcode/recon/torchsim/benchmarks/results/validation.json
+written to /home/mcencini/pulserver-project/refcode/recon/blochsim/benchmarks/results/validation.json
MRF-FISP, 500 repetitions, TR = 10.0 ms, inversion prepared.
Reference: sycomore, every order kept, double precision.
@@ -93,9 +93,9 @@ Reference: sycomore, every order kept, double precision.
32 5.429e-03 3.10e-02
64 2.675e-03 1.53e-02
-TorchSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
+BlochSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
-TorchSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
+BlochSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
KomaMRI.jl, 16 spins against sycomore, per tissue:
T1 = 1000 ms, T2 = 80 ms: max 1.092e-02, NRMSE 2.211e-02
@@ -109,7 +109,7 @@ Per tissue, at 32 orders:
T1 = 3000 ms, T2 = 300 ms: max 3.916e-04, NRMSE 2.288e-03
T1 = 4000 ms, T2 = 2000 ms: max 5.429e-03, NRMSE 3.221e-02
-written to /home/mcencini/pulserver-project/refcode/recon/torchsim/benchmarks/results/validation.json
+written to /home/mcencini/pulserver-project/refcode/recon/blochsim/benchmarks/results/validation.json
MRF-FISP, 500 repetitions, TR = 10.0 ms, inversion prepared.
Reference: sycomore, every order kept, double precision.
@@ -120,9 +120,9 @@ Reference: sycomore, every order kept, double precision.
32 5.429e-03 3.10e-02
64 2.675e-03 1.53e-02
-TorchSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
+BlochSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
-TorchSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
+BlochSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
KomaMRI.jl, 32 spins against sycomore, per tissue:
T1 = 1000 ms, T2 = 80 ms: max 1.225e-03, NRMSE 2.068e-03
@@ -136,7 +136,7 @@ Per tissue, at 32 orders:
T1 = 3000 ms, T2 = 300 ms: max 3.916e-04, NRMSE 2.288e-03
T1 = 4000 ms, T2 = 2000 ms: max 5.429e-03, NRMSE 3.221e-02
-written to /home/mcencini/pulserver-project/refcode/recon/torchsim/benchmarks/results/validation.json
+written to /home/mcencini/pulserver-project/refcode/recon/blochsim/benchmarks/results/validation.json
MRF-FISP, 500 repetitions, TR = 10.0 ms, inversion prepared.
Reference: sycomore, every order kept, double precision.
@@ -147,9 +147,9 @@ Reference: sycomore, every order kept, double precision.
32 5.429e-03 3.10e-02
64 2.675e-03 1.53e-02
-TorchSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
+BlochSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
-TorchSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
+BlochSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
KomaMRI.jl, 64 spins against sycomore, per tissue:
T1 = 1000 ms, T2 = 80 ms: max 1.511e-05, NRMSE 1.352e-04
@@ -163,7 +163,7 @@ Per tissue, at 32 orders:
T1 = 3000 ms, T2 = 300 ms: max 3.916e-04, NRMSE 2.288e-03
T1 = 4000 ms, T2 = 2000 ms: max 5.429e-03, NRMSE 3.221e-02
-written to /home/mcencini/pulserver-project/refcode/recon/torchsim/benchmarks/results/validation.json
+written to /home/mcencini/pulserver-project/refcode/recon/blochsim/benchmarks/results/validation.json
MRF-FISP, 500 repetitions, TR = 10.0 ms, inversion prepared.
Reference: sycomore, every order kept, double precision.
@@ -174,9 +174,9 @@ Reference: sycomore, every order kept, double precision.
32 5.429e-03 3.10e-02
64 2.675e-03 1.53e-02
-TorchSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
+BlochSim on NVIDIA GeForce RTX 4060 Laptop GPU against its CPU kernel: max 1.006e-06, relative 5.75e-06
-TorchSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
+BlochSim against epgpy, both truncated to 32 orders: max 1.614e-03, relative 9.23e-03
KomaMRI.jl, 128 spins against sycomore, per tissue:
T1 = 1000 ms, T2 = 80 ms: max 1.509e-05, NRMSE 1.321e-04
@@ -190,4 +190,4 @@ Per tissue, at 32 orders:
T1 = 3000 ms, T2 = 300 ms: max 3.916e-04, NRMSE 2.288e-03
T1 = 4000 ms, T2 = 2000 ms: max 5.429e-03, NRMSE 3.221e-02
-written to /home/mcencini/pulserver-project/refcode/recon/torchsim/benchmarks/results/validation.json
+written to /home/mcencini/pulserver-project/refcode/recon/blochsim/benchmarks/results/validation.json
diff --git a/benchmarks/run_all.py b/benchmarks/run_all.py
index 52b5ab4e..99ff16fe 100644
--- a/benchmarks/run_all.py
+++ b/benchmarks/run_all.py
@@ -39,7 +39,7 @@
# thousand atoms in seconds; KomaMRI carries `spins` isochromats per tissue and
# is stopped a decade earlier because of it.
SIZES = {
- "torchsim": (1, 10, 100, 1_000, 10_000, 100_000),
+ "blochsim": (1, 10, 100, 1_000, 10_000, 100_000),
"sycomore": (1, 10, 100, 1_000, 10_000),
"epgpy": (1, 10, 100, 1_000, 10_000),
"blochsimulators": (1, 10, 100, 1_000, 10_000, 100_000),
@@ -52,7 +52,7 @@
# fit in the memory of a modest card, and past that what is measured is
# whatever the run spills into rather than the kernel.
DEVICE_SIZES = {
- "torchsim": (1, 10, 100, 1_000, 10_000, 100_000),
+ "blochsim": (1, 10, 100, 1_000, 10_000, 100_000),
"blochsimulators": (1, 10, 100, 1_000, 10_000, 100_000),
"koma": (1, 10, 100, 1_000),
}
@@ -65,7 +65,7 @@ def available(backend: str, device: str) -> bool:
return False
if backend in ("blochsimulators", "koma"):
return bool(julia())
- module = {"torchsim": "torchsim", "sycomore": "sycomore", "epgpy": "epgpy"}[backend]
+ module = {"blochsim": "blochsim", "sycomore": "sycomore", "epgpy": "epgpy"}[backend]
from importlib.util import find_spec
try:
@@ -189,14 +189,14 @@ def main() -> None:
default = DEVICE_SIZES[backend] if device != "cpu" else SIZES[backend]
sizes = [n for n in (chosen or default) if n <= limit]
- if backend == "torchsim":
+ if backend == "blochsim":
for atoms in sizes:
for mode in (
("forward",) if args.forward_only else ("forward", "jacobian")
):
python_run(
- "bench_torchsim.py",
- f"torchsim-{mode}-{atoms}{suffix}",
+ "bench_blochsim.py",
+ f"blochsim-{mode}-{atoms}{suffix}",
atoms=atoms,
mode=mode,
device=device,
@@ -206,8 +206,8 @@ def main() -> None:
if args.forward_only:
continue
python_run(
- "bench_torchsim.py",
- f"torchsim-jacobian1-{atoms}{suffix}",
+ "bench_blochsim.py",
+ f"blochsim-jacobian1-{atoms}{suffix}",
atoms=atoms,
mode="jacobian",
diff="T1",
@@ -221,8 +221,8 @@ def main() -> None:
# the pool; on a card there is no such pair to take.
for atoms in [n for n in sizes if n >= 1_000]:
python_run(
- "bench_torchsim.py",
- f"torchsim-forward-{atoms}-{device}-t1",
+ "bench_blochsim.py",
+ f"blochsim-forward-{atoms}-{device}-t1",
atoms=atoms,
mode="forward",
device=device,
diff --git a/benchmarks/setup.sh b/benchmarks/setup.sh
index 742fab84..d78b8a31 100644
--- a/benchmarks/setup.sh
+++ b/benchmarks/setup.sh
@@ -8,7 +8,7 @@
#
# What it makes, all under $PREFIX (benchmarks/.env by default):
#
-# .env/venv a virtual environment with PyTorch, TorchSim, epgpy and
+# .env/venv a virtual environment with PyTorch, BlochSim, epgpy and
# sycomore in it
# .env/micromamba a package manager, only if conda-forge is needed
# .env/julia a Julia, only if there is not one on the path already
@@ -54,7 +54,7 @@ setup_python() {
say "python environment in $VENV"
# $PYTHON says which interpreter to build on. It matters: an interpreter that
# ships a C++ runtime of its own -- a conda one does -- loads that runtime
- # ahead of the system's, and TorchSim's kernels are compiled against the
+ # ahead of the system's, and BlochSim's kernels are compiled against the
# system's. The extension then fails to load with a missing GLIBCXX version,
# every fused kernel is reported absent, and nothing here can run.
[ -d "$VENV" ] || "${PYTHON:-python3}" -m venv "$VENV"
@@ -67,7 +67,7 @@ setup_python() {
# pip install torch --index-url https://download.pytorch.org/whl/cpu
"$VENV/bin/pip" install --quiet torch
- say "TorchSim, from this checkout"
+ say "BlochSim, from this checkout"
"$VENV/bin/pip" install --quiet -e "$ROOT"
say "epgpy"
@@ -91,7 +91,7 @@ setup_python() {
"$VENV/bin/pip" install --quiet matplotlib
"$VENV/bin/python" - <<'PY'
-for name in ("torch", "torchsim", "epgpy", "sycomore", "matplotlib"):
+for name in ("torch", "blochsim", "epgpy", "sycomore", "matplotlib"):
try:
__import__(name)
print(f" {name}: ok")
diff --git a/benchmarks/validate.py b/benchmarks/validate.py
index 51bc13bc..ba49e81c 100644
--- a/benchmarks/validate.py
+++ b/benchmarks/validate.py
@@ -1,4 +1,4 @@
-"""Whether TorchSim, sycomore and epgpy compute the same fingerprint.
+"""Whether BlochSim, sycomore and epgpy compute the same fingerprint.
Three independent implementations of the same model -- a fused C++ or Triton
state machine over a packed event stream, a C++ EPG library driven one tissue
@@ -6,7 +6,7 @@
to the precision the coarsest of them carries. This says by how much they do,
over a tissue grid, and shows what truncating the configuration orders costs.
-Where there is a card, TorchSim's two kernels are held against each other as
+Where there is a card, BlochSim's two kernels are held against each other as
well: they are separate implementations of one recursion, and a run placed on a
card has to answer what the same run on the CPU answers.
@@ -67,7 +67,7 @@ def epgpy_signal(
return np.asarray(epg.simulate(sequence, max_nstate=states)).T
-def torchsim_signal(
+def blochsim_signal(
T1: np.ndarray,
T2: np.ndarray,
flip: np.ndarray,
@@ -78,7 +78,7 @@ def torchsim_signal(
"""The same train on the fused state machine, at a given order count."""
import torch
- from torchsim.simulators import MRFSimulator
+ from blochsim.simulators import MRFSimulator
where = torch.device(device)
sequence = MRFSimulator(
@@ -152,7 +152,7 @@ def main() -> None:
print("Reference: sycomore, every order kept, double precision.\n")
print(f"{'states':>7s} {'max |difference|':>17s} {'relative':>10s}")
for states in (4, 8, 16, 32, 64):
- signal = torchsim_signal(T1, T2, flip, TR, states)
+ signal = blochsim_signal(T1, T2, flip, TR, states)
# The two differ by the constant phase each writes its echo with.
turn = np.exp(1j * np.angle(np.sum(reference * np.conj(signal))))
difference = np.abs(reference - signal * turn)
@@ -168,11 +168,11 @@ def main() -> None:
# The two kernels are separate implementations of the same recursion,
# so this is a comparison of the C++ one against the Triton one rather
# than a check that a tensor made the trip.
- on_cpu = torchsim_signal(T1, T2, flip, TR, arguments.states)
- on_card = torchsim_signal(T1, T2, flip, TR, arguments.states, device="cuda")
+ on_cpu = blochsim_signal(T1, T2, flip, TR, arguments.states)
+ on_card = blochsim_signal(T1, T2, flip, TR, arguments.states, device="cuda")
difference = np.abs(on_cpu - on_card)
print(
- f"\nTorchSim on {torch.cuda.get_device_name(0)} against its CPU kernel: "
+ f"\nBlochSim on {torch.cuda.get_device_name(0)} against its CPU kernel: "
f"max {difference.max():.3e}, "
f"relative {difference.max() / np.abs(on_cpu).max():.2e}"
)
@@ -187,11 +187,11 @@ def main() -> None:
except ImportError:
other = None
if other is not None:
- signal = torchsim_signal(T1, T2, flip, TR, arguments.states)
+ signal = blochsim_signal(T1, T2, flip, TR, arguments.states)
turn = np.exp(1j * np.angle(np.sum(other * np.conj(signal))))
difference = np.abs(other - signal * turn)
print(
- f"\nTorchSim against epgpy, both truncated to {arguments.states} orders: "
+ f"\nBlochSim against epgpy, both truncated to {arguments.states} orders: "
f"max {difference.max():.3e}, "
f"relative {difference.max() / np.abs(other).max():.2e}"
)
@@ -239,7 +239,7 @@ def main() -> None:
)
print(f"\nPer tissue, at {arguments.states} orders:")
- signal = torchsim_signal(T1, T2, flip, TR, arguments.states)
+ signal = blochsim_signal(T1, T2, flip, TR, arguments.states)
turn = np.exp(1j * np.angle(np.sum(reference * np.conj(signal))))
for row, (a, b) in enumerate(TISSUES):
difference = np.abs(reference[row] - signal[row] * turn)
@@ -248,7 +248,7 @@ def main() -> None:
f"max {difference.max():.3e}, "
f"NRMSE {np.sqrt(np.mean(difference**2)) / np.sqrt(np.mean(np.abs(reference[row]) ** 2)):.3e}"
)
- record.setdefault("against", {}).setdefault("TorchSim", []).append(
+ record.setdefault("against", {}).setdefault("BlochSim", []).append(
{
"T1_ms": a,
"T2_ms": b,
diff --git a/docs/_static/blochsim-logo-dark.svg b/docs/_static/blochsim-logo-dark.svg
new file mode 100644
index 00000000..155c160e
--- /dev/null
+++ b/docs/_static/blochsim-logo-dark.svg
@@ -0,0 +1,19 @@
+
diff --git a/docs/_static/blochsim-logo.svg b/docs/_static/blochsim-logo.svg
new file mode 100644
index 00000000..4428eb30
--- /dev/null
+++ b/docs/_static/blochsim-logo.svg
@@ -0,0 +1,19 @@
+
diff --git a/docs/_static/blochsim-mark-dark.svg b/docs/_static/blochsim-mark-dark.svg
new file mode 100644
index 00000000..3f9b3e12
--- /dev/null
+++ b/docs/_static/blochsim-mark-dark.svg
@@ -0,0 +1,18 @@
+
diff --git a/docs/_static/blochsim-mark.svg b/docs/_static/blochsim-mark.svg
new file mode 100644
index 00000000..548e1d9d
--- /dev/null
+++ b/docs/_static/blochsim-mark.svg
@@ -0,0 +1,18 @@
+
diff --git a/docs/api/estimators.md b/docs/api/estimators.md
index a45a0c46..efcfedc5 100644
--- a/docs/api/estimators.md
+++ b/docs/api/estimators.md
@@ -1,7 +1,7 @@
# Parameter estimation
```{eval-rst}
-.. currentmodule:: torchsim
+.. currentmodule:: blochsim
```
Estimating tissue properties from a measured volume. An estimator is made from
diff --git a/docs/api/execution.md b/docs/api/execution.md
index e230a5eb..3f5e9445 100644
--- a/docs/api/execution.md
+++ b/docs/api/execution.md
@@ -1,7 +1,7 @@
# Running a simulation
```{eval-rst}
-.. currentmodule:: torchsim
+.. currentmodule:: blochsim
```
The differentiable state machine a sequence description is run on, what the
@@ -18,7 +18,7 @@ cards as there are -- and it is stated once, around the call.
The engine runs a {class}`SequenceDescription` directly, which is what
you reach for when a builder or an interpreter handed you a description rather
-than a simulator. A {class}`~torchsim.model.Simulator` wraps it and is how a
+than a simulator. A {class}`~blochsim.model.Simulator` wraps it and is how a
sequence is usually written; see {doc}`model`.
```{eval-rst}
diff --git a/docs/api/index.md b/docs/api/index.md
index f12a0317..5827b3d3 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -1,6 +1,6 @@
# API
-TorchSim is organized around one idea: a **signal model** is the only thing a
+BlochSim is organized around one idea: a **signal model** is the only thing a
sequence has to supply, and everything else -- differentiation, execution
across devices, estimation, reconstruction, design -- is written once against
that interface.
@@ -13,7 +13,7 @@ The pages below follow the subpackages.
order they are played in.
{doc}`simulators`
-: The sequences that ship with TorchSim, as classes and as one-call
+: The sequences that ship with BlochSim, as classes and as one-call
functional wrappers.
{doc}`sequence`
diff --git a/docs/api/model.md b/docs/api/model.md
index 89717b04..d5ef14c8 100644
--- a/docs/api/model.md
+++ b/docs/api/model.md
@@ -1,7 +1,7 @@
# Signal models
```{eval-rst}
-.. currentmodule:: torchsim.model
+.. currentmodule:: blochsim.model
```
**There is one user-facing base class: {class}`Simulator`.**
@@ -56,7 +56,7 @@ Nothing is declared about the tissue. Every property a voxel can have may be
given to any simulator, and giving one is what turns its term on.
A sequence that came from somewhere else is read through those same handlers.
-{func}`~torchsim.sequence.read_mrd_description` decodes the description
+{func}`~blochsim.sequence.read_mrd_description` decodes the description
carried ahead of the acquisitions on an MRD stream, and
{meth}`~Simulator.from_description` turns one of those descriptions into the
chosen simulator. {meth}`~Simulator.from_pulseq` does the same from a Pulseq
@@ -68,8 +68,8 @@ Implement {meth}`~Simulator.evaluate` instead when the signal has a closed
form -- a mono-exponential decay, an inversion-recovery curve, an Ernst
steady state. There is nothing to play and no state to carry, so there is no
`layout` and the `SpinPhysics` carries only the property declaration.
-{class}`~torchsim.simulators.SPGRSimulator` is written this way, and
-{class}`~torchsim.simulators.MP2RAGESimulator` carries both: the closed form a
+{class}`~blochsim.simulators.SPGRSimulator` is written this way, and
+{class}`~blochsim.simulators.MP2RAGESimulator` carries both: the closed form a
lookup table is built from, and the layout a description arriving from a
scanner is compared against.
@@ -132,12 +132,12 @@ the slots itself:
| {data}`REFOCUSED` | the sample at the echo centre | crushed |
```{eval-rst}
-.. autodata:: torchsim.model.SPOILED
+.. autodata:: blochsim.model.SPOILED
:no-value:
-.. autodata:: torchsim.model.UNBALANCED
+.. autodata:: blochsim.model.UNBALANCED
:no-value:
-.. autodata:: torchsim.model.BALANCED
+.. autodata:: blochsim.model.BALANCED
:no-value:
-.. autodata:: torchsim.model.REFOCUSED
+.. autodata:: blochsim.model.REFOCUSED
:no-value:
```
diff --git a/docs/api/optim.md b/docs/api/optim.md
index af5b07fb..6259af72 100644
--- a/docs/api/optim.md
+++ b/docs/api/optim.md
@@ -1,7 +1,7 @@
# Sequence design
```{eval-rst}
-.. currentmodule:: torchsim
+.. currentmodule:: blochsim
```
Choosing a sequence's parameters by minimizing a cost you write. The
diff --git a/docs/api/recon.md b/docs/api/recon.md
index 25e63e5d..3e3f726e 100644
--- a/docs/api/recon.md
+++ b/docs/api/recon.md
@@ -1,7 +1,7 @@
# Model-based reconstruction
```{eval-rst}
-.. currentmodule:: torchsim
+.. currentmodule:: blochsim
```
Solving for parameter maps straight from k-space, with the signal model inside
diff --git a/docs/api/sequence.md b/docs/api/sequence.md
index 3a637463..1448999d 100644
--- a/docs/api/sequence.md
+++ b/docs/api/sequence.md
@@ -1,7 +1,7 @@
# Sequences
```{eval-rst}
-.. currentmodule:: torchsim
+.. currentmodule:: blochsim
```
Device-agnostic description of an acquisition, shared by the interpreter, the
@@ -17,7 +17,7 @@ and the temporal basis a run spans is on {doc}`recon`.
The modules a sequence is assembled from. An operator is a Python function
returning the events it plays and how long it holds the timeline, and `@`
-composes two into one -- so a preparation or a readout TorchSim does not ship
+composes two into one -- so a preparation or a readout BlochSim does not ship
reaches the fused kernels with no change to them.
The five readouts differ only in what they play around the sample, which is
@@ -78,10 +78,10 @@ arriving from a scanner is read into.
{meth}`SequenceDescription.from_pulseq` reads one out of a Pulseq `.seq` file
or a sequence a design holds in memory, {func}`read_mrd_description` reads the
ones an MRD stream carries ahead of its first acquisition,
-and {meth}`~torchsim.model.Simulator.from_description` runs any of them.
+and {meth}`~blochsim.model.Simulator.from_description` runs any of them.
Reading a `.seq` file needs pypulseq, which parses the format and computes the
-gradient trajectory the echo is found on: `pip install torchsim[pulseq]`.
+gradient trajectory the echo is found on: `pip install blochsim[pulseq]`.
Nothing else in the package imports it.
```{eval-rst}
diff --git a/docs/api/simulators.md b/docs/api/simulators.md
index 1a63fb2f..c76ea769 100644
--- a/docs/api/simulators.md
+++ b/docs/api/simulators.md
@@ -1,7 +1,7 @@
# Simulators
-The sequences that ship with TorchSim. A constructor takes the keywords
-{meth}`~torchsim.model.Simulator.simulate` takes and fixes them, so a
+The sequences that ship with BlochSim. A constructor takes the keywords
+{meth}`~blochsim.model.Simulator.simulate` takes and fixes them, so a
sequence and the tissue it is being asked about are written down together and
what is left to give at the call is whatever is actually varying.
@@ -14,22 +14,22 @@ Signals that have an analytic expression, evaluated in one pass.
:toctree: ../generated
:nosignatures:
- torchsim.simulators.bSSFPSimulator
- torchsim.simulators.SPGRSimulator
- torchsim.simulators.MP2RAGESimulator
- torchsim.simulators.InversionRecoverySimulator
- torchsim.simulators.MultiEchoSimulator
- torchsim.simulators.DoubleAngleSimulator
- torchsim.simulators.SpinEchoSimulator
- torchsim.simulators.LookLockerSimulator
- torchsim.simulators.MOLLISimulator
- torchsim.simulators.IRbSSFPSimulator
- torchsim.simulators.HSFPSimulator
- torchsim.simulators.MultiGradientEchoSimulator
- torchsim.simulators.IRMultiGradientEchoSimulator
- torchsim.simulators.DiffusionSimulator
- torchsim.simulators.LorentzianSimulator
- torchsim.simulators.ASLSimulator
+ blochsim.simulators.bSSFPSimulator
+ blochsim.simulators.SPGRSimulator
+ blochsim.simulators.MP2RAGESimulator
+ blochsim.simulators.InversionRecoverySimulator
+ blochsim.simulators.MultiEchoSimulator
+ blochsim.simulators.DoubleAngleSimulator
+ blochsim.simulators.SpinEchoSimulator
+ blochsim.simulators.LookLockerSimulator
+ blochsim.simulators.MOLLISimulator
+ blochsim.simulators.IRbSSFPSimulator
+ blochsim.simulators.HSFPSimulator
+ blochsim.simulators.MultiGradientEchoSimulator
+ blochsim.simulators.IRMultiGradientEchoSimulator
+ blochsim.simulators.DiffusionSimulator
+ blochsim.simulators.LorentzianSimulator
+ blochsim.simulators.ASLSimulator
```
## State machine
@@ -41,16 +41,16 @@ Trains that have to be played out, run on the extended phase graph engine.
:toctree: ../generated
:nosignatures:
- torchsim.simulators.FSESimulator
- torchsim.simulators.HyperechoSimulator
- torchsim.simulators.StimulatedEchoSimulator
- torchsim.simulators.MPRAGESimulator
- torchsim.simulators.MPnRAGESimulator
- torchsim.simulators.MRFSimulator
- torchsim.simulators.FLASHSimulator
- torchsim.simulators.TrueFISPSimulator
- torchsim.simulators.fmSSFPSimulator
- torchsim.simulators.CESTSimulator
+ blochsim.simulators.FSESimulator
+ blochsim.simulators.HyperechoSimulator
+ blochsim.simulators.StimulatedEchoSimulator
+ blochsim.simulators.MPRAGESimulator
+ blochsim.simulators.MPnRAGESimulator
+ blochsim.simulators.MRFSimulator
+ blochsim.simulators.FLASHSimulator
+ blochsim.simulators.TrueFISPSimulator
+ blochsim.simulators.fmSSFPSimulator
+ blochsim.simulators.CESTSimulator
```
## Functional wrappers
@@ -58,8 +58,8 @@ Trains that have to be played out, run on the extended phase graph engine.
The `*_sim` functions are convenience calls for a subset of the shipped
simulators: protocol and tissue go into one function call, with an optional
Jacobian. They are not a second extension API. New sequence families are
-implemented as {class}`~torchsim.model.Simulator` subclasses, so they can use
-both an offline {meth}`~torchsim.model.Simulator.layout` and the same handlers
+implemented as {class}`~blochsim.model.Simulator` subclasses, so they can use
+both an offline {meth}`~blochsim.model.Simulator.layout` and the same handlers
when a Pulseq/MRD description arrives from a scanner.
Prefer the simulator classes whenever the same sequence is reused, bound to a
@@ -73,8 +73,8 @@ from a description. The wrappers remain useful for compact one-off calls.
:toctree: ../generated
:nosignatures:
- torchsim.bssfp_sim
- torchsim.spgr_sim
+ blochsim.bssfp_sim
+ blochsim.spgr_sim
```
### Iterative
@@ -84,9 +84,9 @@ from a description. The wrappers remain useful for compact one-off calls.
:toctree: ../generated
:nosignatures:
- torchsim.fse_sim
- torchsim.mprage_sim
- torchsim.mp2rage_sim
- torchsim.mpnrage_sim
- torchsim.mrf_sim
+ blochsim.fse_sim
+ blochsim.mprage_sim
+ blochsim.mp2rage_sim
+ blochsim.mpnrage_sim
+ blochsim.mrf_sim
```
diff --git a/docs/binder/environment.yml b/docs/binder/environment.yml
index 297f0188..14b4beb8 100644
--- a/docs/binder/environment.yml
+++ b/docs/binder/environment.yml
@@ -1,4 +1,4 @@
-name: torchsim
+name: blochsim
channels:
- conda-forge
dependencies:
@@ -7,4 +7,4 @@ dependencies:
- pip:
# The gallery notebooks import the phantom, coil, Fourier-encoding and
# solver packages while they run, which is the ``examples`` extra.
- - torchsim[examples]
+ - blochsim[examples]
diff --git a/docs/conf.py b/docs/conf.py
index c66b7510..655b4e45 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,9 +51,9 @@ def _cores_allowed() -> int:
# -- Project information -----------------------------------------------------
-project = "torchsim"
-copyright = "2024, TorchSim Contributors"
-author = "TorchSim Contributors"
+project = "blochsim"
+copyright = "2024, BlochSim Contributors"
+author = "BlochSim Contributors"
# -- General configuration ---------------------------------------------------
@@ -142,10 +142,10 @@ def _cores_allowed() -> int:
#: Which of the published versions this build is: ``latest`` for the
#: development branch, the tag for a release. One directory of the site per
#: version, and the switcher marks the one being read.
-DOCS_VERSION = os.environ.get("TORCHSIM_DOCS_VERSION", "latest")
+DOCS_VERSION = os.environ.get("BLOCHSIM_DOCS_VERSION", "latest")
#: Where the pages are served from.
-PAGES_URL = "https://pulserver.github.io/torchsim"
+PAGES_URL = "https://pulserver.github.io/blochsim"
# -- Options for Sphinx Gallery ----------------------------------------------
@@ -189,7 +189,7 @@ def _missing_imports(script: Path) -> list[str]:
#: example runs where everything it imports is installed, so an environment
#: holding the ``examples`` extra executes the whole gallery -- which is what
#: the published pages are built with -- and one holding ``doc`` alone
-#: executes what needs nothing but TorchSim, which is what a branch is
+#: executes what needs nothing but BlochSim, which is what a branch is
#: checked with while it waits.
UNRUNNABLE = {
script: missing
@@ -207,9 +207,9 @@ def _missing_imports(script: Path) -> list[str]:
)
sphinx_gallery_conf = {
- "doc_module": "torchsim",
+ "doc_module": "blochsim",
"backreferences_dir": "generated/gallery_backreferences",
- "reference_url": {"torchsim": None},
+ "reference_url": {"blochsim": None},
"examples_dirs": ["../examples/"],
"gallery_dirs": ["generated/autoexamples"],
"filename_pattern": EXECUTED_PATTERN,
@@ -223,7 +223,7 @@ def _missing_imports(script: Path) -> list[str]:
"reset_modules": ("figure_style.reset", "seaborn"),
"binder": {
"org": "pulserver",
- "repo": "torchsim",
+ "repo": "blochsim",
"branch": "gh-pages",
"binderhub_url": "https://mybinder.org",
"dependencies": [
@@ -258,7 +258,7 @@ def _missing_imports(script: Path) -> list[str]:
html_static_path = ["_static"]
html_css_files = ["custom.css"]
html_theme_options = {
- "repository_url": "https://github.com/pulserver/torchsim",
+ "repository_url": "https://github.com/pulserver/blochsim",
"use_repository_button": True,
"use_issues_button": True,
"use_edit_page_button": True,
@@ -275,6 +275,11 @@ def _missing_imports(script: Path) -> list[str]:
"show_navbar_depth": 1,
"max_navbar_depth": 3,
"navbar_persistent": [],
+ "logo": {
+ "image_light": "_static/blochsim-mark.svg",
+ "image_dark": "_static/blochsim-mark-dark.svg",
+ "alt_text": "blochsim",
+ },
}
#: The theme's own sidebar, with the version switcher under the title.
@@ -291,9 +296,8 @@ def _missing_imports(script: Path) -> list[str]:
#: One canonical address per page, under the version it belongs to.
html_baseurl = f"{PAGES_URL}/{DOCS_VERSION}/"
-# html_logo = "_static/logos/mri-nufft.png"
-# html_favicon = "_static/logos/mri-nufft-icon.png"
-html_title = "TorchSim Documentation"
+html_favicon = "_static/blochsim-mark.svg"
+html_title = "BlochSim Documentation"
def _skip_undocumented_specials(app, what, name, obj, skip, options):
@@ -374,7 +378,7 @@ def _unflagged(content: str) -> str:
def _draw_explanation_figures(app) -> None:
- """Render the explanation pages' figures with the TorchSim being built.
+ """Render the explanation pages' figures with the BlochSim being built.
They are simulated rather than drawn once and checked in, so a figure on
those pages cannot outlive the behaviour it shows.
diff --git a/docs/developer_guide.md b/docs/developer_guide.md
index 49367cbb..ae089677 100644
--- a/docs/developer_guide.md
+++ b/docs/developer_guide.md
@@ -1,6 +1,6 @@
# Developer Guide
-How to set up to change TorchSim: what has to be on the machine, how to
+How to set up to change BlochSim: what has to be on the machine, how to
install the source so your edits take effect, the style the code is written
in, how to run the tests and build these pages, and how a pull request is
opened.
@@ -16,8 +16,8 @@ Beyond what the {doc}`user_guide` asks of everyone -- an isolated environment
and a PyTorch build chosen for your hardware -- working on the source needs:
**A C++17 compiler.** The CPU state machine and the PERK kernel are C++
-extensions built from `src/torchsim/_epg_cpu.cpp` and
-`src/torchsim/_perk_cpu.cpp`. An install from source compiles them, and
+extensions built from `src/blochsim/_epg_cpu.cpp` and
+`src/blochsim/_perk_cpu.cpp`. An install from source compiles them, and
there is no Python fallback to fall back to.
::::{tab-set}
@@ -49,7 +49,7 @@ the compiler is on the path.
::::
-**Git**, and a fork of https://github.com/pulserver/torchsim if you intend to
+**Git**, and a fork of https://github.com/pulserver/blochsim if you intend to
open a pull request.
**An NVIDIA card, if you want to touch the GPU kernels.** They are Triton,
@@ -60,8 +60,8 @@ card runs it for real.
## Installing for development
```sh
-git clone https://github.com/pulserver/torchsim
-cd torchsim
+git clone https://github.com/pulserver/blochsim
+cd blochsim
pip install -e ".[dev]"
pre-commit install
```
@@ -77,7 +77,7 @@ puts the hooks *available*, and this is what puts them *in effect*. See
{ref}`dev-style` for what they check and how to step around one.
An editable install puts the Python sources on the path, so an edit under
-`src/torchsim` takes effect on the next import with nothing to rebuild. The
+`src/blochsim` takes effect on the next import with nothing to rebuild. The
C++ extensions are different -- they are compiled artifacts:
```sh
@@ -92,29 +92,29 @@ the one you just built:
```sh
pip install -e ".[dev]" ; echo "exit: $?"
-python -c "import torchsim._epg_cpu as k; print(k.__file__)"
+python -c "import blochsim._epg_cpu as k; print(k.__file__)"
```
### Where the source lives
-`src/torchsim/sequence/`
+`src/blochsim/sequence/`
: The description an acquisition is assembled from -- events, operators,
builders -- and the dispatch that turns one into a kernel launch. The
Triton kernels are `_epg_triton.py`; the shared parameter ABI, which the
Python dispatch, the C++ extension and the Triton kernels all read, is
`_parameters.py`.
-`src/torchsim/model/`
+`src/blochsim/model/`
: What a signal model is: the physics, the simulator that orders its events,
and the binding that resolves a protocol's structure once and rebinds its
values per call.
-`src/torchsim/_epg_cpu.cpp`, `src/torchsim/_perk_cpu.cpp`
+`src/blochsim/_epg_cpu.cpp`, `src/blochsim/_perk_cpu.cpp`
: The CPU kernels. Every path the GPU has -- forward, forward-mode,
adjoint, forward-over-reverse, the real-subspace specialization, the pool
models -- exists here too, and the two agree to float32 round-off.
-`src/torchsim/simulators/`, `estimators/`, `recon/`, `optim/`
+`src/blochsim/simulators/`, `estimators/`, `recon/`, `optim/`
: The sequences that ship, and what is built on top of them.
`tests/`, `examples/`, `docs/`
@@ -151,7 +151,7 @@ is a second or two.
```sh
pre-commit run --all-files # the whole tree, which is what CI does
pre-commit run ruff-format # one hook, on the staged files
-pre-commit run --files src/torchsim/model/_signal.py
+pre-commit run --files src/blochsim/model/_signal.py
pre-commit autoupdate # move the pinned hook versions forward
```
@@ -221,7 +221,7 @@ test whose name states the invariant -- those cannot go stale silently, and a
stale comment actively misleads the next reader. When you are tempted to
explain *why not the other way*, write a test instead.
-The same applies to these pages. The documentation describes what TorchSim
+The same applies to these pages. The documentation describes what BlochSim
does and why that is right on its own terms; it is not a changelog.
**Units are public at the edges and internal underneath.** A caller writes
@@ -268,7 +268,7 @@ number entirely. Run the whole suite at natural boundaries rather than after
every edit.
When you change physics, add the test that pins it against something outside
-TorchSim: a closed form, a published figure, or an isochromat summation you
+BlochSim: a closed form, a published figure, or an isochromat summation you
write in the test itself. The `tests/epg` files are written that way, and
each states its invariant in its module docstring.
@@ -277,15 +277,15 @@ each states its invariant in its module docstring.
```sh
bash scripts/build_docs.sh # incremental
bash scripts/build_docs.sh --clean # re-execute every example
-PYTHON_BIN=~/envs/torchsim/bin/python bash scripts/build_docs.sh
+PYTHON_BIN=~/envs/blochsim/bin/python bash scripts/build_docs.sh
```
-The script checks that the interpreter it is given can import TorchSim and the
+The script checks that the interpreter it is given can import BlochSim and the
documentation extensions, then builds into `docs/build/html`. Two things
happen during the build that make it slower than a plain Sphinx run and are
the point of it: **sphinx-gallery executes every example**, and **the figures
of the explanation pages are re-rendered** by
-`docs/explanation_figures.py`, which simulates them with the TorchSim in
+`docs/explanation_figures.py`, which simulates them with the BlochSim in
your working tree. A figure on those pages is therefore never older than the
code it illustrates. Read them:
@@ -308,7 +308,7 @@ docstring becomes the page's introduction.
refactor and a fix in the same diff cost the reviewer more than they save
you.
2. **Make the change, with a test that fails without it.** For a physics
- change that test is against something outside TorchSim, as above.
+ change that test is against something outside BlochSim, as above.
3. **Run what CI runs**, so you find out here rather than there:
```sh
@@ -330,6 +330,6 @@ docstring becomes the page's introduction.
changed.
Everything here happens under the {doc}`misc/code_of_conduct`, which applies
-to issues, discussions, pull requests and reviews alike. TorchSim is released
+to issues, discussions, pull requests and reviews alike. BlochSim is released
under the {doc}`misc/license`, and a contribution is released under the same
terms; {doc}`misc/contributors` is generated from the repository history.
diff --git a/docs/explanation_figures.py b/docs/explanation_figures.py
index b2e25d3c..9d7da181 100644
--- a/docs/explanation_figures.py
+++ b/docs/explanation_figures.py
@@ -1,4 +1,4 @@
-"""Figures for the explanation pages, drawn from the TorchSim in the tree.
+"""Figures for the explanation pages, drawn from the BlochSim in the tree.
Every figure is a function returning a Matplotlib figure, registered in
:data:`FIGURES` under the file stem the pages reference it by. The Sphinx
@@ -26,9 +26,9 @@
import torch
from figure_style import FOREGROUND, STYLE
-from torchsim.model import Simulator
-from torchsim.sequence import Delay, EventAction, Excitation, SPGRReadout
-from torchsim.simulators import FSESimulator, MRFSimulator, SPGRSimulator
+from blochsim.model import Simulator
+from blochsim.sequence import Delay, EventAction, Excitation, SPGRReadout
+from blochsim.simulators import FSESimulator, MRFSimulator, SPGRSimulator
#: The example sequence the description figures are read from.
SEQ_FILE = Path(__file__).resolve().parent.parent / "examples/01-framework/fse.seq"
@@ -383,7 +383,7 @@ def phase_graph():
xlabel="time [ms]",
ylabel="|signal|",
ylim=(0, 1.05 * signal.max()),
- title="what TorchSim records there",
+ title="what BlochSim records there",
)
return figure
@@ -1227,7 +1227,7 @@ def _example_sequence():
def description_blocks():
"""A Pulseq block table above the event stream it is read as."""
- from torchsim.sequence import EventType, SequenceDescription
+ from blochsim.sequence import EventType, SequenceDescription
sequence = _example_sequence()
described = SequenceDescription.from_pulseq(SEQ_FILE)
@@ -1311,7 +1311,7 @@ def description_blocks():
def description_echo():
"""Where each readout sits in k-space, and which one bears the echo."""
- from torchsim.sequence import SequenceDescription
+ from blochsim.sequence import SequenceDescription
sequence = _example_sequence()
described = SequenceDescription.from_pulseq(SEQ_FILE)
diff --git a/docs/explanations/description.md b/docs/explanations/description.md
index 526dc405..1cbc4b2c 100644
--- a/docs/explanations/description.md
+++ b/docs/explanations/description.md
@@ -3,15 +3,15 @@
```{admonition} TL;DR
:class: tldr
-- TorchSim consumes a compact sequence description made of RF, ADC and wait
+- BlochSim consumes a compact sequence description made of RF, ADC and wait
events plus reusable RF definitions.
- Pulseq files and scanner MRD streams are both decoded into this same
description.
-- The description states what was played; a {class}`~torchsim.model.Simulator`
+- The description states what was played; a {class}`~blochsim.model.Simulator`
supplies the handlers that decide how those commands affect the EPG state.
```
-A sequence reaches TorchSim as an **event stream**: one repetition's worth of
+A sequence reaches BlochSim as an **event stream**: one repetition's worth of
events, each with a timestamp and the few numbers its kind carries. This page
is what that stream holds, how it is read out of a Pulseq file, and how a
scanner carries it. The physics the events drive is {doc}`epg`; how the kernels
@@ -80,7 +80,7 @@ simulation would otherwise silently stand for whichever came first. Such a file
says which with a `TRRef` definition, or the caller passes `tr_index`.
```python
-from torchsim.simulators import FSESimulator
+from blochsim.simulators import FSESimulator
# The tissue is given; the echo spacing, the train length, the refocusing
# angle and the pulse shapes are read.
@@ -89,7 +89,7 @@ signal = train.simulate(T1=830.0, T2=80.0)
```
Reading a file needs `pypulseq`, which parses the format and computes the
-gradient trajectory: `pip install torchsim[pulseq]`.
+gradient trajectory: `pip install blochsim[pulseq]`.
## Pulse shapes
@@ -157,8 +157,8 @@ A scanner does not send a file. Pulserver's reconstruction proxy reads the
sequence file a series was played from and sends its description on the MRD
stream, after the XML header and before the first acquisition, as one `TEXT`
message holding a description per file of the sequence chain.
-{func}`~torchsim.sequence.read_mrd_description` decodes it into the same object
-{meth}`~torchsim.SequenceDescription.from_pulseq` builds, so a simulation driven
+{func}`~blochsim.sequence.read_mrd_description` decodes it into the same object
+{meth}`~blochsim.SequenceDescription.from_pulseq` builds, so a simulation driven
from a design script and one driven from a running scan read one derivation,
not two. Which model the rows drive is chosen by the reader.
@@ -187,7 +187,7 @@ they are. Naming the simulator is what says which of those the arriving events
are to be read as, and it is the only thing a caller chooses:
```python
-from torchsim.simulators import FSESimulator, MRFSimulator
+from blochsim.simulators import FSESimulator, MRFSimulator
# The same file, read as a refocused train and as an unbalanced one.
refocused = FSESimulator.from_pulseq("scan.seq")
diff --git a/docs/explanations/epg.md b/docs/explanations/epg.md
index 7f87bd52..70ee5fb7 100644
--- a/docs/explanations/epg.md
+++ b/docs/explanations/epg.md
@@ -7,7 +7,7 @@
indexed by dephasing order.
- RF pulses mix state families, gradients shift transverse orders, and
relaxation/evolution acts between events.
-- TorchSim extends that state machine with off-resonance, diffusion, flow and
+- BlochSim extends that state machine with off-resonance, diffusion, flow and
exchange while retaining the same sequence-level abstraction.
```
@@ -17,7 +17,7 @@ fingerprinting schedule: in each, what you sample is not one decaying
magnetization but a sum over routes through the pulses and the gradients, each
of which has spent a different amount of time transverse and a different amount
parked along the field. The extended phase graph (EPG) is the accounting that
-keeps those routes straight, and it is what TorchSim evaluates. Weigel [^2] is
+keeps those routes straight, and it is what BlochSim evaluates. Weigel [^2] is
the introduction to read alongside this one.
The magnetization is decomposed into configuration states, and three operators
@@ -172,7 +172,7 @@ makes rephasing come out right and is the single place a shift is not just a
memory move.
```
-In TorchSim the shift is what an operator such as `Dephase` plays, and one
+In BlochSim the shift is what an operator such as `Dephase` plays, and one
shift is one crusher or one unbalanced readout gradient. Ideal spoiling --
`Spoil` -- is the other option: discard every transverse order instead of
winding it on, which is what a spoiled gradient echo assumes its spoiler and
@@ -208,11 +208,11 @@ crossing at that moment.
```{figure} /generated/figures/phase_graph.png
:width: 100%
-:alt: A phase graph of a refocused train above the echo amplitudes TorchSim computes.
+:alt: A phase graph of a refocused train above the echo amplitudes BlochSim computes.
Above, the phase graph of six refocusing pulses: solid lines are transverse
pathways, dotted lines are magnetization parked along $z$ between
-pulses, dashed verticals are the pulses. Below, what TorchSim records at
+pulses, dashed verticals are the pulses. Below, what BlochSim records at
those crossings. From the fourth pulse on, the sampled signal contains
direct spin echoes and stimulated echoes at once -- pathways that spent one
or more intervals longitudinal, and so were spared $T_2$ decay while
@@ -248,7 +248,7 @@ assuming the decay.
A sequence populates a bounded set of orders: each shift moves the ladder by
one, so after $n$ shifts nothing beyond order $n$ exists, and
attenuation makes the far orders negligible long before that. You choose how
-many to carry with `states=` (or `nstates=` per call), and TorchSim sizes it
+many to carry with `states=` (or `nstates=` per call), and BlochSim sizes it
from the winding the sequence declares when you do not.
```{figure} /generated/figures/truncation.png
@@ -292,7 +292,7 @@ once you know the gradient behind it.
## Second pool
-Tissue is not one proton pool. TorchSim carries the two extensions of the EPG
+Tissue is not one proton pool. BlochSim carries the two extensions of the EPG
formalism that matter in practice, both from Malik et al [^3]: a **semisolid
pool**
that RF saturates through its absorption lineshape and that exchanges
@@ -327,7 +327,7 @@ orders to be interpreted with care. It treats an isochromat's off-resonance as
a constant precession rather than as a distribution, so a spread of field
across the voxel is not something the states carry.
-TorchSim applies that spread to the samples instead. `t2_prime_ms` sets a
+BlochSim applies that spread to the samples instead. `t2_prime_ms` sets a
Lorentzian population of frequencies, half-width $1 / T_2'$ in angular
frequency, whose average over the voxel is $e^{-|\tau| / T_2'}$ in the time
$\tau$ a sample has gone
@@ -339,7 +339,7 @@ train does not, and needs a spread of `b0_hz` across voxels as before.
Pulses are instantaneous rotations unless you say otherwise. A real
slice-selective pulse acts under its gradient, so what it does depends on
-position within the slice; TorchSim can carry that exactly by integrating the
+position within the slice; BlochSim can carry that exactly by integrating the
pulse ahead of time, which is the hybrid Bloch-EPG picture of Guenthner et al
[^4] and is described in {doc}`implementation`.
diff --git a/docs/explanations/implementation.md b/docs/explanations/implementation.md
index bf8bd8a8..a3b68025 100644
--- a/docs/explanations/implementation.md
+++ b/docs/explanations/implementation.md
@@ -1,9 +1,9 @@
-# How TorchSim runs it
+# How BlochSim runs it
```{admonition} TL;DR
:class: tldr
-- {class}`~torchsim.model.Simulator` is the public sequence abstraction.
+- {class}`~blochsim.model.Simulator` is the public sequence abstraction.
Offline it builds a description from `layout()`; scanner-driven use starts
from an incoming description and applies the simulator's handlers.
- The resulting event stream is packed once and executed by fused CPU or
@@ -16,14 +16,14 @@
of the code is what it is: one description of a sequence, one fused kernel per
voxel, and derivatives taken in whichever direction the question asks for.
-You do not need any of it to use TorchSim. You need it to know what a run costs
+You do not need any of it to use BlochSim. You need it to know what a run costs
before you launch it, to read a number that surprises you, and to change
something without breaking the pieces underneath.
## One path, from the sequence you write to the signal
Everything a sequence can say is said once, in a
-{class}`~torchsim.SequenceDescription`: a list of events, each with a
+{class}`~blochsim.SequenceDescription`: a list of events, each with a
timestamp, a payload, and an **action word** saying what the sequence plays
around it. Whatever assembled that description -- operators you composed, a
builder that ships here, a stream that arrived from a scanner -- what runs
@@ -51,14 +51,14 @@ in it can be the variable an optimizer moves.
An event is a wait, an RF pulse, or an ADC. Gradients are not events. A Pulseq
file has no gradient *use* field, so a crusher cannot be told from a phase
encode by looking at waveforms without tracking the k-space moment through the
-whole TR -- and instead of doing that, TorchSim has each event declare its own
+whole TR -- and instead of doing that, BlochSim has each event declare its own
role: `CRUSH_BEFORE`, `CRUSH_AFTER`, `SHIFT_AFTER`, `SPOIL_AFTER`.
```{figure} /generated/figures/event_stream.png
:width: 100%
:alt: A timeline of RF, ADC and wait events for a four-echo refocused train.
-A four-echo refocused train, as {meth}`~torchsim.model.Simulator.describe`
+A four-echo refocused train, as {meth}`~blochsim.model.Simulator.describe`
emits it. The crushers ride on the refocusing pulses that sit between them,
which is why `Refocusing` is one operator rather than three; a spoiled
readout would instead carry `SPOIL_AFTER`, and an unbalanced one
@@ -92,7 +92,7 @@ voxels a card holds rather than by the sequence.
```
You set the order count with `states=` on a simulator, or `nstates=` on a
-call. Left alone, TorchSim counts the winding the description declares --
+call. Left alone, BlochSim counts the winding the description declares --
every `CRUSH`, every `SHIFT_AFTER`, over every repetition -- and sizes the
state from that, clamped into a sensible range. Measuring convergence for your
own sequence is two calls, as in {doc}`epg`.
@@ -174,7 +174,7 @@ off-resonance declared. Only the first is confined to the axis, and it is
several times faster on this machine.
```
-TorchSim decides this per run, in one device round trip, and refuses two
+BlochSim decides this per run, in one device round trip, and refuses two
arrangements that look real but are not: off-resonance is refocused *at* the
echo centres but not between them, and an excitation a quarter turn from the
refocusing pulses gives samples that are real while the states fill the plane.
@@ -190,7 +190,7 @@ direction depends on what you are differentiating.
**With respect to tissue**, forward mode wins. Voxels are independent, so one
directional derivative covers every voxel at once, and the cost is one pass per
*property* -- not per voxel, not per echo. That is
-{meth}`~torchsim.model.Simulator.jacobian`, and it is what a dictionary fit, a
+{meth}`~blochsim.model.Simulator.jacobian`, and it is what a dictionary fit, a
Cramer-Rao bound and a model-based reconstruction all consume. Inside the
kernels it is dual arithmetic: each quantity carries a tangent beside it, and
every operator differentiates itself.
@@ -236,7 +236,7 @@ recover the map exactly; afterwards a call rebuilds them with whole-tensor
arithmetic and leaves the structure alone.
```
-{meth}`~torchsim.model.Simulator.resolved` asks for that. It checks itself:
+{meth}`~blochsim.model.Simulator.resolved` asks for that. It checks itself:
where the map does not hold -- an entry drawing on more than one element, a
rebuild disagreeing with a fresh packing at a point the map never saw -- the
binding is refused and the ordinary path runs instead, same answer, slower.
@@ -257,7 +257,7 @@ match or a kernel regression.
What a run does with a problem. Streaming sizes its chunks to a memory
budget and overlaps one chunk's transfer with another's arithmetic; sharding
splits the voxels across cards and gathers the signal back. Say it around
-the call with {func}`~torchsim.execution` and {func}`~torchsim.offload`, or
+the call with {func}`~blochsim.execution` and {func}`~blochsim.offload`, or
leave it alone and let each call decide against what the devices have free.
```
diff --git a/docs/explanations/index.md b/docs/explanations/index.md
index 8eb37ea8..b41864cd 100644
--- a/docs/explanations/index.md
+++ b/docs/explanations/index.md
@@ -1,10 +1,10 @@
# Explanations
-The concepts behind TorchSim, separate from the step-by-step examples and the
+The concepts behind BlochSim, separate from the step-by-step examples and the
API reference.
{doc}`description`
-: The sequence representation TorchSim consumes: events, RF definitions,
+: The sequence representation BlochSim consumes: events, RF definitions,
readout roles, Pulseq input, and the MRD description a running scanner can
send.
@@ -14,7 +14,7 @@ API reference.
the model.
{doc}`implementation`
-: How a {class}`~torchsim.model.Simulator` turns either an offline layout or
+: How a {class}`~blochsim.model.Simulator` turns either an offline layout or
an incoming sequence description into the fused CPU/GPU state machine, and
how differentiation and execution are arranged.
diff --git a/docs/misc/contributors.md b/docs/misc/contributors.md
index ddb28084..e6af192a 100644
--- a/docs/misc/contributors.md
+++ b/docs/misc/contributors.md
@@ -1,9 +1,9 @@
# Contributors
-Contributions to the project can be seen [here](https://github.com/pulserver/torchsim/graphs/contributors)
+Contributions to the project can be seen [here](https://github.com/pulserver/blochsim/graphs/contributors)
```{raw} html
-
-
+
+
```
diff --git a/docs/misc/related.md b/docs/misc/related.md
index dab32fe7..5aacb04c 100644
--- a/docs/misc/related.md
+++ b/docs/misc/related.md
@@ -17,10 +17,10 @@ of events and the answer is a curve per tissue. That is what a dictionary, a
fit, a Cramer-Rao bound and a model-based reconstruction are built from, and
it is what an extended phase graph computes in a fraction of the states an
isochromat ensemble needs. sycomore [^6], epgpy [^7], EpyG [^8],
-mri-sim-py [^9], snapMRF [^10] and TorchSim answer this one.
+mri-sim-py [^9], snapMRF [^10] and BlochSim answer this one.
BlochSimulators.jl [^11] answers both, from the same sequence description.
-**TorchSim is the second kind.** It has no gradient waveforms, no phantom
+**BlochSim is the second kind.** It has no gradient waveforms, no phantom
coordinates and no encoding operator of its own -- {doc}`../explanations/implementation`
lists what that rules out. What it adds instead is the derivative: the same
kernels that produce a signal produce its Jacobian with respect to tissue, and
@@ -46,23 +46,23 @@ and a signal-model simulator has no reason to carry a coil.
| [EpyG](https://github.com/brennerd11/EpyG) | EPG | Python | CPU | -- | Python |
| [mri-sim-py](https://github.com/utcsilab/mri-sim-py.epg) | EPG | Python, PyTorch | CPU, CUDA | Automatic, reverse mode | Python |
| [snapMRF](https://github.com/dongwang881107/snapMRF) | EPG, with matching | CUDA C | CUDA | -- | Command line |
-| **TorchSim** | EPG, and closed forms | Python, PyTorch, C++ and Triton kernels | CPU threads, CUDA, several cards | Automatic: forward, reverse, and forward over reverse | Python, or a description |
+| **BlochSim** | EPG, and closed forms | Python, PyTorch, C++ and Triton kernels | CPU threads, CUDA, several cards | Automatic: forward, reverse, and forward over reverse | Python, or a description |
## Where each one is the better tool
**A sequence you are developing, and the image it makes.** KomaMRI, JEMRIS,
CMRsim or MRzero-Core. They read the waveforms you will play, carry the spins
-through them at their coordinates, and hand back k-space. TorchSim reads a
+through them at their coordinates, and hand back k-space. BlochSim reads a
sequence as events rather than waveforms, and stops at the signal.
**One curve, read interactively, with the model in front of you.** sycomore or
epgpy. Both are a few milliseconds for one tissue with nothing to warm up, and
-sycomore's units make an expression read like the paper it came from. TorchSim
+sycomore's units make an expression read like the paper it came from. BlochSim
resolves the structure of a sequence before it runs one, which is seconds it
does not repay until there is a dictionary to sweep or a loop to run.
**A dictionary, a fit, a design loop, or a map solved from k-space.**
-TorchSim. The batching across tissues, the derivative, and the estimators and
+BlochSim. The batching across tissues, the derivative, and the estimators and
reconstruction that consume it are the point of the package; `benchmarks/` in
the repository measures the first two against the alternatives above and
states the agreement between them.
diff --git a/docs/sphinx_add_colab_link.py b/docs/sphinx_add_colab_link.py
index 0878f38b..7a85cdd1 100644
--- a/docs/sphinx_add_colab_link.py
+++ b/docs/sphinx_add_colab_link.py
@@ -55,7 +55,7 @@ def run(self):
)
# Generate the Colab URL based on GitHub repo information
- self.colab_url = f"https://colab.research.google.com/github/pulserver/torchsim/blob/{binder['branch']}/{on_the_branch}"
+ self.colab_url = f"https://colab.research.google.com/github/pulserver/blochsim/blob/{binder['branch']}/{on_the_branch}"
# Create the HTML button or link
self.html = f"""
@@ -106,7 +106,7 @@ def notebook_modifier(self, notebook_path, commands):
)
message_class = "warning"
message = (
- "Running this torchsim example requires a GPU, and hence is NOT "
+ "Running this blochsim example requires a GPU, and hence is NOT "
"possible on binder currently We request you to kindly run this notebook "
"on Google Colab by clicking the link below. Additionally, please make "
"sure to set the runtime on Colab to use a GPU and install the below "
diff --git a/docs/user_guide.md b/docs/user_guide.md
index f3dc2e12..463999ce 100644
--- a/docs/user_guide.md
+++ b/docs/user_guide.md
@@ -10,7 +10,7 @@ and a map of where the rest of this documentation is.
## Install
-TorchSim is a PyTorch package with two compiled kernels behind it. `pip`
+BlochSim is a PyTorch package with two compiled kernels behind it. `pip`
installs the Python side and its NumPy and SciPy dependencies for you. Two
things it cannot decide on your behalf, and which you therefore settle first:
**which build of PyTorch you want**, and **where you want the whole thing to
@@ -34,8 +34,8 @@ Any of these works. Pick the one you already use.
Ships with Python, nothing to install first:
```sh
-python -m venv ~/envs/torchsim
-source ~/envs/torchsim/bin/activate # Windows: ~\envs\torchsim\Scripts\activate
+python -m venv ~/envs/blochsim
+source ~/envs/blochsim/bin/activate # Windows: ~\envs\blochsim\Scripts\activate
python -m pip install --upgrade pip
```
:::
@@ -47,11 +47,11 @@ Useful when you also want a specific Python, or non-Python libraries
beside it:
```sh
-conda create -n torchsim python=3.12
-conda activate torchsim
+conda create -n blochsim python=3.12
+conda activate blochsim
```
-Install TorchSim itself with `pip` inside that environment; there is no
+Install BlochSim itself with `pip` inside that environment; there is no
conda package.
:::
@@ -61,8 +61,8 @@ conda package.
The fastest of the three, and it resolves the whole set at once:
```sh
-uv venv --python 3.12 ~/envs/torchsim
-source ~/envs/torchsim/bin/activate
+uv venv --python 3.12 ~/envs/blochsim
+source ~/envs/blochsim/bin/activate
```
Read `uv pip install` for `pip install` in everything that follows.
@@ -72,9 +72,9 @@ Read `uv pip install` for `pip install` in everything that follows.
### PyTorch first
-**Install PyTorch before TorchSim.** The wheel you want depends on hardware
+**Install PyTorch before BlochSim.** The wheel you want depends on hardware
`pip` cannot see: a CPU-only build and a CUDA build have the same name and
-version, and differ only in which index they came from. Install TorchSim first
+version, and differ only in which index they came from. Install BlochSim first
and you get whichever build the default index happens to serve, which is
usually not the one you meant.
@@ -92,7 +92,7 @@ the smallest download by a wide margin:
pip install torch --index-url https://download.pytorch.org/whl/cpu
```
-Everything in TorchSim runs here. The state machine has a threaded,
+Everything in BlochSim runs here. The state machine has a threaded,
vectorized C++ kernel behind it, so a dictionary of a few thousand atoms
is seconds rather than minutes.
:::
@@ -140,10 +140,10 @@ python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda
A CUDA build prints a version and `True`; a CPU build prints `None` and
`False`.
-### TorchSim
+### BlochSim
```sh
-pip install torchsim
+pip install blochsim
```
Where a wheel exists for your platform this is the whole of it. Where one does
@@ -159,7 +159,7 @@ once:
```python
import torch
-from torchsim.simulators import FSESimulator
+from blochsim.simulators import FSESimulator
acquisition = FSESimulator(
ESP=5.0,
@@ -184,14 +184,14 @@ seems to hang is almost always that compile.
## Your first simulation
-The central public object in TorchSim is a
-{class}`~torchsim.model.Simulator`. A shipped simulator fixes the sequence;
+The central public object in BlochSim is a
+{class}`~blochsim.model.Simulator`. A shipped simulator fixes the sequence;
`simulate` receives the tissue and anything you want to vary. Asking for a
Jacobian is the same model and the same protocol:
```python
import numpy as np
-from torchsim.simulators import MRFSimulator
+from blochsim.simulators import MRFSimulator
flip = np.concatenate(
(np.linspace(5.0, 60.0, 300), np.linspace(60.0, 2.0, 300), np.full(280, 2.0))
@@ -205,9 +205,9 @@ respect to T1 and T2. Dictionary fitting, nonlinear least squares,
model-based reconstruction and sequence design all consume the same simulator
interface.
-Functional calls such as {func}`torchsim.mrf_sim` are convenience wrappers
+Functional calls such as {func}`blochsim.mrf_sim` are convenience wrappers
around the shipped simulator classes. They are useful for a one-off call, but
-{class}`~torchsim.model.Simulator` is the interface to learn and the class to
+{class}`~blochsim.model.Simulator` is the interface to learn and the class to
subclass when you implement a sequence.
Arrays go in and come back in whatever library you wrote them in -- NumPy here,
@@ -222,7 +222,7 @@ CuPy or PyTorch elsewhere -- over the same memory rather than a copy.
{doc}`generated/autoexamples/index`
: The executable Course and Tours. The Course starts with a shipped simulator,
then shows how to implement a new sequence by subclassing
- {class}`~torchsim.model.Simulator`; the Tours cover inference, design and
+ {class}`~blochsim.model.Simulator`; the Tours cover inference, design and
model-based reconstruction.
{doc}`api/index`
@@ -231,19 +231,19 @@ CuPy or PyTorch elsewhere -- over the same memory rather than a copy.
placing a run across devices.
{doc}`developer_guide`
-: Setting up to change TorchSim: the editable install, the style the code is
+: Setting up to change BlochSim: the editable install, the style the code is
written in, the tests, and how a pull request is opened.
## Getting help, and reporting what breaks
**Ask a question** in
-[Discussions](https://github.com/pulserver/torchsim/discussions). How to model
+[Discussions](https://github.com/pulserver/blochsim/discussions). How to model
a sequence, whether a signal you got is expected, which estimator suits a
problem -- these belong there, and the answer is then findable by whoever asks
next.
**Report a bug** in
-[Issues](https://github.com/pulserver/torchsim/issues/new/choose), where a
+[Issues](https://github.com/pulserver/blochsim/issues/new/choose), where a
form asks for what a fix needs:
- the shortest script that reproduces it, pasted whole -- a sequence is enough
@@ -252,7 +252,7 @@ form asks for what a fix needs:
bug: say which analytic case, published figure or alternative simulator you
are comparing against;
- the full traceback, if it raises;
-- the environment, as the form's command prints it -- TorchSim, PyTorch, CUDA,
+- the environment, as the form's command prints it -- BlochSim, PyTorch, CUDA,
Python;
- whether you have seen it on the CPU kernels, the CUDA kernels, or both. That
difference is often the whole diagnosis.
@@ -262,8 +262,8 @@ the same form chooser. Name the paper the model comes from and the figure it
would have to reproduce; that is what makes it implementable.
**Report a vulnerability** privately instead: open a draft advisory from the
-repository's [Security tab](https://github.com/pulserver/torchsim/security/advisories/new),
-or email the address in the [security policy](https://github.com/pulserver/torchsim/blob/main/.github/SECURITY.md).
+repository's [Security tab](https://github.com/pulserver/blochsim/security/advisories/new),
+or email the address in the [security policy](https://github.com/pulserver/blochsim/blob/main/.github/SECURITY.md).
The kernels index raw pointers, so anything reachable from ordinary arguments
that reads or writes out of bounds is worth reporting that way rather than in a
public issue. Wrong physics is a bug report, not a vulnerability.
diff --git a/examples/01-framework/01-getting-started.py b/examples/01-framework/01-getting-started.py
index 2e056053..19ec304b 100644
--- a/examples/01-framework/01-getting-started.py
+++ b/examples/01-framework/01-getting-started.py
@@ -3,17 +3,17 @@
Basic Usage
===========
-The scope of this notebook is to showcase the basic functionalities of Torchsim,
+The scope of this notebook is to showcase the basic functionalities of Blochsim,
including how to simulate a signal, calculating derivatives etc.
-This example uses a fast spin echo that ships with TorchSim.
+This example uses a fast spin echo that ships with BlochSim.
"""
# %%
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim
+# !pip install blochsim
# sphinx_gallery_start_ignore
import warnings
@@ -95,8 +95,8 @@ def key(axes, ncols=1):
import numpy as np
import torch
-import torchsim
-from torchsim.simulators import FSESimulator
+import blochsim
+from blochsim.simulators import FSESimulator
# sphinx_gallery_start_ignore
from pathlib import Path
@@ -159,8 +159,8 @@ def key(axes, ncols=1):
# Derivative with respect to tissue parameters
# --------------------------------------------
#
-# Torchsim allows to efficiently evaluate the derivative
-# of the signal wrt input parameters, via :meth:`~torchsim.model.Simulator.jacobian`.
+# Blochsim allows to efficiently evaluate the derivative
+# of the signal wrt input parameters, via :meth:`~blochsim.model.Simulator.jacobian`.
# The desired derivatives can be specified by string:
#
signal, dT2 = simulator.jacobian("T2", flip=flip) # dT2 is (3, 48) too
@@ -231,7 +231,7 @@ def key(axes, ncols=1):
# the object above with the construction folded in, so the answer is the same
# to the bit.
#
-signal, dT2 = torchsim.fse_sim(
+signal, dT2 = blochsim.fse_sim(
flip=flip, ESP=ESP_MS, TR=3000.0, T1=T1_MS, T2=T2_MS, diff="T2"
)
@@ -327,7 +327,7 @@ def key(axes, ncols=1):
# through in chunks. Naming a device insists on it, and a block settles it for
# everything inside:
#
-with torchsim.execution("cpu"):
+with blochsim.execution("cpu"):
on_the_host = simulator.simulate(flip=flip)
print(
@@ -342,7 +342,7 @@ def key(axes, ncols=1):
# thousand voxels is the case they exist for, and it is written like this:
#
if torch.cuda.is_available():
- with torchsim.execution("cuda", stream=True, budget_bytes=1 << 28):
+ with blochsim.execution("cuda", stream=True, budget_bytes=1 << 28):
streamed = simulator.simulate(flip=flip)
agreement = float((streamed.cpu() - signal_ref).abs().max())
print(f" streamed through a card in 256 MiB chunks, agrees to {agreement:.1e}")
@@ -381,7 +381,7 @@ def key(axes, ncols=1):
# %%
#
# You do not have to read it. ``describe`` here only shows what a stream looks
-# like; :meth:`~torchsim.SequenceDescription.plot` draws one, which is the way
+# like; :meth:`~blochsim.SequenceDescription.plot` draws one, which is the way
# to check that a layout laid down what you meant.
#
@@ -458,18 +458,18 @@ def _arrow(start_at, end_at, label=None, side="right"):
#
# A description normally arrives; it is not typed. Writing one by hand once is
# worth it only to see that there is nothing else in it --
-# :meth:`~torchsim.SequenceDescription.from_operators` lays them out.
+# :meth:`~blochsim.SequenceDescription.from_operators` lays them out.
#
-by_hand = torchsim.SequenceDescription.from_operators(
- torchsim.Excitation(math.pi / 2, math.pi / 2),
+by_hand = blochsim.SequenceDescription.from_operators(
+ blochsim.Excitation(math.pi / 2, math.pi / 2),
*[
part
for _ in range(ECHOES)
for part in (
- torchsim.Delay(0.5 * ESP_MS * 1e-3),
- torchsim.Refocusing(math.radians(60.0), 0.0),
- torchsim.Delay(0.5 * ESP_MS * 1e-3),
- torchsim.Readout(0.0),
+ blochsim.Delay(0.5 * ESP_MS * 1e-3),
+ blochsim.Refocusing(math.radians(60.0), 0.0),
+ blochsim.Delay(0.5 * ESP_MS * 1e-3),
+ blochsim.Readout(0.0),
)
],
)
@@ -481,7 +481,7 @@ def _arrow(start_at, end_at, label=None, side="right"):
# %%
#
-# :meth:`~torchsim.model.Simulator.from_description` runs one, and the only
+# :meth:`~blochsim.model.Simulator.from_description` runs one, and the only
# thing given to it is the tissue. The events are already concrete -- each
# carries the action word saying whether it winds, spoils or records -- so no
# layout is walked and no sequence parameter is inferred.
@@ -514,7 +514,7 @@ def _arrow(start_at, end_at, label=None, side="right"):
#
# One thing to know when comparing it against the shipped simulator: a
# description carries the events, and a simulator may carry physics *around*
-# them. :class:`~torchsim.simulators.FSESimulator` folds in the recovery
+# them. :class:`~blochsim.simulators.FSESimulator` folds in the recovery
# between one train and the next in closed form, which is not an event and so
# is not in the stream. The shape of the train is the same; the driven
# equilibrium the shipped object adds does not come along.
@@ -540,7 +540,7 @@ def _arrow(start_at, end_at, label=None, side="right"):
# The stream a scanner sends is read off the Pulseq sequence it is running, so
# the same events can be read from the ``.seq`` file directly -- which is what
# to do when the scan has not been run yet. The file is parsed by pypulseq,
-# which also computes its trajectory: ``pip install torchsim[pulseq]``.
+# which also computes its trajectory: ``pip install blochsim[pulseq]``.
#
# The file states how many blocks one repetition holds, in its ``TRSize``
# definition, so nothing is searched for. What is read off the trajectory is
diff --git a/examples/01-framework/02-expanded-physics.py b/examples/01-framework/02-expanded-physics.py
index 2cc9344c..ee3c44c3 100644
--- a/examples/01-framework/02-expanded-physics.py
+++ b/examples/01-framework/02-expanded-physics.py
@@ -16,7 +16,7 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim
+# !pip install blochsim
# %%
#
@@ -103,9 +103,9 @@ def key(axes, ncols=1):
import numpy as np
import torch
-import torchsim
-from torchsim import SPGRReadout, ShimDefinition, bSSFPReadout
-from torchsim.simulators import FSESimulator, MRFSimulator
+import blochsim
+from blochsim import SPGRReadout, ShimDefinition, bSSFPReadout
+from blochsim.simulators import FSESimulator, MRFSimulator
# %%
# Test sequence
@@ -167,7 +167,7 @@ def key(axes, ncols=1):
#
# On a parallel-transmit system the field is the complex sum of what several
# channels put on the voxel. ``B1`` and ``B1phase`` then carry one row per
-# channel, and a :class:`~torchsim.ShimDefinition` gives the amplitude and
+# channel, and a :class:`~blochsim.ShimDefinition` gives the amplitude and
# phase each channel is driven at.
#
# Four channels whose sensitivities sit a quarter turn apart, driven alike,
@@ -250,7 +250,7 @@ def first_echo(step_rad):
# response rather than a scaling, it cannot be folded into the flip angle: the
# pulse must be integrated.
#
-# TorchSim takes the complex envelope, one row per transmit channel, as it
+# BlochSim takes the complex envelope, one row per transmit channel, as it
# comes off a Pulseq block or an MRD sequence description. This one is an SLR
# 90 degree pulse, 2 ms long over a 5 mm slice, saved beside this file.
#
@@ -260,7 +260,7 @@ def first_echo(step_rad):
DATA = Path("data") if Path("data").is_dir() else Path(__file__).parent / "data"
# sphinx_gallery_end_ignore
waveform = np.load(DATA / "slr90.npz")
-excitation = torchsim.rf_definition(
+excitation = blochsim.rf_definition(
waveform["samples"],
dwell_s=float(waveform["dwell_s"]),
bandwidth_hz=float(waveform["bandwidth_hz"]),
@@ -288,7 +288,7 @@ def first_echo(step_rad):
#
# sphinx_gallery_start_ignore
-from torchsim.sequence._transition import transition_table # noqa: E402
+from blochsim.sequence._transition import transition_table # noqa: E402
positions = torch.linspace(-1.0, 1.0, 121, dtype=torch.float64)
table = transition_table(excitation, positions, bins=64, rf_raster_time_s=1e-6)
diff --git a/examples/01-framework/03-writing-a-simulator.py b/examples/01-framework/03-writing-a-simulator.py
index b9266d52..487e78ff 100644
--- a/examples/01-framework/03-writing-a-simulator.py
+++ b/examples/01-framework/03-writing-a-simulator.py
@@ -3,7 +3,7 @@
Writing a Signal Model
======================
-The scope of this notebook is to write a simulator TorchSim does not ship.
+The scope of this notebook is to write a simulator BlochSim does not ship.
There are two things to say. **Which operator plays each kind of event** --
what an excitation is, what a sample is -- and **what order they are played
@@ -16,7 +16,7 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim
+# !pip install blochsim
# %%
#
@@ -101,7 +101,7 @@ def key(axes, ncols=1):
import numpy as np
import torch
-from torchsim import (
+from blochsim import (
Delay,
Excitation,
Inversion,
@@ -109,17 +109,17 @@ def key(axes, ncols=1):
SSFPFidReadout,
Spoil,
)
-from torchsim.model import Simulator
+from blochsim.model import Simulator
# %%
# Handlers
# --------
# A simulator says what plays each kind of event by naming it. The five
# readouts are what distinguishes one steady-state family from another: an
-# :func:`~torchsim.SSFPFidReadout` winds one configuration order after every
-# sample, an :func:`~torchsim.SSFPEchoReadout` winds it before, an
-# :func:`~torchsim.SPGRReadout` spoils, a :func:`~torchsim.bSSFPReadout` leaves
-# the states where they were, and an :func:`~torchsim.FSEReadout` samples a
+# :func:`~blochsim.SSFPFidReadout` winds one configuration order after every
+# sample, an :func:`~blochsim.SSFPEchoReadout` winds it before, an
+# :func:`~blochsim.SPGRReadout` spoils, a :func:`~blochsim.bSSFPReadout` leaves
+# the states where they were, and an :func:`~blochsim.FSEReadout` samples a
# spin echo the refocusing pulses have already crushed around.
#
# Naming one is the whole of choosing between them, and it is also what says
@@ -133,7 +133,7 @@ def key(axes, ncols=1):
# %%
# Layout
# ------
-# An :class:`~torchsim.model.Simulator` is the protocol. You do not
+# An :class:`~blochsim.model.Simulator` is the protocol. You do not
# write timestamps: ``layout`` returns the *operators* of one repetition in
# order, and the simulator turns the span each one holds into the timestamps a
# description carries.
@@ -205,7 +205,7 @@ def layout(self, *, flip, TR, TI=0.0):
# derivative per property yields every voxel's derivative at once, and the cost
# is one pass per property rather than per voxel.
#
-# That is what :meth:`~torchsim.model.Simulator.jacobian` does. A single name
+# That is what :meth:`~blochsim.model.Simulator.jacobian` does. A single name
# collapses the parameter axis; a sequence of names keeps it.
#
signal, jacobian = sequence.jacobian(("T1", "T2"), T1=1000.0, T2=100.0)
@@ -362,7 +362,7 @@ def layout(self, *, TS, flip, phases=0.0):
# %%
# Inspecting the event stream
# ---------------------------
-# :meth:`~torchsim.model.Simulator.describe` returns the event stream, which is
+# :meth:`~blochsim.model.Simulator.describe` returns the event stream, which is
# the same object a sequence arriving from a scanner is read into: a timestamp
# and an action word on every event. It is worth looking at once, because a
# sequence that plays the wrong thing is far easier to see here than in the
@@ -387,7 +387,7 @@ def layout(self, *, TS, flip, phases=0.0):
# ---------------------------
# A description that came from somewhere else -- an MRD file, a Pulseq export,
# a scanner's own stream -- becomes a simulator through
-# :meth:`~torchsim.model.Simulator.from_description`. No layout is walked: the
+# :meth:`~blochsim.model.Simulator.from_description`. No layout is walked: the
# events are re-emitted through this model's handlers, which is what puts the
# gradients back that the transport does not carry.
#
diff --git a/examples/01-framework/04-custom-operator.py b/examples/01-framework/04-custom-operator.py
index 7fd94cb4..c1f3249b 100644
--- a/examples/01-framework/04-custom-operator.py
+++ b/examples/01-framework/04-custom-operator.py
@@ -3,7 +3,7 @@
Writing a New Operator
======================
-The scope of this notebook is to show how to add a sequence module TorchSim
+The scope of this notebook is to show how to add a sequence module BlochSim
does not ship -- a preparation, or a readout -- without touching a kernel.
An operator is a Python function that returns events and says how long it
@@ -16,7 +16,7 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim
+# !pip install blochsim
# %%
#
@@ -100,7 +100,7 @@ def key(axes, ncols=1):
# sphinx_gallery_end_ignore
import torch
-from torchsim import (
+from blochsim import (
Delay,
Dephase,
Excitation,
@@ -110,7 +110,7 @@ def key(axes, ncols=1):
SSFPFidReadout,
Spoil,
)
-from torchsim.model import Simulator
+from blochsim.model import Simulator
# %%
# Composing existing operators
@@ -124,7 +124,7 @@ def key(axes, ncols=1):
# that is what an operator is.
#
# The Refocusing pulse is asked for uncrushed: a T2 preparation refocuses
-# rather than dephases, and the crusher pair :func:`~torchsim.Refocusing` adds
+# rather than dephases, and the crusher pair :func:`~blochsim.Refocusing` adds
# by default would spoil the echo it exists to form.
@@ -247,7 +247,7 @@ def layout(self, *, TE_prep, ESP, ETL):
# and a sample taken *after* it sits where the next pulse will refocus the
# previous excitation -- an echo, and far more strongly T2-weighted.
#
-# TorchSim ships each of those separately. Taking both in one repetition is a
+# BlochSim ships each of those separately. Taking both in one repetition is a
# double-echo steady state, and writing it is putting the winding between two
# samples rather than on one side of them.
diff --git a/examples/01-framework/README.rst b/examples/01-framework/README.rst
index 4a4e34e2..a47aea1e 100644
--- a/examples/01-framework/README.rst
+++ b/examples/01-framework/README.rst
@@ -1,14 +1,14 @@
Framework
---------
-How to use TorchSim, and how to extend it.
+How to use BlochSim, and how to extend it.
The first notebook covers basic usage: simulating a signal, taking its
derivatives, tuning the run, and reading a sequence back from the description
a scanner streams. The second covers the physics a simulator can carry beyond
T1 and T2.
-The last two are for sequences TorchSim does not ship. A **signal model** of
+The last two are for sequences BlochSim does not ship. A **signal model** of
your own is two pieces -- a physics saying what each kind of event does, and a
simulator saying what order they are played in. An **operator** of your own --
a preparation, a readout -- is a Python function that returns events.
diff --git a/examples/02-parameter-inference/01-dictionary-matching.py b/examples/02-parameter-inference/01-dictionary-matching.py
index 9213b6e7..a008af30 100644
--- a/examples/02-parameter-inference/01-dictionary-matching.py
+++ b/examples/02-parameter-inference/01-dictionary-matching.py
@@ -18,12 +18,12 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim brainweb-dl cmap
+# !pip install blochsim brainweb-dl cmap
# %%
#
# The problem is stated over a simulator carrying the sequence and filled in
-# by an estimator. :func:`~torchsim.execution` decides where that work runs,
+# by an estimator. :func:`~blochsim.execution` decides where that work runs,
# and the timings below are taken inside it.
#
@@ -165,12 +165,12 @@ def key(axes, ncols=1):
import numpy as np
import torch
-import torchsim
-from torchsim import (
+import blochsim
+from blochsim import (
Subspace,
)
-from torchsim.estimators import DictionaryMatcher
-from torchsim.simulators import MRFSimulator
+from blochsim.estimators import DictionaryMatcher
+from blochsim.simulators import MRFSimulator
# %%
#
@@ -291,7 +291,7 @@ def footprint(problem):
def mapped(problem, passes=3):
"""Map the slice a few times: the quickest pass, and what it held."""
on_device = torch.cuda.is_available()
- with torchsim.execution():
+ with blochsim.execution():
problem(measured[:64])
if on_device:
torch.cuda.reset_peak_memory_stats()
diff --git a/examples/02-parameter-inference/02-lookup-table.py b/examples/02-parameter-inference/02-lookup-table.py
index e2bff627..ce00128d 100644
--- a/examples/02-parameter-inference/02-lookup-table.py
+++ b/examples/02-parameter-inference/02-lookup-table.py
@@ -10,19 +10,19 @@
With a single unknown a dictionary degenerates: the atoms lie on a curve rather
than filling a space. Interpolating between the two nearest then costs nothing
and takes the grid spacing out of the answer, which is what
-:class:`~torchsim.LookupTable` does.
+:class:`~blochsim.LookupTable` does.
"""
# %%
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim brainweb-dl cmap
+# !pip install blochsim brainweb-dl cmap
# %%
#
# The problem is stated over a simulator carrying the sequence and filled in
-# by an estimator. :func:`~torchsim.execution` decides where that work runs,
+# by an estimator. :func:`~blochsim.execution` decides where that work runs,
# and the timings below are taken inside it.
#
@@ -164,9 +164,9 @@ def key(axes, ncols=1):
import numpy as np
import torch
-import torchsim
-from torchsim.estimators import DictionaryMatcher, LookupTable
-from torchsim.simulators import MP2RAGESimulator
+import blochsim
+from blochsim.estimators import DictionaryMatcher, LookupTable
+from blochsim.simulators import MP2RAGESimulator
# %%
#
@@ -312,7 +312,7 @@ def footprint(problem):
def mapped(problem, passes=3):
"""Map the slice a few times: the quickest pass, and what it held."""
on_device = torch.cuda.is_available()
- with torchsim.execution():
+ with blochsim.execution():
problem(measured[:64])
if on_device:
torch.cuda.reset_peak_memory_stats()
diff --git a/examples/02-parameter-inference/03-nonlinear-least-squares.py b/examples/02-parameter-inference/03-nonlinear-least-squares.py
index c1c2e6e6..dd1822d1 100644
--- a/examples/02-parameter-inference/03-nonlinear-least-squares.py
+++ b/examples/02-parameter-inference/03-nonlinear-least-squares.py
@@ -19,12 +19,12 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim brainweb-dl cmap
+# !pip install blochsim brainweb-dl cmap
# %%
#
# The problem is stated over a simulator carrying the sequence and filled in
-# by an estimator. :func:`~torchsim.execution` decides where that work runs,
+# by an estimator. :func:`~blochsim.execution` decides where that work runs,
# and the timings below are taken inside it.
#
@@ -166,9 +166,9 @@ def key(axes, ncols=1):
import numpy as np
import torch
-import torchsim
-from torchsim.estimators import DictionaryMatcher, NonlinearLeastSquares
-from torchsim.simulators import MultiEchoSimulator
+import blochsim
+from blochsim.estimators import DictionaryMatcher, NonlinearLeastSquares
+from blochsim.simulators import MultiEchoSimulator
# %%
#
@@ -266,7 +266,7 @@ def footprint(problem):
def mapped(problem, passes=3):
"""Map the slice a few times: the quickest pass, and what it held."""
on_device = torch.cuda.is_available()
- with torchsim.execution():
+ with blochsim.execution():
problem(measured[:64])
if on_device:
torch.cuda.reset_peak_memory_stats()
diff --git a/examples/02-parameter-inference/04-perk.py b/examples/02-parameter-inference/04-perk.py
index bf490d08..3b36dcf6 100644
--- a/examples/02-parameter-inference/04-perk.py
+++ b/examples/02-parameter-inference/04-perk.py
@@ -17,12 +17,12 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim brainweb-dl cmap
+# !pip install blochsim brainweb-dl cmap
# %%
#
# The problem is stated over a simulator carrying the sequence and filled in
-# by an estimator. :func:`~torchsim.execution` decides where that work runs,
+# by an estimator. :func:`~blochsim.execution` decides where that work runs,
# and the timings below are taken inside it.
#
@@ -164,12 +164,12 @@ def key(axes, ncols=1):
import numpy as np
import torch
-import torchsim
-from torchsim import (
+import blochsim
+from blochsim import (
Subspace,
)
-from torchsim.estimators import PERK, DictionaryMatcher
-from torchsim.simulators import MRFSimulator
+from blochsim.estimators import PERK, DictionaryMatcher
+from blochsim.simulators import MRFSimulator
# %%
#
@@ -292,7 +292,7 @@ def footprint(problem):
def mapped(problem, passes=3):
"""Map the slice a few times: the quickest pass, and what it held."""
on_device = torch.cuda.is_available()
- with torchsim.execution():
+ with blochsim.execution():
problem(measured[:64])
if on_device:
torch.cuda.reset_peak_memory_stats()
@@ -579,7 +579,7 @@ def painted(values):
#
_signal, sensitivity = simulator.jacobian("T1 T2".split(), **truth)
sensitivity = sensitivity.real * density[:, None, None]
-floor = torchsim.crlb(sensitivity, noise_variance=NOISE_STD**2, singular="infinite")
+floor = blochsim.crlb(sensitivity, noise_variance=NOISE_STD**2, singular="infinite")
bound = {"T1": floor[:, 0].sqrt(), "T2": floor[:, 1].sqrt()}
# sphinx_gallery_start_ignore
diff --git a/examples/03-sequence-optimization/01-echo-train-design.py b/examples/03-sequence-optimization/01-echo-train-design.py
index c36135bc..e51d02da 100644
--- a/examples/03-sequence-optimization/01-echo-train-design.py
+++ b/examples/03-sequence-optimization/01-echo-train-design.py
@@ -19,12 +19,12 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim
+# !pip install blochsim
# %%
#
# A design is a simulator with its tissue fixed on it, a cost written on what
-# it records, and the bounded parameters :class:`~torchsim.SequenceDesign`
+# it records, and the bounded parameters :class:`~blochsim.SequenceDesign`
# drives.
#
@@ -107,8 +107,8 @@ def key(axes, ncols=1):
import torch
-from torchsim.optim import Bounded, SequenceDesign
-from torchsim.simulators import FSESimulator
+from blochsim.optim import Bounded, SequenceDesign
+from blochsim.simulators import FSESimulator
# %%
#
@@ -273,7 +273,7 @@ def single_train(control):
#
# A conventional prescription to start from: 50 degree minimum, 90 degree
# centre-of-k-space angle, 150 degree maximum. The limits are what the scanner
-# will play, and :class:`~torchsim.Bounded` holds them exactly.
+# will play, and :class:`~blochsim.Bounded` holds them exactly.
#
design = SequenceDesign(single_train, control=Bounded(PRESCRIBED, LOWEST, HIGHEST))
diff --git a/examples/03-sequence-optimization/02-joint-relaxometry.py b/examples/03-sequence-optimization/02-joint-relaxometry.py
index 75d28263..107f3360 100644
--- a/examples/03-sequence-optimization/02-joint-relaxometry.py
+++ b/examples/03-sequence-optimization/02-joint-relaxometry.py
@@ -18,12 +18,12 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim brainweb-dl cmap
+# !pip install blochsim brainweb-dl cmap
# %%
#
# The two closed-form sequences being designed, the three pieces a design is
-# stated in, and :func:`~torchsim.crlb`, which is the cost.
+# stated in, and :func:`~blochsim.crlb`, which is the cost.
#
# sphinx_gallery_start_ignore
@@ -159,9 +159,9 @@ def key(axes, ncols=1):
import torch
-import torchsim
-from torchsim.optim import Bounded, SequenceDesign
-from torchsim.simulators import SPGRSimulator, bSSFPSimulator
+import blochsim
+from blochsim.optim import Bounded, SequenceDesign
+from blochsim.simulators import SPGRSimulator, bSSFPSimulator
# %%
#
@@ -213,7 +213,7 @@ def bounds(spgr_flip, ssfp_flip):
together = torch.cat(
(rows(spgr, flip=spgr_flip), rows(ssfp, flip=ssfp_flip)), dim=-1
)
- return torchsim.crlb(together, noise_variance=NOISE**2)
+ return blochsim.crlb(together, noise_variance=NOISE**2)
# %%
@@ -415,13 +415,13 @@ def precision(spgr_flip, ssfp_flip):
# %%
#
# The two blocks are one experiment and are fitted as one: a
-# :class:`~torchsim.model.Simulator` that plays each and concatenates what
+# :class:`~blochsim.model.Simulator` that plays each and concatenates what
# they record. The fit is the one thing held fixed between the protocols --
# the same nonlinear least squares over the same four unknowns, from the same
# guess.
#
-from torchsim.estimators import NonlinearLeastSquares
-from torchsim.model import Simulator
+from blochsim.estimators import NonlinearLeastSquares
+from blochsim.model import Simulator
class JointRelaxometry(Simulator):
@@ -521,7 +521,7 @@ def predicted_sigma(spgr_flip, ssfp_flip):
together = torch.cat(
(rows(at_voxel[0], flip=spgr_flip), rows(at_voxel[1], flip=ssfp_flip)), dim=-1
)
- bound = torchsim.crlb(together, noise_variance=NOISE**2)
+ bound = blochsim.crlb(together, noise_variance=NOISE**2)
return {
"T1": bound[..., 0].sqrt() / truth["T1"],
"T2": bound[..., 1].sqrt() / truth["T2"],
diff --git a/examples/03-sequence-optimization/03-rf-pulse-design.py b/examples/03-sequence-optimization/03-rf-pulse-design.py
index e6563a2e..c2ff4430 100644
--- a/examples/03-sequence-optimization/03-rf-pulse-design.py
+++ b/examples/03-sequence-optimization/03-rf-pulse-design.py
@@ -20,7 +20,7 @@
# .. colab-link::
# :needs_gpu: 0
#
-# !pip install torchsim
+# !pip install blochsim
# sphinx_gallery_start_ignore
import warnings
@@ -52,7 +52,7 @@
import torch
-from torchsim import SequenceDesign, compose_spinor
+from blochsim import SequenceDesign, compose_spinor
# %%
#
@@ -125,7 +125,7 @@ def cost(real, imag):
# ---------------
#
# The real and imaginary parts of every sample are the designed parameters,
-# free of limits: the scanner's peak B1 would be a :class:`~torchsim.Bounded`
+# free of limits: the scanner's peak B1 would be a :class:`~blochsim.Bounded`
# on them.
#
diff --git a/examples/03-sequence-optimization/README.rst b/examples/03-sequence-optimization/README.rst
index 3ebc9892..0e6c46eb 100644
--- a/examples/03-sequence-optimization/README.rst
+++ b/examples/03-sequence-optimization/README.rst
@@ -6,7 +6,7 @@ given.
A design problem is three pieces: a simulator with the tissue it is designed
for already fixed on it; a cost, which is a plain function of what that
-simulator records; and a :class:`~torchsim.SequenceDesign`, which holds the
+simulator records; and a :class:`~blochsim.SequenceDesign`, which holds the
parameters inside the limits the scanner will play and runs the loop.
Only the cost changes between the first two examples here. One asks for a
diff --git a/examples/04-model-based-imaging/01-linear-subspace.py b/examples/04-model-based-imaging/01-linear-subspace.py
index 847686a7..afd67a52 100644
--- a/examples/04-model-based-imaging/01-linear-subspace.py
+++ b/examples/04-model-based-imaging/01-linear-subspace.py
@@ -19,7 +19,7 @@
# .. colab-link::
# :needs_gpu: 1
#
-# !pip install torchsim brainweb-dl cmap mri-nufft[finufft,cufinufft] deepinv
+# !pip install blochsim brainweb-dl cmap mri-nufft[finufft,cufinufft] deepinv
# %%
#
@@ -122,14 +122,14 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
# %%
#
-# The Fourier encoding is not TorchSim's and never will be. ``mri-nufft``
+# The Fourier encoding is not BlochSim's and never will be. ``mri-nufft``
# supplies the radial trajectory and the non-uniform transform that plays it;
# ``deepinv`` supplies the linear solver a Gauss-Newton step hands its
# linearized problem to, and the :class:`~deepinv.physics.LinearPhysics` base
# class the encoding operator below is written against.
#
# That base class is the whole of the adapter: anything exposing ``A`` and
-# ``A_adjoint`` composes with what TorchSim supplies, so the operator built a
+# ``A_adjoint`` composes with what BlochSim supplies, so the operator built a
# few cells down is the only glue this integration needs.
#
import mrinufft
@@ -140,8 +140,8 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
# %%
#
-# From TorchSim: the sequence, the estimator the contrast-then-fit routes
-# need, and :attr:`~torchsim.Subspace.modes`, which hands the temporal
+# From BlochSim: the sequence, the estimator the contrast-then-fit routes
+# need, and :attr:`~blochsim.Subspace.modes`, which hands the temporal
# basis to mri-nufft in the layout its subspace operator reads.
#
import time
@@ -149,8 +149,8 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
import numpy as np
import torch
-from torchsim.estimators import DictionaryMatcher
-from torchsim.simulators import MultiEchoSimulator
+from blochsim.estimators import DictionaryMatcher
+from blochsim.simulators import MultiEchoSimulator
# %%
@@ -258,7 +258,7 @@ class RadialEncoding(LinearPhysics):
"""``(batch, echoes, x, y)`` images to k-space, one trajectory per echo.
This is the whole of ``P F C`` for this experiment, and none of it is
- TorchSim's: it wraps mri-nufft, which is what a real pipeline would do
+ BlochSim's: it wraps mri-nufft, which is what a real pipeline would do
with its own trajectory, its own density compensation and its own coils.
"""
@@ -335,7 +335,7 @@ def A_adjoint(self, y, **kwargs):
# Estimator and subspace basis
# ----------------------------
#
-# One :class:`~torchsim.DictionaryMatcher` states the problem and serves every
+# One :class:`~blochsim.DictionaryMatcher` states the problem and serves every
# route. Asking it for a rank fits a temporal basis to the training signals;
# that basis is what the subspace reconstruction is given, and the coefficients
# it returns come back to the same mapping. Three directions hold essentially
@@ -503,6 +503,6 @@ def report(name, seconds, found):
# because the basis must span the product of the ranges. Both cases put the
# model inside the operator, which is the nonlinear route.
#
-# The rank is not a guess: :attr:`~torchsim.Subspace.retained` says what a
+# The rank is not a guess: :attr:`~blochsim.Subspace.retained` says what a
# basis keeps before anything is projected through it.
#
diff --git a/examples/04-model-based-imaging/02-nonlinear-inversion.py b/examples/04-model-based-imaging/02-nonlinear-inversion.py
index 33e1587d..da26b708 100644
--- a/examples/04-model-based-imaging/02-nonlinear-inversion.py
+++ b/examples/04-model-based-imaging/02-nonlinear-inversion.py
@@ -17,7 +17,7 @@
-- sampling, Fourier encoding, coil sensitivities, and the **signal model** --
and the parameter maps are solved for directly against the k-space that was
measured. Only the last factor changes with the sequence, and it is the only
-one TorchSim supplies: :class:`~torchsim.recon.ModelOperator` turns any
+one BlochSim supplies: :class:`~blochsim.recon.ModelOperator` turns any
simulator into it, and the encoding comes from mri-nufft.
Unlike a subspace this stays nonlinear, so it needs a starting guess and a loop
@@ -34,7 +34,7 @@
# .. colab-link::
# :needs_gpu: 1
#
-# !pip install torchsim brainweb-dl cmap mri-nufft[finufft,cufinufft] deepinv
+# !pip install blochsim brainweb-dl cmap mri-nufft[finufft,cufinufft] deepinv
# %%
#
@@ -137,12 +137,12 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
# %%
#
-# The Fourier encoding is not TorchSim's and never will be. ``mri-nufft``
+# The Fourier encoding is not BlochSim's and never will be. ``mri-nufft``
# supplies the radial trajectory and the non-uniform transform that plays it;
# ``deepinv`` supplies the :class:`~deepinv.physics.LinearPhysics` base class
# the encoding operator is written against, and the linear solver a
# Gauss-Newton step hands its linearized problem to. Anything exposing ``A``
-# and ``A_adjoint`` composes with what TorchSim supplies.
+# and ``A_adjoint`` composes with what BlochSim supplies.
#
import mrinufft
from deepinv.physics import LinearPhysics
@@ -150,8 +150,8 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
# %%
#
-# From TorchSim: the sequence, the estimator the contrast-then-fit routes
-# need, and :class:`~torchsim.recon.ModelOperator`, which is the signal
+# From BlochSim: the sequence, the estimator the contrast-then-fit routes
+# need, and :class:`~blochsim.recon.ModelOperator`, which is the signal
# model as a factor of the forward operator.
#
import time
@@ -159,9 +159,9 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
import numpy as np
import torch
-from torchsim.estimators import DictionaryMatcher
-from torchsim.recon import GaussNewton, ModelOperator, Schedule, iterative
-from torchsim.simulators import MultiEchoSimulator
+from blochsim.estimators import DictionaryMatcher
+from blochsim.recon import GaussNewton, ModelOperator, Schedule, iterative
+from blochsim.simulators import MultiEchoSimulator
# %%
@@ -236,7 +236,7 @@ def resampled(values):
# A multi-echo spin echo on a golden-angle radial trajectory that rotates
# between echoes. Sixteen spokes per echo across a 96-sample matrix is roughly
# ninefold undersampled. The protocol stays on the host;
-# :class:`~torchsim.recon.ModelOperator` takes it wherever the maps are.
+# :class:`~blochsim.recon.ModelOperator` takes it wherever the maps are.
#
TE = torch.linspace(10.0, 150.0, ECHOES)
simulator = MultiEchoSimulator(TE=TE)
@@ -263,7 +263,7 @@ class RadialEncoding(LinearPhysics):
"""``(batch, echoes, x, y)`` images to k-space, one trajectory per echo.
This is the whole of ``P F C`` for this experiment, and none of it is
- TorchSim's: it wraps mri-nufft, which is what a real pipeline would do
+ BlochSim's: it wraps mri-nufft, which is what a real pipeline would do
with its own trajectory, its own density compensation and its own coils.
"""
@@ -425,8 +425,8 @@ def report(name, seconds, found):
# %%
#
# An iteratively regularized Gauss-Newton: linearize, solve, step, lower the
-# damping. TorchSim supplies the loop and the derivative but not the linear
-# solver -- :func:`~torchsim.recon.iterative` hands the linearized problem to
+# damping. BlochSim supplies the loop and the derivative but not the linear
+# solver -- :func:`~blochsim.recon.iterative` hands the linearized problem to
# the same deepinv routine the baseline called. A proximal solver under a
# wavelet prior is a change to that one argument.
#
@@ -538,7 +538,7 @@ def timed(call, repeats=5):
# -------------------------
#
# The model is the only thing above that names a relaxation time, and it is an
-# ordinary :class:`~torchsim.model.Simulator` -- the same object the fitting
+# ordinary :class:`~blochsim.model.Simulator` -- the same object the fitting
# and sequence-design notebooks use. Water-fat separation, T2* with a field
# map, a Look-Locker inversion recovery: each is a different ``evaluate``, and
# the operator, the loop and the encoding are unchanged.
diff --git a/examples/05-misc/01-synthetic-data.py b/examples/05-misc/01-synthetic-data.py
index bf839f0b..3c0e967c 100644
--- a/examples/05-misc/01-synthetic-data.py
+++ b/examples/05-misc/01-synthetic-data.py
@@ -14,7 +14,7 @@
fully sampled one are the pair, with the ground-truth maps and the
segmentation.
-Only the simulation is TorchSim's. The phantom, the coils and the encoding come
+Only the simulation is BlochSim's. The phantom, the coils and the encoding come
from torchio, deepmriprep, SigPy and mri-nufft.
"""
@@ -22,11 +22,11 @@
# .. colab-link::
# :needs_gpu: 1
#
-# !pip install torchsim torchio deepmriprep sigpy cmap mri-nufft[finufft,cufinufft]
+# !pip install blochsim torchio deepmriprep sigpy cmap mri-nufft[finufft,cufinufft]
# %%
#
-# Only one step of this pipeline is TorchSim's. Four other packages do the
+# Only one step of this pipeline is BlochSim's. Four other packages do the
# rest, and each has exactly one job here:
#
# * ``torchio`` fetches a T1-weighted IXI subject and gives it as tensors,
@@ -152,7 +152,7 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
# %%
#
-# TorchSim's part is the third step: one fingerprinting simulation per tissue
+# BlochSim's part is the third step: one fingerprinting simulation per tissue
# class, rather than one per voxel.
#
import tempfile
@@ -162,7 +162,7 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
import numpy as np
import torch
-from torchsim.simulators import MRFSimulator
+from blochsim.simulators import MRFSimulator
# %%
@@ -194,7 +194,7 @@ def canvas(rows, columns, shape, *, bars=1, extra=0.6):
# ``download=True`` fetches the archive once, a few hundred MB, into a cache
# under the home directory.
#
-CACHE = Path.home() / ".cache" / "torchsim" / "ixi-tiny"
+CACHE = Path.home() / ".cache" / "blochsim" / "ixi-tiny"
subject = tio.datasets.IXITiny(str(CACHE), download=True)[0]
@@ -311,7 +311,7 @@ def slab(image):
# --------------------
#
# An inversion, then four hundred repetitions whose flip angle sweeps.
-# :class:`~torchsim.simulators.MRFSimulator` takes arrays of tissue properties,
+# :class:`~blochsim.simulators.MRFSimulator` takes arrays of tissue properties,
# so the whole table is one call: three extended phase graph runs rather than
# sixteen thousand.
#
diff --git a/examples/_gallery_header.md b/examples/_gallery_header.md
index 537c7f58..29e1f5b1 100644
--- a/examples/_gallery_header.md
+++ b/examples/_gallery_header.md
@@ -11,16 +11,16 @@ and uses the same public interfaces documented in the API reference.
Read the four **Framework** lessons in order. They are the shortest path from a
shipped sequence to one of your own:
-1. **Getting started** — run a shipped {class}`torchsim.model.Simulator`,
+1. **Getting started** — run a shipped {class}`blochsim.model.Simulator`,
inspect its sequence description, differentiate the signal, and see where
execution happens.
2. **Expanded physics** — turn on off-resonance, transmit variation,
diffusion, flow, exchange and magnetization transfer without changing the
sequence abstraction.
3. **Writing a simulator** — the extension point of the framework:
- subclass {class}`torchsim.model.Simulator`, choose the command handlers
+ subclass {class}`blochsim.model.Simulator`, choose the command handlers
that interpret an incoming Pulseq/MRD event stream, and implement
- {meth}`torchsim.model.Simulator.layout` for offline construction.
+ {meth}`blochsim.model.Simulator.layout` for offline construction.
4. **Custom operator** — package a preparation or readout as an operator while
leaving the state-machine kernels untouched.
diff --git a/pyproject.toml b/pyproject.toml
index 0cf97ded..10c0508c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ requires = ["scikit-build-core>=0.10", "setuptools-scm>=8"]
[project]
-name = "torchsim"
+name = "blochsim"
description = "A feature-rich MR simulator supporting massive parallelization on GPU and automatic differentiation."
readme = "README.md"
@@ -62,7 +62,7 @@ pulseq = [
]
# What it takes to run the suite. Nothing here is imported by the package.
test = [
-"torchsim[pulseq]",
+"blochsim[pulseq]",
"pytest>=7.0",
"pytest-cov",
"pytest-sugar",
@@ -75,7 +75,7 @@ test = [
doc = [
# The explanation figures and the getting-started example read the shipped
# ``.seq`` file, which pypulseq parses.
-"torchsim[pulseq]",
+"blochsim[pulseq]",
"sphinx>=7.0",
"sphinx_book_theme>=1.1",
"sphinx-design>=0.6",
@@ -92,7 +92,7 @@ doc = [
# phantoms, the segmentation, the coils, the Fourier encoding and the linear
# solvers all come from elsewhere, which is the point several examples make.
examples = [
-"torchsim[pulseq]",
+"blochsim[pulseq]",
"brainweb-dl>=0.3",
"deepinv>=0.2",
"deepmriprep>=0.3",
@@ -102,7 +102,7 @@ examples = [
]
# The whole toolchain in one command, which is what a contributor installs.
dev = [
-"torchsim[test,doc,examples]",
+"blochsim[test,doc,examples]",
"ruff>=0.14",
"mypy",
"pre-commit",
@@ -111,23 +111,23 @@ dev = [
# List URLs that are relevant to your project
# This field corresponds to the "Project-URL" and "Home-Page" metadata fields:
[project.urls] # Optional
-"Homepage" = "https://github.com/pulserver/torchsim"
-"Documentation" = "https://pulserver.github.io/torchsim/"
-"Bug Reports" = "https://github.com/pulserver/torchsim/issues"
-"Source" = "https://github.com/pulserver/torchsim"
+"Homepage" = "https://github.com/pulserver/blochsim"
+"Documentation" = "https://pulserver.github.io/blochsim/"
+"Bug Reports" = "https://github.com/pulserver/blochsim/issues"
+"Source" = "https://github.com/pulserver/blochsim"
[tool.scikit-build]
minimum-version = "build-system.requires"
build-dir = "build/{wheel_tag}"
cmake.version = ">=3.26"
-wheel.packages = ["src/torchsim"]
+wheel.packages = ["src/blochsim"]
# One wheel per platform rather than one per interpreter: the kernels are built
# against the CPython stable ABI from 3.10 on, so the tag is cp310-abi3.
wheel.py-api = "cp310"
# What ships is the compiled kernel, not the source it was compiled from.
wheel.exclude = ["*.cpp", "*.hpp"]
metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
-sdist.include = ["src/torchsim/_version.py"]
+sdist.include = ["src/blochsim/_version.py"]
# One wheel per platform, built by cibuildwheel in .github/workflows/wheels.yml.
[tool.cibuildwheel]
@@ -159,7 +159,7 @@ archs = ["arm64", "x86_64"]
archs = ["AMD64"]
[tool.setuptools_scm]
-write_to = "src/torchsim/_version.py"
+write_to = "src/blochsim/_version.py"
version_scheme = "python-simplified-semver"
local_scheme="no-local-version"
fallback_version="v99-dev"
@@ -167,7 +167,7 @@ fallback_version="v99-dev"
[tool.pytest.ini_options]
minversion = "6.0"
addopts = [
- "--cov=torchsim",
+ "--cov=blochsim",
"--cov-report=term-missing",
"--cov-report=xml",
"-m", "not interpreted"
@@ -186,7 +186,7 @@ markers = [
required-version = ">=0.14"
src = ["src", "tests"]
line-length = 88
-extend-exclude = ["docs/generated", "src/torchsim/_version.py"]
+extend-exclude = ["docs/generated", "src/blochsim/_version.py"]
[tool.ruff.format]
quote-style = "double"
@@ -206,7 +206,7 @@ ignore = [
"D401",
"D404", # Imperative mode is not necessary everywhere, trust me.
"E501", # line length is the formatter's to decide, not the linter's
- # Every collaborator TorchSim takes at a boundary is duck-typed on purpose:
+ # Every collaborator BlochSim takes at a boundary is duck-typed on purpose:
# an acquisition is anything with ``simulate``, an encoding anything with
# ``A`` and ``A_adjoint``, and naming a class there would close the door
# this package exists to hold open. The docstring says what is expected.
@@ -227,10 +227,10 @@ ignore = [
"scripts/**" = ["ANN", "D1"]
# A Triton kernel's parameters are compile-time constants and tensors the
# compiler types itself; an annotation on one says nothing Python will read.
-"src/torchsim/**/*_triton.py" = ["ANN"]
+"src/blochsim/**/*_triton.py" = ["ANN"]
[tool.ruff.lint.isort]
-known-first-party = ["torchsim"]
+known-first-party = ["blochsim"]
[tool.ruff.lint.pydocstyle]
convention = "numpy"
diff --git a/scripts/build_docs.sh b/scripts/build_docs.sh
index cc07ca04..48e66604 100755
--- a/scripts/build_docs.sh
+++ b/scripts/build_docs.sh
@@ -6,7 +6,7 @@
# bash scripts/build_docs.sh --clean # re-run every example from scratch
#
# The examples are executed by sphinx-gallery, so this needs an interpreter
-# that can import torchsim as well as the documentation extensions. Set
+# that can import blochsim as well as the documentation extensions. Set
# PYTHON_BIN to choose one.
#
set -euo pipefail
@@ -34,7 +34,7 @@ def absent(name):
missing = [
name
- for name in ("torchsim", "torchsim._epg_cpu", "torch", "matplotlib",
+ for name in ("blochsim", "blochsim._epg_cpu", "torch", "matplotlib",
"sphinx", "sphinx_gallery", "sphinx_copybutton",
"sphinx_exec_directive", "sphinx_book_theme", "myst_parser",
"linkify_it", "pypulseq")
@@ -53,7 +53,7 @@ if [ "$clean" -eq 1 ]; then
rm -rf "$root/docs/build" "$root/docs/generated"
fi
-# The pages are built against the installed TorchSim, kernels and all. Put
+# The pages are built against the installed BlochSim, kernels and all. Put
# ``src`` on the path instead and the compiled halves of the package are gone:
# a figure the fused kernel draws then fails the build.
cd "$root/docs"
diff --git a/scripts/check_wheel.py b/scripts/check_wheel.py
index 6637cc9c..48ef33ce 100644
--- a/scripts/check_wheel.py
+++ b/scripts/check_wheel.py
@@ -2,7 +2,7 @@
Run by cibuildwheel against every wheel it builds, on the interpreter that
wheel claims to support. It loads each extension by file path rather than by
-importing :mod:`torchsim`, so the check needs no PyTorch and says something
+importing :mod:`blochsim`, so the check needs no PyTorch and says something
about the binary alone: that the stable-ABI module initialises on this
interpreter, and that it carries no vendored library.
@@ -22,9 +22,9 @@
def kernel_directory() -> pathlib.Path:
"""Where the installed package lives, without executing it."""
- spec = importlib.util.find_spec("torchsim")
+ spec = importlib.util.find_spec("blochsim")
if spec is None or not spec.submodule_search_locations:
- raise SystemExit("torchsim is not installed")
+ raise SystemExit("blochsim is not installed")
return pathlib.Path(next(iter(spec.submodule_search_locations)))
@@ -38,7 +38,7 @@ def main() -> int:
for path in kernels:
name = path.name.split(".")[0]
- spec = importlib.util.spec_from_file_location(f"torchsim.{name}", path)
+ spec = importlib.util.spec_from_file_location(f"blochsim.{name}", path)
if spec is None or spec.loader is None:
raise SystemExit(f"no loader for {path}")
module = importlib.util.module_from_spec(spec)
diff --git a/scripts/make_artwork.py b/scripts/make_artwork.py
new file mode 100644
index 00000000..e33de43e
--- /dev/null
+++ b/scripts/make_artwork.py
@@ -0,0 +1,161 @@
+"""Write the blochsim logo and its mark into ``docs/_static``.
+
+ python scripts/make_artwork.py
+
+The mark is a Bloch sphere: the magnetization vector, tipped onto the
+transverse plane, at the end of the precessing path an excitation traces from
+the longitudinal axis. The logo is the mark followed by the wordmark. Each
+image is written twice, ``.svg`` for a light background and
+``-dark.svg`` for a dark one. The wordmark is SVG text in the reader's
+sans-serif font; no font is embedded.
+"""
+
+from __future__ import annotations
+
+import math
+from pathlib import Path
+
+OUT = Path(__file__).resolve().parent.parent / "docs" / "_static"
+
+#: The colours of each theme, by role: the pulserver family's blue and amber.
+PALETTE = {
+ "light": {
+ "ink": "#12212b",
+ "muted": "#8a9aa8",
+ "blue": "#2b76ad",
+ "amber": "#f0a500",
+ },
+ "dark": {
+ "ink": "#e8eef3",
+ "muted": "#6f8394",
+ "blue": "#5b9bd0",
+ "amber": "#ffbe2e",
+ },
+}
+
+#: Sphere radius, and the elevation the sphere is seen from, in radians.
+RADIUS = 46.0
+ELEVATION = math.radians(22)
+
+#: Precession turns of the excitation path, and the azimuth it ends at.
+TURNS = 2.75
+END_AZIMUTH = math.radians(-35)
+
+
+def _project(x: float, y: float, z: float) -> tuple[float, float, bool]:
+ """Screen position of a point on the sphere, and whether it faces the viewer."""
+ up = z * math.cos(ELEVATION) + y * math.sin(ELEVATION)
+ toward = -y * math.cos(ELEVATION) + z * math.sin(ELEVATION)
+ return x, -up, toward >= 0
+
+
+def _path() -> list[tuple[float, float, bool]]:
+ """The tip of the magnetization during excitation: tip angle 0 to 90 degrees."""
+ points = []
+ n = 400
+ for i in range(n + 1):
+ t = i / n
+ tip = 0.5 * math.pi * t
+ azimuth = END_AZIMUTH - 2 * math.pi * TURNS * (1 - t)
+ r = RADIUS * math.sin(tip)
+ points.append(
+ _project(
+ r * math.cos(azimuth), r * math.sin(azimuth), RADIUS * math.cos(tip)
+ )
+ )
+ return points
+
+
+def _runs(points: list[tuple[float, float, bool]]) -> list[tuple[bool, str]]:
+ """The path split into runs in front of and behind the sphere's centre."""
+ runs: list[tuple[bool, list[tuple[float, float]]]] = []
+ for x, y, front in points:
+ if not runs or runs[-1][0] != front:
+ runs.append((front, [runs[-1][1][-1]] if runs else []))
+ runs[-1][1].append((x, y))
+ return [
+ (front, "M" + " L".join(f"{x:.2f} {y:.2f}" for x, y in pts))
+ for front, pts in runs
+ ]
+
+
+def _mark(colours: dict[str, str]) -> str:
+ """The Bloch sphere, centred on the origin, as SVG elements."""
+ ry = RADIUS * math.sin(ELEVATION)
+ tip_x, tip_y, _ = _path()[-1]
+ length = math.hypot(tip_x, tip_y)
+ ux, uy = tip_x / length, tip_y / length
+ shaft_x, shaft_y = tip_x - 9 * ux, tip_y - 9 * uy
+ head = (
+ f"{tip_x:.2f},{tip_y:.2f} "
+ f"{shaft_x - 6 * uy:.2f},{shaft_y + 6 * ux:.2f} "
+ f"{shaft_x + 6 * uy:.2f},{shaft_y - 6 * ux:.2f}"
+ )
+ parts = [
+ f'',
+ f'',
+ f'',
+ f'',
+ ]
+ for front, d in _runs(_path()):
+ opacity = "1" if front else "0.4"
+ parts.append(
+ f''
+ )
+ parts += [
+ f'',
+ f'',
+ f'',
+ ]
+ return "\n".join(parts)
+
+
+def _svg(view_box: str, title: str, desc: str, body: str) -> str:
+ return (
+ f'\n"
+ )
+
+
+def mark(colours: dict[str, str]) -> str:
+ """The compact mark, used in the sidebar and as the favicon."""
+ return _svg(
+ "0 0 112 112",
+ "blochsim",
+ "A Bloch sphere with the magnetization vector at the end of its precessing excitation path",
+ f'\n{_mark(colours)}\n',
+ )
+
+
+def logo(colours: dict[str, str]) -> str:
+ """The mark followed by the wordmark."""
+ word = (
+ ''
+ f'bloch'
+ f'sim'
+ )
+ return _svg(
+ "0 0 520 128",
+ "blochsim",
+ "A Bloch sphere with a precessing magnetization vector, followed by the wordmark blochsim",
+ f'\n{_mark(colours)}\n\n{word}',
+ )
+
+
+def main() -> None:
+ OUT.mkdir(parents=True, exist_ok=True)
+ for theme, colours in PALETTE.items():
+ suffix = "" if theme == "light" else "-dark"
+ (OUT / f"blochsim-mark{suffix}.svg").write_text(mark(colours))
+ (OUT / f"blochsim-logo{suffix}.svg").write_text(logo(colours))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/publish_docs.py b/scripts/publish_docs.py
index 958335f0..34bad259 100755
--- a/scripts/publish_docs.py
+++ b/scripts/publish_docs.py
@@ -34,7 +34,7 @@
- TorchSim documentation
+ BlochSim documentation
@@ -130,7 +130,7 @@ def main(argv: list[str] | None = None) -> int:
parser.add_argument("site", type=Path, help="a checkout of the site branch")
parser.add_argument(
"--url",
- default="https://pulserver.github.io/torchsim",
+ default="https://pulserver.github.io/blochsim",
help="where the site is served from",
)
arguments = parser.parse_args(argv)
diff --git a/src/torchsim/__init__.py b/src/blochsim/__init__.py
similarity index 96%
rename from src/torchsim/__init__.py
rename to src/blochsim/__init__.py
index d7cc2ed1..2041b0ec 100644
--- a/src/torchsim/__init__.py
+++ b/src/blochsim/__init__.py
@@ -1,4 +1,4 @@
-"""Main TorchSim API."""
+"""Main BlochSim API."""
__all__ = []
diff --git a/src/torchsim/_bounds.py b/src/blochsim/_bounds.py
similarity index 100%
rename from src/torchsim/_bounds.py
rename to src/blochsim/_bounds.py
diff --git a/src/torchsim/_epg_cpu.cpp b/src/blochsim/_epg_cpu.cpp
similarity index 99%
rename from src/torchsim/_epg_cpu.cpp
rename to src/blochsim/_epg_cpu.cpp
index 4aa7fbae..da2fe903 100644
--- a/src/torchsim/_epg_cpu.cpp
+++ b/src/blochsim/_epg_cpu.cpp
@@ -24,11 +24,11 @@
// MSVC rejects the GNU spelling of both of these outright, and the kernel is
// built by whichever compiler the platform ships.
#if defined(_MSC_VER)
-#define TORCHSIM_ALWAYS_INLINE __forceinline
-#define TORCHSIM_RESTRICT __restrict
+#define BLOCHSIM_ALWAYS_INLINE __forceinline
+#define BLOCHSIM_RESTRICT __restrict
#else
-#define TORCHSIM_ALWAYS_INLINE __attribute__((always_inline)) inline
-#define TORCHSIM_RESTRICT __restrict__
+#define BLOCHSIM_ALWAYS_INLINE __attribute__((always_inline)) inline
+#define BLOCHSIM_RESTRICT __restrict__
#endif
namespace {
@@ -82,10 +82,10 @@ struct TissueLayout {
}
};
-using torchsim::MIN_WORK_PER_THREAD;
-using torchsim::usable_processors;
-using torchsim::WorkerPool;
-using torchsim::worker_count;
+using blochsim::MIN_WORK_PER_THREAD;
+using blochsim::usable_processors;
+using blochsim::WorkerPool;
+using blochsim::worker_count;
// Which optional terms a launch carries. A property the caller never passed
// is not an input, so its term is out and its gradient is zero -- the same
@@ -2467,7 +2467,7 @@ inline void rotate(
// clone is a copy of the caller, so a body reached through a call is
// compiled once for the baseline instruction set and no more.
template
-TORCHSIM_ALWAYS_INLINE void simulate_jvp_range(
+BLOCHSIM_ALWAYS_INLINE void simulate_jvp_range(
const JvpBuffers& buffers,
const std::int64_t work_begin,
const std::int64_t work_end,
@@ -3004,7 +3004,7 @@ TORCHSIM_ALWAYS_INLINE void simulate_jvp_range(
}
}
-#if TORCHSIM_MULTIVERSIONED
+#if BLOCHSIM_MULTIVERSIONED
__attribute__((target_clones("default", "sse4.2", "avx2", "avx512f")))
#endif
inline void shift_real(
@@ -3301,13 +3301,13 @@ void simulate_real_jvp_range(
constexpr std::size_t REAL_LANES = 8;
-// Setting TORCHSIM_REAL_SCALAR=1 routes the real-subspace path through the
+// Setting BLOCHSIM_REAL_SCALAR=1 routes the real-subspace path through the
// one-train-at-a-time kernels instead. They compute the same thing, so this
// selects between two implementations of one contract: useful for timing one
// against the other, and as a way out if a host miscompiles the lane arithmetic.
inline bool lane_kernels_enabled() {
static const bool enabled = [] {
- const char* const value = std::getenv("TORCHSIM_REAL_SCALAR");
+ const char* const value = std::getenv("BLOCHSIM_REAL_SCALAR");
return value == nullptr || value[0] != '1';
}();
return enabled;
@@ -3512,12 +3512,12 @@ void simulate_real_jvp_atom_lane_range(
const std::size_t states = static_cast(state_count);
const std::size_t width = states * REAL_LANES;
std::vector storage(6U * width);
- float* const TORCHSIM_RESTRICT plus = storage.data();
- float* const TORCHSIM_RESTRICT minus = plus + width;
- float* const TORCHSIM_RESTRICT longitudinal = minus + width;
- float* const TORCHSIM_RESTRICT dot_plus = longitudinal + width;
- float* const TORCHSIM_RESTRICT dot_minus = dot_plus + width;
- float* const TORCHSIM_RESTRICT dot_longitudinal = dot_minus + width;
+ float* const BLOCHSIM_RESTRICT plus = storage.data();
+ float* const BLOCHSIM_RESTRICT minus = plus + width;
+ float* const BLOCHSIM_RESTRICT longitudinal = minus + width;
+ float* const BLOCHSIM_RESTRICT dot_plus = longitudinal + width;
+ float* const BLOCHSIM_RESTRICT dot_minus = dot_plus + width;
+ float* const BLOCHSIM_RESTRICT dot_longitudinal = dot_minus + width;
float t1[REAL_LANES], t2[REAL_LANES], dot_t1[REAL_LANES], dot_t2[REAL_LANES];
float rate1[REAL_LANES], rate2[REAL_LANES];
@@ -3706,12 +3706,12 @@ void simulate_real_jvp_lane_range(
const std::size_t states = static_cast(state_count);
const std::size_t width = states * REAL_LANES;
std::vector storage(6U * width);
- float* const TORCHSIM_RESTRICT plus = storage.data();
- float* const TORCHSIM_RESTRICT minus = plus + width;
- float* const TORCHSIM_RESTRICT longitudinal = minus + width;
- float* const TORCHSIM_RESTRICT dot_plus = longitudinal + width;
- float* const TORCHSIM_RESTRICT dot_minus = dot_plus + width;
- float* const TORCHSIM_RESTRICT dot_longitudinal = dot_minus + width;
+ float* const BLOCHSIM_RESTRICT plus = storage.data();
+ float* const BLOCHSIM_RESTRICT minus = plus + width;
+ float* const BLOCHSIM_RESTRICT longitudinal = minus + width;
+ float* const BLOCHSIM_RESTRICT dot_plus = longitudinal + width;
+ float* const BLOCHSIM_RESTRICT dot_minus = dot_plus + width;
+ float* const BLOCHSIM_RESTRICT dot_longitudinal = dot_minus + width;
float dt[REAL_LANES], dt_dot[REAL_LANES];
float flip[REAL_LANES], flip_dot[REAL_LANES];
@@ -4837,7 +4837,7 @@ inline void shift_adjoint(DualState& fplus_bar, DualState& fminus_bar) {
// clone is a copy of the caller, so a body reached through a call is
// compiled once for the baseline instruction set and no more.
template
-TORCHSIM_ALWAYS_INLINE void simulate_vjp_range(
+BLOCHSIM_ALWAYS_INLINE void simulate_vjp_range(
const VjpBuffers& buffers,
const std::int64_t work_begin,
const std::int64_t work_end,
@@ -6304,12 +6304,12 @@ void simulate_real_vjp_atom_lane_range(
static_cast(event_count) * stride
);
std::vector storage(6U * width);
- float* const TORCHSIM_RESTRICT plus = storage.data();
- float* const TORCHSIM_RESTRICT minus = plus + width;
- float* const TORCHSIM_RESTRICT longitudinal = minus + width;
- float* const TORCHSIM_RESTRICT plus_bar = longitudinal + width;
- float* const TORCHSIM_RESTRICT minus_bar = plus_bar + width;
- float* const TORCHSIM_RESTRICT longitudinal_bar = minus_bar + width;
+ float* const BLOCHSIM_RESTRICT plus = storage.data();
+ float* const BLOCHSIM_RESTRICT minus = plus + width;
+ float* const BLOCHSIM_RESTRICT longitudinal = minus + width;
+ float* const BLOCHSIM_RESTRICT plus_bar = longitudinal + width;
+ float* const BLOCHSIM_RESTRICT minus_bar = plus_bar + width;
+ float* const BLOCHSIM_RESTRICT longitudinal_bar = minus_bar + width;
float t1[REAL_LANES], t2[REAL_LANES], m0[REAL_LANES], b1[REAL_LANES];
float efficiency[REAL_LANES];
@@ -7757,7 +7757,7 @@ inline DualFloat shim_dual(
// clone is a copy of the caller, so a body reached through a call is
// compiled once for the baseline instruction set and no more.
template
-TORCHSIM_ALWAYS_INLINE void simulate_vjp_jvp_range(
+BLOCHSIM_ALWAYS_INLINE void simulate_vjp_jvp_range(
const VjpJvpBuffers& buffers,
const std::int64_t work_begin,
const std::int64_t work_end,
@@ -10505,7 +10505,7 @@ void simulate_pooled_vjp_jvp_range(
// that is what the ``always_inline`` on the range kernels is for. Taking the
// address of one still emits an out-of-line copy, which is what the two-pool
// dispatch selects.
-#if TORCHSIM_MULTIVERSIONED
+#if BLOCHSIM_MULTIVERSIONED
#define CLONED_KERNEL \
__attribute__((target_clones("default", "sse4.2", "avx2", "avx512f")))
#else
@@ -10835,12 +10835,12 @@ PyObject* simulate(PyObject*, PyObject* arguments) {
return nullptr;
}
- // TORCHSIM_LANES=1 selects a lane-vectorized forward that walks the
+ // BLOCHSIM_LANES=1 selects a lane-vectorized forward that walks the
// (atom, train block) product instead of (atom, train), putting a block of
// trains in the SIMD lanes. It is opt-in: it is measurably faster only for
// very wide batches, and it does not agree bit for bit with the scalar
// kernel, so enabling it changes results in the last place.
- const char* const lane_override = std::getenv("TORCHSIM_LANES");
+ const char* const lane_override = std::getenv("BLOCHSIM_LANES");
const bool lanes_enabled = lane_override != nullptr && lane_override[0] == '1';
const bool vectorize = lanes_enabled && train_count >= 4
&& buffers.shim_count == 1
@@ -11781,7 +11781,7 @@ PyMODINIT_FUNC PyInit__epg_cpu() {
// What a caller cannot see from the outside: whether this build has
// several instruction sets to pick a kernel from, and so whether the
// vector state a pool job leaves behind is a thing that exists here.
- if (PyModule_AddIntConstant(created, "multiversioned", TORCHSIM_MULTIVERSIONED)
+ if (PyModule_AddIntConstant(created, "multiversioned", BLOCHSIM_MULTIVERSIONED)
< 0) {
Py_DECREF(created);
return nullptr;
diff --git a/src/torchsim/_execution.py b/src/blochsim/_execution.py
similarity index 100%
rename from src/torchsim/_execution.py
rename to src/blochsim/_execution.py
diff --git a/src/torchsim/_functional/__init__.py b/src/blochsim/_functional/__init__.py
similarity index 100%
rename from src/torchsim/_functional/__init__.py
rename to src/blochsim/_functional/__init__.py
diff --git a/src/torchsim/_functional/_bssfp.py b/src/blochsim/_functional/_bssfp.py
similarity index 100%
rename from src/torchsim/_functional/_bssfp.py
rename to src/blochsim/_functional/_bssfp.py
diff --git a/src/torchsim/_functional/_fse.py b/src/blochsim/_functional/_fse.py
similarity index 100%
rename from src/torchsim/_functional/_fse.py
rename to src/blochsim/_functional/_fse.py
diff --git a/src/torchsim/_functional/_mp2rage.py b/src/blochsim/_functional/_mp2rage.py
similarity index 100%
rename from src/torchsim/_functional/_mp2rage.py
rename to src/blochsim/_functional/_mp2rage.py
diff --git a/src/torchsim/_functional/_mpnrage.py b/src/blochsim/_functional/_mpnrage.py
similarity index 100%
rename from src/torchsim/_functional/_mpnrage.py
rename to src/blochsim/_functional/_mpnrage.py
diff --git a/src/torchsim/_functional/_mprage.py b/src/blochsim/_functional/_mprage.py
similarity index 100%
rename from src/torchsim/_functional/_mprage.py
rename to src/blochsim/_functional/_mprage.py
diff --git a/src/torchsim/_functional/_mrf.py b/src/blochsim/_functional/_mrf.py
similarity index 100%
rename from src/torchsim/_functional/_mrf.py
rename to src/blochsim/_functional/_mrf.py
diff --git a/src/torchsim/_functional/_run.py b/src/blochsim/_functional/_run.py
similarity index 100%
rename from src/torchsim/_functional/_run.py
rename to src/blochsim/_functional/_run.py
diff --git a/src/torchsim/_functional/_spgr.py b/src/blochsim/_functional/_spgr.py
similarity index 100%
rename from src/torchsim/_functional/_spgr.py
rename to src/blochsim/_functional/_spgr.py
diff --git a/src/torchsim/_ifunc.hpp b/src/blochsim/_ifunc.hpp
similarity index 83%
rename from src/torchsim/_ifunc.hpp
rename to src/blochsim/_ifunc.hpp
index a4b057e3..0359bbc5 100644
--- a/src/torchsim/_ifunc.hpp
+++ b/src/blochsim/_ifunc.hpp
@@ -1,5 +1,5 @@
-#ifndef TORCHSIM_IFUNC_HPP
-#define TORCHSIM_IFUNC_HPP
+#ifndef BLOCHSIM_IFUNC_HPP
+#define BLOCHSIM_IFUNC_HPP
// ``__GLIBC__`` comes from a libc header rather than from the compiler, so one
// has to have been included before the test below can read it.
@@ -13,9 +13,9 @@
// announces itself with no macro of its own -- so the test is for the glibc
// whose absence on Linux leaves only musl.
#if defined(__linux__) && !defined(__GLIBC__)
-#define TORCHSIM_HAS_IFUNC 0
+#define BLOCHSIM_HAS_IFUNC 0
#else
-#define TORCHSIM_HAS_IFUNC 1
+#define BLOCHSIM_HAS_IFUNC 1
#endif
// Whether the EPG kernels are compiled once per instruction set. It takes the
@@ -29,11 +29,11 @@
// build that does not has one code path and nothing to clear, so the module
// reports this and a caller measuring the difference knows whether there is
// one to measure.
-#if defined(__GNUC__) && !defined(__clang__) && TORCHSIM_HAS_IFUNC \
+#if defined(__GNUC__) && !defined(__clang__) && BLOCHSIM_HAS_IFUNC \
&& (defined(__x86_64__) || defined(__i386__))
-#define TORCHSIM_MULTIVERSIONED 1
+#define BLOCHSIM_MULTIVERSIONED 1
#else
-#define TORCHSIM_MULTIVERSIONED 0
+#define BLOCHSIM_MULTIVERSIONED 0
#endif
-#endif // TORCHSIM_IFUNC_HPP
+#endif // BLOCHSIM_IFUNC_HPP
diff --git a/src/torchsim/_perk_cpu.cpp b/src/blochsim/_perk_cpu.cpp
similarity index 97%
rename from src/torchsim/_perk_cpu.cpp
rename to src/blochsim/_perk_cpu.cpp
index f7cf1ff1..e9138f11 100644
--- a/src/torchsim/_perk_cpu.cpp
+++ b/src/blochsim/_perk_cpu.cpp
@@ -30,16 +30,16 @@
// the function per instruction set and the dynamic linker picks the copy this
// processor can run.
#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__)) \
- && TORCHSIM_HAS_IFUNC
-#define TORCHSIM_VECTORIZED __attribute__((target_clones("avx2", "default")))
+ && BLOCHSIM_HAS_IFUNC
+#define BLOCHSIM_VECTORIZED __attribute__((target_clones("avx2", "default")))
#else
-#define TORCHSIM_VECTORIZED
+#define BLOCHSIM_VECTORIZED
#endif
namespace {
-using torchsim::WorkerPool;
-using torchsim::worker_count;
+using blochsim::WorkerPool;
+using blochsim::worker_count;
// Voxels per block, and features per block. The feature block is what has to
// stay in cache across the contrast loop.
@@ -101,7 +101,7 @@ struct Problem {
// and the feature innermost, the inner statement is an AXPY along contiguous
// memory that a compiler vectorizes, where the natural order -- a dot product
// per feature -- is a reduction it cannot.
-TORCHSIM_VECTORIZED inline void angles(
+BLOCHSIM_VECTORIZED inline void angles(
const Problem& problem,
const std::int64_t begin,
const std::int64_t width,
@@ -130,7 +130,7 @@ TORCHSIM_VECTORIZED inline void angles(
}
// One block of voxels, from signal to parameters.
-TORCHSIM_VECTORIZED void regress_block(
+BLOCHSIM_VECTORIZED void regress_block(
const Problem& problem,
const std::int64_t begin,
const std::int64_t end,
@@ -186,7 +186,7 @@ TORCHSIM_VECTORIZED void regress_block(
// The derivative of one block with respect to its signals. The angle is
// rebuilt rather than kept, which is the same trade the forward pass makes.
-TORCHSIM_VECTORIZED void regress_vjp_block(
+BLOCHSIM_VECTORIZED void regress_vjp_block(
const Problem& problem,
const std::int64_t begin,
const std::int64_t end,
@@ -392,7 +392,7 @@ PyMethodDef methods[] = {
};
PyModuleDef module = {
- PyModuleDef_HEAD_INIT, "torchsim._perk_cpu", nullptr, -1, methods,
+ PyModuleDef_HEAD_INIT, "blochsim._perk_cpu", nullptr, -1, methods,
nullptr, nullptr, nullptr, nullptr,
};
diff --git a/src/torchsim/_subspace.py b/src/blochsim/_subspace.py
similarity index 98%
rename from src/torchsim/_subspace.py
rename to src/blochsim/_subspace.py
index 830ccabf..3b06cf3d 100644
--- a/src/torchsim/_subspace.py
+++ b/src/blochsim/_subspace.py
@@ -34,7 +34,7 @@ class Subspace:
dictionary : torch.Tensor, optional
``(atoms, contrasts)`` -- the signals the basis was fitted to, where
they were simulated for the purpose.
- :func:`~torchsim.simulate_subspace` fills this in;
+ :func:`~blochsim.simulate_subspace` fills this in;
:meth:`fit` leaves it out, since the caller already holds them.
simulation : SimulationResult, optional
What that simulation recorded, with the labels that say which sample
diff --git a/src/torchsim/_threads.hpp b/src/blochsim/_threads.hpp
similarity index 96%
rename from src/torchsim/_threads.hpp
rename to src/blochsim/_threads.hpp
index bf4548b5..6dce0df6 100644
--- a/src/torchsim/_threads.hpp
+++ b/src/blochsim/_threads.hpp
@@ -1,8 +1,8 @@
-// Shared by every TorchSim CPU kernel: a worker pool that outlives the calls
+// Shared by every BlochSim CPU kernel: a worker pool that outlives the calls
// that use it, and the two questions of how many workers a piece of work can
// actually feed.
-#ifndef TORCHSIM_THREADS_HPP
-#define TORCHSIM_THREADS_HPP
+#ifndef BLOCHSIM_THREADS_HPP
+#define BLOCHSIM_THREADS_HPP
#include
#include
@@ -16,7 +16,7 @@
#include
#endif
-namespace torchsim {
+namespace blochsim {
// Leave the vector registers in the state a legacy-encoded caller expects.
//
@@ -164,6 +164,6 @@ inline unsigned int worker_count(
return static_cast(std::max(1, count));
}
-} // namespace torchsim
+} // namespace blochsim
-#endif // TORCHSIM_THREADS_HPP
+#endif // BLOCHSIM_THREADS_HPP
diff --git a/src/torchsim/estimators/__init__.py b/src/blochsim/estimators/__init__.py
similarity index 95%
rename from src/torchsim/estimators/__init__.py
rename to src/blochsim/estimators/__init__.py
index 017315b6..16d9b1eb 100644
--- a/src/torchsim/estimators/__init__.py
+++ b/src/blochsim/estimators/__init__.py
@@ -1,4 +1,4 @@
-"""Quantitative-MRI estimators built around TorchSim signal models.
+"""Quantitative-MRI estimators built around BlochSim signal models.
An estimator is made from the acquisition it inverts and whatever settings the
method itself has. ``fit`` states which tissue properties are unknown and over
diff --git a/src/torchsim/estimators/_dictionary.py b/src/blochsim/estimators/_dictionary.py
similarity index 98%
rename from src/torchsim/estimators/_dictionary.py
rename to src/blochsim/estimators/_dictionary.py
index 6736c1e3..42a114f7 100644
--- a/src/torchsim/estimators/_dictionary.py
+++ b/src/blochsim/estimators/_dictionary.py
@@ -55,11 +55,11 @@ class DictionaryMatcher(Estimator):
Parameters
----------
acquisition : Simulator, optional
- The sequence being inverted: a simulator that ships with TorchSim, one
- written by subclassing :class:`~torchsim.model.Simulator`, or any other
- :class:`~torchsim.model.Simulator`. Every tissue property that is
+ The sequence being inverted: a simulator that ships with BlochSim, one
+ written by subclassing :class:`~blochsim.model.Simulator`, or any other
+ :class:`~blochsim.model.Simulator`. Every tissue property that is
neither unknown nor measured separately is fixed on it beforehand, with
- the constructor or :meth:`~torchsim.model.Simulator.bind`. Leave it
+ the constructor or :meth:`~blochsim.model.Simulator.bind`. Leave it
out to fit from signals handed to :meth:`fit` directly.
dictionary : torch.Tensor, optional
Simulated atoms shaped ``(n_atoms, n_contrasts)``. Leave it out and
diff --git a/src/torchsim/estimators/_grouped.py b/src/blochsim/estimators/_grouped.py
similarity index 100%
rename from src/torchsim/estimators/_grouped.py
rename to src/blochsim/estimators/_grouped.py
diff --git a/src/torchsim/estimators/_lookup.py b/src/blochsim/estimators/_lookup.py
similarity index 97%
rename from src/torchsim/estimators/_lookup.py
rename to src/blochsim/estimators/_lookup.py
index 9f3f0c63..9354d45f 100644
--- a/src/torchsim/estimators/_lookup.py
+++ b/src/blochsim/estimators/_lookup.py
@@ -29,11 +29,11 @@ class LookupTable(Estimator):
Parameters
----------
acquisition : Simulator, optional
- The sequence being inverted: a simulator that ships with TorchSim, one
- written by subclassing :class:`~torchsim.model.Simulator`, or any other
- :class:`~torchsim.model.Simulator`. Every tissue property that is
+ The sequence being inverted: a simulator that ships with BlochSim, one
+ written by subclassing :class:`~blochsim.model.Simulator`, or any other
+ :class:`~blochsim.model.Simulator`. Every tissue property that is
neither unknown nor measured separately is fixed on it beforehand, with
- the constructor or :meth:`~torchsim.model.Simulator.bind`. Leave it
+ the constructor or :meth:`~blochsim.model.Simulator.bind`. Leave it
out to fit from signals handed to :meth:`fit` directly.
combine : callable, optional
Reduce a model's contrasts to the one number the curve is in, called
@@ -66,8 +66,8 @@ class LookupTable(Estimator):
.. exec::
import torch
- from torchsim.estimators import LookupTable
- from torchsim.simulators import MP2RAGESimulator
+ from blochsim.estimators import LookupTable
+ from blochsim.simulators import MP2RAGESimulator
protocol = dict(
TI=(800.0, 2700.0), flip=(4.0, 5.0),
diff --git a/src/torchsim/estimators/_mapping.py b/src/blochsim/estimators/_mapping.py
similarity index 98%
rename from src/torchsim/estimators/_mapping.py
rename to src/blochsim/estimators/_mapping.py
index 3aa452f0..40fd3670 100644
--- a/src/torchsim/estimators/_mapping.py
+++ b/src/blochsim/estimators/_mapping.py
@@ -45,8 +45,8 @@
class Estimator(torch.nn.Module):
"""What every mapping method is, and the machinery all of them share.
- :class:`~torchsim.DictionaryMatcher`, :class:`~torchsim.LookupTable`,
- :class:`~torchsim.NonlinearLeastSquares` and :class:`~torchsim.PERK` are
+ :class:`~blochsim.DictionaryMatcher`, :class:`~blochsim.LookupTable`,
+ :class:`~blochsim.NonlinearLeastSquares` and :class:`~blochsim.PERK` are
all one of these. What they inherit is the whole problem statement --
:meth:`fit` naming the unknowns and drawing the training set from
:attr:`acquisition`, :meth:`map` returning one named map per unknown --
@@ -152,14 +152,14 @@ def fit(
what teaches a method how far to trust a measurement, so it should
be the noise the scan actually has.
rank : int, optional
- Fit a temporal :class:`~torchsim.Subspace` of this rank to the
+ Fit a temporal :class:`~blochsim.Subspace` of this rank to the
training signals and work in it. Every contrast dropped is
arithmetic neither training nor mapping has to do;
:attr:`subspace` reports what the compression kept.
subspace : Subspace, optional
Work in a basis fitted elsewhere rather than fitting one here --
the one another estimator carries, or one
- :func:`~torchsim.simulate_subspace` produced. This is what lets a
+ :func:`~blochsim.simulate_subspace` produced. This is what lets a
reconstruction and the estimator that reads its coefficients agree
on the basis by construction. Not with ``rank``, which asks for a
basis to be fitted.
@@ -426,7 +426,7 @@ def uncertainty_of(self, volume: Any, known: Any = None) -> Any:
It is not the error. An estimator that is biased is biased the same way
in every realization, so repeating the measurement never reveals that
part and this number does not contain it. Read against
- :func:`~torchsim.crlb`, which is the lowest standard deviation an
+ :func:`~blochsim.crlb`, which is the lowest standard deviation an
unbiased estimate could have, it says how much of the distance to the
truth the acquisition is responsible for.
@@ -551,7 +551,7 @@ def _spread(
f"whose answer is a grid point moves in steps rather than "
f"smoothly, so propagating the noise through it gives zero "
f"rather than an error bar; PERK and NonlinearLeastSquares do "
- f"state one, and torchsim.crlb bounds what any unbiased method "
+ f"state one, and blochsim.crlb bounds what any unbiased method "
f"could reach on this sequence."
)
return spread
diff --git a/src/torchsim/estimators/_nlls.py b/src/blochsim/estimators/_nlls.py
similarity index 94%
rename from src/torchsim/estimators/_nlls.py
rename to src/blochsim/estimators/_nlls.py
index 8e97aa39..5a15207d 100644
--- a/src/torchsim/estimators/_nlls.py
+++ b/src/blochsim/estimators/_nlls.py
@@ -24,10 +24,10 @@ class NonlinearLeastSquares(Estimator):
it gives up is the guarantee: it finds a local minimum of the residual,
and which one depends on where it started.
- This is an :class:`~torchsim.Estimator` face on
- :class:`~torchsim.recon.GaussNewton`, and holds no algorithm of its own.
+ This is an :class:`~blochsim.Estimator` face on
+ :class:`~blochsim.recon.GaussNewton`, and holds no algorithm of its own.
Fitting images voxel by voxel and reconstructing maps from k-space are the
- same loop over the same :class:`~torchsim.recon.ModelOperator` with
+ same loop over the same :class:`~blochsim.recon.ModelOperator` with
nothing encoding the voxels together, so what is here is only the
adaptation: where the fit starts, and what the training set is for.
@@ -39,11 +39,11 @@ class NonlinearLeastSquares(Estimator):
Parameters
----------
acquisition : Simulator, optional
- The sequence being inverted: a simulator that ships with TorchSim, one
- written by subclassing :class:`~torchsim.model.Simulator`, or any other
- :class:`~torchsim.model.Simulator`. Every tissue property that is
+ The sequence being inverted: a simulator that ships with BlochSim, one
+ written by subclassing :class:`~blochsim.model.Simulator`, or any other
+ :class:`~blochsim.model.Simulator`. Every tissue property that is
neither unknown nor measured separately is fixed on it beforehand, with
- the constructor or :meth:`~torchsim.model.Simulator.bind`. Leave it
+ the constructor or :meth:`~blochsim.model.Simulator.bind`. Leave it
out to fit from signals handed to :meth:`fit` directly.
bounds : mapping, optional
``{name: (low, high)}``, either end ``None`` for unbounded. A bound is
@@ -58,8 +58,8 @@ class NonlinearLeastSquares(Estimator):
loop : GaussNewton, optional
The solve to run, and where every knob it has lives -- how many steps,
how the damping moves, which tolerance stops a voxel. The default is
- Levenberg-Marquardt: a :class:`~torchsim.recon.TrustRegion` over
- :func:`~torchsim.recon.direct`, twenty steps.
+ Levenberg-Marquardt: a :class:`~blochsim.recon.TrustRegion` over
+ :func:`~blochsim.recon.direct`, twenty steps.
Notes
-----
@@ -249,7 +249,7 @@ def _uncertainty_arrays(
A least-squares solution moves with the noise by as much as the model
is insensitive to the parameters there, which is the inverse Fisher
- matrix at the solution -- the same quantity :func:`~torchsim.crlb`
+ matrix at the solution -- the same quantity :func:`~blochsim.crlb`
bounds an unbiased estimate by, read at the answer rather than at a
truth nobody has. It is a linearization about the solution, so it is
the standard error a fit reports and holds where the residual is small
@@ -287,9 +287,9 @@ def _solve(
) -> torch.Tensor:
"""Levenberg-Marquardt over every voxel, compacting as they finish.
- The loop is :class:`~torchsim.recon.GaussNewton` under a per-voxel
+ The loop is :class:`~blochsim.recon.GaussNewton` under a per-voxel
trust region, and the model, its bounds and its derivative are a
- :class:`~torchsim.recon.ModelOperator` -- the same two pieces a
+ :class:`~blochsim.recon.ModelOperator` -- the same two pieces a
model-based reconstruction is built from, with nothing encoding the
voxels together.
"""
diff --git a/src/torchsim/estimators/_perk.py b/src/blochsim/estimators/_perk.py
similarity index 99%
rename from src/torchsim/estimators/_perk.py
rename to src/blochsim/estimators/_perk.py
index 4a9d9fd0..135abf3b 100644
--- a/src/torchsim/estimators/_perk.py
+++ b/src/blochsim/estimators/_perk.py
@@ -27,11 +27,11 @@ class PERK(Estimator):
Parameters
----------
acquisition : Simulator, optional
- The sequence being inverted: a simulator that ships with TorchSim, one
- written by subclassing :class:`~torchsim.model.Simulator`, or any other
- :class:`~torchsim.model.Simulator`. Every tissue property that is
+ The sequence being inverted: a simulator that ships with BlochSim, one
+ written by subclassing :class:`~blochsim.model.Simulator`, or any other
+ :class:`~blochsim.model.Simulator`. Every tissue property that is
neither unknown nor measured separately is fixed on it beforehand, with
- the constructor or :meth:`~torchsim.model.Simulator.bind`. Leave it
+ the constructor or :meth:`~blochsim.model.Simulator.bind`. Leave it
out to fit from signals handed to :meth:`fit` directly.
n_features : int, optional
Number of random Fourier features. The default is ``1000``.
@@ -721,7 +721,7 @@ def _given_length_scale(self, width: int, device: torch.device) -> torch.Tensor:
def _loaded(name: str) -> Any:
"""One kernel backend, or ``None`` where it cannot be imported."""
try:
- return __import__(f"torchsim.estimators.{name}", fromlist=[name])
+ return __import__(f"blochsim.estimators.{name}", fromlist=[name])
except ImportError:
return None
diff --git a/src/torchsim/estimators/_perk_native.py b/src/blochsim/estimators/_perk_native.py
similarity index 100%
rename from src/torchsim/estimators/_perk_native.py
rename to src/blochsim/estimators/_perk_native.py
diff --git a/src/torchsim/estimators/_perk_triton.py b/src/blochsim/estimators/_perk_triton.py
similarity index 100%
rename from src/torchsim/estimators/_perk_triton.py
rename to src/blochsim/estimators/_perk_triton.py
diff --git a/src/torchsim/model/__init__.py b/src/blochsim/model/__init__.py
similarity index 100%
rename from src/torchsim/model/__init__.py
rename to src/blochsim/model/__init__.py
diff --git a/src/torchsim/model/_binding.py b/src/blochsim/model/_binding.py
similarity index 99%
rename from src/torchsim/model/_binding.py
rename to src/blochsim/model/_binding.py
index 7c009a67..4925481e 100644
--- a/src/torchsim/model/_binding.py
+++ b/src/blochsim/model/_binding.py
@@ -176,11 +176,11 @@ def bind(
Parameters
----------
simulator:
- The protocol, whose :meth:`~torchsim.model.Simulator.describe`
+ The protocol, whose :meth:`~blochsim.model.Simulator.describe`
is walked here and, if this succeeds, not again.
values:
The protocol arguments as
- :meth:`~torchsim.model.Simulator.played` returns them. The
+ :meth:`~blochsim.model.Simulator.played` returns them. The
floating-point arrays among them are the ones rebound.
repetitions, record, rf_raster_time_s, device:
The run this is for. A call differing in any of them gets no binding.
diff --git a/src/torchsim/model/_signal.py b/src/blochsim/model/_signal.py
similarity index 98%
rename from src/torchsim/model/_signal.py
rename to src/blochsim/model/_signal.py
index 45a275a8..98967169 100644
--- a/src/torchsim/model/_signal.py
+++ b/src/blochsim/model/_signal.py
@@ -18,7 +18,7 @@
arguments arrive in whatever they were written in -- NumPy, CuPy, torch -- and
are read through DLPack, over the same memory rather than a copy. The signal
goes back the same way. Everything between is torch, which is what the kernels
-and the autograd graph are written against; see :mod:`torchsim.sequence._array`
+and the autograd graph are written against; see :mod:`blochsim.sequence._array`
for what that costs, and for the one thing it cannot carry back.
**Derivatives are taken in the mode that suits what is differentiated.** A
@@ -54,7 +54,7 @@
class _SignalModel(ABC):
"""What every model owes, and the two derivative modes over it.
- The interface :class:`~torchsim.model.Simulator` presents, and everything
+ The interface :class:`~blochsim.model.Simulator` presents, and everything
downstream consumes: declared properties, and a signal. What a model has
to supply is :attr:`properties` and :meth:`evaluate`.
@@ -67,7 +67,7 @@ class _SignalModel(ABC):
properties : mapping or sequence of str
What the model exposes, as names. A mapping is read for its keys, so a
subclass that also needs to say where each property goes -- as
- :class:`~torchsim.model.SpinPhysics` does, naming the tissue
+ :class:`~blochsim.model.SpinPhysics` does, naming the tissue
field each fills -- declares both at once.
bound : mapping
The arguments fixed on this model, under the names :meth:`simulate`
diff --git a/src/torchsim/model/_state_machine.py b/src/blochsim/model/_state_machine.py
similarity index 98%
rename from src/torchsim/model/_state_machine.py
rename to src/blochsim/model/_state_machine.py
index cfb7388f..af8da69f 100644
--- a/src/torchsim/model/_state_machine.py
+++ b/src/blochsim/model/_state_machine.py
@@ -14,7 +14,7 @@
**The operators are resolved before a description exists.** A simulator binds
each slot when it is constructed; what a protocol then produces is an ordinary
-:class:`~torchsim.sequence.SequenceDescription`, whose events carry their own
+:class:`~blochsim.sequence.SequenceDescription`, whose events carry their own
action word. From there the path is the fused one -- packing, the feature
mask, the real-subspace verdict, offload and sharding -- and nothing consults
an operator slot again. There is no interpretation at run time and none per
@@ -24,8 +24,8 @@
vocabulary at three sizes. :class:`EventOperators` has one slot per role a
sequence is written in terms of, and its values are operator factories, read
only while a description is being assembled. Each factory emits events tagged
-with an :class:`~torchsim.sequence.RfUse`, which is what a Pulseq file
-carries, and with an :class:`~torchsim.sequence.EventAction`, which is the bit
+with an :class:`~blochsim.sequence.RfUse`, which is what a Pulseq file
+carries, and with an :class:`~blochsim.sequence.EventAction`, which is the bit
field the kernels read. The three do not line up one to one and are not meant
to: the :attr:`~EventOperators.saturation` slot plays a pulse tagged
``RfUse.EXCITATION``, because what the scanner is told about a pulse and what
@@ -105,7 +105,7 @@
"across_slice",
)
-# The raster :class:`~torchsim.sequence.EpgEngine` reads a pulse's shape on,
+# The raster :class:`~blochsim.sequence.EpgEngine` reads a pulse's shape on,
# named here because a packing resolved against one is not valid against
# another.
_RF_RASTER_TIME_S = 1e-6
@@ -246,8 +246,8 @@ class EventOperators:
These are roles a sequence is written in terms of, not the tags the events
end up carrying: a factory here decides which
- :class:`~torchsim.sequence.RfUse` and which
- :class:`~torchsim.sequence.EventAction` its events are emitted with.
+ :class:`~blochsim.sequence.RfUse` and which
+ :class:`~blochsim.sequence.EventAction` its events are emitted with.
"""
#: What a pulse that tips magnetization into the transverse plane plays.
@@ -496,7 +496,7 @@ def __init__(
execution:
Where to run -- ``"auto"`` to decide per call against what the
devices have free, ``"cpu"``, or a device or list of devices.
- ``None`` follows whatever :func:`~torchsim.sequence.execution`
+ ``None`` follows whatever :func:`~blochsim.sequence.execution`
block is in scope, which is what lets a caller decide instead.
pulse:
The waveform the events drive, taking the place of the ideal hard
@@ -832,9 +832,9 @@ def from_description(
the use its designer gave it, and an ADC window -- and says nothing
about the gradients between them, because the transport carries none.
The dephasing lives in the handlers instead: a
- :func:`~torchsim.SSFPFidReadout` winds one order after every sample, a
- :func:`~torchsim.SSFPEchoReadout` winds it before, a
- :func:`~torchsim.SPGRReadout` spoils, and a refocusing pulse is
+ :func:`~blochsim.SSFPFidReadout` winds one order after every sample, a
+ :func:`~blochsim.SSFPEchoReadout` winds it before, a
+ :func:`~blochsim.SPGRReadout` spoils, and a refocusing pulse is
crushed either side. So the events are re-emitted through this model's
own operators rather than taken as they arrive.
diff --git a/src/torchsim/optim/__init__.py b/src/blochsim/optim/__init__.py
similarity index 100%
rename from src/torchsim/optim/__init__.py
rename to src/blochsim/optim/__init__.py
diff --git a/src/torchsim/optim/_design.py b/src/blochsim/optim/_design.py
similarity index 100%
rename from src/torchsim/optim/_design.py
rename to src/blochsim/optim/_design.py
diff --git a/src/torchsim/recon/__init__.py b/src/blochsim/recon/__init__.py
similarity index 100%
rename from src/torchsim/recon/__init__.py
rename to src/blochsim/recon/__init__.py
diff --git a/src/torchsim/recon/_gauss_newton.py b/src/blochsim/recon/_gauss_newton.py
similarity index 98%
rename from src/torchsim/recon/_gauss_newton.py
rename to src/blochsim/recon/_gauss_newton.py
index dd1060d1..fe7ea664 100644
--- a/src/torchsim/recon/_gauss_newton.py
+++ b/src/blochsim/recon/_gauss_newton.py
@@ -93,7 +93,7 @@ class Solution:
----------
x : torch.Tensor
The maps, as the variables that were solved for. Pass them through
- :meth:`~torchsim.recon.ModelOperator.split` for the properties.
+ :meth:`~blochsim.recon.ModelOperator.split` for the properties.
cost : torch.Tensor
The squared residual after each step, so convergence is read rather
than assumed.
@@ -199,7 +199,7 @@ def iterative(
Raises
------
ImportError
- If no solver is given and deepinv is not installed. TorchSim does not
+ If no solver is given and deepinv is not installed. BlochSim does not
depend on it: pass a :class:`LeastSquares` of your own, or use
:func:`direct` where the model stands alone.
"""
@@ -217,7 +217,7 @@ def solve(
except ImportError as error: # pragma: no cover - depends on env
raise ImportError(
"iterative() falls back to deepinv's least_squares, and "
- "TorchSim does not depend on deepinv. pip install "
+ "BlochSim does not depend on deepinv. pip install "
"deepinv, pass a LeastSquares of your own, or use "
"direct() where the model stands alone."
) from error
@@ -485,14 +485,14 @@ def minimize(
Parameters
----------
operator:
- A :class:`~torchsim.recon.ModelOperator`.
+ A :class:`~blochsim.recon.ModelOperator`.
measured:
The data. Without an encoding, ``(..., contrasts)`` -- the
contrast images themselves. With one, whatever the encoding
produces.
initial:
``(..., channels)``, where to start. Build it with
- :meth:`~torchsim.recon.ModelOperator.initial`.
+ :meth:`~blochsim.recon.ModelOperator.initial`.
encoding:
Anything with ``A`` and ``A_adjoint`` -- a deepinv
``LinearPhysics``, an mri-nufft operator through its deepinv
diff --git a/src/torchsim/recon/_operator.py b/src/blochsim/recon/_operator.py
similarity index 98%
rename from src/torchsim/recon/_operator.py
rename to src/blochsim/recon/_operator.py
index c4fbecb5..acced5ca 100644
--- a/src/torchsim/recon/_operator.py
+++ b/src/blochsim/recon/_operator.py
@@ -52,7 +52,7 @@
class ModelOperator(torch.nn.Module):
"""A signal model over parameter maps, with its derivatives.
- Maps are stacked on the **last** axis, as everywhere else in TorchSim:
+ Maps are stacked on the **last** axis, as everywhere else in BlochSim:
``(..., channels)`` in, ``(..., contrasts)`` out, every leading axis a
voxel axis. :meth:`physics` is where that flips to the channel-first
convention deepinv and mri-nufft use, and it is the only place it flips.
@@ -82,7 +82,7 @@ class ModelOperator(torch.nn.Module):
On, which is what a reconstruction wants; off for a fit whose model
already exposes its own proton density.
subspace : Subspace, optional
- A :class:`~torchsim.Subspace` the prediction is projected through, for
+ A :class:`~blochsim.Subspace` the prediction is projected through, for
a reconstruction that solves in the temporal basis rather than in the
contrasts.
@@ -382,21 +382,21 @@ def physics(self, **kwargs: Any) -> Any:
Raises
------
ImportError
- If deepinv is not installed. TorchSim does not depend on it.
+ If deepinv is not installed. BlochSim does not depend on it.
Notes
-----
A ``ComposedPhysics`` takes its Jacobian products by automatic
differentiation through the whole chain, so composing this way gives
up the analytic derivative below.
- :class:`~torchsim.recon.GaussNewton` chains the two operators' own
+ :class:`~blochsim.recon.GaussNewton` chains the two operators' own
products instead, and keeps it.
"""
try:
from deepinv.physics import Physics
except ImportError as error: # pragma: no cover - depends on the env
raise ImportError(
- "physics() needs deepinv; TorchSim does not depend on it. "
+ "physics() needs deepinv; BlochSim does not depend on it. "
"pip install deepinv"
) from error
@@ -488,7 +488,7 @@ def _voxelwise(
"""Run a per-voxel body wherever the execution policy says to.
Every axis but the last is a voxel axis, so the work is flattened to
- one, handed to :func:`~torchsim._execution.per_voxel`, and folded
+ one, handed to :func:`~blochsim._execution.per_voxel`, and folded
back. Where no policy is in force it runs as written.
So does a call whose inputs carry a derivative, and that covers two
diff --git a/src/torchsim/sequence/__init__.py b/src/blochsim/sequence/__init__.py
similarity index 100%
rename from src/torchsim/sequence/__init__.py
rename to src/blochsim/sequence/__init__.py
diff --git a/src/torchsim/sequence/_accelerators.py b/src/blochsim/sequence/_accelerators.py
similarity index 99%
rename from src/torchsim/sequence/_accelerators.py
rename to src/blochsim/sequence/_accelerators.py
index 564780d2..e9362678 100644
--- a/src/torchsim/sequence/_accelerators.py
+++ b/src/blochsim/sequence/_accelerators.py
@@ -613,7 +613,7 @@ def simulate_native(
``pools`` counts the chemically exchanging pools the tissue carries from
pool B on, and ``extra_pools`` holds the prepared buffers of pools C to E.
- From :data:`~torchsim.sequence._pools.TABULATE_FROM` pools on, the kernels
+ From :data:`~blochsim.sequence._pools.TABULATE_FROM` pools on, the kernels
read their operators from a table rather than forming them.
"""
device = prepared_tissue[0].device
@@ -702,7 +702,7 @@ def simulate_native(
exchanging=pools,
semisolid=lineshape is not None,
)
- threads = int(os.environ.get("TORCHSIM_NUM_THREADS", str(torch.get_num_threads())))
+ threads = int(os.environ.get("BLOCHSIM_NUM_THREADS", str(torch.get_num_threads())))
signal = _NativeEpg.apply(
*tissue,
packed.duration,
@@ -888,7 +888,7 @@ def sample_times_us(interval_us: torch.Tensor, recorded: torch.Tensor) -> torch.
own. That is what lets a rebinding produce the times from the intervals it
already maps, where a repetition time reaching every later timestamp is not
something one source element accounts for; see
- :mod:`torchsim.model._binding`, which is where this is read.
+ :mod:`blochsim.model._binding`, which is where this is read.
The sum is taken at double width. The intervals are float32 and a long
train accumulates thousands of them, which is a rounding the samples' own
@@ -1861,7 +1861,7 @@ def vmap(
def _backend_available(device: torch.device) -> bool:
try:
if device.type == "cpu":
- from torchsim import _epg_cpu # noqa: F401
+ from blochsim import _epg_cpu # noqa: F401
else:
from . import _epg_triton # noqa: F401
except ImportError:
@@ -1997,7 +1997,7 @@ def real_subspace_axis(
out.
``features`` names the terms the caller's tissue gives anything to do, as
- :func:`torchsim.sequence._parameters.features_of` reads them off the values
+ :func:`blochsim.sequence._parameters.features_of` reads them off the values
passed in. A term absent from that set is at its identity everywhere, which
settles three of the four questions here without reading a buffer at all.
"""
@@ -2687,7 +2687,7 @@ def _choose(
The policy is general; what an EPG pass costs is not. The work estimate and
the per-voxel footprint are worked out here, and the decision itself is
- :func:`torchsim._execution.choose`.
+ :func:`blochsim._execution.choose`.
"""
train_count = _train_count(events)
voxels = int(tissue[0].numel())
@@ -3403,7 +3403,7 @@ def _run_packed(
features=features,
pools=pools,
)
- from torchsim import _epg_cpu
+ from blochsim import _epg_cpu
trains = _train_count(events)
shape = (
@@ -3628,7 +3628,7 @@ def _run_packed_vjp(
pools=pools,
)
return adjoint
- from torchsim import _epg_cpu
+ from blochsim import _epg_cpu
grad_output = grad_output.resolve_conj().contiguous()
grad_real = grad_output.real.contiguous()
@@ -3856,7 +3856,7 @@ def _run_packed_vjp_jvp(
features=features,
pools=pools,
)
- from torchsim import _epg_cpu
+ from blochsim import _epg_cpu
grad_output = grad_output.resolve_conj().contiguous()
grad_real = grad_output.real.contiguous()
@@ -4074,7 +4074,7 @@ def _run_packed_jvp(
features=features,
pools=pools,
)
- from torchsim import _epg_cpu
+ from blochsim import _epg_cpu
trains = _train_count(events)
shape = (
diff --git a/src/torchsim/sequence/_array.py b/src/blochsim/sequence/_array.py
similarity index 99%
rename from src/torchsim/sequence/_array.py
rename to src/blochsim/sequence/_array.py
index 3edb706f..0762da7d 100644
--- a/src/torchsim/sequence/_array.py
+++ b/src/blochsim/sequence/_array.py
@@ -14,7 +14,7 @@
**What does not survive the round trip is the autograd graph.** A gradient
needs the tensor it was taken with respect to, so a cost differentiated with
:meth:`torch.Tensor.backward` has to be built on torch inputs. Forward-mode
-derivatives are unaffected -- :meth:`~torchsim.model.Simulator.jacobian`
+derivatives are unaffected -- :meth:`~blochsim.model.Simulator.jacobian`
takes them internally and hands back plain arrays in whatever the caller
brought.
"""
diff --git a/src/torchsim/sequence/_builders.py b/src/blochsim/sequence/_builders.py
similarity index 100%
rename from src/torchsim/sequence/_builders.py
rename to src/blochsim/sequence/_builders.py
diff --git a/src/torchsim/sequence/_calibration.py b/src/blochsim/sequence/_calibration.py
similarity index 100%
rename from src/torchsim/sequence/_calibration.py
rename to src/blochsim/sequence/_calibration.py
diff --git a/src/torchsim/sequence/_description.py b/src/blochsim/sequence/_description.py
similarity index 99%
rename from src/torchsim/sequence/_description.py
rename to src/blochsim/sequence/_description.py
index 7b12de26..95d24ffa 100644
--- a/src/torchsim/sequence/_description.py
+++ b/src/blochsim/sequence/_description.py
@@ -745,7 +745,7 @@ def plot(self, axis: Any = None, *, upto_s: float | None = None) -> Any:
import matplotlib.pyplot as plt
except ImportError as reason: # pragma: no cover - depends on the env
raise ImportError(
- "plotting a description needs matplotlib, which torchsim does "
+ "plotting a description needs matplotlib, which blochsim does "
"not depend on; pip install matplotlib"
) from reason
diff --git a/src/torchsim/sequence/_epg_triton.py b/src/blochsim/sequence/_epg_triton.py
similarity index 99%
rename from src/torchsim/sequence/_epg_triton.py
rename to src/blochsim/sequence/_epg_triton.py
index 1bf67e0b..fe227f58 100644
--- a/src/torchsim/sequence/_epg_triton.py
+++ b/src/blochsim/sequence/_epg_triton.py
@@ -366,11 +366,11 @@ def _three_pool_step(
of which is non-positive, so a long interval cannot overflow.
``narrow`` says the caller has bounded the spread below
- :data:`torchsim.sequence._parameters.NARROW_SPREAD` for every voxel and
+ :data:`blochsim.sequence._parameters.NARROW_SPREAD` for every voxel and
every interval it will pass, so
only the series can be reached. The roots then cost nothing, and the series
holds the answer to float32 without being carried in double --
- :func:`torchsim.sequence._parameters.narrow_three_pool` is what decides it.
+ :func:`blochsim.sequence._parameters.narrow_three_pool` is what decides it.
"""
work: tl.constexpr = tl.float32 if narrow else tl.float64
terms: tl.constexpr = 24 if narrow else 16
@@ -17685,7 +17685,7 @@ def simulate_vjp(
property, then event duration, flip and phase, and the pair's cotangent
where one is given. A shard takes this same kernel a level down and a
streamed volume has chunked launchers of its own;
- :func:`torchsim.sequence._accelerators` decides which route a run takes.
+ :func:`blochsim.sequence._accelerators` decides which route a run takes.
Carrying no forward direction, this records two trajectory planes per
recorded state where that pass records four, and holds one state where it
diff --git a/src/torchsim/sequence/_fixed_point.py b/src/blochsim/sequence/_fixed_point.py
similarity index 100%
rename from src/torchsim/sequence/_fixed_point.py
rename to src/blochsim/sequence/_fixed_point.py
diff --git a/src/torchsim/sequence/_lineshape.py b/src/blochsim/sequence/_lineshape.py
similarity index 100%
rename from src/torchsim/sequence/_lineshape.py
rename to src/blochsim/sequence/_lineshape.py
diff --git a/src/torchsim/sequence/_mrd.py b/src/blochsim/sequence/_mrd.py
similarity index 100%
rename from src/torchsim/sequence/_mrd.py
rename to src/blochsim/sequence/_mrd.py
diff --git a/src/torchsim/sequence/_operators.py b/src/blochsim/sequence/_operators.py
similarity index 100%
rename from src/torchsim/sequence/_operators.py
rename to src/blochsim/sequence/_operators.py
diff --git a/src/torchsim/sequence/_parameters.py b/src/blochsim/sequence/_parameters.py
similarity index 99%
rename from src/torchsim/sequence/_parameters.py
rename to src/blochsim/sequence/_parameters.py
index 2717522c..dadc978b 100644
--- a/src/torchsim/sequence/_parameters.py
+++ b/src/blochsim/sequence/_parameters.py
@@ -137,7 +137,7 @@ class Geometry:
# water alone. They are not in the ABI above. A tissue with more than one
# exchanging pool reaches the kernels as its relaxation and exchange operators,
# tabulated per interval from these and pool B's own properties
-# (``torchsim.sequence._pools``), so no kernel reads them one by one.
+# (``blochsim.sequence._pools``), so no kernel reads them one by one.
EXTRA_POOLS: tuple[str, ...] = ("c", "d", "e")
POOL_PARAMETERS: tuple[Parameter, ...] = tuple(
@@ -357,7 +357,7 @@ def features_of(tissue: Any) -> frozenset[str]:
def feature_flags(features: Any, geometry: Geometry) -> dict[str, bool]:
"""Which optional terms a launch is to carry.
- ``features`` is the set :func:`torchsim.sequence._parameters.features_of`
+ ``features`` is the set :func:`blochsim.sequence._parameters.features_of`
reads off the tissue; ``None`` is a caller who did not declare, and every
term stays.
diff --git a/src/torchsim/sequence/_pools.py b/src/blochsim/sequence/_pools.py
similarity index 100%
rename from src/torchsim/sequence/_pools.py
rename to src/blochsim/sequence/_pools.py
diff --git a/src/torchsim/sequence/_pools_triton.py b/src/blochsim/sequence/_pools_triton.py
similarity index 99%
rename from src/torchsim/sequence/_pools_triton.py
rename to src/blochsim/sequence/_pools_triton.py
index 08830659..b5fa16ee 100644
--- a/src/torchsim/sequence/_pools_triton.py
+++ b/src/blochsim/sequence/_pools_triton.py
@@ -1,7 +1,7 @@
"""Triton kernels for a tissue whose pools are tabulated per interval.
The device side of ``simulate_pooled`` and ``simulate_pooled_adjoint`` in
-``_epg_cpu.cpp``, reading the tables ``torchsim.sequence._pools`` builds. One
+``_epg_cpu.cpp``, reading the tables ``blochsim.sequence._pools`` builds. One
program carries one (train, voxel) problem, and its states are tiles of pools
by dephasing orders, so an interval's relaxation and exchange is a product of
the tabulated operator with the tile and a pulse turns every exchanging pool at
diff --git a/src/torchsim/sequence/_pulseq.py b/src/blochsim/sequence/_pulseq.py
similarity index 98%
rename from src/torchsim/sequence/_pulseq.py
rename to src/blochsim/sequence/_pulseq.py
index 95bcc1e3..7f4bb527 100644
--- a/src/torchsim/sequence/_pulseq.py
+++ b/src/blochsim/sequence/_pulseq.py
@@ -5,7 +5,7 @@
simulation needs and Pulseq does not write down: which pulse shapes the RF rows
share, and which ADC sample each readout passes through k = 0 in.
-``pypulseq`` is an optional dependency -- ``pip install torchsim[pulseq]``.
+``pypulseq`` is an optional dependency -- ``pip install blochsim[pulseq]``.
"""
from __future__ import annotations
@@ -49,8 +49,8 @@ def _read(source: Any) -> Any:
import pypulseq
except ImportError as missing: # pragma: no cover - exercised by the message
raise ImportError(
- "reading a Pulseq file needs pypulseq, which TorchSim does not "
- "require otherwise: pip install torchsim[pulseq]"
+ "reading a Pulseq file needs pypulseq, which BlochSim does not "
+ "require otherwise: pip install blochsim[pulseq]"
) from missing
sequence = pypulseq.Sequence()
sequence.read(str(source))
diff --git a/src/torchsim/sequence/_settling.py b/src/blochsim/sequence/_settling.py
similarity index 100%
rename from src/torchsim/sequence/_settling.py
rename to src/blochsim/sequence/_settling.py
diff --git a/src/torchsim/sequence/_simulation.py b/src/blochsim/sequence/_simulation.py
similarity index 99%
rename from src/torchsim/sequence/_simulation.py
rename to src/blochsim/sequence/_simulation.py
index 730edb80..33b73cd3 100644
--- a/src/torchsim/sequence/_simulation.py
+++ b/src/blochsim/sequence/_simulation.py
@@ -274,11 +274,11 @@ class EpgEngine:
This is the direct route, for when what you hold is a description rather
than a simulator: one a builder such as
- :func:`~torchsim.fse_description` emitted, one assembled from operators by
+ :func:`~blochsim.fse_description` emitted, one assembled from operators by
hand, or one that arrived from a scanner. A
- :class:`~torchsim.model.Simulator` wraps this and is what most sequences
+ :class:`~blochsim.model.Simulator` wraps this and is what most sequences
are written as -- it fixes tissue and protocol on itself, and adds
- :meth:`~torchsim.model.Simulator.jacobian`, binding and device
+ :meth:`~blochsim.model.Simulator.jacobian`, binding and device
placement. Nothing here does any of that: give it a description and a
tissue, get a :class:`SimulationResult` back.
@@ -386,7 +386,7 @@ def simulate(
``events`` is the description's stream already packed into buffers, for
a caller that holds the structure fixed and rebuilds only what varies;
- see :mod:`torchsim.optim`. It must be the packing of ``description``.
+ see :mod:`blochsim.optim`. It must be the packing of ``description``.
Raises
------
@@ -672,7 +672,7 @@ def _prepare_tissue(
"""The tissue as one flat buffer per property, and how many voxels there are.
``features`` names the terms the run carries, as
- :func:`torchsim.sequence._parameters.features_of` reads them off the
+ :func:`blochsim.sequence._parameters.features_of` reads them off the
tissue. A property belonging to a term the run leaves out is kept as the
one value it was given rather than laid out per voxel: the kernels take its
pointer and the branch that would read it is compiled away. ``None`` is a
diff --git a/src/torchsim/sequence/_transition.py b/src/blochsim/sequence/_transition.py
similarity index 100%
rename from src/torchsim/sequence/_transition.py
rename to src/blochsim/sequence/_transition.py
diff --git a/src/torchsim/sequence/_transmit.py b/src/blochsim/sequence/_transmit.py
similarity index 100%
rename from src/torchsim/sequence/_transmit.py
rename to src/blochsim/sequence/_transmit.py
diff --git a/src/torchsim/simulators/__init__.py b/src/blochsim/simulators/__init__.py
similarity index 96%
rename from src/torchsim/simulators/__init__.py
rename to src/blochsim/simulators/__init__.py
index d8f2b5a5..4266fc01 100644
--- a/src/torchsim/simulators/__init__.py
+++ b/src/blochsim/simulators/__init__.py
@@ -1,4 +1,4 @@
-"""The sequences that ship with TorchSim, as simulators.
+"""The sequences that ship with BlochSim, as simulators.
Each names its protocol arguments at construction and its tissue properties
at the call, so parameter inference, sequence optimization and a
diff --git a/src/torchsim/simulators/_contrast.py b/src/blochsim/simulators/_contrast.py
similarity index 100%
rename from src/torchsim/simulators/_contrast.py
rename to src/blochsim/simulators/_contrast.py
diff --git a/src/torchsim/simulators/_pulses.py b/src/blochsim/simulators/_pulses.py
similarity index 98%
rename from src/torchsim/simulators/_pulses.py
rename to src/blochsim/simulators/_pulses.py
index 309f2b35..153f88a3 100644
--- a/src/torchsim/simulators/_pulses.py
+++ b/src/blochsim/simulators/_pulses.py
@@ -1,4 +1,4 @@
-"""Pulse shapes sampled for :func:`~torchsim.sequence.SampledPulse`, and the preparations they build."""
+"""Pulse shapes sampled for :func:`~blochsim.sequence.SampledPulse`, and the preparations they build."""
from __future__ import annotations
diff --git a/src/torchsim/simulators/asl.py b/src/blochsim/simulators/asl.py
similarity index 99%
rename from src/torchsim/simulators/asl.py
rename to src/blochsim/simulators/asl.py
index 097aadfb..d4053af5 100644
--- a/src/torchsim/simulators/asl.py
+++ b/src/blochsim/simulators/asl.py
@@ -45,7 +45,7 @@ class ASLSimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import ASLSimulator
+ from blochsim.simulators import ASLSimulator
sequence = ASLSimulator(t=torch.linspace(0.0, 5000.0, 51), tau=1800.0)
signal = sequence.simulate(CBF=(20.0, 60.0), ATT=1200.0, T1=1400.0)
diff --git a/src/torchsim/simulators/bssfp.py b/src/blochsim/simulators/bssfp.py
similarity index 98%
rename from src/torchsim/simulators/bssfp.py
rename to src/blochsim/simulators/bssfp.py
index b021ab66..ecfe559f 100644
--- a/src/torchsim/simulators/bssfp.py
+++ b/src/blochsim/simulators/bssfp.py
@@ -34,7 +34,7 @@ class bSSFPSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import bSSFPSimulator
+ from blochsim.simulators import bSSFPSimulator
sequence = bSSFPSimulator(flip=60.0, TR=10.0, TE=5.0)
signal = sequence.simulate(T1=1000.0, T2=100.0)
diff --git a/src/torchsim/simulators/cest.py b/src/blochsim/simulators/cest.py
similarity index 98%
rename from src/torchsim/simulators/cest.py
rename to src/blochsim/simulators/cest.py
index 5923c4e3..43a3bff1 100644
--- a/src/torchsim/simulators/cest.py
+++ b/src/blochsim/simulators/cest.py
@@ -49,7 +49,7 @@ class LorentzianSimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import LorentzianSimulator
+ from blochsim.simulators import LorentzianSimulator
sequence = LorentzianSimulator(2, offsets=torch.linspace(-5.0, 5.0, 41))
signal = sequence.simulate(
@@ -135,7 +135,7 @@ class CESTSimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import CESTSimulator
+ from blochsim.simulators import CESTSimulator
sequence = CESTSimulator(offsets=torch.linspace(-5.0, 5.0, 21))
spectrum = sequence.simulate(
diff --git a/src/torchsim/simulators/diffusion.py b/src/blochsim/simulators/diffusion.py
similarity index 98%
rename from src/torchsim/simulators/diffusion.py
rename to src/blochsim/simulators/diffusion.py
index 721b2ce8..4dfc1157 100644
--- a/src/torchsim/simulators/diffusion.py
+++ b/src/blochsim/simulators/diffusion.py
@@ -38,7 +38,7 @@ class DiffusionSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import DiffusionSimulator
+ from blochsim.simulators import DiffusionSimulator
sequence = DiffusionSimulator(b=(0.0, 500.0, 1000.0, 2000.0))
signal = sequence.simulate(D=(0.8, 3.0))
diff --git a/src/torchsim/simulators/flash.py b/src/blochsim/simulators/flash.py
similarity index 99%
rename from src/torchsim/simulators/flash.py
rename to src/blochsim/simulators/flash.py
index cb75cab1..1c2467fd 100644
--- a/src/torchsim/simulators/flash.py
+++ b/src/blochsim/simulators/flash.py
@@ -50,7 +50,7 @@ class FLASHSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import FLASHSimulator
+ from blochsim.simulators import FLASHSimulator
sequence = FLASHSimulator(
flip=8.0, TR=5.0, TE=2.0, nshots=100, inversion="adiabatic"
diff --git a/src/torchsim/simulators/fmssfp.py b/src/blochsim/simulators/fmssfp.py
similarity index 98%
rename from src/torchsim/simulators/fmssfp.py
rename to src/blochsim/simulators/fmssfp.py
index 4b6e634d..78709f2f 100644
--- a/src/torchsim/simulators/fmssfp.py
+++ b/src/blochsim/simulators/fmssfp.py
@@ -44,7 +44,7 @@ class fmSSFPSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import fmSSFPSimulator
+ from blochsim.simulators import fmSSFPSimulator
sequence = fmSSFPSimulator(flip=30.0, TR=5.0, nshots=100)
signal = sequence.simulate(T1=1000.0, T2=100.0, B0=20.0)
diff --git a/src/torchsim/simulators/fse.py b/src/blochsim/simulators/fse.py
similarity index 98%
rename from src/torchsim/simulators/fse.py
rename to src/blochsim/simulators/fse.py
index 9383777a..37aeaf5d 100644
--- a/src/torchsim/simulators/fse.py
+++ b/src/blochsim/simulators/fse.py
@@ -22,7 +22,7 @@ class FSESimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import FSESimulator
+ from blochsim.simulators import FSESimulator
sequence = FSESimulator(flip=180.0 * torch.ones(128), ESP=2.0, TR=5000.0)
signal = sequence.simulate(T1=1000.0, T2=80.0)
@@ -156,7 +156,7 @@ class HyperechoSimulator(FSESimulator):
.. exec::
import torch
- from torchsim.simulators import HyperechoSimulator
+ from blochsim.simulators import HyperechoSimulator
sequence = HyperechoSimulator(flip=60.0 * torch.ones(5), ESP=10.0)
signal = sequence.simulate(T1=1000.0, T2=80.0)
diff --git a/src/torchsim/simulators/gradient_echo.py b/src/blochsim/simulators/gradient_echo.py
similarity index 97%
rename from src/torchsim/simulators/gradient_echo.py
rename to src/blochsim/simulators/gradient_echo.py
index 76632d82..6a1fff6a 100644
--- a/src/torchsim/simulators/gradient_echo.py
+++ b/src/blochsim/simulators/gradient_echo.py
@@ -139,7 +139,7 @@ class MultiGradientEchoSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import MultiGradientEchoSimulator
+ from blochsim.simulators import MultiGradientEchoSimulator
sequence = MultiGradientEchoSimulator(TE=(1.2, 2.4, 3.6, 4.8, 6.0, 7.2))
signal = sequence.simulate(fat_fraction=(0.0, 0.2), T2star=30.0, B0=15.0)
@@ -183,7 +183,7 @@ def _signal(
field_strength:
Main field in tesla, which sets the fat peaks' frequencies.
fat_spectrum:
- A key of :data:`~torchsim.simulators.gradient_echo.FAT_SPECTRA`.
+ A key of :data:`~blochsim.simulators.gradient_echo.FAT_SPECTRA`.
"""
held = across_contrasts(properties, TE)
return _water_and_fat(held, 1.0, 1.0, TE, field_strength, fat_spectrum)
@@ -207,7 +207,7 @@ class IRMultiGradientEchoSimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import IRMultiGradientEchoSimulator
+ from blochsim.simulators import IRMultiGradientEchoSimulator
TI, TE = torch.meshgrid(
torch.tensor([20.0, 200.0, 800.0, 2000.0]),
@@ -264,7 +264,7 @@ def _signal(
field_strength:
Main field in tesla, which sets the fat peaks' frequencies.
fat_spectrum:
- A key of :data:`~torchsim.simulators.gradient_echo.FAT_SPECTRA`.
+ A key of :data:`~blochsim.simulators.gradient_echo.FAT_SPECTRA`.
"""
held = across_contrasts(properties, TI, TE)
inversion_s = TI * 1e-3
diff --git a/src/torchsim/simulators/hsfp.py b/src/blochsim/simulators/hsfp.py
similarity index 98%
rename from src/torchsim/simulators/hsfp.py
rename to src/blochsim/simulators/hsfp.py
index e20fb6bd..0a7dd0c5 100644
--- a/src/torchsim/simulators/hsfp.py
+++ b/src/blochsim/simulators/hsfp.py
@@ -41,7 +41,7 @@ class HSFPSimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import HSFPSimulator
+ from blochsim.simulators import HSFPSimulator
train = 60.0 * torch.sin(torch.linspace(0.0, torch.pi, 400)) ** 2
sequence = HSFPSimulator(flip=train, TR=4.5)
diff --git a/src/torchsim/simulators/looklocker.py b/src/blochsim/simulators/looklocker.py
similarity index 98%
rename from src/torchsim/simulators/looklocker.py
rename to src/blochsim/simulators/looklocker.py
index 20f2fd3a..f2c057c3 100644
--- a/src/torchsim/simulators/looklocker.py
+++ b/src/blochsim/simulators/looklocker.py
@@ -66,7 +66,7 @@ class LookLockerSimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import LookLockerSimulator
+ from blochsim.simulators import LookLockerSimulator
sequence = LookLockerSimulator(flip=6.0, TR=4.0, TI=4.0 * torch.arange(500))
signal = sequence.simulate(T1=(800.0, 1400.0))
@@ -149,7 +149,7 @@ class MOLLISimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import MOLLISimulator
+ from blochsim.simulators import MOLLISimulator
sequence = MOLLISimulator(flip=35.0, TR=2.5, readouts=(40, 40, 40), recovery=800.0)
signal = sequence.simulate(T1=(800.0, 1400.0))
@@ -244,7 +244,7 @@ class IRbSSFPSimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import IRbSSFPSimulator
+ from blochsim.simulators import IRbSSFPSimulator
sequence = IRbSSFPSimulator(flip=45.0, TI=4.5 * torch.arange(1000))
signal = sequence.simulate(T1=1000.0, T2=(50.0, 100.0))
diff --git a/src/torchsim/simulators/mp2rage.py b/src/blochsim/simulators/mp2rage.py
similarity index 99%
rename from src/torchsim/simulators/mp2rage.py
rename to src/blochsim/simulators/mp2rage.py
index 65c5cb37..a7219575 100644
--- a/src/torchsim/simulators/mp2rage.py
+++ b/src/blochsim/simulators/mp2rage.py
@@ -36,7 +36,7 @@ class MP2RAGESimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import MP2RAGESimulator
+ from blochsim.simulators import MP2RAGESimulator
sequence = MP2RAGESimulator(
TI=(500.0, 1500.0),
diff --git a/src/torchsim/simulators/mpnrage.py b/src/blochsim/simulators/mpnrage.py
similarity index 98%
rename from src/torchsim/simulators/mpnrage.py
rename to src/blochsim/simulators/mpnrage.py
index 5fa945e1..705af150 100644
--- a/src/torchsim/simulators/mpnrage.py
+++ b/src/blochsim/simulators/mpnrage.py
@@ -21,7 +21,7 @@ class MPnRAGESimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import MPnRAGESimulator
+ from blochsim.simulators import MPnRAGESimulator
sequence = MPnRAGESimulator(nshots=128, flip=5.0, TR=10.0)
signal = sequence.simulate(T1=1000.0, inv_efficiency=0.95)
diff --git a/src/torchsim/simulators/mprage.py b/src/blochsim/simulators/mprage.py
similarity index 98%
rename from src/torchsim/simulators/mprage.py
rename to src/blochsim/simulators/mprage.py
index dea5fbb7..73a72458 100644
--- a/src/torchsim/simulators/mprage.py
+++ b/src/blochsim/simulators/mprage.py
@@ -22,7 +22,7 @@ class MPRAGESimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import MPRAGESimulator
+ from blochsim.simulators import MPRAGESimulator
sequence = MPRAGESimulator(TI=500.0, flip=5.0, TRspgr=5.0, nshots=128)
signal = sequence.simulate(T1=(200.0, 1000.0), inv_efficiency=0.95)
diff --git a/src/torchsim/simulators/mrf.py b/src/blochsim/simulators/mrf.py
similarity index 97%
rename from src/torchsim/simulators/mrf.py
rename to src/blochsim/simulators/mrf.py
index b7b2691b..ee1041c1 100644
--- a/src/torchsim/simulators/mrf.py
+++ b/src/blochsim/simulators/mrf.py
@@ -22,7 +22,7 @@ class MRFSimulator(Simulator):
.. exec::
import torch
- from torchsim.simulators import MRFSimulator
+ from blochsim.simulators import MRFSimulator
sequence = MRFSimulator(
flip=torch.linspace(5.0, 60.0, 1000), TR=10.0, states=20
diff --git a/src/torchsim/simulators/relaxometry.py b/src/blochsim/simulators/relaxometry.py
similarity index 97%
rename from src/torchsim/simulators/relaxometry.py
rename to src/blochsim/simulators/relaxometry.py
index f4a75469..44a4034d 100644
--- a/src/torchsim/simulators/relaxometry.py
+++ b/src/blochsim/simulators/relaxometry.py
@@ -35,7 +35,7 @@ class InversionRecoverySimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import InversionRecoverySimulator
+ from blochsim.simulators import InversionRecoverySimulator
sequence = InversionRecoverySimulator(TI=(50.0, 400.0, 1100.0, 2500.0))
signal = sequence.simulate(T1=(800.0, 1400.0))
@@ -99,7 +99,7 @@ class MultiEchoSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import MultiEchoSimulator
+ from blochsim.simulators import MultiEchoSimulator
sequence = MultiEchoSimulator(TE=(10.0, 20.0, 40.0, 80.0))
signal = sequence.simulate(T2=(40.0, 90.0))
@@ -153,7 +153,7 @@ class DoubleAngleSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import DoubleAngleSimulator
+ from blochsim.simulators import DoubleAngleSimulator
sequence = DoubleAngleSimulator(flip=(60.0, 120.0))
signal = sequence.simulate(B1=(0.8, 1.0, 1.2))
diff --git a/src/torchsim/simulators/spgr.py b/src/blochsim/simulators/spgr.py
similarity index 98%
rename from src/torchsim/simulators/spgr.py
rename to src/blochsim/simulators/spgr.py
index d57ac851..91e7d0f5 100644
--- a/src/torchsim/simulators/spgr.py
+++ b/src/blochsim/simulators/spgr.py
@@ -38,7 +38,7 @@ class SPGRSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import SPGRSimulator
+ from blochsim.simulators import SPGRSimulator
sequence = SPGRSimulator(flip=13.0, TR=10.0, TE=5.0)
signal = sequence.simulate(T1=1000.0, T2star=30.0)
diff --git a/src/torchsim/simulators/spin_echo.py b/src/blochsim/simulators/spin_echo.py
similarity index 98%
rename from src/torchsim/simulators/spin_echo.py
rename to src/blochsim/simulators/spin_echo.py
index 5f7a0de4..1a646562 100644
--- a/src/torchsim/simulators/spin_echo.py
+++ b/src/blochsim/simulators/spin_echo.py
@@ -35,7 +35,7 @@ class SpinEchoSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import SpinEchoSimulator
+ from blochsim.simulators import SpinEchoSimulator
sequence = SpinEchoSimulator(TE=(10.0, 30.0, 60.0, 100.0), TR=2000.0)
signal = sequence.simulate(T1=1000.0, T2=(50.0, 100.0))
diff --git a/src/torchsim/simulators/stimulated_echo.py b/src/blochsim/simulators/stimulated_echo.py
similarity index 98%
rename from src/torchsim/simulators/stimulated_echo.py
rename to src/blochsim/simulators/stimulated_echo.py
index c1304e22..dfbe2ccb 100644
--- a/src/torchsim/simulators/stimulated_echo.py
+++ b/src/blochsim/simulators/stimulated_echo.py
@@ -36,7 +36,7 @@ class StimulatedEchoSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import StimulatedEchoSimulator
+ from blochsim.simulators import StimulatedEchoSimulator
sequence = StimulatedEchoSimulator(flip=90.0, TE=20.0, TM=100.0)
spin_echo, stimulated_echo = sequence.simulate(T1=1000.0, T2=80.0)
diff --git a/src/torchsim/simulators/truefisp.py b/src/blochsim/simulators/truefisp.py
similarity index 99%
rename from src/torchsim/simulators/truefisp.py
rename to src/blochsim/simulators/truefisp.py
index 47fc24f2..dd3f7a14 100644
--- a/src/torchsim/simulators/truefisp.py
+++ b/src/blochsim/simulators/truefisp.py
@@ -43,7 +43,7 @@ class TrueFISPSimulator(Simulator):
--------
.. exec::
- from torchsim.simulators import TrueFISPSimulator
+ from blochsim.simulators import TrueFISPSimulator
sequence = TrueFISPSimulator(
flip=45.0, TR=4.5, nshots=200, inversion="adiabatic"
diff --git a/src/torchsim/utils/__init__.py b/src/blochsim/utils/__init__.py
similarity index 100%
rename from src/torchsim/utils/__init__.py
rename to src/blochsim/utils/__init__.py
diff --git a/src/torchsim/utils/_b1rms.py b/src/blochsim/utils/_b1rms.py
similarity index 100%
rename from src/torchsim/utils/_b1rms.py
rename to src/blochsim/utils/_b1rms.py
diff --git a/tests/conftest.py b/tests/conftest.py
index 6512fa38..791e3392 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -12,6 +12,6 @@ def always_worth_detecting(monkeypatch):
trains one by one -- can fall on opposite sides of it and be compared
across kernels.
"""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
monkeypatch.setattr(_accelerators, "detection", lambda kind, device: 0.0)
diff --git a/tests/estimators/test_dictionary.py b/tests/estimators/test_dictionary.py
index 9a0b6596..0036dbd7 100644
--- a/tests/estimators/test_dictionary.py
+++ b/tests/estimators/test_dictionary.py
@@ -5,8 +5,8 @@
import pytest
import torch
-from torchsim.estimators import DictionaryMatcher
-from torchsim.simulators import MultiEchoSimulator
+from blochsim.estimators import DictionaryMatcher
+from blochsim.simulators import MultiEchoSimulator
@pytest.mark.parametrize(
diff --git a/tests/estimators/test_grouped.py b/tests/estimators/test_grouped.py
index 04efc15b..e1552bed 100644
--- a/tests/estimators/test_grouped.py
+++ b/tests/estimators/test_grouped.py
@@ -5,9 +5,9 @@
import pytest
import torch
-from torchsim.estimators import DictionaryMatcher
-from torchsim.estimators._grouped import Grouping
-from torchsim.simulators import MRFSimulator
+from blochsim.estimators import DictionaryMatcher
+from blochsim.estimators._grouped import Grouping
+from blochsim.simulators import MRFSimulator
@pytest.fixture(scope="module")
@@ -220,7 +220,7 @@ def test_grouping_composes_with_a_temporal_subspace(fingerprints, measured) -> N
other, and the answer is the one the compressed dictionary gives without
grouping -- the grouping must not add error of its own.
"""
- from torchsim import (
+ from blochsim import (
Subspace,
)
@@ -247,7 +247,7 @@ def test_clustering_happens_inside_the_global_basis(fingerprints) -> None:
leaving the space the measurement is in. Everything stays at the rank the
dictionary was compressed to.
"""
- from torchsim import (
+ from blochsim import (
Subspace,
)
diff --git a/tests/estimators/test_lookup.py b/tests/estimators/test_lookup.py
index c65e6336..f32c0fab 100644
--- a/tests/estimators/test_lookup.py
+++ b/tests/estimators/test_lookup.py
@@ -5,8 +5,8 @@
import pytest
import torch
-from torchsim.estimators import LookupTable
-from torchsim.simulators import MP2RAGESimulator
+from blochsim.estimators import LookupTable
+from blochsim.simulators import MP2RAGESimulator
PROTOCOL = dict(
TI=(800.0, 2700.0),
diff --git a/tests/estimators/test_mapping.py b/tests/estimators/test_mapping.py
index a79294db..0dacf4d9 100644
--- a/tests/estimators/test_mapping.py
+++ b/tests/estimators/test_mapping.py
@@ -10,12 +10,12 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
PERK,
DictionaryMatcher,
Estimator,
)
-from torchsim.simulators import FSESimulator, MRFSimulator
+from blochsim.simulators import FSESimulator, MRFSimulator
ECHOES = 32
diff --git a/tests/estimators/test_mapping_comparison.py b/tests/estimators/test_mapping_comparison.py
index 0bbe2fb6..169d8e3a 100644
--- a/tests/estimators/test_mapping_comparison.py
+++ b/tests/estimators/test_mapping_comparison.py
@@ -4,8 +4,8 @@
import torch
-from torchsim.estimators import PERK, DictionaryMatcher
-from torchsim.simulators import FSESimulator
+from blochsim.estimators import PERK, DictionaryMatcher
+from blochsim.simulators import FSESimulator
def test_perk_is_comparable_to_dictionary_matching_under_noise() -> None:
diff --git a/tests/estimators/test_nlls.py b/tests/estimators/test_nlls.py
index c8f6d73d..ae776f89 100644
--- a/tests/estimators/test_nlls.py
+++ b/tests/estimators/test_nlls.py
@@ -7,9 +7,9 @@
import torch
from scipy.optimize import least_squares
-import torchsim.recon._operator as _operator
-from torchsim.estimators import NonlinearLeastSquares
-from torchsim.simulators import (
+import blochsim.recon._operator as _operator
+from blochsim.estimators import NonlinearLeastSquares
+from blochsim.simulators import (
FSESimulator,
InversionRecoverySimulator,
MultiEchoSimulator,
@@ -255,7 +255,7 @@ def test_an_equality_constraint_is_written_into_the_model() -> None:
restoring it after each step, so the constraint holds at every iterate
and not merely at the answer.
"""
- from torchsim.model import Simulator
+ from blochsim.model import Simulator
class FatWater(Simulator):
"""Two species at their own frequencies, in known proportion."""
@@ -327,7 +327,7 @@ def test_the_solve_is_a_gauss_newton_and_nothing_else_is_here() -> None:
moves, what stops a voxel -- lives on the loop, so there is one place to
set it and one place for it to be wrong.
"""
- from torchsim.recon import GaussNewton, TrustRegion, direct
+ from blochsim.recon import GaussNewton, TrustRegion, direct
default = NonlinearLeastSquares()
@@ -347,7 +347,7 @@ def test_the_solve_is_a_gauss_newton_and_nothing_else_is_here() -> None:
)
def test_loop_settings_that_make_no_sense(settings, complaint) -> None:
"""Caught where they are written, not where they misbehave."""
- from torchsim.recon import GaussNewton
+ from blochsim.recon import GaussNewton
with pytest.raises(ValueError, match=complaint):
GaussNewton(**settings)
diff --git a/tests/estimators/test_perk.py b/tests/estimators/test_perk.py
index 7348cf8a..636851ab 100644
--- a/tests/estimators/test_perk.py
+++ b/tests/estimators/test_perk.py
@@ -7,9 +7,9 @@
import pytest
import torch
-from torchsim.estimators import PERK, DictionaryMatcher
-from torchsim.model import Simulator
-from torchsim.simulators import MultiEchoSimulator
+from blochsim.estimators import PERK, DictionaryMatcher
+from blochsim.model import Simulator
+from blochsim.simulators import MultiEchoSimulator
@pytest.mark.parametrize(
diff --git a/tests/estimators/test_perk_kernel.py b/tests/estimators/test_perk_kernel.py
index f9b4e930..666a8397 100644
--- a/tests/estimators/test_perk_kernel.py
+++ b/tests/estimators/test_perk_kernel.py
@@ -12,8 +12,8 @@
import pytest
import torch
-import torchsim.estimators._perk as _perk
-from torchsim.estimators import PERK
+import blochsim.estimators._perk as _perk
+from blochsim.estimators import PERK
def assert_agrees(
@@ -157,7 +157,7 @@ def test_a_gradient_wanted_for_a_fitted_tensor_falls_back(device, monkeypatch) -
def _through_the_polynomial(angles: torch.Tensor) -> torch.Tensor:
"""``sqrt(2) * cos(angle)``, computed by the host kernel and nothing else."""
- native = pytest.importorskip("torchsim.estimators._perk_native")
+ native = pytest.importorskip("blochsim.estimators._perk_native")
one = torch.ones(1, 1)
return native.regress(
angles, one, one, torch.zeros(1), torch.zeros(1), one, torch.zeros(1)
diff --git a/tests/estimators/test_streaming.py b/tests/estimators/test_streaming.py
index 7f8ff177..546535ff 100644
--- a/tests/estimators/test_streaming.py
+++ b/tests/estimators/test_streaming.py
@@ -11,13 +11,13 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
PERK,
DictionaryMatcher,
)
-from torchsim.estimators import _perk
-from torchsim.sequence import execution
-from torchsim.simulators import MRFSimulator
+from blochsim.estimators import _perk
+from blochsim.sequence import execution
+from blochsim.simulators import MRFSimulator
CUDA = pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
TWO_CARDS = pytest.mark.skipif(
diff --git a/tests/estimators/test_subspace.py b/tests/estimators/test_subspace.py
index f350ac4d..1d65fed7 100644
--- a/tests/estimators/test_subspace.py
+++ b/tests/estimators/test_subspace.py
@@ -10,10 +10,10 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
Subspace,
)
-from torchsim.simulators import FSESimulator
+from blochsim.simulators import FSESimulator
RANKS = (1, 2, 4, 8)
diff --git a/tests/estimators/test_uncertainty.py b/tests/estimators/test_uncertainty.py
index 483ab7d0..ea821e66 100644
--- a/tests/estimators/test_uncertainty.py
+++ b/tests/estimators/test_uncertainty.py
@@ -19,9 +19,9 @@
import pytest
import torch
-import torchsim
-from torchsim.estimators import PERK, DictionaryMatcher, NonlinearLeastSquares
-from torchsim.simulators import MRFSimulator
+import blochsim
+from blochsim.estimators import PERK, DictionaryMatcher, NonlinearLeastSquares
+from blochsim.simulators import MRFSimulator
CONTRASTS, VOXELS, DRAWS = 400, 40, 300
@@ -290,7 +290,7 @@ def test_a_standard_error_is_never_below_the_bound(
stated = _least_squares(acquisition, noise_std).uncertainty_of(clean)
_signal, sensitivity = acquisition.jacobian(["T1", "T2"], T1=truth[0], T2=truth[1])
- floor = torchsim.crlb(sensitivity, noise_variance=noise_std**2).sqrt()
+ floor = blochsim.crlb(sensitivity, noise_variance=noise_std**2).sqrt()
for column, name in enumerate(("T1", "T2")):
assert float((stated[name] / floor[:, column]).min()) > 0.99, name
@@ -307,9 +307,9 @@ def test_a_voxel_that_cannot_be_read_is_infinite_rather_than_a_failure() -> None
sensitivity[1, 1] = 0.0 # nothing there responds to the second parameter
with pytest.raises(torch.linalg.LinAlgError):
- torchsim.crlb(sensitivity)
+ blochsim.crlb(sensitivity)
- bound = torchsim.crlb(sensitivity, singular="infinite")
+ bound = blochsim.crlb(sensitivity, singular="infinite")
assert torch.isinf(bound[1]).all()
assert torch.isfinite(bound[[0, 2]]).all()
diff --git a/tests/functionals/test_bssfp_func.py b/tests/functionals/test_bssfp_func.py
index 2ea0cef4..cdb9261d 100644
--- a/tests/functionals/test_bssfp_func.py
+++ b/tests/functionals/test_bssfp_func.py
@@ -3,7 +3,7 @@
import numpy as np
from pytest import fixture
-from torchsim import (
+from blochsim import (
bssfp_sim,
)
diff --git a/tests/functionals/test_fse_func.py b/tests/functionals/test_fse_func.py
index 82c1e4de..1ea15221 100644
--- a/tests/functionals/test_fse_func.py
+++ b/tests/functionals/test_fse_func.py
@@ -3,7 +3,7 @@
import numpy as np
from pytest import fixture
-from torchsim import (
+from blochsim import (
fse_sim,
)
@@ -45,7 +45,7 @@ def test_multiple_gradient(flip):
def test_current_simulator_physics_passes_through(flip):
"""The convenience call does not hide physics the Simulator accepts."""
- from torchsim.simulators import FSESimulator
+ from blochsim.simulators import FSESimulator
got = fse_sim(
flip,
diff --git a/tests/functionals/test_mp2rage_func.py b/tests/functionals/test_mp2rage_func.py
index 3022d2ff..963ca65d 100644
--- a/tests/functionals/test_mp2rage_func.py
+++ b/tests/functionals/test_mp2rage_func.py
@@ -1,6 +1,6 @@
"""MP2RAGE tests."""
-from torchsim import mp2rage_sim
+from blochsim import mp2rage_sim
def test_scalar_forward():
diff --git a/tests/functionals/test_mp2rage_reference.py b/tests/functionals/test_mp2rage_reference.py
index d01e994e..fb0bacbd 100644
--- a/tests/functionals/test_mp2rage_reference.py
+++ b/tests/functionals/test_mp2rage_reference.py
@@ -12,8 +12,8 @@
import pytest
import torch
-from torchsim.model import Simulator
-from torchsim.simulators import MP2RAGESimulator
+from blochsim.model import Simulator
+from blochsim.simulators import MP2RAGESimulator
# The protocol of Marques et al., Neuroimage 49(2):1271, Table 1 at 7 T.
TI_MS = (800.0, 2700.0)
diff --git a/tests/functionals/test_mpnrage_func.py b/tests/functionals/test_mpnrage_func.py
index 2b69af30..cb58ae82 100644
--- a/tests/functionals/test_mpnrage_func.py
+++ b/tests/functionals/test_mpnrage_func.py
@@ -1,6 +1,6 @@
"""MPnRAGE tests."""
-from torchsim import (
+from blochsim import (
mpnrage_sim,
)
diff --git a/tests/functionals/test_mprage_func.py b/tests/functionals/test_mprage_func.py
index 1e11aa4f..3f8d5a62 100644
--- a/tests/functionals/test_mprage_func.py
+++ b/tests/functionals/test_mprage_func.py
@@ -1,6 +1,6 @@
"""MPRAGE tests."""
-from torchsim import (
+from blochsim import (
mprage_sim,
)
diff --git a/tests/functionals/test_mrf_func.py b/tests/functionals/test_mrf_func.py
index 1af564bc..9c837d44 100644
--- a/tests/functionals/test_mrf_func.py
+++ b/tests/functionals/test_mrf_func.py
@@ -3,7 +3,7 @@
import numpy as np
from pytest import fixture
-from torchsim import (
+from blochsim import (
mrf_sim,
)
@@ -45,7 +45,7 @@ def test_multiple_gradient(flip):
def test_current_simulator_protocol_arguments_pass_through(flip):
"""The convenience call accepts protocol fields added to MRFSimulator."""
- from torchsim.simulators import MRFSimulator
+ from blochsim.simulators import MRFSimulator
phases = np.linspace(0.0, 117.0, flip.size, dtype=np.float32)
got = mrf_sim(
diff --git a/tests/functionals/test_spgr_func.py b/tests/functionals/test_spgr_func.py
index ce97ec5f..b21c760e 100644
--- a/tests/functionals/test_spgr_func.py
+++ b/tests/functionals/test_spgr_func.py
@@ -3,7 +3,7 @@
import numpy as np
from pytest import fixture
-from torchsim import (
+from blochsim import (
spgr_sim,
)
diff --git a/tests/model/test_array_backends.py b/tests/model/test_array_backends.py
index c5cec521..8a0ad4b1 100644
--- a/tests/model/test_array_backends.py
+++ b/tests/model/test_array_backends.py
@@ -12,9 +12,9 @@
import pytest
import torch
-import torchsim
-from torchsim.sequence._array import as_torch, backend_of, brought, like, matched
-from torchsim.simulators import FSESimulator, MRFSimulator, SPGRSimulator
+import blochsim
+from blochsim.sequence._array import as_torch, backend_of, brought, like, matched
+from blochsim.simulators import FSESimulator, MRFSimulator, SPGRSimulator
cupy = pytest.importorskip("cupy", reason="no second device backend installed")
@@ -129,13 +129,13 @@ def test_a_shared_parameter_is_spread_over_the_events() -> None:
@pytest.mark.parametrize(
"call",
[
- lambda d: torchsim.spgr_sim(
+ lambda d: blochsim.spgr_sim(
5.0, TE=2.0, TR=10.0, T1=1000.0, T2star=100.0, device=d
),
- lambda d: torchsim.bssfp_sim(
+ lambda d: blochsim.bssfp_sim(
5.0, TE=2.0, TR=10.0, T1=1000.0, T2=100.0, device=d
),
- lambda d: torchsim.mp2rage_sim(
+ lambda d: blochsim.mp2rage_sim(
TI=(500.0, 1500.0),
flip=5.0,
TRspgr=5.0,
@@ -144,14 +144,14 @@ def test_a_shared_parameter_is_spread_over_the_events() -> None:
T1=1000.0,
device=d,
),
- lambda d: torchsim.mrf_sim(FLIP, TR=10.0, T1=1000.0, T2=80.0, device=d),
- lambda d: torchsim.mprage_sim(
+ lambda d: blochsim.mrf_sim(FLIP, TR=10.0, T1=1000.0, T2=80.0, device=d),
+ lambda d: blochsim.mprage_sim(
TI=500.0, flip=5.0, TRspgr=5.0, nshots=128, T1=1000.0, device=d
),
- lambda d: torchsim.mpnrage_sim(
+ lambda d: blochsim.mpnrage_sim(
nshots=8, flip=5.0, TR=10.0, T1=1000.0, device=d
),
- lambda d: torchsim.fse_sim(
+ lambda d: blochsim.fse_sim(
flip=np.full(4, 120.0, dtype=np.float32),
ESP=5.0,
T1=1000.0,
diff --git a/tests/model/test_jacobian_contract.py b/tests/model/test_jacobian_contract.py
index e9347d58..e7d3a7fe 100644
--- a/tests/model/test_jacobian_contract.py
+++ b/tests/model/test_jacobian_contract.py
@@ -11,15 +11,15 @@
import pytest
import torch
-import torchsim
-from torchsim.model import Simulator
+import blochsim
+from blochsim.model import Simulator
FLIP = np.ones(7) * 120.0
ECHOES = 7
def _fse(t1, t2, diff):
- return torchsim.fse_sim(flip=FLIP, ESP=5.0, T1=t1, T2=t2, diff=diff)
+ return blochsim.fse_sim(flip=FLIP, ESP=5.0, T1=t1, T2=t2, diff=diff)
VECTOR_T1 = torch.tensor([800.0, 1000.0, 1200.0])
@@ -47,8 +47,8 @@ def test_jacobian_matches_finite_differences() -> None:
"""Forward mode is exact where central differences are only close."""
step = 1e-2
_, jacobian = _fse(VECTOR_T1, VECTOR_T2, "T2")
- forward = torchsim.fse_sim(flip=FLIP, ESP=5.0, T1=VECTOR_T1, T2=VECTOR_T2 + step)
- backward = torchsim.fse_sim(flip=FLIP, ESP=5.0, T1=VECTOR_T1, T2=VECTOR_T2 - step)
+ forward = blochsim.fse_sim(flip=FLIP, ESP=5.0, T1=VECTOR_T1, T2=VECTOR_T2 + step)
+ backward = blochsim.fse_sim(flip=FLIP, ESP=5.0, T1=VECTOR_T1, T2=VECTOR_T2 - step)
numerical = (forward - backward) / (2.0 * step)
assert torch.allclose(jacobian, numerical, atol=1e-4)
@@ -56,7 +56,7 @@ def test_jacobian_matches_finite_differences() -> None:
def test_signal_matches_undifferentiated_call() -> None:
"""The primal returned alongside the Jacobian is the ordinary signal."""
signal, _ = _fse(VECTOR_T1, VECTOR_T2, "T2")
- plain = torchsim.fse_sim(flip=FLIP, ESP=5.0, T1=VECTOR_T1, T2=VECTOR_T2)
+ plain = blochsim.fse_sim(flip=FLIP, ESP=5.0, T1=VECTOR_T1, T2=VECTOR_T2)
assert torch.equal(signal, plain)
@@ -68,7 +68,7 @@ def test_the_answer_comes_back_in_the_array_library_it_was_asked_in() -> None:
"""
numpy_t1 = VECTOR_T1.numpy()
numpy_t2 = VECTOR_T2.numpy()
- signal, jacobian = torchsim.fse_sim(
+ signal, jacobian = blochsim.fse_sim(
flip=FLIP, ESP=5.0, T1=numpy_t1, T2=numpy_t2, diff="T2"
)
assert isinstance(signal, np.ndarray)
diff --git a/tests/model/test_model_physics.py b/tests/model/test_model_physics.py
index 0cd04efb..c4192a08 100644
--- a/tests/model/test_model_physics.py
+++ b/tests/model/test_model_physics.py
@@ -12,14 +12,14 @@
import pytest
import torch
-from torchsim.model import UNBALANCED, Simulator, SpinPhysics
-from torchsim.sequence import (
+from blochsim.model import UNBALANCED, Simulator, SpinPhysics
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
_accelerators,
mrf_description,
)
-from torchsim.sequence._parameters import FLOAT_NAMES, TISSUE_NAMES
+from blochsim.sequence._parameters import FLOAT_NAMES, TISSUE_NAMES
FLIP = torch.linspace(5.0, 60.0, 24)
T1 = torch.tensor([600.0, 1000.0, 1400.0])
diff --git a/tests/model/test_signal_model.py b/tests/model/test_signal_model.py
index b24fe641..214946be 100644
--- a/tests/model/test_signal_model.py
+++ b/tests/model/test_signal_model.py
@@ -13,8 +13,8 @@
import pytest
import torch
-from torchsim.model import REFOCUSED, Simulator, SpinPhysics
-from torchsim.sequence import _simulation
+from blochsim.model import REFOCUSED, Simulator, SpinPhysics
+from blochsim.sequence import _simulation
class Relaxation(Simulator):
diff --git a/tests/model/test_state_machine.py b/tests/model/test_state_machine.py
index 1b47b129..949fcd70 100644
--- a/tests/model/test_state_machine.py
+++ b/tests/model/test_state_machine.py
@@ -15,7 +15,7 @@
import pytest
import torch
-from torchsim.model import (
+from blochsim.model import (
BALANCED,
REFOCUSED,
SPOILED,
@@ -24,8 +24,8 @@
Simulator,
SpinPhysics,
)
-from torchsim.sequence import EpgEngine, EventAction, EventType, ideal_rf_definition
-from torchsim.simulators import MRFSimulator
+from blochsim.sequence import EpgEngine, EventAction, EventType, ideal_rf_definition
+from blochsim.simulators import MRFSimulator
FLIP = torch.linspace(5.0, 60.0, 12)
T1 = torch.tensor([600.0, 1000.0, 1400.0])
@@ -102,7 +102,7 @@ def test_the_trigger_leaves_the_description_ordinary() -> None:
def test_a_shipped_simulator_reaches_the_fused_kernels(monkeypatch) -> None:
"""A fast path has to be shown taken, not inferred from agreement."""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
seen: list[int] = []
original = _accelerators._run_packed
@@ -177,7 +177,7 @@ def test_a_handed_over_description_agrees_with_the_engine_directly() -> None:
described = protocol.describe(flip=FLIP, TR=10.0)
handed = Simulator.from_description(described, protocol.model, states=8)
- from torchsim.sequence import TissueProperties
+ from blochsim.sequence import TissueProperties
direct = (
EpgEngine()
@@ -198,7 +198,7 @@ def test_the_protocol_may_be_overridden_per_call() -> None:
def test_a_trigger_table_defaults_to_the_bare_operators() -> None:
"""An unassigned slot is the plain event, not a surprise."""
- from torchsim.sequence import Excitation, Readout
+ from blochsim.sequence import Excitation, Readout
table = EventOperators()
assert table.excitation is Excitation
@@ -238,9 +238,9 @@ def test_a_gradient_standing_on_its_own_does_not_survive_the_round_trip() -> Non
nor a sample, so nothing reinstates it, and a stream arriving from a
scanner could not have carried it either.
"""
- from torchsim import Delay, Excitation, Spoil
- from torchsim.model._state_machine import realised
- from torchsim.sequence import EventAction
+ from blochsim import Delay, Excitation, Spoil
+ from blochsim.model._state_machine import realised
+ from blochsim.sequence import EventAction
class Prepared(Simulator):
model = _with(SPOILED).model
@@ -277,8 +277,8 @@ def test_shim_definition_reaches_the_pulses():
array whose sensitivities are alike cancels exactly when the drive is a
half turn apart, and does not when it is not.
"""
- from torchsim import ShimDefinition
- from torchsim.simulators import FSESimulator
+ from blochsim import ShimDefinition
+ from blochsim.simulators import FSESimulator
array = dict(
T1=torch.tensor([1000.0]),
diff --git a/tests/optim/test_binding.py b/tests/optim/test_binding.py
index 9d8938a5..ade5a224 100644
--- a/tests/optim/test_binding.py
+++ b/tests/optim/test_binding.py
@@ -14,11 +14,11 @@
import pytest
import torch
-from torchsim.model import Simulator, SpinPhysics
-from torchsim.model._binding import bind
-from torchsim.sequence import _accelerators
-from torchsim.sequence._accelerators import pack_description
-from torchsim.simulators import (
+from blochsim.model import Simulator, SpinPhysics
+from blochsim.model._binding import bind
+from blochsim.sequence import _accelerators
+from blochsim.sequence._accelerators import pack_description
+from blochsim.simulators import (
FSESimulator,
MPnRAGESimulator,
MPRAGESimulator,
diff --git a/tests/optim/test_design.py b/tests/optim/test_design.py
index 2789ed8e..803da1ae 100644
--- a/tests/optim/test_design.py
+++ b/tests/optim/test_design.py
@@ -12,8 +12,8 @@
import pytest
import torch
-from torchsim.optim import Bounded, SequenceDesign, crlb
-from torchsim.simulators import FSESimulator, SPGRSimulator, bSSFPSimulator
+from blochsim.optim import Bounded, SequenceDesign, crlb
+from blochsim.simulators import FSESimulator, SPGRSimulator, bSSFPSimulator
TISSUE = {"T1": [800.0, 1400.0], "T2": [45.0, 120.0]}
diff --git a/tests/recon/test_constraints.py b/tests/recon/test_constraints.py
index b43e59bc..c7e67f1f 100644
--- a/tests/recon/test_constraints.py
+++ b/tests/recon/test_constraints.py
@@ -9,8 +9,8 @@
import pytest
import torch
-from torchsim.model import Simulator
-from torchsim.recon import (
+from blochsim.model import Simulator
+from blochsim.recon import (
GaussNewton,
ModelOperator,
Schedule,
@@ -18,10 +18,10 @@
direct,
iterative,
)
-from torchsim.simulators import MultiEchoSimulator
+from blochsim.simulators import MultiEchoSimulator
#: ``iterative()`` with nothing named falls back to deepinv's ``least_squares``,
-#: which TorchSim does not depend on.
+#: which BlochSim does not depend on.
needs_deepinv = pytest.mark.skipif(
importlib.util.find_spec("deepinv") is None,
reason="deepinv supplies the fallback inner solve",
diff --git a/tests/recon/test_deepinv.py b/tests/recon/test_deepinv.py
index e568b891..193367b7 100644
--- a/tests/recon/test_deepinv.py
+++ b/tests/recon/test_deepinv.py
@@ -5,11 +5,11 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
execution,
)
-from torchsim.recon import ModelOperator
-from torchsim.simulators import MultiEchoSimulator
+from blochsim.recon import ModelOperator
+from blochsim.simulators import MultiEchoSimulator
deepinv = pytest.importorskip("deepinv")
@@ -68,7 +68,7 @@ def test_differentiating_through_the_operator_does_not_stream_it(
differentiated, and the streamed path would silently give it nothing, so
a call under one runs where it stands however loud the policy is.
"""
- from torchsim.recon import _operator as module
+ from blochsim.recon import _operator as module
entered = []
real = module.per_voxel
@@ -108,7 +108,7 @@ def test_a_deepinv_optimizer_drives_the_composed_operator(operator) -> None:
Composing this way gives up the analytic derivative -- deepinv takes the
Jacobian products by differentiating the whole chain -- which is the trade
for reaching every optimizer and prior it has.
- :class:`~torchsim.recon.GaussNewton` chains the two operators' own
+ :class:`~blochsim.recon.GaussNewton` chains the two operators' own
products instead and keeps it.
"""
from deepinv.optim import L2
diff --git a/tests/recon/test_gauss_newton.py b/tests/recon/test_gauss_newton.py
index 41d001b6..7fc66083 100644
--- a/tests/recon/test_gauss_newton.py
+++ b/tests/recon/test_gauss_newton.py
@@ -8,8 +8,8 @@
import pytest
import torch
-from torchsim.estimators import NonlinearLeastSquares
-from torchsim.recon import (
+from blochsim.estimators import NonlinearLeastSquares
+from blochsim.recon import (
GaussNewton,
LeastSquares,
Linearization,
@@ -19,10 +19,10 @@
direct,
iterative,
)
-from torchsim.simulators import MultiEchoSimulator
+from blochsim.simulators import MultiEchoSimulator
#: ``iterative()`` with nothing named falls back to deepinv's ``least_squares``,
-#: which TorchSim does not depend on. What is asserted under this mark is the
+#: which BlochSim does not depend on. What is asserted under this mark is the
#: fallback; the duck-typed route beside it is what holds without deepinv.
needs_deepinv = pytest.mark.skipif(
importlib.util.find_spec("deepinv") is None,
@@ -119,7 +119,7 @@ def test_the_two_policies_land_in_the_same_place(problem) -> None:
def test_a_trust_region_is_the_fit_that_ships(problem) -> None:
"""The generalization is exact: the loop reproduces the estimator.
- :class:`~torchsim.NonlinearLeastSquares` is this loop under a per-voxel
+ :class:`~blochsim.NonlinearLeastSquares` is this loop under a per-voxel
trust region with the voxel-diagonal solve, and nothing else.
"""
acquisition = MultiEchoSimulator(TE=TE_MS)
@@ -351,7 +351,7 @@ def test_one_of_deepinvs_solvers_is_that_function_with_its_argument_bound(
) -> None:
"""Which one suits is the caller's to measure, so all of them must run.
- Binding the argument is the caller's own composition -- TorchSim takes an
+ Binding the argument is the caller's own composition -- BlochSim takes an
object and never a name.
"""
least_squares = pytest.importorskip("deepinv.optim.linear").least_squares
diff --git a/tests/recon/test_operator.py b/tests/recon/test_operator.py
index 0c42ffa5..15a359bf 100644
--- a/tests/recon/test_operator.py
+++ b/tests/recon/test_operator.py
@@ -5,12 +5,12 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
Subspace,
execution,
)
-from torchsim.recon import ModelOperator
-from torchsim.simulators import FSESimulator, MultiEchoSimulator
+from blochsim.recon import ModelOperator
+from blochsim.simulators import FSESimulator, MultiEchoSimulator
TE_MS = torch.tensor([10.0, 20.0, 40.0, 80.0, 160.0])
BOUND = {"T2": (10.0, 300.0)}
diff --git a/tests/recon/test_subspace_route.py b/tests/recon/test_subspace_route.py
index 216039e2..bbc5f240 100644
--- a/tests/recon/test_subspace_route.py
+++ b/tests/recon/test_subspace_route.py
@@ -6,11 +6,11 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
Subspace,
)
-from torchsim.estimators import DictionaryMatcher
-from torchsim.simulators import MultiEchoSimulator
+from blochsim.estimators import DictionaryMatcher
+from blochsim.simulators import MultiEchoSimulator
mrinufft = pytest.importorskip("mrinufft")
diff --git a/tests/sequence/test_against_epgpy.py b/tests/sequence/test_against_epgpy.py
index 42e9cf28..4b91b6f2 100644
--- a/tests/sequence/test_against_epgpy.py
+++ b/tests/sequence/test_against_epgpy.py
@@ -1,8 +1,8 @@
"""The same sequences, run by another EPG library.
-Every other test in this suite is TorchSim measured against a closed form, a
+Every other test in this suite is BlochSim measured against a closed form, a
published figure, or an integration written out beside it. This one is
-TorchSim measured against a second implementation of the whole formalism:
+BlochSim measured against a second implementation of the whole formalism:
`epgpy `_, which shares no code, no author
and no array library with this one.
@@ -22,7 +22,7 @@
import numpy as np
import pytest
-from torchsim import (
+from blochsim import (
Delay,
Dephase,
Excitation,
@@ -30,7 +30,7 @@
Refocusing,
SequenceDescription,
)
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
)
@@ -41,7 +41,7 @@
ORDERS = 40
-def _torchsim(parts, nstates=ORDERS):
+def _blochsim(parts, nstates=ORDERS):
signal = (
EpgEngine()
.simulate(
@@ -92,7 +92,7 @@ def test_a_refocused_train_agrees_echo_for_echo(flip_deg: float) -> None:
Readout(0.0),
]
- theirs, ours = _epgpy(theirs), _torchsim(ours)
+ theirs, ours = _epgpy(theirs), _blochsim(ours)
assert ours.shape == theirs.shape
assert np.abs(ours - theirs).max() / theirs.max() < 1e-5
@@ -131,7 +131,7 @@ def test_a_spoiled_train_agrees_over_two_hundred_repetitions() -> None:
)
]
- theirs, ours = _epgpy(theirs, nstates=30), _torchsim(ours, nstates=30)
+ theirs, ours = _epgpy(theirs, nstates=30), _blochsim(ours, nstates=30)
assert np.abs(ours - theirs).max() / theirs.max() < 1e-3
# And it is a settled answer rather than two curves that never started.
assert theirs[-1] > 0.01
diff --git a/tests/sequence/test_both_pools.py b/tests/sequence/test_both_pools.py
index 7e5e0d80..80a9a77c 100644
--- a/tests/sequence/test_both_pools.py
+++ b/tests/sequence/test_both_pools.py
@@ -17,18 +17,18 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
NO_GEOMETRY,
_pack_events,
_run_packed,
)
-from torchsim.sequence._lineshape import lineshape_table
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._lineshape import lineshape_table
+from blochsim.sequence._simulation import _prepare_tissue
from utils.packed_reference import simulate_packed
ECHOES = 8
@@ -156,7 +156,7 @@ def _free_induction(times_s, **properties):
``times_s`` are measured from the excitation; the events carry the
intervals between them, which is what the state machine steps through.
"""
- from torchsim.sequence._accelerators import _EXCITATION, _RECORD
+ from blochsim.sequence._accelerators import _EXCITATION, _RECORD
intervals = [
after - before for before, after in zip((0.0, *times_s), times_s, strict=False)
@@ -252,7 +252,7 @@ def _inversion_recovery(delays_s, **properties):
fixed point. The readout is a hard ninety degrees, which writes ``Z`` into
the transverse plane.
"""
- from torchsim.sequence._accelerators import _EXCITATION, _INVERSION, _RECORD
+ from blochsim.sequence._accelerators import _EXCITATION, _INVERSION, _RECORD
prepared, _, _ = _prepare_tissue(
TissueProperties(**{"t1_ms": 1000.0, "t2_ms": 80.0, **properties}), "cpu"
@@ -357,7 +357,7 @@ def _live_events():
of a difference, which is a property of the probe rather than of the
kernel; here the recovery interval makes them live.
"""
- from torchsim.sequence._accelerators import _EXCITATION, _INVERSION, _RECORD
+ from blochsim.sequence._accelerators import _EXCITATION, _INVERSION, _RECORD
return (
torch.tensor([0.0, 0.35, 0.0, 0.0, 3e-3, 0.0], dtype=torch.float32),
@@ -381,7 +381,7 @@ def _prepared(device="cpu", **properties):
def _live_readout(prepared, seed=None, device="cpu"):
"""The reading, or its directional derivative."""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
events = tuple(value.to(device) for value in _live_events())
table = lineshape_table(device=torch.device(device))
@@ -407,7 +407,7 @@ def test_forward_mode_matches_finite_differences(name: str) -> None:
"""Every direction the three-pool system carries, including the two that
only the semisolid pool has and the five only the exchanging one does.
"""
- from torchsim.sequence._parameters import TISSUE_NAMES
+ from blochsim.sequence._parameters import TISSUE_NAMES
index = TISSUE_NAMES.index(name)
prepared = _prepared()
@@ -493,7 +493,7 @@ def run(value):
def _live_adjoint(prepared, seed):
"""The gradients a cotangent on the reading leaves."""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
return _run_packed_vjp(
prepared,
@@ -531,7 +531,7 @@ def test_the_adjoint_matches_finite_differences(name: str) -> None:
"""Every direction three pools carry, including the semisolid pool's own
three and the exchanging pool's five.
"""
- from torchsim.sequence._parameters import TISSUE_NAMES
+ from blochsim.sequence._parameters import TISSUE_NAMES
index = TISSUE_NAMES.index(name)
seed = _cotangent()
@@ -581,7 +581,7 @@ def test_the_second_order_pass_differentiates_the_adjoint():
"""Given no direction to follow, the forward-over-reverse kernel returns
the adjoint on its own -- and given one, the adjoint's own derivative.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
prepared = _prepared()
events = _live_events()
@@ -603,7 +603,7 @@ def test_the_second_order_pass_differentiates_the_adjoint():
if scale > 1e-9:
assert float((expected - measured).abs().max()) / scale < 1e-4
- from torchsim.sequence._parameters import TISSUE_NAMES
+ from blochsim.sequence._parameters import TISSUE_NAMES
# The direction is confined to the properties the difference below moves,
# so the two are contracted against the same one.
@@ -648,7 +648,7 @@ def test_the_second_order_pass_saturates_the_pool_the_pulse_deposits_into():
what the first-order adjoint does, and the two reach the saturation by
different code.
"""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_run_packed_vjp,
_run_packed_vjp_jvp,
)
@@ -863,7 +863,7 @@ def _train_events():
def _routes(leaves, events):
"""The kernels and the oracle, fed from one place."""
- from torchsim.sequence._accelerators import _NativeEpg
+ from blochsim.sequence._accelerators import _NativeEpg
fused = _NativeEpg.apply(
*leaves,
@@ -1034,7 +1034,7 @@ def run(device):
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_a_streamed_volume_matches_the_whole_one():
"""Streaming cuts the voxel axis, which all three pools follow."""
- from torchsim.sequence._accelerators import offload
+ from blochsim.sequence._accelerators import offload
voxels = 3000
packed = _pack_events(
@@ -1088,7 +1088,7 @@ def test_a_streamed_adjoint_matches_the_whole_one():
Given no direction to follow the forward-over-reverse kernel returns the
adjoint on its own, and that is the reverse route the offload plan reaches.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp, offload
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp, offload
voxels = 3000
packed = _pack_events(
@@ -1205,7 +1205,7 @@ def run(device):
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_a_streamed_forward_mode_matches_the_whole_one():
"""Streaming cuts the voxel axis, which all three pools' seeds follow."""
- from torchsim.sequence._accelerators import offload
+ from blochsim.sequence._accelerators import offload
voxels = 3000
prepared = _prepared(
@@ -1243,8 +1243,8 @@ def _instantaneous_table():
"""A pulse with no gradient across it: one rotation, every position."""
import numpy as np
- from torchsim.sequence._description import RfDefinition, RfShape
- from torchsim.sequence._transition import transition_table
+ from blochsim.sequence._description import RfDefinition, RfShape
+ from blochsim.sequence._transition import transition_table
flat = RfDefinition(
id=0,
@@ -1262,7 +1262,7 @@ def test_a_tabulated_rotation_reaches_both_pools() -> None:
"""The kernels are templated on the rotation mode and the pool count
together, so a table beside three pools is an instantiation of its own.
"""
- from torchsim.sequence._accelerators import _NativeEpg
+ from blochsim.sequence._accelerators import _NativeEpg
leaves = _prepared()
events = _train_events()
@@ -1308,9 +1308,9 @@ def test_three_pools_take_the_first_order_kernel_on_the_card(
forward-over-reverse pass on the same card: two arms of one wrong kernel
agree with each other, and the backends share no code.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
- from torchsim.sequence._parameters import TISSUE_NAMES
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._parameters import TISSUE_NAMES
voxels = 64
tissue = TissueProperties(
@@ -1473,8 +1473,8 @@ def test_the_series_carries_the_answer_up_to_the_spread_it_is_trusted_to() -> No
import triton
import triton.language as tl
- from torchsim.sequence._epg_triton import _three_pool_step
- from torchsim.sequence._parameters import NARROW_SPREAD
+ from blochsim.sequence._epg_triton import _three_pool_step
+ from blochsim.sequence._parameters import NARROW_SPREAD
@triton.jit
def only_the_series(
@@ -1557,7 +1557,7 @@ def test_a_long_interval_declines_the_series_branch() -> None:
so the launch has to fall back to the roots -- checked on the predicate the
launchers call, not inferred from the answer agreeing.
"""
- from torchsim.sequence._parameters import narrow_three_pool
+ from blochsim.sequence._parameters import narrow_three_pool
voxels = 256
tissue, _, _ = _prepare_tissue(
@@ -1597,7 +1597,7 @@ def test_the_series_branch_gives_the_answer_the_roots_give(state_count) -> None:
The gate cannot be measured against the host: that comparison moves for
reasons of its own. What it has to be held to is the branch it replaces.
"""
- from torchsim.sequence import _accelerators, _epg_triton
+ from blochsim.sequence import _accelerators, _epg_triton
voxels = 256
tissue, _, _ = _prepare_tissue(
diff --git a/tests/sequence/test_bound_pool.py b/tests/sequence/test_bound_pool.py
index 1c7128d1..155611f0 100644
--- a/tests/sequence/test_bound_pool.py
+++ b/tests/sequence/test_bound_pool.py
@@ -17,20 +17,20 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
NO_GEOMETRY,
_pack_events,
_run_packed,
)
-from torchsim.sequence._description import EventType, RfDefinition, RfShape
-from torchsim.sequence._lineshape import lineshape_table
-from torchsim.sequence._parameters import TISSUE_NAMES
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._description import EventType, RfDefinition, RfShape
+from blochsim.sequence._lineshape import lineshape_table
+from blochsim.sequence._parameters import TISSUE_NAMES
+from blochsim.sequence._simulation import _prepare_tissue
from utils.packed_reference import simulate_packed
ECHOES = 8
@@ -131,7 +131,7 @@ def _inversion_recovery(delays_s, **properties):
90 degrees, which writes ``Z`` into the transverse plane, so the recorded
signal is proportional to the free pool's longitudinal state.
"""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_EXCITATION,
_INVERSION,
_RECORD,
@@ -336,7 +336,7 @@ def _saturation_events(saturation, offset_hz, delay_s=0.3):
the reading proportional to ``Z_a``, unlike an echo train's magnitudes,
which mix pathways and are not monotone in it.
"""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_EXCITATION,
_RECORD,
_SPOIL_AFTER,
@@ -473,7 +473,7 @@ def test_the_builders_idealized_pulse_declares_almost_no_power():
def _live_readout(prepared, seed=None):
"""The saturate-then-read signal, or its directional derivative."""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
events = _saturation_events(MILLISECOND_PULSE, PULSE_OFFSET_HZ, delay_s=0.4)
table = lineshape_table()
@@ -640,7 +640,7 @@ def _live_events():
def _live_adjoint(prepared, seed, profile=None):
"""Gradients of ``Re()`` w.r.t. every differentiable input."""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
return _run_packed_vjp(
prepared,
@@ -678,7 +678,7 @@ def test_the_adjoint_is_the_transpose_of_the_forward_direction(
reaches the answer through both the rotation and the power the pulse
deposits, and the two arrive at its gradient by different routes.
"""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
profile = _instantaneous_table() if tabulated else None
prepared = _prepared(**LIVE)
@@ -774,7 +774,7 @@ def test_the_second_order_pass_carries_the_bound_pool():
so an error in any entry of it shows up rather than only in the one the
bound pool is most obviously responsible for.
"""
- from torchsim.sequence._accelerators import _NativeEpg
+ from blochsim.sequence._accelerators import _NativeEpg
prepared = _prepared(**LIVE)
events = _live_events()
@@ -975,7 +975,7 @@ def _tuned(offset_hz: float):
def test_the_offset_a_sequence_drives_its_pulses_to_is_read_off_the_events():
- from torchsim.sequence._accelerators import largest_pulse_offset
+ from blochsim.sequence._accelerators import largest_pulse_offset
assert largest_pulse_offset(_description()) == 0.0
assert largest_pulse_offset(_tuned(-6.0e3)) == 6.0e3
@@ -990,7 +990,7 @@ def test_a_table_stopping_short_cannot_tell_two_far_pulses_apart():
a pulse at 60 kHz and one at 90 kHz the same answer; one sized to reach
them does not.
"""
- from torchsim.sequence._lineshape import lineshape_reaching
+ from blochsim.sequence._lineshape import lineshape_reaching
near, far = torch.tensor(60.0e3), torch.tensor(90.0e3)
stops_short = lineshape_table()
@@ -1003,7 +1003,7 @@ def test_a_table_stopping_short_cannot_tell_two_far_pulses_apart():
def test_reaching_further_adds_knots_rather_than_spreading_them():
"""A sequence played further out gets a longer table, not a coarser one."""
- from torchsim.sequence._lineshape import lineshape_reaching
+ from blochsim.sequence._lineshape import lineshape_reaching
default = lineshape_table()
stretched = lineshape_reaching(90.0e3)
@@ -1021,7 +1021,7 @@ def test_sequences_asking_for_similar_ranges_share_one_table():
that happen for ranges that merely differ, rather than only for ones that
match to the hertz.
"""
- from torchsim.sequence._lineshape import OFFSET_MAX_HZ, lineshape_reaching
+ from blochsim.sequence._lineshape import OFFSET_MAX_HZ, lineshape_reaching
assert lineshape_reaching(0.0) is lineshape_reaching(30.0e3)
assert lineshape_reaching(0.0).offset_max_hz == OFFSET_MAX_HZ
@@ -1034,7 +1034,7 @@ def test_the_voxel_is_counted_beside_the_pulse():
from the sequence alone still stops short of what an off-resonant voxel
reads.
"""
- from torchsim.sequence._simulation import _absorption_table
+ from blochsim.sequence._simulation import _absorption_table
still = _absorption_table(_tuned(33.0e3), torch.zeros(1), None)
spread = _absorption_table(_tuned(33.0e3), torch.tensor([-8.0e3, 8.0e3]), None)
@@ -1073,7 +1073,7 @@ def signal(offset_hz):
def _instantaneous_table():
"""A pulse with no gradient across it: one rotation, every position."""
- from torchsim.sequence._transition import transition_table
+ from blochsim.sequence._transition import transition_table
flat = RfDefinition(
id=0,
@@ -1113,7 +1113,7 @@ def test_a_tabulated_bound_pool_run_is_differentiable_both_ways():
"""The profiled and bound-pool kernels are one instantiation, so the two
features have to be checked together rather than each beside a plain run.
"""
- from torchsim.sequence._accelerators import _run_packed_jvp, _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_jvp, _run_packed_vjp
prepared = _prepared(**LIVE)
events = _live_events()
@@ -1177,7 +1177,7 @@ def moved(step):
def _routes(leaves, events, profile=None):
"""The kernels and the oracle, fed from one place."""
- from torchsim.sequence._accelerators import _NativeEpg
+ from blochsim.sequence._accelerators import _NativeEpg
table = lineshape_table()
fused = _NativeEpg.apply(
@@ -1349,7 +1349,7 @@ def run(device):
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_the_cuda_forward_mode_matches_the_cpu_kernel():
"""The tangent of the two-pool step, and of the saturation, on the card."""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
voxels = 6
spread = dict(
@@ -1385,7 +1385,7 @@ def run(device):
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_a_streamed_volume_matches_the_whole_one():
"""Streaming cuts the voxel axis, which the bound pool's buffers follow."""
- from torchsim.sequence._accelerators import offload
+ from blochsim.sequence._accelerators import offload
voxels = 3000
packed = _pack_events(
@@ -1416,7 +1416,7 @@ def test_a_streamed_volume_matches_the_whole_one():
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_a_streamed_forward_mode_matches_the_whole_one():
"""Streaming cuts the voxel axis, which the bound pool's seeds follow."""
- from torchsim.sequence._accelerators import _run_packed_jvp, offload
+ from blochsim.sequence._accelerators import _run_packed_jvp, offload
voxels = 3000
prepared = _prepared(
@@ -1484,7 +1484,7 @@ def test_the_cuda_adjoint_matches_the_cpu_kernel():
the signal and disagrees on every gradient, including the ones that have
nothing to do with the bound pool.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
voxels = 6
events = _saturation_events(MILLISECOND_PULSE, PULSE_OFFSET_HZ, delay_s=0.4)
@@ -1507,7 +1507,7 @@ def run(device):
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_the_cuda_second_order_pass_matches_the_cpu_kernel():
"""Forward-over-reverse, where a direction rides through the adjoint."""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
voxels = 6
events = _saturation_events(MILLISECOND_PULSE, PULSE_OFFSET_HZ, delay_s=0.4)
@@ -1546,7 +1546,7 @@ def test_a_streamed_adjoint_matches_the_whole_one():
its own is that pass given no direction to follow -- so it is the one the
chunked buffers have to carry the second pool through.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp, offload
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp, offload
voxels = 3000
prepared = _prepared(**_spread(voxels))
@@ -1593,7 +1593,7 @@ def test_a_streamed_public_simulation_matches_the_whole_one():
"""The path a user takes: a volume simulated through the public API with
the devices given less memory than it needs, so it arrives in chunks.
"""
- from torchsim.sequence._accelerators import offload
+ from blochsim.sequence._accelerators import offload
voxels = 3000
tissue = _volume(
@@ -1622,7 +1622,7 @@ def test_a_gradient_taken_after_a_streamed_forward_matches_the_whole_one():
replays the trajectory from the inputs rather than from anything the
forward left, so the two halves have to agree across that boundary.
"""
- from torchsim.sequence._accelerators import offload
+ from blochsim.sequence._accelerators import offload
voxels = 3000
@@ -1659,8 +1659,8 @@ def test_a_pool_takes_the_first_order_kernel_on_the_card(state_count) -> None:
forward-over-reverse pass on the same card: two arms of one wrong kernel
agree with each other, and the backends share no code.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
voxels = 64
tissue = TissueProperties(
diff --git a/tests/sequence/test_calibration.py b/tests/sequence/test_calibration.py
index 4826f541..96d517f5 100644
--- a/tests/sequence/test_calibration.py
+++ b/tests/sequence/test_calibration.py
@@ -11,7 +11,7 @@
import pytest
import torch
-from torchsim.sequence._calibration import crossover, detection
+from blochsim.sequence._calibration import crossover, detection
KINDS = ["forward", "jvp", "adjoint"]
diff --git a/tests/sequence/test_cuda_parity.py b/tests/sequence/test_cuda_parity.py
index e734c2c6..f2be8b6e 100644
--- a/tests/sequence/test_cuda_parity.py
+++ b/tests/sequence/test_cuda_parity.py
@@ -8,16 +8,16 @@
import pytest
import torch
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
_pack_events,
_run_packed,
_run_packed_jvp,
_run_packed_vjp_jvp,
geometry_of,
)
-from torchsim.sequence._builders import fse_description
-from torchsim.sequence._parameters import OUTSIDE_THE_SUBSPACE
-from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
+from blochsim.sequence._builders import fse_description
+from blochsim.sequence._parameters import OUTSIDE_THE_SUBSPACE
+from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
pytestmark = pytest.mark.skipif(
not torch.cuda.is_available(), reason="CUDA is unavailable"
@@ -34,7 +34,7 @@ def _probed_atoms(kind="forward", trains=64, share=8):
Taken from the threshold rather than written down, so the size follows it
wherever it moves.
"""
- from torchsim.sequence._calibration import detection
+ from blochsim.sequence._calibration import detection
events, _prepared, _count = _real_case(trains, "cpu", atoms=1)
per_atom = trains * int(events[1].numel())
@@ -231,7 +231,7 @@ def test_an_off_resonance_seed_keeps_the_complex_kernel_on_cuda():
@pytest.mark.parametrize("block_states", [4, 16])
def test_the_packing_width_is_a_power_of_two(block_states):
"""It indexes a ``tl.arange``, which rejects anything else."""
- from torchsim.sequence._epg_triton import _problems_per_program
+ from blochsim.sequence._epg_triton import _problems_per_program
width = _problems_per_program(block_states)
assert width >= 1
@@ -246,7 +246,7 @@ def test_the_packing_width_ignores_how_many_problems_there_are(block_states):
off the launch size would make a streamed volume answer differently from
the same volume run whole.
"""
- from torchsim.sequence._epg_triton import _problems_per_program
+ from blochsim.sequence._epg_triton import _problems_per_program
assert _problems_per_program(block_states) >= 1
@@ -269,7 +269,7 @@ def test_a_small_problem_skips_the_subspace_test_on_cuda():
A GPU clears the work behind the verdict fast enough that a test worth
running on the CPU is pure overhead here.
"""
- from torchsim.sequence._calibration import detection
+ from blochsim.sequence._calibration import detection
assert detection("forward", torch.device("cuda", 0)) > detection(
"forward", torch.device("cpu")
@@ -362,7 +362,7 @@ def test_an_inversion_pulse_reaches_the_same_gradients(inversion):
def test_a_trajectory_too_large_for_one_launch_is_split(monkeypatch):
"""The grid rounds up past a wave, onto rows the next launch owns."""
- from torchsim.sequence import _epg_triton
+ from blochsim.sequence import _epg_triton
expected = _second_order("cpu", 17, 5)
monkeypatch.setattr(_epg_triton, "_TRAJECTORY_BUDGET_BYTES", 40_000)
@@ -389,7 +389,7 @@ def test_the_directions_outside_the_subspace_stay_zero():
def _spgr_description(flip):
- from torchsim.sequence._builders import spgr_description
+ from blochsim.sequence._builders import spgr_description
return spgr_description(
flip,
@@ -496,7 +496,7 @@ def test_the_complex_second_order_kernel_matches_across_shapes(trains, atoms):
def test_the_complex_trajectory_splits_into_waves(monkeypatch):
"""Twice the planes of the real one, so it reaches the budget sooner."""
- from torchsim.sequence import _epg_triton
+ from blochsim.sequence import _epg_triton
expected = _complex_second_order("cpu", 17, 5)
monkeypatch.setattr(_epg_triton, "_TRAJECTORY_BUDGET_BYTES", 40_000)
@@ -542,7 +542,7 @@ def test_the_second_order_path_needs_no_subspace_verdict_on_cuda():
def test_device_tensors_are_refused_by_the_cpu_kernels():
"""A CPU kernel must never be handed a device pointer."""
- from torchsim.sequence._accelerators import _pointers
+ from blochsim.sequence._accelerators import _pointers
events, prepared, _ = _real_case(4, "cuda", atoms=2)
with pytest.raises(ValueError, match="CPU tensors"):
@@ -573,7 +573,7 @@ def test_one_half_of_the_off_axis_turn_matches_the_cpu_kernel(given):
after each echo winds unlike the crushers, which keeps off-resonance in
the states rather than on the samples.
"""
- from torchsim.sequence import (
+ from blochsim.sequence import (
AdcRole,
EpgEngine,
EventAction,
diff --git a/tests/sequence/test_description.py b/tests/sequence/test_description.py
index 6cd37014..e98b946c 100644
--- a/tests/sequence/test_description.py
+++ b/tests/sequence/test_description.py
@@ -6,7 +6,7 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
AdcRole,
EventType,
RfDefinition,
diff --git a/tests/sequence/test_description_helper.py b/tests/sequence/test_description_helper.py
index f92f1f52..35e6bb3a 100644
--- a/tests/sequence/test_description_helper.py
+++ b/tests/sequence/test_description_helper.py
@@ -1,7 +1,7 @@
"""Assembling a description out of operators in one call.
-The claim is only that :func:`~torchsim.description` is
-:func:`~torchsim.compose` with the surrounding fields filled in, so what is
+The claim is only that :func:`~blochsim.description` is
+:func:`~blochsim.compose` with the surrounding fields filled in, so what is
checked is that a description built the short way simulates to the same signal
as one built the long way -- and that the fields a caller does not name take
values that mean "nothing here" rather than being quietly dropped.
@@ -14,7 +14,7 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
Delay,
Excitation,
Readout,
@@ -22,7 +22,7 @@
SequenceDescription,
ShimDefinition,
)
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
compose,
diff --git a/tests/sequence/test_diffusion_kernels.py b/tests/sequence/test_diffusion_kernels.py
index 92a5cfbb..4974915b 100644
--- a/tests/sequence/test_diffusion_kernels.py
+++ b/tests/sequence/test_diffusion_kernels.py
@@ -12,11 +12,11 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
NO_GEOMETRY,
_NativeEpg,
_pack_events,
@@ -25,8 +25,8 @@
_run_packed_vjp_jvp,
damping_scale,
)
-from torchsim.sequence._parameters import FLOAT_NAMES, TISSUE_NAMES
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._parameters import FLOAT_NAMES, TISSUE_NAMES
+from blochsim.sequence._simulation import _prepare_tissue
from utils.epg import diffusion_op
from utils.packed_reference import simulate_packed
diff --git a/tests/sequence/test_distribute.py b/tests/sequence/test_distribute.py
index 58bd3a20..8ba4288f 100644
--- a/tests/sequence/test_distribute.py
+++ b/tests/sequence/test_distribute.py
@@ -19,9 +19,9 @@
_worst_disagreement,
)
-import torchsim.sequence._accelerators as accelerators
-from torchsim.sequence import distribute
-from torchsim.sequence._accelerators import (
+import blochsim.sequence._accelerators as accelerators
+from blochsim.sequence import distribute
+from blochsim.sequence._accelerators import (
_run_packed,
_run_packed_jvp,
_run_packed_vjp_jvp,
@@ -191,7 +191,7 @@ def test_a_sharded_first_order_adjoint_matches_the_whole_one(count):
"""Shards of a gradient are partial sums, and each takes the kernel the
whole one would have.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
events, prepared, outputs = _real_case(TRAINS, "cuda", atoms=ATOMS)
seed = torch.ones((TRAINS, ATOMS, outputs), dtype=torch.complex64, device="cuda")
@@ -214,8 +214,8 @@ def test_a_shard_reaches_the_first_order_kernel(monkeypatch):
to the forward-over-reverse pass, which costs twice the time and twice the
trajectory for a derivative nobody asked for.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
events, prepared, outputs = _real_case(TRAINS, "cuda", atoms=ATOMS)
seed = torch.ones((TRAINS, ATOMS, outputs), dtype=torch.complex64, device="cuda")
diff --git a/tests/sequence/test_dynamic_transmit.py b/tests/sequence/test_dynamic_transmit.py
index a5c19675..4771a0ad 100644
--- a/tests/sequence/test_dynamic_transmit.py
+++ b/tests/sequence/test_dynamic_transmit.py
@@ -14,8 +14,8 @@
import pytest
import torch
-from torchsim.sequence._description import RfDefinition, RfShape
-from torchsim.sequence._transition import (
+from blochsim.sequence._description import RfDefinition, RfShape
+from blochsim.sequence._transition import (
dynamic_pair,
transition_table,
)
@@ -294,10 +294,10 @@ def _train():
the tabulated route and the per-voxel one describe -- so it is the case
that can hold them against each other.
"""
- from torchsim.sequence._accelerators import _pack_events, _shim_count
- from torchsim.sequence._builders import fse_description
- from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _pack_events, _shim_count
+ from blochsim.sequence._builders import fse_description
+ from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
+ from blochsim.sequence._transition import DynamicPairs
definition = _pulse(samples=96)
flips = torch.deg2rad(torch.linspace(100.0, 170.0, ECHOES))
@@ -363,7 +363,7 @@ def test_the_reference_reads_a_dynamic_pair_where_it_reads_a_table():
The pair is integrated at each pulse's own flip, so a row per pulse is what
the table's flip axis stands in for.
"""
- from torchsim.sequence._transition import transition_table
+ from blochsim.sequence._transition import transition_table
definition, prepared, events, pairs = _train()
table = transition_table(
@@ -389,7 +389,7 @@ def test_the_host_kernel_reads_the_pair_the_reference_does():
The two share no code: the reference turns a pulse in torch and the kernel
reads four floats per voxel out of a packed buffer.
"""
- from torchsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._accelerators import _run_packed
_, prepared, events, pairs = _train()
@@ -407,8 +407,8 @@ def test_the_host_kernel_still_reads_a_table_where_one_is_given():
"""The mode is picked from which buffer the caller filled, so a sequence
with a table has to be untouched by the pair's arrival.
"""
- from torchsim.sequence._accelerators import _run_packed
- from torchsim.sequence._transition import SliceTables, transition_table
+ from blochsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._transition import SliceTables, transition_table
definition, prepared, events, _ = _train()
table = SliceTables.alone(
@@ -445,8 +445,8 @@ def test_the_host_forward_mode_follows_a_direction_along_the_pair():
stored in ``complex64``, so the difference stops improving below a step of
about 1e-2; this one sits at that floor rather than under it.
"""
- from torchsim.sequence._accelerators import _run_packed, _run_packed_jvp
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _run_packed, _run_packed_jvp
+ from blochsim.sequence._transition import DynamicPairs
_, prepared, events, pairs = _train()
generator = torch.Generator().manual_seed(13)
@@ -495,7 +495,7 @@ def test_a_direction_along_nothing_moves_nothing():
"""Seeding no direction at all has to leave the forward-mode result at
zero, which is what catches a buffer read where none was given.
"""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
_, prepared, events, pairs = _train()
still = tuple(torch.zeros_like(value) for value in prepared)
@@ -529,8 +529,8 @@ def test_the_host_adjoint_returns_the_cotangent_on_the_pair():
entry has exactly one writer and the reverse pass accumulates nothing
across threads.
"""
- from torchsim.sequence._accelerators import _run_packed, _run_packed_vjp
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _run_packed, _run_packed_vjp
+ from blochsim.sequence._transition import DynamicPairs
_, prepared, events, pairs = _train()
generator = torch.Generator().manual_seed(21)
@@ -580,7 +580,7 @@ def test_a_sequence_with_no_pair_still_gets_the_gradients_it_did():
"""The pair's arrival adds a buffer to every entry point's tail, so a run
that fills none of it has to come back exactly as it was.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
_, prepared, events, _ = _train()
generator = torch.Generator().manual_seed(23)
@@ -602,7 +602,7 @@ def test_the_second_order_pass_returns_the_adjoint_given_no_direction():
what the first-order one does -- including on the pair, which the two reach
by different code.
"""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_run_packed_vjp,
_run_packed_vjp_jvp,
)
@@ -658,11 +658,11 @@ def test_the_second_order_pass_differentiates_the_pair_gradient():
"""Given a direction along the pair, the curvature is what the first-order
gradient's own derivative is.
"""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_run_packed_vjp,
_run_packed_vjp_jvp,
)
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._transition import DynamicPairs
_, prepared, events, pairs = _train()
generator = torch.Generator().manual_seed(21)
@@ -724,9 +724,9 @@ def test_the_cuda_forward_reads_the_pair_the_host_does():
read honest on the card: a row taken from the wrong train would still
produce a plausible train.
"""
- from torchsim.sequence._accelerators import _run_packed
- from torchsim.sequence._epg_triton import simulate
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._epg_triton import simulate
+ from blochsim.sequence._transition import DynamicPairs
_, prepared, events, pairs = _train()
host = _run_packed(prepared, events, 16, ECHOES, 1, dynamic=pairs)
@@ -753,8 +753,8 @@ def test_the_cuda_reverse_agrees_at_every_width(state_count: int) -> None:
the card: each one is a kernel of its own, and a rotation read per voxel
rather than built from a flip angle is the largest of them.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._transition import DynamicPairs
_, prepared, events, pairs = _train()
generator = torch.Generator().manual_seed(21)
@@ -787,8 +787,8 @@ def test_the_cuda_reverse_agrees_at_every_width(state_count: int) -> None:
@pytest.mark.parametrize("state_count", [15, 16, 17])
def test_the_cuda_second_order_agrees_at_every_width(state_count: int) -> None:
"""The curvature along a direction on the pair, against the host."""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._transition import DynamicPairs
_, prepared, events, pairs = _train()
generator = torch.Generator().manual_seed(21)
@@ -841,8 +841,8 @@ def test_the_cuda_second_order_agrees_at_every_width(state_count: int) -> None:
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_the_cuda_forward_mode_follows_the_direction_the_host_does():
"""Forward mode along a direction on the pair, against the host."""
- from torchsim.sequence._accelerators import _run_packed_jvp
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._transition import DynamicPairs
_, prepared, events, pairs = _train()
generator = torch.Generator().manual_seed(29)
@@ -898,10 +898,10 @@ def _real_subspace_train(trains: int = 1):
pair argument, so a verdict of 1 does not slow this train down, it plays a
different pulse.
"""
- from torchsim.sequence._accelerators import _pack_events
- from torchsim.sequence._builders import fse_description
- from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _pack_events
+ from blochsim.sequence._builders import fse_description
+ from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
+ from blochsim.sequence._transition import DynamicPairs
definition = _pulse(samples=96)
flips = torch.deg2rad(torch.linspace(100.0, 170.0, ECHOES))
@@ -965,7 +965,7 @@ def test_the_subspace_verdict_refuses_a_per_voxel_pair():
"""The predicate itself, held to the reason: this train is inside the real
subspace, and a pair still has to rule the reduced kernels out.
"""
- from torchsim.sequence._accelerators import real_subspace_axis
+ from blochsim.sequence._accelerators import real_subspace_axis
prepared, events, pairs, _ = _real_subspace_train()
assert real_subspace_axis(events, prepared) == 1
@@ -979,7 +979,7 @@ def test_a_pair_survives_a_train_the_real_kernel_would_have_taken(monkeypatch):
test that hopes to clear it by carrying enough voxels is testing the
threshold. Forcing it to zero asks the question directly.
"""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
monkeypatch.setattr(_accelerators, "detection", lambda kind, device: 0.0)
prepared, events, pairs, output_count = _real_subspace_train()
@@ -996,14 +996,14 @@ def test_the_lane_forward_leaves_a_pair_to_the_scalar_kernel(monkeypatch):
"""The lane kernel carries no rotation to read a pair into, so a run that
would otherwise vectorize has to fall back rather than play a hard pulse.
"""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
- monkeypatch.setenv("TORCHSIM_LANES", "1")
+ monkeypatch.setenv("BLOCHSIM_LANES", "1")
prepared, events, pairs, output_count = _real_subspace_train(trains=4)
lanes = _accelerators._run_packed(
prepared, events, 16, output_count, 1, dynamic=pairs
)
- monkeypatch.delenv("TORCHSIM_LANES")
+ monkeypatch.delenv("BLOCHSIM_LANES")
scalar = _accelerators._run_packed(
prepared, events, 16, output_count, 1, dynamic=pairs
)
@@ -1175,8 +1175,8 @@ def test_a_pulse_reaches_its_rotation_one_way_or_the_other():
"""Handed both, the kernels read the pair and the table says nothing, so a
caller who built one is owed the news rather than a silent choice.
"""
- from torchsim.sequence._accelerators import _run_packed
- from torchsim.sequence._transition import transition_table
+ from blochsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._transition import transition_table
definition, prepared, events, pairs = _train()
table = transition_table(
@@ -1202,7 +1202,7 @@ def _split_across_two_channels(flips: torch.Tensor):
"""
from dataclasses import replace
- from torchsim.sequence._builders import fse_description
+ from blochsim.sequence._builders import fse_description
base = fse_description(
flips,
@@ -1227,7 +1227,7 @@ def test_a_split_pulse_records_the_train_its_sum_records():
the per-voxel rotation, and the answer is the one the flip-and-phase route
gives for the field the channels sum to.
"""
- from torchsim.sequence import EpgEngine, TissueProperties
+ from blochsim.sequence import EpgEngine, TissueProperties
flips = torch.deg2rad(torch.linspace(100.0, 170.0, ECHOES))
split, plain = _split_across_two_channels(flips)
@@ -1272,7 +1272,7 @@ def test_a_split_pulse_leaves_the_transmit_phase_to_the_rotation():
is already in it. Left on the tissue as well it would be turned by twice,
and the run would not answer the summed field.
"""
- from torchsim.sequence._simulation import _dynamic_transmit
+ from blochsim.sequence._simulation import _dynamic_transmit
split, _ = _split_across_two_channels(torch.deg2rad(torch.full((ECHOES,), 140.0)))
magnitude, phase = _sensitivities()
@@ -1291,7 +1291,7 @@ def test_a_split_pulse_leaves_the_transmit_phase_to_the_rotation():
def _tissue_properties(magnitude, phase):
- from torchsim.sequence import TissueProperties
+ from blochsim.sequence import TissueProperties
return TissueProperties(
t1_ms=torch.linspace(700.0, 1300.0, VOXELS),
@@ -1303,8 +1303,8 @@ def _tissue_properties(magnitude, phase):
def test_a_single_channel_train_never_reaches_the_pair():
"""Nothing a builder emits is to be diverted onto the integrated route."""
- from torchsim.sequence._builders import fse_description
- from torchsim.sequence._simulation import _dynamic_transmit
+ from blochsim.sequence._builders import fse_description
+ from blochsim.sequence._simulation import _dynamic_transmit
plain = fse_description(torch.deg2rad(torch.full((ECHOES,), 140.0)), 5e-3)
magnitude, phase = _sensitivities()
@@ -1319,8 +1319,8 @@ def test_a_single_channel_train_never_reaches_the_pair():
def test_a_static_shim_beside_a_dynamic_pulse_is_refused():
from dataclasses import replace
- from torchsim.sequence import ShimDefinition
- from torchsim.sequence._simulation import _dynamic_transmit
+ from blochsim.sequence import ShimDefinition
+ from blochsim.sequence._simulation import _dynamic_transmit
split, _ = _split_across_two_channels(torch.deg2rad(torch.full((ECHOES,), 140.0)))
shimmed = replace(
@@ -1334,7 +1334,7 @@ def test_a_static_shim_beside_a_dynamic_pulse_is_refused():
def test_a_transmit_map_short_of_the_channels_is_refused():
- from torchsim.sequence._simulation import _dynamic_transmit
+ from blochsim.sequence._simulation import _dynamic_transmit
split, _ = _split_across_two_channels(torch.deg2rad(torch.full((ECHOES,), 140.0)))
magnitude, phase = _sensitivities()
@@ -1347,7 +1347,7 @@ def test_the_dispatch_hands_the_kernels_a_pair_and_no_table(monkeypatch):
"""Agreement with the flip-and-phase route is only evidence about the pair
if the pair is what ran.
"""
- from torchsim.sequence import EpgEngine, _accelerators
+ from blochsim.sequence import EpgEngine, _accelerators
split, _ = _split_across_two_channels(torch.deg2rad(torch.full((ECHOES,), 140.0)))
magnitude, phase = _sensitivities()
@@ -1375,7 +1375,7 @@ def test_a_dynamic_pulse_is_integrated_across_the_slice():
"""Asking for positions spreads each voxel over them and averages the
recorded signal, so a selective pulse records less than an ideal one.
"""
- from torchsim.sequence import EpgEngine, exact_slice_profile
+ from blochsim.sequence import EpgEngine, exact_slice_profile
flips = torch.deg2rad(torch.full((ECHOES,), 150.0))
split, _ = _split_across_two_channels(flips)
@@ -1407,7 +1407,7 @@ def test_a_relaxation_gradient_reaches_through_the_pair():
around it, so a relaxation gradient is the one the flip-and-phase route
returns for the same field.
"""
- from torchsim.sequence import EpgEngine, TissueProperties
+ from blochsim.sequence import EpgEngine, TissueProperties
flips = torch.deg2rad(torch.linspace(100.0, 170.0, ECHOES))
split, plain = _split_across_two_channels(flips)
@@ -1442,7 +1442,7 @@ def test_the_flip_gradient_comes_back_through_the_pulse_integral():
it has to be the gradient the flip-and-phase route gives for the field the
channels sum to.
"""
- from torchsim.sequence import EpgEngine, TissueProperties
+ from blochsim.sequence import EpgEngine, TissueProperties
magnitude, phase = _sensitivities()
combined = torch.polar(magnitude, phase).mean(dim=0)
@@ -1479,7 +1479,7 @@ def test_a_transmit_gradient_comes_back_through_the_pulse_integral(name: str):
"""Against central differences, because there is no second route to the
per-channel maps: the pair is the only thing that reads them.
"""
- from torchsim.sequence import EpgEngine, TissueProperties
+ from blochsim.sequence import EpgEngine, TissueProperties
magnitude, phase = _sensitivities()
split, _ = _split_across_two_channels(
@@ -1526,7 +1526,7 @@ def test_a_forward_direction_follows_the_pair():
brings a cotangent back from it, so a directional derivative through the
per-channel maps is the one differencing the simulation gives.
"""
- from torchsim.sequence import EpgEngine, TissueProperties
+ from blochsim.sequence import EpgEngine, TissueProperties
magnitude, phase = _sensitivities()
split, _ = _split_across_two_channels(
@@ -1568,7 +1568,7 @@ def test_a_hessian_vector_product_reaches_through_the_pair():
"""The second derivative is the first one differenced, and the pair carries
both halves: a direction along it going in, a curvature coming back.
"""
- from torchsim.sequence import EpgEngine, TissueProperties
+ from blochsim.sequence import EpgEngine, TissueProperties
magnitude, phase = _sensitivities()
nominal = torch.deg2rad(torch.linspace(100.0, 170.0, ECHOES))
@@ -1755,7 +1755,7 @@ def test_a_moving_gradient_has_to_last_the_pulse():
gradient that runs out before the RF does is a caller's mistake, not a
shorter pulse.
"""
- from torchsim.sequence._transition import compose_spinor
+ from blochsim.sequence._transition import compose_spinor
drive = [torch.tensor(0.1, dtype=torch.complex128) for _ in range(4)]
turns = [torch.zeros(2, dtype=torch.float64) for _ in range(3)]
@@ -1798,7 +1798,7 @@ class _Elsewhere:
def _streamed(monkeypatch):
- from torchsim import (
+ from blochsim import (
_execution,
)
@@ -1812,7 +1812,7 @@ def test_the_streamed_route_refuses_a_per_voxel_pair(monkeypatch, pass_name):
would have to cut it the same way -- and is told to say so rather than
quietly reach for a hard pulse.
"""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_run_packed,
_run_packed_jvp,
_run_packed_vjp,
@@ -1838,7 +1838,7 @@ def test_the_streamed_route_still_takes_a_train_with_no_pair(monkeypatch):
"""The guard has to be about the pair and not about the route, or a plain
sequence would lose streaming with it.
"""
- from torchsim.sequence._accelerators import _carries_the_pair
+ from blochsim.sequence._accelerators import _carries_the_pair
assert _carries_the_pair(None, "streamed") is None
@@ -1848,8 +1848,8 @@ def test_the_sharded_route_refuses_a_per_voxel_pair():
"""Shards cut the trains rather than the voxels, but each device gets its
own launch and the pair would have to travel with it.
"""
- from torchsim.sequence._accelerators import _run_packed, distribute
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _run_packed, distribute
+ from blochsim.sequence._transition import DynamicPairs
prepared, events, pairs, echoes = _real_subspace_train(trains=4)
prepared = tuple(value.cuda() for value in prepared)
@@ -1871,10 +1871,10 @@ def _pooled_train(**properties):
so a pair beside a pool is an instantiation of its own -- and one nothing
reached until now.
"""
- from torchsim.sequence._accelerators import _pack_events
- from torchsim.sequence._builders import fse_description
- from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence._accelerators import _pack_events
+ from blochsim.sequence._builders import fse_description
+ from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
+ from blochsim.sequence._transition import DynamicPairs
definition = _pulse(samples=96)
flips = torch.deg2rad(torch.linspace(100.0, 170.0, ECHOES))
@@ -1960,8 +1960,8 @@ def test_a_pair_reaches_every_pool_the_kernels_carry(pool: str):
"""One instantiation of the kernel template per (rotation, pools), against
the oracle reading the same buffers.
"""
- from torchsim.sequence._accelerators import _run_packed
- from torchsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._lineshape import lineshape_table
prepared, events, pairs, echoes = _pooled_train(**POOLS[pool])
carried = dict(
@@ -1991,8 +1991,8 @@ def test_a_pool_moves_the_answer_a_pair_gives(pool: str):
"""The agreement above is only worth having if the pool is doing something,
so the same train without one must record a different signal.
"""
- from torchsim.sequence._accelerators import _run_packed
- from torchsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._lineshape import lineshape_table
prepared, events, pairs, echoes = _pooled_train(**POOLS[pool])
bare, _, _, _ = _pooled_train()
@@ -2022,9 +2022,9 @@ def test_a_pair_takes_the_first_order_kernel_on_the_card(state_count):
state count before, and the pair adds a per-event load and an atomic the
others do not make.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
- from torchsim.sequence._transition import DynamicPairs
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._transition import DynamicPairs
_, prepared, events, pairs = _train()
prepared = tuple(value.cuda() for value in prepared)
diff --git a/tests/sequence/test_engine_coverage.py b/tests/sequence/test_engine_coverage.py
index a1ec4a70..0b4713e4 100644
--- a/tests/sequence/test_engine_coverage.py
+++ b/tests/sequence/test_engine_coverage.py
@@ -9,12 +9,12 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
fse_description,
)
-from torchsim.sequence._description import RfUse
+from blochsim.sequence._description import RfUse
ECHOES = 8
STATES = 10
@@ -36,8 +36,8 @@ def _tissue():
def _reference(description, tissue, state_count: int = STATES):
"""The same train through the state machine written out in torch."""
- from torchsim.sequence._accelerators import _pack_events, geometry_of
- from torchsim.sequence._simulation import _prepare_tissue
+ from blochsim.sequence._accelerators import _pack_events, geometry_of
+ from blochsim.sequence._simulation import _prepare_tissue
from utils.packed_reference import simulate_packed
prepared, shape, device = _prepare_tissue(tissue, "cpu")
@@ -90,8 +90,8 @@ def _handmade():
This is the shape an autogenerated description arrives in, and the point
of carrying the action word is that it reaches the kernels anyway.
"""
- from torchsim.sequence import EventAction, ideal_rf_definition
- from torchsim.sequence._description import (
+ from blochsim.sequence import EventAction, ideal_rf_definition
+ from blochsim.sequence._description import (
AdcRole,
SequenceDescription,
SequenceEvent,
@@ -139,7 +139,7 @@ def test_the_crushers_are_what_the_description_says():
"""
from dataclasses import replace
- from torchsim.sequence import EventAction
+ from blochsim.sequence import EventAction
described = _handmade()
bare = replace(
@@ -165,7 +165,7 @@ def test_a_tissue_no_kernel_can_take_says_so():
"""With one route there is no fallback, so an unreachable device raises
rather than quietly running somewhere else.
"""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
original = _accelerators._backend_available
_accelerators._backend_available = lambda device: False
diff --git a/tests/sequence/test_exact_profile.py b/tests/sequence/test_exact_profile.py
index 1de92759..1bad85f0 100644
--- a/tests/sequence/test_exact_profile.py
+++ b/tests/sequence/test_exact_profile.py
@@ -15,16 +15,16 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
rf_definition,
)
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
exact_slice_profile,
fse_description,
)
-from torchsim.sequence._description import RfDefinition
-from torchsim.sequence._simulation import TissueProperties
+from blochsim.sequence._description import RfDefinition
+from blochsim.sequence._simulation import TissueProperties
ECHOES = 6
STATES = 10
@@ -120,7 +120,7 @@ def _two_shapes(second: RfDefinition):
"""An FSE whose excitation and refocusing are shaped differently."""
from dataclasses import replace
- from torchsim.sequence._description import RfUse
+ from blochsim.sequence._description import RfUse
description = _describe(definition=_sinc(4.0e3))
events = []
@@ -215,7 +215,7 @@ def test_a_shaped_pulse_is_integrated_without_being_asked() -> None:
"""A definition carrying a waveform worth integrating is tabulated on its
own, so the caller never names a mode -- only where to sample it.
"""
- from torchsim.sequence._accelerators import _wants_a_table
+ from blochsim.sequence._accelerators import _wants_a_table
shaped = _describe(definition=_sinc(4.0e3))
hard = _describe()
@@ -231,13 +231,13 @@ def test_a_builder_pulse_stays_the_hard_pulse_to_the_bit() -> None:
reading it at a flip between knots would not, so it must not happen by
itself.
"""
- from torchsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._accelerators import _run_packed
description = _describe()
seen = {}
signal = None
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
original = _accelerators._run_packed
@@ -272,7 +272,7 @@ def test_the_table_lays_its_copies_out_voxel_major() -> None:
"""The mean at the end folds the last axis, so the copies have to be there
and each voxel's have to be adjacent.
"""
- from torchsim.sequence._accelerators import _across_the_table
+ from blochsim.sequence._accelerators import _across_the_table
tissue = tuple(torch.tensor([800.0, 1400.0], dtype=torch.float32) for _ in range(7))
spread = _across_the_table(tissue, 3)
@@ -290,7 +290,7 @@ def test_the_positions_are_counted_by_the_memory_policy() -> None:
"""
from test_offload import _peak_over_baseline
- from torchsim.sequence import offload
+ from blochsim.sequence import offload
voxels, points = 40_000, 5
budget = 8 << 20
@@ -322,14 +322,14 @@ def test_a_table_takes_the_first_order_kernel_on_the_card(state_count) -> None:
The flip gradient comes off the table's own slope here rather than off a
differentiated rotation, which is the part a single width would not check.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import (
_pack_events,
_run_packed_vjp,
geometry_of,
)
- from torchsim.sequence._simulation import _prepare_tissue
- from torchsim.sequence._transition import transition_table
+ from blochsim.sequence._simulation import _prepare_tissue
+ from blochsim.sequence._transition import transition_table
description = _describe(definition=_sinc(4.0e3))
packed = _pack_events(
diff --git a/tests/sequence/test_exchange.py b/tests/sequence/test_exchange.py
index c0aec106..7e25ce65 100644
--- a/tests/sequence/test_exchange.py
+++ b/tests/sequence/test_exchange.py
@@ -16,18 +16,18 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
NO_GEOMETRY,
_pack_events,
_run_packed,
)
-from torchsim.sequence._parameters import TISSUE_NAMES
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._parameters import TISSUE_NAMES
+from blochsim.sequence._simulation import _prepare_tissue
from utils.packed_reference import simulate_packed
ECHOES = 8
@@ -112,7 +112,7 @@ def _free_induction(times_s, **properties):
``times_s`` are measured from the excitation; the events carry the
intervals between them, which is what the state machine steps through.
"""
- from torchsim.sequence._accelerators import _EXCITATION, _RECORD
+ from blochsim.sequence._accelerators import _EXCITATION, _RECORD
intervals = [
after - before for before, after in zip((0.0, *times_s), times_s, strict=False)
@@ -288,7 +288,7 @@ def _prepared(device="cpu", **properties):
def _live_events():
- from torchsim.sequence._accelerators import _EXCITATION, _RECORD
+ from blochsim.sequence._accelerators import _EXCITATION, _RECORD
times = READ_TIMES_S
intervals = [
@@ -309,7 +309,7 @@ def _live_events():
def _live_readout(prepared, seed=None):
"""The free-induction reading, or its directional derivative."""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
events = _live_events()
if seed is None:
@@ -457,7 +457,7 @@ def signal(value):
def _live_adjoint(prepared, seed):
"""The gradients a cotangent on the free-induction reading leaves."""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
return _run_packed_vjp(
prepared,
@@ -755,8 +755,8 @@ def _instantaneous_table(device="cpu"):
"""A pulse with no gradient across it: one rotation, every position."""
import numpy as np
- from torchsim.sequence._description import RfDefinition, RfShape
- from torchsim.sequence._transition import transition_table
+ from blochsim.sequence._description import RfDefinition, RfShape
+ from blochsim.sequence._transition import transition_table
flat = RfDefinition(
id=0,
@@ -774,7 +774,7 @@ def _instantaneous_table(device="cpu"):
def _routes(leaves, events, profile=None):
"""The kernels and the oracle, fed from one place."""
- from torchsim.sequence._accelerators import _NativeEpg
+ from blochsim.sequence._accelerators import _NativeEpg
fused = _NativeEpg.apply(
*leaves,
@@ -831,7 +831,7 @@ def _inverted_events():
sweep saturates a bound pool and turns free water over, so the two must
start at ``-(1 - f), -f`` rather than at ``-(1 - f), f``.
"""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_EXCITATION,
_INVERSION,
_RECORD,
@@ -856,7 +856,7 @@ def test_an_inversion_turns_both_pools_over():
"""
leaves = _oracle_leaves()
events = _inverted_events()
- from torchsim.sequence._accelerators import _NativeEpg
+ from blochsim.sequence._accelerators import _NativeEpg
fused = _NativeEpg.apply(
*leaves, *events, STATES, 1, 1, NO_GEOMETRY, None, None, None, None, True, None
@@ -879,7 +879,7 @@ def test_the_inversion_efficiency_carries_a_gradient_from_both_pools():
"""
leaves = _oracle_leaves()
events = _inverted_events()
- from torchsim.sequence._accelerators import _NativeEpg
+ from blochsim.sequence._accelerators import _NativeEpg
seed = torch.full((1, 1), 1.0 + 1.0j, dtype=torch.complex64)
fused = torch.autograd.grad(
@@ -1029,7 +1029,7 @@ def test_the_cuda_forward_mode_matches_the_cpu_kernel():
transverse pool's derivative honest there: a shift dropped from the
tangent alone still produces a plausible direction.
"""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
voxels = 6
spread = dict(
@@ -1067,7 +1067,7 @@ def run(device):
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_a_streamed_forward_mode_matches_the_whole_one():
"""Streaming cuts the voxel axis, which the second pool's seeds follow."""
- from torchsim.sequence._accelerators import _run_packed_jvp, offload
+ from blochsim.sequence._accelerators import _run_packed_jvp, offload
voxels = 3000
prepared = _prepared(
@@ -1140,7 +1140,7 @@ def run(device):
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is unavailable")
def test_a_streamed_volume_matches_the_whole_one():
"""Streaming cuts the voxel axis, which the second pool's buffers follow."""
- from torchsim.sequence._accelerators import offload
+ from blochsim.sequence._accelerators import offload
voxels = 3000
packed = _pack_events(
@@ -1174,7 +1174,7 @@ def test_the_cuda_adjoint_matches_the_cpu_kernel():
"""The two backends share no code, so agreement is what keeps the second
transverse pool's cotangent honest there.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
voxels = 6
spread = dict(
@@ -1224,7 +1224,7 @@ def test_a_streamed_adjoint_matches_the_whole_one():
The forward-over-reverse pass is the one that streams, and it is also what
a first-order adjoint on the card runs with no direction to follow.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp, offload
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp, offload
voxels = 3000
prepared = _prepared(
@@ -1278,8 +1278,8 @@ def test_an_exchanging_pool_takes_the_first_order_kernel_on_the_card(
forward-over-reverse pass on the same card: two arms of one wrong kernel
agree with each other, and the backends share no code.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
voxels = 64
tissue = TissueProperties(
@@ -1353,8 +1353,8 @@ def test_a_tabulated_rotation_beside_a_pool_takes_the_first_order_kernel() -> No
contributes to the flip and the phase through the table's slopes rather
than through the instant rotation's derivative. Held against the host.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
voxels = 64
tissue = TissueProperties(
diff --git a/tests/sequence/test_execution.py b/tests/sequence/test_execution.py
index ffcd67a2..a2d718a7 100644
--- a/tests/sequence/test_execution.py
+++ b/tests/sequence/test_execution.py
@@ -13,9 +13,9 @@
import torch
from test_offload import _seeds, _volume # noqa: E402
-import torchsim._execution as _policy
-from torchsim.sequence import execution
-from torchsim.sequence._accelerators import (
+import blochsim._execution as _policy
+from blochsim.sequence import execution
+from blochsim.sequence._accelerators import (
_FLOAT_INPUTS,
_bytes_per_voxel,
_choose,
@@ -24,8 +24,8 @@
_run_packed_vjp,
_run_packed_vjp_jvp,
)
-from torchsim.sequence._calibration import crossover
-from torchsim.sequence._parameters import OUTSIDE_THE_SUBSPACE
+from blochsim.sequence._calibration import crossover
+from blochsim.sequence._parameters import OUTSIDE_THE_SUBSPACE
STATES = 10
cuda_only = pytest.mark.skipif(
diff --git a/tests/sequence/test_feature_gates.py b/tests/sequence/test_feature_gates.py
index 2e167c8b..558550cf 100644
--- a/tests/sequence/test_feature_gates.py
+++ b/tests/sequence/test_feature_gates.py
@@ -14,12 +14,12 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
fse_description,
)
-from torchsim.sequence._parameters import (
+from blochsim.sequence._parameters import (
TISSUE_NAMES,
TISSUE_PARAMETERS,
Geometry,
@@ -28,12 +28,12 @@
)
# The kernel module imports Triton at its top, and Triton is a dependency of
-# the CUDA build of PyTorch rather than of TorchSim. Everything below the guard
+# the CUDA build of PyTorch rather than of BlochSim. Everything below the guard
# reaches it, so the two imports stay here rather than moving up with the rest.
pytest.importorskip("triton")
-from torchsim.sequence import _epg_triton # noqa: E402
-from torchsim.sequence._epg_triton import _feature_flags # noqa: E402
+from blochsim.sequence import _epg_triton # noqa: E402
+from blochsim.sequence._epg_triton import _feature_flags # noqa: E402
ECHOES = 8
STATES = 8
@@ -294,8 +294,8 @@ def test_an_off_resonance_asked_for_its_gradient_gets_a_real_one():
def _gate_inputs(extra):
- from torchsim.sequence._accelerators import _pack_events
- from torchsim.sequence._simulation import _prepare_tissue
+ from blochsim.sequence._accelerators import _pack_events
+ from blochsim.sequence._simulation import _prepare_tissue
generator = torch.Generator().manual_seed(7)
@@ -356,8 +356,8 @@ def test_the_forward_answer_does_not_depend_on_the_gate(extra, geometry):
"""The forward kernel, run once with the terms the tissue asks for and
once with every term, on the same input.
"""
- from torchsim.sequence._accelerators import _run_packed
- from torchsim.sequence._parameters import features_of
+ from blochsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._parameters import features_of
prepared, events, output_count = _gate_inputs(extra)
live = features_of(
@@ -393,8 +393,8 @@ def test_the_forward_direction_does_not_depend_on_the_gate(extra, geometry):
product beside it to be rounded on its own, which moves the last bits: the
value half of this same pair is exact.
"""
- from torchsim.sequence._accelerators import _run_packed_jvp
- from torchsim.sequence._parameters import features_of
+ from blochsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._parameters import features_of
prepared, events, output_count = _gate_inputs(extra)
seeds = _seeds(prepared, events, {"t1_ms", "t2_ms", *extra})
@@ -446,7 +446,7 @@ def test_a_diffusion_coefficient_asked_for_its_gradient_gets_a_real_one():
def _laid_out(tissue: TissueProperties, features) -> set[str]:
"""Which tissue properties got a value per voxel."""
- from torchsim.sequence._simulation import _prepare_tissue
+ from blochsim.sequence._simulation import _prepare_tissue
prepared, _, _ = _prepare_tissue(tissue, torch.device("cpu"), 1, features)
return {
diff --git a/tests/sequence/test_features.py b/tests/sequence/test_features.py
index 6e8fe901..ac21d9be 100644
--- a/tests/sequence/test_features.py
+++ b/tests/sequence/test_features.py
@@ -12,7 +12,7 @@
import pytest
import torch
-from torchsim.sequence._parameters import (
+from blochsim.sequence._parameters import (
TISSUE_NAMES,
TISSUE_PARAMETERS,
at_identity,
@@ -20,7 +20,7 @@
wants_bound_pool,
wants_exchange_pool,
)
-from torchsim.sequence._simulation import TissueProperties
+from blochsim.sequence._simulation import TissueProperties
def _parameter(name: str):
diff --git a/tests/sequence/test_first_order_adjoint.py b/tests/sequence/test_first_order_adjoint.py
index 02f1eb4b..0e476192 100644
--- a/tests/sequence/test_first_order_adjoint.py
+++ b/tests/sequence/test_first_order_adjoint.py
@@ -12,17 +12,17 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
_accelerators,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
_pack_events,
_run_packed_vjp,
_train_count,
)
-from torchsim.sequence._parameters import FLOAT_NAMES, Geometry
-from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
+from blochsim.sequence._parameters import FLOAT_NAMES, Geometry
+from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
ECHOES = 12
ECHO_SPACING_S = 5e-3
diff --git a/tests/sequence/test_fixed_point.py b/tests/sequence/test_fixed_point.py
index aa0a777d..a4d7e439 100644
--- a/tests/sequence/test_fixed_point.py
+++ b/tests/sequence/test_fixed_point.py
@@ -19,18 +19,18 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
SequenceDescription,
)
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
_builders,
ideal_rf_definition,
operator,
)
-from torchsim.sequence._fixed_point import carries_one_order
-from torchsim.sequence._operators import compose
+from blochsim.sequence._fixed_point import carries_one_order
+from blochsim.sequence._operators import compose
TR_S, TE_S, FLIP_DEG = 5e-3, 2.5e-3, 30.0
diff --git a/tests/sequence/test_flow_kernels.py b/tests/sequence/test_flow_kernels.py
index ea163102..68567f8a 100644
--- a/tests/sequence/test_flow_kernels.py
+++ b/tests/sequence/test_flow_kernels.py
@@ -13,11 +13,11 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
Geometry,
_pack_events,
_run_packed,
@@ -28,8 +28,8 @@
geometry_of,
real_subspace_axis,
)
-from torchsim.sequence._parameters import FLOAT_NAMES, TISSUE_NAMES
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._parameters import FLOAT_NAMES, TISSUE_NAMES
+from blochsim.sequence._simulation import _prepare_tissue
from utils.epg import flow_op
from utils.packed_reference import simulate_packed
diff --git a/tests/sequence/test_gradient_operators.py b/tests/sequence/test_gradient_operators.py
index 204303c8..54a436fa 100644
--- a/tests/sequence/test_gradient_operators.py
+++ b/tests/sequence/test_gradient_operators.py
@@ -13,7 +13,7 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
AdcRole,
Delay,
Dephase,
diff --git a/tests/sequence/test_host_feature_mask.py b/tests/sequence/test_host_feature_mask.py
index 2935b239..ddd85c37 100644
--- a/tests/sequence/test_host_feature_mask.py
+++ b/tests/sequence/test_host_feature_mask.py
@@ -3,7 +3,7 @@
The Triton backend takes a flag per term because each one compiles a kernel of
its own; the host kernels take one integer and branch on it at run time. These
pin the two ends together -- that the mask says what
-:func:`torchsim.sequence._parameters.feature_flags` says, and that a host
+:func:`blochsim.sequence._parameters.feature_flags` says, and that a host
kernel told to drop a term gives the answer it gives when told to keep it.
"""
@@ -12,13 +12,13 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
_accelerators,
fse_description,
)
-from torchsim.sequence._parameters import (
+from blochsim.sequence._parameters import (
FEATURE_BITS,
Geometry,
feature_flags,
@@ -237,7 +237,7 @@ def test_an_inversion_the_tissue_never_declared_is_left_out(monkeypatch) -> None
"""The one term a refocused train cannot exercise: it drives no inversion,
so the gate is held against a sequence that does.
"""
- from torchsim.sequence import EpgEngine, mprage_description
+ from blochsim.sequence import EpgEngine, mprage_description
def signal(mask, efficiency):
monkeypatch.setattr(
diff --git a/tests/sequence/test_lineshape.py b/tests/sequence/test_lineshape.py
index 275ccc4a..79c503fc 100644
--- a/tests/sequence/test_lineshape.py
+++ b/tests/sequence/test_lineshape.py
@@ -16,7 +16,7 @@
import pytest
import torch
-from torchsim.sequence._lineshape import BOUND_T2_S, lineshape_table
+from blochsim.sequence._lineshape import BOUND_T2_S, lineshape_table
def _integral(offset_hz, bound_t2_s: float = BOUND_T2_S, quadrature: int = 20000):
diff --git a/tests/sequence/test_many_pools.py b/tests/sequence/test_many_pools.py
index a1920865..eae49806 100644
--- a/tests/sequence/test_many_pools.py
+++ b/tests/sequence/test_many_pools.py
@@ -3,7 +3,7 @@
BART's Bloch-McConnell model carries the free water, up to four pools that
exchange with it and not with each other, and a semisolid pool. Past one
exchanging pool the kernels read a tissue as the operators its intervals apply,
-tabulated per voxel and per interval length by ``torchsim.sequence._pools``, so
+tabulated per voxel and per interval length by ``blochsim.sequence._pools``, so
what is held here is the tables and the kernels that walk them: against the
state machine written out in torch, against the package's own N-pool
operators, against the closed-form kernels at one pool, and against the
@@ -19,8 +19,8 @@
import torch
from torch.autograd import forward_ad
-from torchsim.sequence import EpgEngine, TissueProperties, _pools, fse_description
-from torchsim.sequence._accelerators import (
+from blochsim.sequence import EpgEngine, TissueProperties, _pools, fse_description
+from blochsim.sequence._accelerators import (
_EXCITATION,
_INVERSION,
_POST_SHIFT,
@@ -31,9 +31,9 @@
NO_GEOMETRY,
_NativeEpg,
)
-from torchsim.sequence._lineshape import lineshape_table
-from torchsim.sequence._parameters import POOL_NAMES, Geometry
-from torchsim.sequence._transition import DynamicPairs
+from blochsim.sequence._lineshape import lineshape_table
+from blochsim.sequence._parameters import POOL_NAMES, Geometry
+from blochsim.sequence._transition import DynamicPairs
from utils.packed_reference import simulate_packed_pools
STATES = 6
@@ -93,8 +93,8 @@ def _instantaneous_table():
"""
import numpy as np
- from torchsim.sequence._description import RfDefinition, RfShape
- from torchsim.sequence._transition import transition_table
+ from blochsim.sequence._description import RfDefinition, RfShape
+ from blochsim.sequence._transition import transition_table
flat = RfDefinition(
id=0,
diff --git a/tests/sequence/test_mrd_reader.py b/tests/sequence/test_mrd_reader.py
index b06171ed..8349a1fc 100644
--- a/tests/sequence/test_mrd_reader.py
+++ b/tests/sequence/test_mrd_reader.py
@@ -19,10 +19,10 @@
import pytest
import torch
-from torchsim import Excitation, SPGRReadout
-from torchsim.model import Simulator
-from torchsim.sequence import EventType, RfUse, read_mrd_description
-from torchsim.simulators import SPGRSimulator
+from blochsim import Excitation, SPGRReadout
+from blochsim.model import Simulator
+from blochsim.sequence import EventType, RfUse, read_mrd_description
+from blochsim.simulators import SPGRSimulator
pp = pytest.importorskip("pypulseq", reason="building the sequence needs pypulseq")
diff --git a/tests/sequence/test_offload.py b/tests/sequence/test_offload.py
index 1c98c5d2..d40c0bee 100644
--- a/tests/sequence/test_offload.py
+++ b/tests/sequence/test_offload.py
@@ -15,10 +15,10 @@
import pytest
import torch
-import torchsim._execution as _policy
-import torchsim.sequence._accelerators as accelerators
-from torchsim.sequence import EpgEngine, offload
-from torchsim.sequence._accelerators import (
+import blochsim._execution as _policy
+import blochsim.sequence._accelerators as accelerators
+from blochsim.sequence import EpgEngine, offload
+from blochsim.sequence._accelerators import (
_FLOAT_INPUTS,
_bytes_per_voxel,
_chunk_voxels,
@@ -27,9 +27,9 @@
_pack_events,
_run_packed,
)
-from torchsim.sequence._builders import fse_description
-from torchsim.sequence._parameters import NO_GEOMETRY, OUTSIDE_THE_SUBSPACE, Geometry
-from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
+from blochsim.sequence._builders import fse_description
+from blochsim.sequence._parameters import NO_GEOMETRY, OUTSIDE_THE_SUBSPACE, Geometry
+from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
pytestmark = pytest.mark.skipif(
not torch.cuda.is_available(), reason="CUDA is unavailable"
@@ -240,7 +240,7 @@ def _spgr_volume(voxels, trains=1, pulses=12):
rounding noise around zero and comparing it says nothing. SPGR records at an
echo time, so every gradient is live and worth checking.
"""
- from torchsim.sequence._builders import spgr_description
+ from blochsim.sequence._builders import spgr_description
generator = torch.Generator().manual_seed(0)
packed = [
@@ -303,7 +303,7 @@ def _seeds(events, prepared, index):
@pytest.mark.parametrize("real_axis", [1, -1])
@pytest.mark.parametrize("trains", [1, 3])
def test_a_streamed_forward_mode_matches_the_cpu_run(budget, real_axis, trains):
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
events, prepared, outputs = _volume(4000, trains=trains)
tissue_seed, event_seed = _seeds(events, prepared, 1)
@@ -319,7 +319,7 @@ def test_a_streamed_forward_mode_matches_the_cpu_run(budget, real_axis, trains):
def test_an_event_seed_reaches_every_chunk():
"""Event seeds are shared by all voxels, so they are replicated not sliced."""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
events, prepared, outputs = _volume(4000)
tissue_seed = tuple(torch.zeros_like(value) for value in prepared)
@@ -340,7 +340,7 @@ def test_an_event_seed_reaches_every_chunk():
def _adjoint(
events, prepared, outputs, voxels, trains, real_axis, budget, geometry=NO_GEOMETRY
):
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
tissue_seed, event_seed = _seeds(events, prepared, 1)
generator = torch.Generator().manual_seed(7)
@@ -522,7 +522,7 @@ def test_a_streamed_shimmed_volume_matches_the_whole_one(budget, trains):
@pytest.mark.parametrize("budget", [1 << 20, 512 << 20])
def test_a_streamed_shimmed_forward_mode_matches_the_whole_one(budget):
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
events, prepared, outputs = _shim_volume(4000)
# Seed the transmit magnitude, so the tangent travels the shim rows too.
@@ -605,7 +605,7 @@ def _first_order_adjoint(
geometry=NO_GEOMETRY,
):
"""The route ``torch.autograd`` takes for a plain ``.backward()``."""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
generator = torch.Generator().manual_seed(7)
shape = (trains, voxels, outputs) if trains > 1 else (voxels, outputs)
@@ -678,7 +678,7 @@ def test_a_host_resident_first_order_adjoint_follows_the_execution_target():
"""The forward moves to the card under this policy, so the backward has to
move with it rather than stay behind.
"""
- from torchsim.sequence import execution
+ from blochsim.sequence import execution
voxels = 20_000
events, prepared, outputs = _volume(voxels)
@@ -727,7 +727,7 @@ def test_a_backward_through_the_public_api_streams():
``torch.autograd`` reaches the adjoint by a route of its own, and it is
the one an ordinary user's ``.backward()`` takes.
"""
- from torchsim.sequence import TissueProperties
+ from blochsim.sequence import TissueProperties
voxels = 20_000
reached = []
@@ -785,7 +785,7 @@ def test_a_streamed_first_order_adjoint_takes_its_own_kernel(trains):
"""A chunk of a first-order adjoint is a first-order adjoint, so streaming
must not cost the kernel written for it.
"""
- from torchsim.sequence import _accelerators as accelerators
+ from blochsim.sequence import _accelerators as accelerators
voxels = 2000
events, prepared, outputs = _volume(voxels, trains=trains)
@@ -813,7 +813,7 @@ def test_streaming_a_first_order_adjoint_makes_wider_chunks():
"""Half the trajectory is the second saving: for one budget the chunks come
out wider than the pass carrying a forward direction would allow.
"""
- from torchsim.sequence._accelerators import _bytes_per_voxel
+ from blochsim.sequence._accelerators import _bytes_per_voxel
shape = (4, 40, 8, 16, None, 1)
around = _bytes_per_voxel("adjoint", *shape)
diff --git a/tests/sequence/test_operators.py b/tests/sequence/test_operators.py
index 4e219d43..18bde3ad 100644
--- a/tests/sequence/test_operators.py
+++ b/tests/sequence/test_operators.py
@@ -13,16 +13,16 @@
import pytest
import torch
-from torchsim.sequence._accelerators import _pack_events
-from torchsim.sequence._builders import (
+from blochsim.sequence._accelerators import _pack_events
+from blochsim.sequence._builders import (
fse_description,
mpnrage_description,
mprage_description,
mrf_description,
spgr_description,
)
-from torchsim.sequence._description import AdcRole, EventAction, EventType
-from torchsim.sequence._operators import (
+from blochsim.sequence._description import AdcRole, EventAction, EventType
+from blochsim.sequence._operators import (
Delay,
Excitation,
Readout,
diff --git a/tests/sequence/test_parameters.py b/tests/sequence/test_parameters.py
index 3d648b57..442d9969 100644
--- a/tests/sequence/test_parameters.py
+++ b/tests/sequence/test_parameters.py
@@ -11,14 +11,14 @@
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
_accelerators,
fse_description,
)
-from torchsim.sequence._accelerators import _pack_events
-from torchsim.sequence._parameters import (
+from blochsim.sequence._accelerators import _pack_events
+from blochsim.sequence._parameters import (
EVENT_PARAMETERS,
FLOAT_INPUTS,
PACKED_COUNT,
@@ -32,7 +32,7 @@
tissue_gradient_height,
tissue_gradient_rows,
)
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._simulation import _prepare_tissue
def test_the_table_names_the_tissue_dataclass_fields():
diff --git a/tests/sequence/test_pulseq_reader.py b/tests/sequence/test_pulseq_reader.py
index a26fb493..1ec75838 100644
--- a/tests/sequence/test_pulseq_reader.py
+++ b/tests/sequence/test_pulseq_reader.py
@@ -14,10 +14,10 @@
import pytest
import torch
-from torchsim import Excitation, SequenceDescription, SPGRReadout
-from torchsim.model import Simulator
-from torchsim.sequence import AdcRole, EventType, RfUse
-from torchsim.simulators import SPGRSimulator
+from blochsim import Excitation, SequenceDescription, SPGRReadout
+from blochsim.model import Simulator
+from blochsim.sequence import AdcRole, EventType, RfUse
+from blochsim.simulators import SPGRSimulator
pp = pytest.importorskip("pypulseq", reason="reading a .seq file needs pypulseq")
diff --git a/tests/sequence/test_real_subspace.py b/tests/sequence/test_real_subspace.py
index c9eaa622..2dd67699 100644
--- a/tests/sequence/test_real_subspace.py
+++ b/tests/sequence/test_real_subspace.py
@@ -10,19 +10,19 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
fse_description,
)
-from torchsim.sequence import _accelerators as accelerators
-from torchsim.sequence._accelerators import (
+from blochsim.sequence import _accelerators as accelerators
+from blochsim.sequence._accelerators import (
_one_axis,
_pack_events,
_run_packed,
real_subspace_axis,
)
-from torchsim.sequence._parameters import FLOAT_NAMES, OUTSIDE_THE_SUBSPACE
-from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
+from blochsim.sequence._parameters import FLOAT_NAMES, OUTSIDE_THE_SUBSPACE
+from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
# The gradients a real adjoint does produce: every differentiable input the
# subspace contains. Derived rather than listed, so a new parameter cannot
@@ -120,7 +120,7 @@ def _buffers(packed):
@pytest.mark.parametrize("phase", [0.0, torch.pi / 4, torch.pi / 2])
def test_real_kernel_reproduces_the_complex_one(phase):
"""The real kernel is an optimization, not an approximation."""
- from torchsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._accelerators import _run_packed
description = fse_description(
_flip(),
@@ -147,7 +147,7 @@ def test_real_kernel_reproduces_the_complex_one(phase):
def test_real_jvp_reproduces_the_complex_one():
"""Forward mode along T2 stays inside the subspace, so the kernels agree."""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
description = fse_description(
_flip(),
@@ -192,7 +192,7 @@ def test_real_adjoint_reproduces_the_complex_one(phase, always_worth_detecting):
-- so the verdict is reached through ``wanted`` rather than handed over,
which is the route the public adjoint takes.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
description = fse_description(
_flip(),
@@ -243,7 +243,7 @@ def test_the_real_adjoint_reaches_the_inversion_gradient():
the representation describes a sequence the builders produce, and a train
assembled from scratch is not one.
"""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_INVERSION,
_run_packed_vjp,
)
@@ -310,7 +310,7 @@ def ahead(values, first):
def test_real_second_order_kernel_reproduces_the_complex_one():
"""Forward-over-reverse agrees on every gradient the subspace contains."""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
description = fse_description(
_flip(),
@@ -385,7 +385,7 @@ def _packed(trains):
# the block's first train. These counts straddle that boundary.
@pytest.mark.parametrize("trains", [1, 7, 8, 9, 17])
def test_partial_train_blocks_match_the_complex_kernel(trains):
- from torchsim.sequence._accelerators import _run_packed_jvp, _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp, _run_packed_vjp_jvp
packed = _packed(trains)
events = _buffers(packed)
@@ -456,7 +456,7 @@ def _tissue_events(trains, echoes=20, atoms=64, b0_hz=0.0):
def _trains_worth(share, echoes=20, atoms=64):
"""Enough trains to carry ``share`` of the work the test is worth at."""
- from torchsim.sequence._calibration import detection
+ from blochsim.sequence._calibration import detection
events, _tissue, _count = _tissue_events(1, echoes=echoes, atoms=atoms)
per_train = atoms * int(events[1].numel())
@@ -466,7 +466,7 @@ def _trains_worth(share, echoes=20, atoms=64):
def test_the_fast_path_is_chosen_without_being_asked():
"""A caller should not have to know the subspace rule to benefit from it."""
- from torchsim.sequence._accelerators import _auto_real_axis, _run_packed
+ from blochsim.sequence._accelerators import _auto_real_axis, _run_packed
events, tissue, count = _tissue_events(_trains_worth(8))
assert _auto_real_axis("forward", events, tissue) == 1
@@ -477,7 +477,7 @@ def test_the_fast_path_is_chosen_without_being_asked():
def test_off_resonance_is_not_chosen():
- from torchsim.sequence._accelerators import _auto_real_axis
+ from blochsim.sequence._accelerators import _auto_real_axis
events, tissue, _ = _tissue_events(_trains_worth(8), b0_hz=15.0)
assert _auto_real_axis("forward", events, tissue) is None
@@ -492,8 +492,8 @@ def test_a_tiny_problem_skips_the_test_that_would_cost_more_than_it_saves(monkey
machine could land either side of it. The rule is the same whatever the
number: work under the threshold is not worth the test.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _auto_real_axis
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _auto_real_axis
events, tissue, _ = _tissue_events(1, atoms=2)
work = 2 * int(events[1].numel())
@@ -511,7 +511,7 @@ def test_a_seed_that_leaves_the_subspace_is_not_chosen(direction):
verdict has to look at the seed as well; otherwise the fast path would
silently return zero for exactly the direction that was asked for.
"""
- from torchsim.sequence._accelerators import _auto_real_axis
+ from blochsim.sequence._accelerators import _auto_real_axis
events, tissue, _ = _tissue_events(_trains_worth(8))
seed = tuple(
@@ -525,7 +525,7 @@ def test_a_seed_that_leaves_the_subspace_is_not_chosen(direction):
def test_an_rf_phase_seed_is_not_chosen():
- from torchsim.sequence._accelerators import _auto_real_axis
+ from blochsim.sequence._accelerators import _auto_real_axis
events, tissue, _ = _tissue_events(_trains_worth(8))
zeros = tuple(torch.zeros_like(value) for value in tissue)
@@ -584,7 +584,7 @@ def _only(*names):
)
def test_the_adjoint_verdict_follows_what_the_caller_will_read(wanted, expected):
"""A real adjoint is three gradients short, so it depends who is asking."""
- from torchsim.sequence._accelerators import _auto_real_axis_adjoint
+ from blochsim.sequence._accelerators import _auto_real_axis_adjoint
events, tissue, tangents, _cotangent, _count = _adjoint_case(_trains_worth(8))
@@ -596,11 +596,11 @@ def test_the_adjoint_verdict_follows_what_the_caller_will_read(wanted, expected)
)
def test_wanting_a_gradient_outside_the_subspace_keeps_the_complex_kernel(position):
"""Each is genuinely non-zero; returning zero would be wrong."""
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_auto_real_axis_adjoint,
_run_packed_vjp_jvp,
)
- from torchsim.sequence._parameters import Geometry
+ from blochsim.sequence._parameters import Geometry
events, tissue, tangents, cotangent, count = _adjoint_case(_trains_worth(8))
wanted = _only(_FLIP, position)
@@ -626,7 +626,7 @@ def test_wanting_a_gradient_outside_the_subspace_keeps_the_complex_kernel(positi
def test_an_adjoint_that_stays_in_the_subspace_reaches_the_real_kernel():
"""Bitwise, because only the same kernel gives the same bits."""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
events, tissue, tangents, cotangent, count = _adjoint_case(_trains_worth(8))
shared = (tissue, events, tangents, cotangent, 10, count, 1)
@@ -639,10 +639,10 @@ def test_an_adjoint_that_stays_in_the_subspace_reaches_the_real_kernel():
def _forward_over_reverse(atoms):
"""A directional derivative, differentiated: what backward fuses."""
- from torchsim.sequence import (
+ from blochsim.sequence import (
EpgEngine,
)
- from torchsim.sequence._simulation import TissueProperties
+ from blochsim.sequence._simulation import TissueProperties
generator = torch.Generator().manual_seed(0)
flip = torch.deg2rad(80.0 + 80.0 * torch.rand(20, generator=generator))
@@ -668,7 +668,7 @@ def test_autograd_asks_for_what_the_graph_needs(monkeypatch, always_worth_detect
Differentiating T2 stays inside the subspace, so the fast adjoint is
available -- and the answer must not depend on it being taken.
"""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
chosen = []
original = _accelerators._auto_real_axis_adjoint
@@ -706,8 +706,8 @@ def test_the_cuda_real_adjoint_agrees_with_the_second_order_kernel(
Widths are swept because a reverse kernel has miscompiled silently at one
state count before, and a single width would not have caught it.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
description = fse_description(
_flip(),
@@ -786,8 +786,8 @@ def test_the_device_adjoint_stops_short_of_the_forward_over_reverse_pass(
"""The first-order kernel is the point of this route, so the test above has
to be reaching it rather than agreeing with itself.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
description = fse_description(
_flip(),
@@ -839,7 +839,7 @@ def test_the_device_adjoint_stops_short_of_the_forward_over_reverse_pass(
def _spoiled_axis(phases_rad):
"""The verdict and the signal for an unbalanced train of the given phases."""
- from torchsim.sequence import (
+ from blochsim.sequence import (
mrf_description,
)
@@ -887,7 +887,7 @@ def test_rf_spoiling_breaks_the_subspace():
def test_the_real_kernel_reproduces_the_complex_one_without_refocusing():
"""The widened verdict is an optimization, not an approximation."""
- from torchsim.sequence._accelerators import _run_packed
+ from blochsim.sequence._accelerators import _run_packed
axis, _, packed, prepared = _spoiled_axis(0.0)
assert axis == 1
@@ -905,7 +905,7 @@ def test_a_declared_tissue_settles_the_question_without_a_buffer():
is the whole answer: the verdict is the same one the reduction gives, and
it is reached without reading the buffer that reduction would read.
"""
- from torchsim.sequence._parameters import features_of
+ from blochsim.sequence._parameters import features_of
description = fse_description(
_flip(), echo_spacing_s=ECHO_SPACING_S, phases_rad=0.0, excitation_phase_rad=0.0
@@ -942,7 +942,7 @@ def test_a_map_of_zeros_keeps_the_axis_it_declares_it_has_left():
whatever the map holds, and a map of zeros is one a run should not lose the
fast path to.
"""
- from torchsim.sequence._parameters import features_of
+ from blochsim.sequence._parameters import features_of
description = fse_description(
_flip(), echo_spacing_s=ECHO_SPACING_S, phases_rad=0.0, excitation_phase_rad=0.0
@@ -1000,7 +1000,7 @@ def test_a_rewritten_phase_buffer_is_read_again():
# block writes and every per-event gradient it sums has to ignore them.
@pytest.mark.parametrize("atoms", [7, 8, 9, 17])
def test_partial_atom_blocks_of_the_adjoint_match_the_complex_kernel(atoms):
- from torchsim.sequence._accelerators import (
+ from blochsim.sequence._accelerators import (
_auto_real_axis_adjoint,
_run_packed_vjp,
)
diff --git a/tests/sequence/test_rf_definition.py b/tests/sequence/test_rf_definition.py
index 2c678faa..e5c4550f 100644
--- a/tests/sequence/test_rf_definition.py
+++ b/tests/sequence/test_rf_definition.py
@@ -17,16 +17,16 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
rf_definition,
)
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
RfMode,
exact_slice_profile,
fse_description,
)
-from torchsim.sequence._simulation import TissueProperties
+from blochsim.sequence._simulation import TissueProperties
ECHOES = 6
STATES = 10
diff --git a/tests/sequence/test_settling.py b/tests/sequence/test_settling.py
index 768aa234..b4d9bd8f 100644
--- a/tests/sequence/test_settling.py
+++ b/tests/sequence/test_settling.py
@@ -16,18 +16,18 @@
import pytest
import torch
-from torchsim import (
+from blochsim import (
SequenceDescription,
)
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
_builders,
ideal_rf_definition,
operator,
)
-from torchsim.sequence._operators import Excitation, compose
-from torchsim.sequence._settling import settled
+from blochsim.sequence._operators import Excitation, compose
+from blochsim.sequence._settling import settled
STATES = 32
diff --git a/tests/sequence/test_simulation.py b/tests/sequence/test_simulation.py
index 12748177..7da8d169 100644
--- a/tests/sequence/test_simulation.py
+++ b/tests/sequence/test_simulation.py
@@ -7,7 +7,7 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
fse_description,
@@ -91,7 +91,7 @@ def test_subspace_basis_uses_time_as_leading_dimension() -> None:
@pytest.mark.skipif(
- importlib.util.find_spec("torchsim._epg_cpu") is None,
+ importlib.util.find_spec("blochsim._epg_cpu") is None,
reason="CPU extension has not been built",
)
@pytest.mark.parametrize("policy_name", ["fse", "ssfp-fid", "spgr"])
@@ -233,8 +233,8 @@ def _oracle(description, tissue, device: str, state_count: int = 10):
route -- description, preparation, packing, kernel -- against an
implementation that shares none of it.
"""
- from torchsim.sequence._accelerators import _pack_events, geometry_of
- from torchsim.sequence._simulation import _prepare_tissue
+ from blochsim.sequence._accelerators import _pack_events, geometry_of
+ from blochsim.sequence._simulation import _prepare_tissue
from utils.packed_reference import simulate_packed
prepared, shape, resolved = _prepare_tissue(tissue, device)
diff --git a/tests/sequence/test_slice_table.py b/tests/sequence/test_slice_table.py
index 55bcffb4..668d7539 100644
--- a/tests/sequence/test_slice_table.py
+++ b/tests/sequence/test_slice_table.py
@@ -18,20 +18,20 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
_pack_events,
_run_packed,
offload,
real_subspace_axis,
)
-from torchsim.sequence._description import RfDefinition, RfShape
-from torchsim.sequence._parameters import FLOAT_NAMES, TISSUE_COUNT
-from torchsim.sequence._simulation import _prepare_tissue
-from torchsim.sequence._transition import transition_table
+from blochsim.sequence._description import RfDefinition, RfShape
+from blochsim.sequence._parameters import FLOAT_NAMES, TISSUE_COUNT
+from blochsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._transition import transition_table
from utils.packed_reference import simulate_packed
ECHOES = 4
@@ -272,7 +272,7 @@ def test_a_chunk_holds_whole_voxels_when_a_table_is_read() -> None:
So a chunk boundary inside a voxel's slice copies shifts every row after
it, which returns a plausible signal for an entirely different slice.
"""
- from torchsim.sequence._accelerators import _Offload, _offload_plan
+ from blochsim.sequence._accelerators import _Offload, _offload_plan
_, events, outputs = _packed(96)
plan = _Offload(devices=(torch.device("cuda"),), budget_bytes=1 << 12, lanes=1)
@@ -292,7 +292,7 @@ def test_a_chunk_holds_whole_voxels_when_a_table_is_read() -> None:
@pytest.mark.skipif(not torch.cuda.is_available(), reason="needs CUDA")
def test_a_streamed_adjoint_reads_the_table_a_whole_one_does() -> None:
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
locations, voxels = 3, 24
table = transition_table(
@@ -328,7 +328,7 @@ def test_a_streamed_adjoint_reads_the_table_a_whole_one_does() -> None:
def test_forward_mode_follows_the_table() -> None:
"""The tangent rides the slope the table already stores."""
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
locations, voxels = 3, 3
table = transition_table(
@@ -393,7 +393,7 @@ def _compare(expected, actual, names, tolerance=2e-4) -> None:
def test_the_adjoint_follows_the_table() -> None:
"""Every gradient the table stands in front of, against the oracle."""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
locations, voxels = 3, 3
table = transition_table(
@@ -439,7 +439,7 @@ def test_the_adjoint_follows_the_table() -> None:
def test_an_unprofiled_adjoint_is_untouched() -> None:
"""The table path is another kernel, not a branch inside this one."""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
tissue, events, outputs = _packed(6)
generator = torch.Generator().manual_seed(5)
@@ -456,7 +456,7 @@ def test_an_unprofiled_adjoint_is_untouched() -> None:
def test_the_second_order_pass_follows_the_table() -> None:
"""Forward-over-reverse, which needs the Hermite's own curvature."""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
locations, voxels = 3, 2
table = transition_table(
@@ -512,7 +512,7 @@ def test_the_second_order_pass_follows_the_table() -> None:
def test_an_unprofiled_second_order_pass_is_untouched() -> None:
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
tissue, events, outputs = _packed(6)
generator = torch.Generator().manual_seed(7)
@@ -536,7 +536,7 @@ def test_an_unprofiled_second_order_pass_is_untouched() -> None:
@pytest.mark.skipif(not torch.cuda.is_available(), reason="needs CUDA")
def test_the_card_follows_the_table_through_both_adjoint_passes() -> None:
"""One kernel serves both: the card reaches a first adjoint through it."""
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
locations, voxels = 3, 2
table = transition_table(
@@ -582,7 +582,7 @@ def test_the_card_follows_the_table_through_both_adjoint_passes() -> None:
@pytest.mark.skipif(not torch.cuda.is_available(), reason="needs CUDA")
def test_the_card_follows_the_table_in_forward_mode() -> None:
- from torchsim.sequence._accelerators import _run_packed_jvp
+ from blochsim.sequence._accelerators import _run_packed_jvp
locations, voxels = 3, 3
table = transition_table(
diff --git a/tests/sequence/test_ssfp_readouts.py b/tests/sequence/test_ssfp_readouts.py
index 9e170067..c303a5cf 100644
--- a/tests/sequence/test_ssfp_readouts.py
+++ b/tests/sequence/test_ssfp_readouts.py
@@ -5,7 +5,7 @@
would refocus, which is the same state read after the gradient rather than
before it. :func:`SSFPFidReadout` takes the first and :func:`SSFPEchoReadout`
the second, and this pins both against an extended phase graph written out here
-from the operators in the literature rather than from anything TorchSim does.
+from the operators in the literature rather than from anything BlochSim does.
The reference is held honest by being asked for both: it has to reproduce the
FID sample, which is what the rest of the suite and the published comparisons
@@ -17,17 +17,17 @@
import numpy as np
import pytest
-from torchsim import (
+from blochsim import (
SequenceDescription,
SSFPEchoReadout,
SSFPFidReadout,
)
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
ideal_rf_definition,
)
-from torchsim.sequence._operators import Excitation, compose
+from blochsim.sequence._operators import Excitation, compose
# More orders than the train can reach, so neither side truncates and the
# comparison is of the physics rather than of two truncation policies.
@@ -128,7 +128,7 @@ def test_the_first_repetition_has_no_echo_to_read():
def test_it_is_reachable_by_name():
- from torchsim.sequence import (
+ from blochsim.sequence import (
operator,
operator_names,
)
diff --git a/tests/sequence/test_static_spread.py b/tests/sequence/test_static_spread.py
index c9c22b97..3ecc2ed5 100644
--- a/tests/sequence/test_static_spread.py
+++ b/tests/sequence/test_static_spread.py
@@ -6,7 +6,7 @@
gone unrefocused -- growing either side of a spin echo and never recovering
after a gradient echo.
-These hold that against things outside TorchSim: the characteristic function of
+These hold that against things outside BlochSim: the characteristic function of
the Cauchy distribution, summed here over a quadrature rather than taken on
trust, and the closed form ``T2*`` a gradient echo decays at. The ensemble is
also played through the states themselves, so the claim that a spread and a
@@ -19,18 +19,18 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
EventAction,
TissueProperties,
_builders,
)
-from torchsim.sequence._description import (
+from blochsim.sequence._description import (
AdcRole,
SequenceDescription,
ideal_rf_definition,
)
-from torchsim.sequence._operators import (
+from blochsim.sequence._operators import (
Dephase,
Excitation,
Readout,
@@ -152,7 +152,7 @@ def _signal(description, *, repetitions=1, **tissue) -> np.ndarray:
def _unrefocused_s(description) -> np.ndarray:
"""How long each sample has gone unrefocused, from the run's own labels."""
- from torchsim.sequence._accelerators import _pack_events
+ from blochsim.sequence._accelerators import _pack_events
packed = _pack_events(
description,
@@ -340,8 +340,8 @@ def test_a_settled_state_carries_the_spread(route):
def test_a_model_declares_it_like_any_other_property():
- from torchsim.model._state_machine import SpinPhysics
- from torchsim.sequence._parameters import features_of
+ from blochsim.model._state_machine import SpinPhysics
+ from blochsim.sequence._parameters import features_of
physics = SpinPhysics(
properties={"T1": "t1_ms", "T2": "t2_ms", "T2p": "t2_prime_ms"}
diff --git a/tests/sequence/test_steady_state.py b/tests/sequence/test_steady_state.py
index 6e745a05..ab497df5 100644
--- a/tests/sequence/test_steady_state.py
+++ b/tests/sequence/test_steady_state.py
@@ -7,7 +7,7 @@
dictionary the dictionary the scanner acquires.
The settled answer is held against a closed form written out here, so what is
-being checked is the physics rather than TorchSim's agreement with itself.
+being checked is the physics rather than BlochSim's agreement with itself.
"""
from __future__ import annotations
@@ -18,7 +18,7 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
_builders,
@@ -131,7 +131,7 @@ def test_settling_is_refused_where_it_makes_no_sense():
def test_a_sequence_may_declare_how_far_it_has_to_settle():
"""A simulator knows its own physics; a caller may still overrule it."""
- from torchsim.simulators import MRFSimulator
+ from blochsim.simulators import MRFSimulator
flip = torch.linspace(5.0, 60.0, 20)
assert MRFSimulator(flip=flip, TR=10.0, states=16).repetitions == 1
diff --git a/tests/sequence/test_subspace_streams.py b/tests/sequence/test_subspace_streams.py
index c6597ef9..2046c9e8 100644
--- a/tests/sequence/test_subspace_streams.py
+++ b/tests/sequence/test_subspace_streams.py
@@ -17,8 +17,8 @@
import pytest
import torch
-from torchsim.sequence._accelerators import _run_packed, real_subspace_axis
-from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
+from blochsim.sequence._accelerators import _run_packed, real_subspace_axis
+from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
VOXELS = 32
STATES = 8
@@ -111,7 +111,7 @@ def _pair(backend: str, tissue, events, recorded):
if backend == "host":
shape = (tissue, events, STATES, recorded)
return _run_packed(*shape, 1), _run_packed(*shape, 1, real_axis=1)
- from torchsim.sequence import _epg_triton
+ from blochsim.sequence import _epg_triton
shape = dict(state_count=STATES, output_count=recorded)
return (
diff --git a/tests/sequence/test_train_batching.py b/tests/sequence/test_train_batching.py
index 05c864df..a5238814 100644
--- a/tests/sequence/test_train_batching.py
+++ b/tests/sequence/test_train_batching.py
@@ -8,14 +8,14 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import _pack_events, _run_packed, _run_packed_vjp
-from torchsim.sequence._parameters import TISSUE_COUNT
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._accelerators import _pack_events, _run_packed, _run_packed_vjp
+from blochsim.sequence._parameters import TISSUE_COUNT
+from blochsim.sequence._simulation import _prepare_tissue
# Where duration, flip and phase land in a gradient tuple.
_EVENT_GRADIENTS = (TISSUE_COUNT, TISSUE_COUNT + 1, TISSUE_COUNT + 2)
@@ -215,7 +215,7 @@ def test_mismatched_train_widths_are_rejected(always_worth_detecting):
def _second_order(threads):
- from torchsim.sequence._accelerators import _run_packed_vjp_jvp
+ from blochsim.sequence._accelerators import _run_packed_vjp_jvp
flip = _schedules(TRAINS, 12)
tissue = TissueProperties(
@@ -330,7 +330,7 @@ def test_placing_a_definition_at_once_writes_what_placing_it_per_pulse_writes(tr
out rather than after, so that one entry rounds a step earlier. Every other
entry is bit for bit.
"""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
flip = _schedules(trains, 6)
scattered = _pack(flip)
@@ -355,7 +355,7 @@ def test_a_schedule_of_tensors_is_placed_bit_for_bit():
gathering changes no dtype and the two ways of filling the buffers do the
same arithmetic on the same values.
"""
- from torchsim.sequence import _accelerators, _builders
+ from blochsim.sequence import _accelerators, _builders
echoes = 12
description = _builders.mrf_description(
@@ -386,7 +386,7 @@ def test_a_definition_is_asked_what_it_turns_through_once_per_packing():
nested forward-mode interpreters, so a call per pulse is the cost that
makes a long train expensive to resolve.
"""
- from torchsim.sequence._description import RfDefinition
+ from blochsim.sequence._description import RfDefinition
flip = _schedules(1, 64)
calls = 0
diff --git a/tests/sequence/test_transition.py b/tests/sequence/test_transition.py
index f7af1c72..6e0928a1 100644
--- a/tests/sequence/test_transition.py
+++ b/tests/sequence/test_transition.py
@@ -19,9 +19,9 @@
import pytest
import torch
-from torchsim import compose_spinor
-from torchsim.sequence._description import RfDefinition, RfShape
-from torchsim.sequence._transition import transition_table
+from blochsim import compose_spinor
+from blochsim.sequence._description import RfDefinition, RfShape
+from blochsim.sequence._transition import transition_table
SAMPLES = 256
RASTER = 1e-5
@@ -368,7 +368,7 @@ def test_a_dynamic_pair_reads_the_same_sample_times() -> None:
"""The per-voxel integrator and the table are the same integration, so a
declared duration has to reach both.
"""
- from torchsim.sequence._transition import dynamic_pair
+ from blochsim.sequence._transition import dynamic_pair
stretch = 2.0
coarse = stretch * np.arange(SAMPLES, dtype=np.float64)
diff --git a/tests/sequence/test_transmit_array.py b/tests/sequence/test_transmit_array.py
index af12b882..36ffe62b 100644
--- a/tests/sequence/test_transmit_array.py
+++ b/tests/sequence/test_transmit_array.py
@@ -19,12 +19,12 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
_across_the_table,
_pack_events,
_run_packed,
@@ -33,10 +33,10 @@
_run_packed_vjp_jvp,
offload,
)
-from torchsim.sequence._description import EventType, ShimDefinition
-from torchsim.sequence._parameters import TISSUE_COUNT
-from torchsim.sequence._simulation import _prepare_tissue, _resolve_transmit
-from torchsim.sequence._transmit import (
+from blochsim.sequence._description import EventType, ShimDefinition
+from blochsim.sequence._parameters import TISSUE_COUNT
+from blochsim.sequence._simulation import _prepare_tissue, _resolve_transmit
+from blochsim.sequence._transmit import (
channel_count,
shim_rows,
transmit_field,
@@ -855,7 +855,7 @@ def _pooled_shim_packed(voxels: int = 3, **properties):
@pytest.mark.parametrize("pool", sorted(POOLS))
def test_the_shim_rows_reach_every_pool_the_kernels_carry(pool: str) -> None:
"""Against the oracle, which indexes the rows and the pools independently."""
- from torchsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence._lineshape import lineshape_table
tissue, events, output_count = _pooled_shim_packed(**POOLS[pool])
carried = dict(
@@ -884,7 +884,7 @@ def test_a_pooled_pulse_still_reads_only_the_shim_it_names(pool: str) -> None:
"""A row no pulse drives is dead weight whatever pools are carried, so
filling it must leave the signal alone to the bit.
"""
- from torchsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence._lineshape import lineshape_table
tissue, events, output_count = _pooled_shim_packed(**POOLS[pool])
atoms = tissue[0].numel()
@@ -916,8 +916,8 @@ def test_a_shimmed_gradient_takes_the_first_order_kernel(state_count) -> None:
state count before, and the shim adds a per-event load the others do not
make.
"""
- from torchsim.sequence import _accelerators
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence import _accelerators
+ from blochsim.sequence._accelerators import _run_packed_vjp
tissue, events, output_count = _two_shim_packed(voxels=64, device="cuda")
seed = torch.randn(
@@ -961,7 +961,7 @@ def test_a_row_no_pulse_drives_gets_no_gradient() -> None:
"""The gradient lands in the shim the pulse named, so a row nobody drives
comes back at zero -- which is what says the row index was read.
"""
- from torchsim.sequence._accelerators import _run_packed_vjp
+ from blochsim.sequence._accelerators import _run_packed_vjp
tissue, events, output_count = _two_shim_packed(voxels=32, device="cuda")
atoms = tissue[0].numel()
diff --git a/tests/sequence/test_unrefocused_time.py b/tests/sequence/test_unrefocused_time.py
index 6c00abb6..196986fa 100644
--- a/tests/sequence/test_unrefocused_time.py
+++ b/tests/sequence/test_unrefocused_time.py
@@ -20,12 +20,12 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
_builders,
)
-from torchsim.sequence._accelerators import _pack_events
+from blochsim.sequence._accelerators import _pack_events
T1_MS, T2_MS = 1000.0, 80.0
# Deliberately not a whole number of turns per repetition or echo spacing: a
@@ -162,7 +162,7 @@ def test_a_spoiled_train_carries_its_echo_time_to_every_sample():
def _both_ways(description, tissue, wants_grad=False):
"""The same run with the field on the samples and carried by the states."""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
answers = []
for analytic in (True, False):
@@ -226,7 +226,7 @@ def test_off_resonance_no_longer_keeps_a_run_off_the_real_kernels(
is not: the states never see it, so they stay on the axis and the reduced
kernels stay available -- for the plain pass and for the derivatives alike.
"""
- from torchsim.sequence import _accelerators
+ from blochsim.sequence import _accelerators
verdicts = []
original = _accelerators._auto_real_axis
diff --git a/tests/sequence/test_vjp.py b/tests/sequence/test_vjp.py
index 3edb2863..7f0e7f82 100644
--- a/tests/sequence/test_vjp.py
+++ b/tests/sequence/test_vjp.py
@@ -5,7 +5,7 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
EpgEngine,
TissueProperties,
_accelerators,
@@ -14,14 +14,14 @@
mrf_description,
spgr_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
NO_GEOMETRY,
_NativeEpg,
_pack_events,
_run_packed_vjp,
)
-from torchsim.sequence._parameters import FLOAT_NAMES, TISSUE_COUNT
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._parameters import FLOAT_NAMES, TISSUE_COUNT
+from blochsim.sequence._simulation import _prepare_tissue
from utils.packed_reference import simulate_packed
# Gradient tuples arrive in the packing order, so name them from the same place.
@@ -231,17 +231,17 @@ def test_fused_vjp_is_bitwise_deterministic(monkeypatch) -> None:
for expected, actual in zip(first, _gradients(), strict=True):
assert torch.equal(expected, actual)
- monkeypatch.setenv("TORCHSIM_NUM_THREADS", "1")
+ monkeypatch.setenv("BLOCHSIM_NUM_THREADS", "1")
single = _gradients()
- monkeypatch.setenv("TORCHSIM_NUM_THREADS", "8")
+ monkeypatch.setenv("BLOCHSIM_NUM_THREADS", "8")
for expected, actual in zip(single, _gradients(), strict=True):
assert torch.equal(expected, actual)
def _objective_gradient():
"""A precision cost, which is a directional derivative differentiated back."""
- from torchsim.optim import crlb
- from torchsim.simulators import FSESimulator
+ from blochsim.optim import crlb
+ from blochsim.simulators import FSESimulator
sequence = FSESimulator(ESP=5.0, TR=3000.0, states=10)
flip = torch.linspace(150.0, 90.0, 12).requires_grad_(True)
diff --git a/tests/sequence/test_washout_kernels.py b/tests/sequence/test_washout_kernels.py
index bb0ae6a6..1c442a78 100644
--- a/tests/sequence/test_washout_kernels.py
+++ b/tests/sequence/test_washout_kernels.py
@@ -16,11 +16,11 @@
import pytest
import torch
-from torchsim.sequence import (
+from blochsim.sequence import (
TissueProperties,
fse_description,
)
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
NO_GEOMETRY,
Geometry,
_pack_events,
@@ -30,8 +30,8 @@
_run_packed_vjp_jvp,
geometry_of,
)
-from torchsim.sequence._parameters import FLOAT_NAMES, TISSUE_NAMES
-from torchsim.sequence._simulation import _prepare_tissue
+from blochsim.sequence._parameters import FLOAT_NAMES, TISSUE_NAMES
+from blochsim.sequence._simulation import _prepare_tissue
from utils.epg import washout_op
from utils.packed_reference import simulate_packed
diff --git a/tests/sequence/test_worker_pool.py b/tests/sequence/test_worker_pool.py
index 0a64da7d..1aaf5e92 100644
--- a/tests/sequence/test_worker_pool.py
+++ b/tests/sequence/test_worker_pool.py
@@ -15,7 +15,7 @@
Only a build that multiversions has a wide clone to leave a state behind. Where
the kernels are compiled once -- clang, MSVC, musl, anything not x86 -- the two
arms run the same code and the ratio measures how quiet the machine was, which
-is not a property of TorchSim.
+is not a property of BlochSim.
"""
from __future__ import annotations
@@ -25,14 +25,14 @@
import pytest
import torch
-import torchsim._epg_cpu as kernels
-from torchsim.sequence._accelerators import (
+import blochsim._epg_cpu as kernels
+from blochsim.sequence._accelerators import (
_pack_events,
_run_packed,
_run_packed_jvp,
)
-from torchsim.sequence._builders import mrf_description
-from torchsim.sequence._simulation import TissueProperties, _prepare_tissue
+from blochsim.sequence._builders import mrf_description
+from blochsim.sequence._simulation import TissueProperties, _prepare_tissue
ATOMS = 4000
REPETITIONS = 120
diff --git a/tests/simulators/test_closed_forms.py b/tests/simulators/test_closed_forms.py
index 14a1f70b..f00da180 100644
--- a/tests/simulators/test_closed_forms.py
+++ b/tests/simulators/test_closed_forms.py
@@ -10,7 +10,7 @@
import pytest
import torch
-from torchsim.simulators import (
+from blochsim.simulators import (
ASLSimulator,
DiffusionSimulator,
HSFPSimulator,
diff --git a/tests/simulators/test_epg_trains.py b/tests/simulators/test_epg_trains.py
index 74e83987..d9f57370 100644
--- a/tests/simulators/test_epg_trains.py
+++ b/tests/simulators/test_epg_trains.py
@@ -15,7 +15,7 @@
import numpy as np
import pytest
-from torchsim.simulators import (
+from blochsim.simulators import (
FLASHSimulator,
HyperechoSimulator,
StimulatedEchoSimulator,
diff --git a/tests/simulators/test_sampled_pulses.py b/tests/simulators/test_sampled_pulses.py
index 530bc61d..4eb22347 100644
--- a/tests/simulators/test_sampled_pulses.py
+++ b/tests/simulators/test_sampled_pulses.py
@@ -18,8 +18,8 @@
import torch
from scipy.linalg import expm
-from torchsim.sequence import SampledPulse
-from torchsim.simulators import CESTSimulator, FLASHSimulator, TrueFISPSimulator
+from blochsim.sequence import SampledPulse
+from blochsim.simulators import CESTSimulator, FLASHSimulator, TrueFISPSimulator
GAMMA_BAR = 42.57747892e6
DWELL = 1e-5
diff --git a/tests/utils/interpreted.py b/tests/utils/interpreted.py
index d795fb0a..43622be3 100644
--- a/tests/utils/interpreted.py
+++ b/tests/utils/interpreted.py
@@ -73,16 +73,16 @@ class _Poisoned:
def install(poison: bool = False) -> None:
"""Point the kernels at an ``acos`` the interpreter can evaluate."""
- from torchsim.sequence import _epg_triton
+ from blochsim.sequence import _epg_triton
_epg_triton.libdevice = _Poisoned if poison else _Interpretable
def _tissue(voxels: int) -> tuple[torch.Tensor, ...]:
- from torchsim.sequence import (
+ from blochsim.sequence import (
TissueProperties,
)
- from torchsim.sequence._simulation import _prepare_tissue
+ from blochsim.sequence._simulation import _prepare_tissue
prepared, _, _ = _prepare_tissue(
TissueProperties(
@@ -101,7 +101,7 @@ def _tissue(voxels: int) -> tuple[torch.Tensor, ...]:
def _events(description: Any) -> tuple[tuple[torch.Tensor, ...], int]:
- from torchsim.sequence._accelerators import _pack_events
+ from blochsim.sequence._accelerators import _pack_events
packed = _pack_events(
description,
@@ -131,7 +131,7 @@ def _both(run: Any, force_narrow: bool) -> tuple[Any, Any]:
narrow, so every row takes the series and the two arms should agree to the
bit.
"""
- from torchsim.sequence import _epg_triton
+ from blochsim.sequence import _epg_triton
original = _epg_triton._tabulate_three_pool
built: list[bool] = []
@@ -188,7 +188,7 @@ def _chunked(voxels: int) -> Any:
The cotangent table is sized and indexed per chunk, so a single-chunk run
cannot tell a chunk-local index from a global one.
"""
- from torchsim.sequence import _epg_triton
+ from blochsim.sequence import _epg_triton
cut: list[int] = []
@@ -210,9 +210,9 @@ def _unread(voxels: int, states: int) -> None:
is what says so rather than reading the branches and believing it.
"""
install(poison=True)
- from torchsim.sequence import _builders, _epg_triton
- from torchsim.sequence._lineshape import lineshape_table
- from torchsim.sequence._parameters import narrow_three_pool
+ from blochsim.sequence import _builders, _epg_triton
+ from blochsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence._parameters import narrow_three_pool
echoes = 6
tissue = _tissue(voxels)
@@ -257,7 +257,7 @@ def _streamed(voxels: int, states: int) -> None:
and nothing else here reaches it.
"""
install()
- from torchsim.sequence import _builders, _epg_triton
+ from blochsim.sequence import _builders, _epg_triton
echoes = 6
tissue = _tissue(voxels)
@@ -301,9 +301,9 @@ def _washed(voxels: int, states: int) -> None:
than one, because the gradients it pools are scaled by it.
"""
install()
- from torchsim.sequence import _builders, _epg_triton
- from torchsim.sequence._lineshape import lineshape_table
- from torchsim.sequence._parameters import TISSUE_NAMES, Geometry
+ from blochsim.sequence import _builders, _epg_triton
+ from blochsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence._parameters import TISSUE_NAMES, Geometry
echoes = 6
tissue = list(_tissue(voxels))
@@ -353,9 +353,9 @@ def _real(voxels: int, states: int, shims: int = 1) -> None:
of it is reached by the other cases here, which are all complex.
"""
install()
- from torchsim.sequence import _builders, _epg_triton
- from torchsim.sequence._accelerators import real_subspace_axis
- from torchsim.sequence._parameters import FLOAT_NAMES, OUTSIDE_THE_SUBSPACE
+ from blochsim.sequence import _builders, _epg_triton
+ from blochsim.sequence._accelerators import real_subspace_axis
+ from blochsim.sequence._parameters import FLOAT_NAMES, OUTSIDE_THE_SUBSPACE
from utils.packed_reference import simulate_packed
echoes = 6
@@ -465,9 +465,9 @@ def _spoiled(voxels: int, states: int) -> None:
each held to the complex kernels that carry the same train.
"""
install()
- from torchsim.sequence import _builders, _epg_triton
- from torchsim.sequence._accelerators import real_subspace_axis
- from torchsim.sequence._parameters import FLOAT_NAMES, OUTSIDE_THE_SUBSPACE
+ from blochsim.sequence import _builders, _epg_triton
+ from blochsim.sequence._accelerators import real_subspace_axis
+ from blochsim.sequence._parameters import FLOAT_NAMES, OUTSIDE_THE_SUBSPACE
from utils.packed_reference import simulate_packed
repetitions = 6
@@ -547,8 +547,8 @@ def _pooled(voxels: int, states: int, pools: int) -> None:
and neither is exercised at one or two pools without a card.
"""
install()
- from torchsim.sequence import _builders, _epg_triton
- from torchsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence import _builders, _epg_triton
+ from blochsim.sequence._lineshape import lineshape_table
from utils.packed_reference import simulate_packed
echoes = 6
@@ -611,8 +611,8 @@ def _shimmed(voxels: int, states: int) -> None:
case that would show one standing in for the other.
"""
install()
- from torchsim.sequence import _builders, _epg_triton
- from torchsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence import _builders, _epg_triton
+ from blochsim.sequence._lineshape import lineshape_table
shims, echoes = 2, 6
held = _tissue(voxels)
@@ -712,10 +712,10 @@ def _profiled(voxels: int, states: int) -> None:
import numpy as np
- from torchsim import rf_definition
- from torchsim.sequence import _builders, _epg_triton
- from torchsim.sequence._accelerators import _across_the_table
- from torchsim.sequence._transition import (
+ from blochsim import rf_definition
+ from blochsim.sequence import _builders, _epg_triton
+ from blochsim.sequence._accelerators import _across_the_table
+ from blochsim.sequence._transition import (
SliceTables,
exact_slice_profile,
transition_table,
@@ -773,14 +773,14 @@ def _narrowed(voxels: int, states: int) -> None:
install()
import math
- from torchsim.sequence import (
+ from blochsim.sequence import (
TissueProperties,
_builders,
_epg_triton,
)
- from torchsim.sequence._accelerators import real_subspace_axis
- from torchsim.sequence._parameters import TISSUE_NAMES, features_of
- from torchsim.sequence._simulation import _prepare_tissue
+ from blochsim.sequence._accelerators import real_subspace_axis
+ from blochsim.sequence._parameters import TISSUE_NAMES, features_of
+ from blochsim.sequence._simulation import _prepare_tissue
echoes = 6
tissue = TissueProperties(
@@ -861,8 +861,8 @@ def _case(name: str) -> None:
_unread(3, 4)
return
install()
- from torchsim.sequence import _builders, _epg_triton
- from torchsim.sequence._lineshape import lineshape_table
+ from blochsim.sequence import _builders, _epg_triton
+ from blochsim.sequence._lineshape import lineshape_table
voxels, states = 3, 4
if name == "narrow":
diff --git a/tests/utils/interpreted_pools.py b/tests/utils/interpreted_pools.py
index 464703e0..d3ffb917 100644
--- a/tests/utils/interpreted_pools.py
+++ b/tests/utils/interpreted_pools.py
@@ -21,6 +21,10 @@
import torch
+from blochsim.sequence import _accelerators, _pools, _pools_triton
+from blochsim.sequence._lineshape import lineshape_table
+from blochsim.sequence._parameters import NO_GEOMETRY
+from blochsim.sequence._transition import DynamicPairs
from sequence.test_many_pools import (
_PAIR_ROW,
GEOMETRY,
@@ -32,10 +36,6 @@
_instantaneous_table,
_tissue,
)
-from torchsim.sequence import _accelerators, _pools, _pools_triton
-from torchsim.sequence._lineshape import lineshape_table
-from torchsim.sequence._parameters import NO_GEOMETRY
-from torchsim.sequence._transition import DynamicPairs
# What one kernel is held to against the other, relative to the largest entry
# of each result. Both accumulate in float32, in different orders.
diff --git a/tests/utils/packed_reference.py b/tests/utils/packed_reference.py
index 07dc2e78..e4e69b2b 100644
--- a/tests/utils/packed_reference.py
+++ b/tests/utils/packed_reference.py
@@ -20,7 +20,7 @@
import torch
-from torchsim.sequence._accelerators import (
+from blochsim.sequence._accelerators import (
_INVERSION,
_POST_SHIFT,
_PRE_SHIFT,
@@ -28,7 +28,7 @@
_SHIFT_AFTER,
_SPOIL_AFTER,
)
-from torchsim.sequence._parameters import NO_GEOMETRY, Geometry
+from blochsim.sequence._parameters import NO_GEOMETRY, Geometry
__all__ = ["simulate_packed", "simulate_packed_pools"]
@@ -63,12 +63,12 @@ def simulate_packed(
The two scales the velocity is read through, exactly as the kernels
take them.
profile
- A :class:`~torchsim.sequence._transition.TransitionTable`, or ``None``
+ A :class:`~blochsim.sequence._transition.TransitionTable`, or ``None``
for the instantaneous pulse. Given one, a pulse turns through the
rotation the table holds at its effective flip rather than through a
flip and a phase.
dynamic
- A :class:`~torchsim.sequence._transition.DynamicPairs`, for a pulse
+ A :class:`~blochsim.sequence._transition.DynamicPairs`, for a pulse
whose channel weights vary while it plays. The rotation is already
integrated at that pulse's own flip, so only the event's phase is left
to apply. Mutually exclusive with ``profile``: a pair integrated at the
@@ -78,7 +78,7 @@ def simulate_packed(
voxel-major, so a voxel's position along the slice is its index modulo
this, which is the row of the table it reads.
lineshape
- A :class:`~torchsim.sequence._lineshape.LineshapeTable`, or ``None``
+ A :class:`~blochsim.sequence._lineshape.LineshapeTable`, or ``None``
for a single pool. Given one, the longitudinal step carries a bound
pool alongside the free water and each pulse saturates it.
exchanging
@@ -634,7 +634,7 @@ def simulate_packed_pools(
events, state_count, output_count, geometry, profile, dynamic, locations
As :func:`simulate_packed` takes them.
lineshape
- A :class:`~torchsim.sequence._lineshape.LineshapeTable` for a
+ A :class:`~blochsim.sequence._lineshape.LineshapeTable` for a
semisolid pool, or ``None`` for none.
Returns