By default, NUPACK 4 uses a single CPU core for each analysis job, design trial, or utilities job. For example, a call to tube_analysis will use 1 core, whereas a call to tube_design with trials=4 will use 4 cores (up to the number of logical cores on the machine). Additional parallelism may be enabled using the config.parallelism flag (default False):

from nupack import *
config.parallelism = True

If this flag is set to True, then NUPACK jobs will be permitted to use all available cores on your machine. This type of parallelism enables:

  • Block-level parallelism. Subcomplex blocks in the dynamic program will be calculated in parallel (e.g., triangular blocks A, B, C, and rectangular blocks AB, BC, and ABC for complex ABC; see Figure 8 of [Fornace20]). This mode of parallelism will be enabled for all complexes in a multi-tube ensemble.

  • Element-level parallelism. Subsequence elements will be calculated in parallel within a subcomplex block. This mode of parallelism will only be employed for subcomplex blocks containing at least 128 nt.


NUPACK 4 introduces subcomplex block caching to achieve dramatic speedups by avoiding recalculation of subcomplex intermediates for a multi-tube ensemble (see Figure 8 of [Fornace20]). The config.cache flag (GB; default 2.0) controls the gigabytes of memory that each analysis job, design trial, or utilities job can use.

from nupack import *
config.cache = 8.0 # GB

This flag may be set to 0.0 to disable caching if your hardware has very little memory.

Naming conventions

Analysis objects of type Strand, Complex, Tube and design objects of type Domain, TargetStrand, TargetComplex, and TargetTube all accept a name specified using the name keyword.


Within the context of a single calculation, every object name must be unique (e.g., each Strand, Complex, and Tube in an analysis calculation must have a unique name).

The name may specified as a tuple or list instead of a str, in which case a '[]'-based string will be automatically generated. This convention is especially useful for repeated definitions:

domains = [Domain('N6', name=['a', i]) for i in range(4)]
print([ for d in domains]) # --> ['a[0]', 'a[1]', 'a[2]', 'a[3]']

See the examples below that make use of this convention to specify designs for orthogonal reaction pathways.

Design orthogonal reaction pathways

Reaction pathways can be designed by specifying target test tubes and formulating a constrained multi-tube design problem. Following the target test tube specification of [Wolfe17] (see Supplemenatary Section S2.2), for a reaction pathway with M elementary steps, to design N orthogonal systems, there are N*(M+1) elementary step tubes plus 1 global crosstalk tube. Below, we provide example design specifications and Jupyter notebooks for designing N orthogonal systems for 1-step and multi-step reaction pathways:


Note that target test tubes for N orthogonal systems can be concisely defined using a Python loop.


Sample \LaTeX files are provided for the above multi-tube design specifications to assist with making new design specs in a standardized format.


Fornace M.E., Porubsky N.J., Pierce N.A.: A Unified Dynamic Programming Framework for the Analysis of Interacting Nucleic Acid Strands: Enhanced Models, Scalability, and Speed. ACS Synth. Biol., (2020)


Wolfe B.R., Porubsky N.J., Zadeh J.N., Dirks R.M., Pierce N.A.: Constrained Multistate Sequence Design for Nucleic Acid Reaction Pathway Engineering. J Am. Chem. Soc., 139, (2017)