Skip to content

Pretty-printing of basis expansion for states #2618

@Sola85

Description

@Sola85

Problem Description

The current string representation of Qobj's is often not very useful, in particular when dealing with states that are a superposition of just a few basis elements. E.g.

ket = 0.3*tensor(basis(8, 5), basis(2, 1), basis(3, 0)) + 0.5*tensor(basis(8, 3), basis(2, 0), basis(3, 2))
print(ket)

This spams my console with 50 (mostly empty) lines. Having the option to print this as 0.3 |5, 1, 0> + 0.5 |3, 0, 2> would be much nicer.

Proposed Solution

If there is interest for this, I could open a PR to add something like Qobj.print_basis_expansion() or similar (I'd be open for suggestions for a better name).

This is the implementation I've been using so far.

def print_basis_expansion(state: qutip.Qobj, decimal_places: int = 14, dim_separator: str = "auto", 
                          term_separator: str = " +\n", sort: str = "largest-first") -> str:
    """
        Return a string-representation of the basis expansion for a ket or a bra, e.g.

        "0.5 |010> + 0.25 |000> + ..."

        Parameters
        ----------
        decimal_places : int
            The number of decimal places to show for the coefficients.

        dim_separator: str
            Separator string between dimension indices. E.g. use ", " to print as "|0, 0, 0>". 
            If dim_separator="auto", then "" is used for qubits and ", " is used otherwise.

        term_separator: str
            Separator string between terms. E.g. use " + "
            to print in a single line as "|000> + |111>"

        sort: str
            Show largest elements (by magnitude) first if "largest-first".
            Unsorted otherwise.

        Returns
        -------
        basis_expansion : str
            The requested string representation.
    """

    if not (state.isket or state.isbra):
        raise ValueError(f"Only implemented for states, not {state.type}.")
    dims = state.dims[0 if state.isket else 1]

    if dim_separator == "auto":
        # No separator for pure qubit states, but comma-separator otherwise,
        # since bitstrings are nice, but e.g. |153> would be ambiguous. 
        dim_separator = ", " if any([dim > 2 for dim in dims]) else ""

    template = "{} |{}>" if state.isket else "{} <{}|"
    
    ket_full = np.round(state.full(squeeze=True), decimals=decimal_places)

    indices = range(len(ket_full))
    if sort == "largest-first":
        indices = np.argsort(np.abs(ket_full))[::-1]

    parts = []
    for i in indices:
        if np.abs(ket_full[i]) > 10**-(decimal_places):
            coeff = ket_full.item(i)
            basis_str = dim_separator.join(map(str, np.unravel_index(i, dims)))
            parts.append(template.format(coeff, basis_str))

    if len(parts) == 0: # return something for norm-zero states.
        return "0"
    
    return term_separator.join(parts)

Alternate Solutions

No response

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions