Skip to content

qubosolver.bitstrings

qubosolver.Bitstrings module-attribute

Bitstrings: TypeAlias = jaxtyping.Int8[torch.Tensor, 'n m']

2-D int8 tensor of shape (n, m) representing a batch of n bitstrings each of length m.

qubosolver.bitstrings

Batch bitstring utilities for QUBO solvers.

A Bitstrings collection is a 2-D torch.int8 tensor of shape (count, n_bits), where each row is an individual bitstring. This module provides factory functions and converters for creating and manipulating batches of bitstrings on the globally configured torch device.

Typical usage:

bs = bitstrings.from_strings(["1010", "0110", "1100"])
ss = bitstrings.to_strings(bs)          # ["1010", "0110", "1100"]
z  = bitstrings.zeros(4, 8)             # 4 zero bitstrings of length 8
f  = bitstrings.round([[1.0, 0.0, 0.9999999]])  # from a MIP solver's output

See also qubosolver.bitstring for single-bitstring operations.

Functions:

  • as_tensor –

    Convenience wrapper for torch.as_tensor that converts data to a bitstrings tensor.

  • device –

    Returns the globally configured torch device.

  • dtype –

    Returns the dtype used for bitstrings (torch.int8).

  • from_strings –

    Creates a 2-D bitstrings tensor from a sequence of '0'/'1' strings.

  • rand –

    Creates a 2-D bitstrings tensor with independent uniformly random bits.

  • round –

    Rounds near-integral float values to a 2-D bitstrings tensor.

  • tensor –

    Creates a 2-D bitstrings tensor from the given data.

  • to_strings –

    Converts a 2-D bitstrings tensor into a list of '0'/'1' strings.

  • zeros –

    Creates a zero-filled 2-D bitstrings tensor.

  • zeros_field –

    Creates a dataclass field defaulting to a zero-filled bitstrings tensor.

as_tensor

as_tensor(data: Any) -> Bitstrings

Convenience wrapper for torch.as_tensor that converts data to a bitstrings tensor.

Avoids a copy when possible. If data is already a tensor with the right dtype and on the right device, it is returned as-is, sharing the same underlying memory. A numpy array is also shared rather than copied if it already has int8 dtype and the global device is cpu (numpy arrays only live on CPU, so any other dtype or device forces a copy). Lists, tuples, and other array-like inputs are always copied.

Parameters:

  • data (Any) –

    Input data (tensor, numpy array, nested list, etc.).

Returns:

  • Bitstrings –

    A 2-D int8 tensor on the global device.

Source code in qubosolver/types/bitstrings.py
def as_tensor(data: Any) -> Bitstrings:  # noqa: ANN401 (array-like input forwarded to torch.as_tensor)
    """Convenience wrapper for `torch.as_tensor` that converts data to a bitstrings tensor.

    Avoids a copy when possible. If *data* is already a tensor with the right dtype and on
    the right device, it is returned as-is, sharing the same underlying memory. A numpy
    array is also shared rather than copied if it already has ``int8`` dtype and the global
    device is ``cpu`` (numpy arrays only live on CPU, so any other dtype or device forces a
    copy). Lists, tuples, and other array-like inputs are always copied.

    Args:
        data: Input data (tensor, numpy array, nested list, etc.).

    Returns:
        A 2-D ``int8`` tensor on the global device.
    """
    return torch.as_tensor(data, dtype=dtype(), device=device())

device

device() -> torch.device

Returns the globally configured torch device.

Source code in qubosolver/types/bitstrings.py
def device() -> torch.device:
    """Returns the globally configured torch device."""
    return linalg.device()

dtype

dtype() -> torch.dtype

Returns the dtype used for bitstrings (torch.int8).

Source code in qubosolver/types/bitstrings.py
def dtype() -> torch.dtype:
    """Returns the dtype used for bitstrings (``torch.int8``)."""
    return torch.int8

from_strings

from_strings(strings: Sequence[str], *, device: torch.device | None = None) -> Bitstrings

Creates a 2-D bitstrings tensor from a sequence of '0'/'1' strings.

Parameters:

  • strings (Sequence[str]) –

    A sequence of strings, each consisting of '0' and '1' characters. All strings must have the same length.

  • device (torch.device | None, default: None ) –

    Torch device for the tensor.

Returns:

  • Bitstrings –

    A 2-D int8 tensor of shape (len(strings), len(strings[0])), possibly empty.

Raises:

  • ValueError –

    If the strings have differing lengths.

