Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions docs/api/sequence.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ in-memory sequence, {func}`read_mrd_description` reads those an MRD stream
carries ahead of its first acquisition, and
{meth}`~blochsim.model.Simulator.from_description` simulates any of them.

Reading Pulseq needs pypulseq: `pip install blochsim[pulseq]`.

| Object | Description |
| --- | --- |
| {obj}`~blochsim.SequenceDescription` | Event stream and RF resources of one repetition of a sequence. |
Expand Down
3 changes: 1 addition & 2 deletions docs/developer-guide/internals/description.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ gradient per axis and one ADC window. One block becomes one event: a block with
a pulse is an RF event, one with an ADC and no pulse an ADC event, any other a
WAIT. Gradients are not events.

The file is parsed and its trajectory computed by pypulseq
(`pip install blochsim[pulseq]`); a sequence in memory with pypulseq's reading
The file is parsed and its trajectory computed by pypulseq; a sequence in memory with pypulseq's reading
interface, pypulseq's or pypulseqpp's, is read the same way. The gradients are
read for two things only: the k-space trajectory that places each ADC event,
and the slice-select amplitude under each pulse's isocentre, the magnitude of
Expand Down
5 changes: 2 additions & 3 deletions docs/user-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,8 @@ pip install "blochsim[cu13]" # plus the GPU kernels, for a CUDA 13 PyTorch

The GPU kernels are compiled ahead of time and need a driver, not the CUDA
toolkit. Install the extra that matches PyTorch's CUDA major version; BlochSim
refuses a mismatched one and names the extra to install. Other extras:
`pulseq` reads `.seq` files, and `examples` brings the packages the gallery
imports.
refuses a mismatched one and names the extra to install. The `examples` extra
brings the packages the gallery imports.

Check the installation by simulating a fast spin echo for three tissues:

Expand Down
7 changes: 3 additions & 4 deletions examples/01-framework/04-description-based-simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# .. colab-link::
# :needs_gpu: 0
#
# !pip install "blochsim[pulseq]" matplotlib
# !pip install blochsim matplotlib
# !wget --quiet --no-clobber https://raw.githubusercontent.com/pulserver/blochsim/main/docs/figure_style.py
# !wget --quiet --no-clobber https://raw.githubusercontent.com/pulserver/blochsim/main/examples/01-framework/fse.seq

Expand Down Expand Up @@ -136,9 +136,8 @@
# Pulseq file
# -----------
#
# ``from_pulseq`` reads the description from a ``.seq`` file; it requires the
# ``pulseq`` extra (``pip install "blochsim[pulseq]"``). The file below is a
# short FSE:
# ``from_pulseq`` reads the description from a ``.seq`` file, parsed by
# pypulseq. The file below is a short FSE:
from_file = FSESimulator.from_pulseq(SEQ_FILE, states=20)
file_signal = from_file.simulate(T1=1000.0, T2=torch.tensor([80.0, 110.0]))

Expand Down
14 changes: 3 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ dependencies = [
# torch.func (jacrev/jacfwd) and torch.linalg are used by the optimization and
# estimator packages.
"torch>=2.1",
# The format's reference implementation parses a Pulseq .seq file and
# computes its trajectory.
"pypulseq>=1.4",
]

[project.optional-dependencies]
Expand All @@ -64,15 +67,8 @@ cu12 = [
cu13 = [
"blochsim-cuda13; sys_platform == 'linux' and platform_machine == 'x86_64'",
]
# What it takes to read a Pulseq .seq file. The format's reference
# implementation parses it and computes its trajectory; nothing else in the
# package imports it.
pulseq = [
"pypulseq>=1.4",
]
# What it takes to run the suite. Nothing here is imported by the package.
test = [
"blochsim[pulseq]",
"pytest>=7.0",
"pytest-cov",
"pytest-sugar",
Expand All @@ -83,9 +79,6 @@ test = [
# import while they execute -- those are the ``examples`` extra, so a
# documentation build without the gallery does not drag them in.
doc = [
# The explanation figures and Course lesson 4 read the shipped
# ``.seq`` file, which pypulseq parses.
"blochsim[pulseq]",
"sphinx>=7.0",
"sphinx_book_theme>=1.1",
"sphinx-design>=0.6",
Expand All @@ -102,7 +95,6 @@ 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 = [
"blochsim[pulseq]",
"brainweb-dl>=0.3",
"deepinv>=0.2",
"deepmriprep>=0.3",
Expand Down
4 changes: 0 additions & 4 deletions src/blochsim/sequence/_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,8 +852,6 @@ def from_operators(
def from_pulseq(cls, source: Any, **settings: Any) -> SequenceDescription:
"""Read one repetition of a Pulseq sequence.

Requires pypulseq (``pip install blochsim[pulseq]``).

Parameters
----------
source : str, Path or sequence
Expand All @@ -872,8 +870,6 @@ def from_pulseq(cls, source: Any, **settings: Any) -> SequenceDescription:

Raises
------
ImportError
If pypulseq is not installed.
ValueError
If the file declares no ``TRSize``, ``tr_index`` is out of range,
or the repetitions differ and none was named.
Expand Down
13 changes: 2 additions & 11 deletions src/blochsim/sequence/_pulseq.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
reference implementation of the format. What is added here is the reading a
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 blochsim[pulseq]``.
"""

from __future__ import annotations
Expand Down Expand Up @@ -45,13 +43,8 @@ def _read(source: Any) -> Any:
"""Return ``source`` if it is a sequence already, or the file it names parsed."""
if hasattr(source, "block_events"):
return source
try:
import pypulseq
except ImportError as missing: # pragma: no cover - exercised by the message
raise ImportError(
"reading a Pulseq file needs pypulseq, which BlochSim does not "
"require otherwise: pip install blochsim[pulseq]"
) from missing
import pypulseq

sequence = pypulseq.Sequence()
sequence.read(str(source))
return sequence
Expand Down Expand Up @@ -354,8 +347,6 @@ def read_pulseq_description(

Raises
------
ImportError
If pypulseq is not installed.
ValueError
If the file declares no ``TRSize``, if ``tr_index`` is out of range, or
if the repetitions differ and none was named.
Expand Down
3 changes: 1 addition & 2 deletions tests/sequence/test_mrd_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import struct

import numpy as np
import pypulseq as pp
import pytest
import torch

Expand All @@ -24,8 +25,6 @@
from blochsim.sequence import EventType, RfUse, read_mrd_description
from blochsim.simulators import SPGRSimulator

pp = pytest.importorskip("pypulseq", reason="building the sequence needs pypulseq")

FLIP_DEG = 30.0
TR_S = 20e-3
SAMPLES, DWELL_S = 64, 10e-6
Expand Down
3 changes: 1 addition & 2 deletions tests/sequence/test_pulseq_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import math

import numpy as np
import pypulseq as pp
import pytest
import torch

Expand All @@ -19,8 +20,6 @@
from blochsim.sequence import AdcRole, EventType, RfUse
from blochsim.simulators import SPGRSimulator

pp = pytest.importorskip("pypulseq", reason="reading a .seq file needs pypulseq")

FLIP_DEG = 30.0
TR_S = 20e-3
FOV_M, SAMPLES, DWELL_S = 0.22, 64, 10e-6
Expand Down
Loading