Source code in qubosolver/types/bitstrings.py
def from_strings(strings: Sequence[str], *, device: torch.device | None = None) -> Bitstrings:
    """Creates a 2-D bitstrings tensor from a sequence of '0'/'1' strings.

    Args:
        strings: A sequence of strings, each consisting of '0' and '1' characters.
            All strings must have the same length.
        device: Torch device for the tensor.

    Returns:
        A 2-D ``int8`` tensor of shape ``(len(strings), len(strings[0]))``, possibly empty.

    Raises:
        ValueError: If the strings have differing lengths.
    """
    device = device or _device()
    if len(strings) == 0:
        return zeros(0, 0, device=device)
    lengths = {len(s) for s in strings}
    if len(lengths) != 1:
        raise ValueError(
            f"All bitstrings must have the same length, got lengths: {sorted(lengths)}"
        )
    return tensor([[int(c) for c in s] for s in strings], device=device)

rand

rand(count: int, n_bits: int, *, device: torch.device | None = None, rng: torch.Generator | None = None) -> Bitstrings

Creates a 2-D bitstrings tensor with independent uniformly random bits.

Parameters:

  • count (int) –

    Number of bitstrings (rows).

  • n_bits (int) –

    Length of each bitstring (columns).

  • device (torch.device | None, default: None ) –

    Torch device for the tensor.

  • rng (torch.Generator | None, default: None ) –

    PyTorch random number generator controlling the sampling.

Returns:

  • Bitstrings –

    A 2-D int8 tensor of shape (count, n_bits) containing 0s and 1s.

Source code in qubosolver/types/bitstrings.py
def rand(
    count: int,
    n_bits: int,
    *,
    device: torch.device | None = None,
    rng: torch.Generator | None = None,
) -> Bitstrings:
    """Creates a 2-D bitstrings tensor with independent uniformly random bits.

    Args:
        count: Number of bitstrings (rows).
        n_bits: Length of each bitstring (columns).
        device: Torch device for the tensor.
        rng: PyTorch random number generator controlling the sampling.

    Returns:
        A 2-D ``int8`` tensor of shape ``(count, n_bits)`` containing 0s and 1s.
    """
    device = device or _device()
    rng = rng or torch_rng()
    return torch.randint(0, 2, (count, n_bits), generator=rng, device=device, dtype=dtype())

round

round(data: Any, *, atol: float = 1e-06, device: torch.device | None = None) -> Bitstrings

Rounds near-integral float values to a 2-D bitstrings tensor.

Values are compared in float64 regardless of the globally configured float dtype, so atol keeps its meaning even when the global dtype is narrower (e.g. float32, which would round 0.9999999998 to exactly 1.0 before the check could see it).

Parameters:

  • data (Any) –

    Input data (tensor, numpy array, nested list, etc.) of floats, each within atol of 0 or 1. Nested sequences must not be ragged.

  • atol (float, default: 1e-06 ) –

    Maximum absolute distance from 0 or 1 tolerated before raising.

  • device (torch.device | None, default: None ) –

    Torch device for the tensor.

Returns:

  • Bitstrings –

    A 2-D int8 tensor of shape (count, n_bits).

Raises:

  • ValueError –

    If any value is further than atol from both 0 and 1, or if the input is a ragged nested sequence.

Source code in qubosolver/types/bitstrings.py
def round(
    data: Any,  # noqa: ANN401 (array-like input forwarded to torch.as_tensor)
    *,
    atol: float = 1e-6,
    device: torch.device | None = None,
) -> Bitstrings:
    """Rounds near-integral float values to a 2-D bitstrings tensor.

    Values are compared in ``float64`` regardless of the globally configured
    float dtype, so *atol* keeps its meaning even when the global dtype is
    narrower (e.g. ``float32``, which would round ``0.9999999998`` to exactly
    ``1.0`` before the check could see it).

    Args:
        data: Input data (tensor, numpy array, nested list, etc.) of floats, each
            within *atol* of 0 or 1. Nested sequences must not be ragged.
        atol: Maximum absolute distance from 0 or 1 tolerated before raising.
        device: Torch device for the tensor.

    Returns:
        A 2-D ``int8`` tensor of shape ``(count, n_bits)``.

    Raises:
        ValueError: If any value is further than *atol* from both 0 and 1, or if
            the input is a ragged nested sequence.
    """
    device = device or _device()
    values = torch.as_tensor(data, dtype=torch.float64)
    bits = torch.round(values)
    invalid = ((bits != 0) & (bits != 1)) | ((values - bits).abs() > atol)
    if bool(invalid.any()):
        raise ValueError(
            f"Expected values within {atol} of 0 or 1, got "
            f"{values[invalid].tolist()} in {values.tolist()}"
        )
    if bits.shape == (0,):
        # `torch.as_tensor([])` is 1-D: keep empty bitstrings 2-D.
        bits = bits.reshape(0, 0)
    return bits.to(dtype=dtype(), device=device)

tensor

tensor(data: Any, *, device: torch.device | None = None, **kwargs: Any) -> Bitstrings

Creates a 2-D bitstrings tensor from the given data.

Parameters:

  • data (Any) –

    Input data (nested list or array-like of 0s and 1s).

  • device (torch.device | None, default: None ) –

    Torch device for the tensor.

  • **kwargs (Any, default: {} ) –

    Extra keyword arguments forwarded to torch.tensor.

Returns:

  • Bitstrings –

    A 2-D int8 tensor; an empty list gives a (0, 0) tensor.

Source code in qubosolver/types/bitstrings.py
def tensor(
    data: Any,  # noqa: ANN401 (array-like input forwarded to torch.tensor)
    *,
    device: torch.device | None = None,
    **kwargs: Any,  # noqa: ANN401 (forwarded to torch.tensor)
) -> Bitstrings:
    """Creates a 2-D bitstrings tensor from the given data.

    Args:
        data: Input data (nested list or array-like of 0s and 1s).
        device: Torch device for the tensor.
        **kwargs: Extra keyword arguments forwarded to `torch.tensor`.

    Returns:
        A 2-D ``int8`` tensor; an empty list gives a ``(0, 0)`` tensor.
    """
    device = device or _device()
    result = torch.tensor(data, dtype=dtype(), device=device, **kwargs)
    # `torch.tensor([])` is 1-D: keep empty bitstrings 2-D.
    return result.reshape(0, 0) if result.shape == (0,) else result

to_strings

to_strings(bitstrings: Bitstrings) -> list[str]

Converts a 2-D bitstrings tensor into a list of '0'/'1' strings.

Parameters:

  • bitstrings (Bitstrings) –

    A 2-D int8 tensor of shape (n, m) containing 0s and 1s.

Returns:

  • list[str] –

    A list of n strings, each of length m, representing each row of the tensor.

Source code in qubosolver/types/bitstrings.py
def to_strings(bitstrings: Bitstrings) -> list[str]:
    """Converts a 2-D bitstrings tensor into a list of '0'/'1' strings.

    Args:
        bitstrings: A 2-D ``int8`` tensor of shape ``(n, m)`` containing 0s and 1s.

    Returns:
        A list of *n* strings, each of length *m*, representing each row of the tensor.
    """
    return ["".join(str(b.item()) for b in row) for row in bitstrings]

zeros

zeros(count: int, n_bits: int, *, device: torch.device | None = None) -> Bitstrings

Creates a zero-filled 2-D bitstrings tensor.

Parameters:

  • count (int) –

    Number of bitstrings (rows).

  • n_bits (int) –

    Length of each bitstring (columns).

  • device (torch.device | None, default: None ) –

    Torch device for the tensor.

Returns:

  • Bitstrings –

    A 2-D int8 tensor of shape (count, n_bits).

Source code in qubosolver/types/bitstrings.py
def zeros(count: int, n_bits: int, *, device: torch.device | None = None) -> Bitstrings:
    """Creates a zero-filled 2-D bitstrings tensor.

    Args:
        count: Number of bitstrings (rows).
        n_bits: Length of each bitstring (columns).
        device: Torch device for the tensor.

    Returns:
        A 2-D ``int8`` tensor of shape ``(count, n_bits)``.
    """
    device = device or _device()
    return torch.zeros((count, n_bits), dtype=dtype(), device=device)

zeros_field

zeros_field(count: int, n_bits: int, *, device: torch.device | None = None) -> Bitstrings

Creates a dataclass field defaulting to a zero-filled bitstrings tensor.

Parameters:

  • count (int) –

    Number of bitstrings (rows).

  • n_bits (int) –

    Length of each bitstring (columns).

  • device (torch.device | None, default: None ) –

    Torch device for the tensor.

Returns:

  • Bitstrings –

    A dataclass field (typed as Bitstrings for the enclosing class) whose

  • Bitstrings –

    default_factory builds a fresh zero tensor per instance.

Source code in qubosolver/types/bitstrings.py
@no_runtime_typecheck
def zeros_field(count: int, n_bits: int, *, device: torch.device | None = None) -> Bitstrings:
    """Creates a dataclass field defaulting to a zero-filled bitstrings tensor.

    Args:
        count: Number of bitstrings (rows).
        n_bits: Length of each bitstring (columns).
        device: Torch device for the tensor.

    Returns:
        A dataclass field (typed as `Bitstrings` for the enclosing class) whose
        `default_factory` builds a fresh zero tensor per instance.
    """
    return field(default_factory=lambda: zeros(count, n_bits, device=device))