Skip to content

Wigner_Time

Timeline creation and management for open-loop control in AMO experiments and beyond. Keep scrolling for a quick overview.

Status

Beta. The package has been in use on two cold-atom setups for more than two years, but a number of known issues remain open — including several silent-failure cases surfaced by a systematic code review. These are catalogued in KNOWN_ISSUES.md and are being worked through while the accompanying paper is refereed; some of the resulting fixes will be deliberately breaking, most notably to the origin mechanism.

The accompanying paper, submitted to SciPost Physics Codebases, is on arXiv as arXiv:2609.06230.

Optional dependencies (package extras)

  • performance_and_export (Recommended): Installs pyarrow for memory management, sharing between systems and export to parquet.
  • display: Installs matplotlib and pyqt for visualization.

Developer Notes

Tests can be run from the root folder with

poetry run pytest

Getting Started

  1. Abstract
  2. Why not ADbasic?
  3. Why Wigner Time?
    1. Easy (separation of concerns)
    2. ’Simple’
    3. Flexible
    4. Portable
    5. Robust
    6. Graphical display
  4. How it works?
  5. Example (For ADwin systems)
  6. Future?
  7. Status

Abstract

We introduce Wigner Time, an approach and Python package for defining and manipulating experimental timelines in real-time open-loop control systems. Fundamentally, procedures are expressed functionally and implemented tabularly, such that the core timelines can be represented with in-memory databases, e.g. pandas.DataFrame. The associated functional-style API is clear, flexible, and integrates well with the broader scientific Python ecosystem. The package has been optimized for ADwin-based quantum-optics experiments, but is broadly applicable to any experimental domain requiring precisely timed, multi-device control.

An example timeline.

Why not ADbasic?

ADbasic combines the power and precision of a low-level programming language with the intuitive clarity of a low-level programming languge.

Why Wigner Time?

Easy (separation of concerns)

So much of physics is choosing the right level of abstraction.

Wigner Time allows you to use a normal and popular programming language to design your experimental timelines, while still utilizing the low-level features of other languages when you really need it.

Don’t write ADbasic unless you need to!

’Simple’

Easy tools often become complicated, but Wigner Time has been carefully designed so that all conveniences are optional. You can always drop down a level of abstraction to manually implement a new feature - to the point of just adding CSV tables.

Flexible

By using a functional, data-oriented and bottom-up programming approach to timeline creation, Wigner Time makes it very easy to add and substract changes from the timeline and so can repsond to fast-changing lab requirements.

Portable

The essential data is always accesible and transferrable to any other language or collaborator

  • even a spreadsheet!

Robust

Implemented ontop of the `pandas` system, the most widely-used data science package.

Graphical display

Easily generate and investigate your timeline.

How it works?

Wigner Time is based around the idea of a ’timeline’, which is, at heart, simply a table of rows and columns. The columns detail ’parameters’, e.g. ’variable’ (the name of a quantity), ’time’, ’value’, ’context’ (a concise description of a real-world situation, e.g. ’OpticalTrap’ )

  • Add more parameters by adding columns
  • Add more operations by adding rows

By boiling the design down to a ’table’ as the foundation, then we can benfit from decades of database development, particularly in-memory database-like systems like `pandas`. Therefore, when in doubt, the user can simply manipulate their timeline using the well-developed `pandas` ecosystem. For most operations however, even this won’t be necessary as wignertime provides layers of conveninece functions ontop of this for designing open-loop experiments.

Example (For ADwin systems)

You want to control an optical shutter, an AOM and a laser lock.

For each channel, simply name the ADwin port using standard Python lists. These keep track of the physical connections. A name is <device>__<UID>, followed by __<unit> for an analog channel.

    from wignertime.adwin import connection as adcon
    from wignertime import device
    from wignertime import conversion as conv

    connections = adcon.new(
        ["shutter__MOT", 1, 11],
        ["AOM__MOT", 1, 1],
        ["AOM__MOT__transmission", 3, 1],
        ["lockbox__MOT__MHz", 3, 8],
    )
For analog connections, also specify a linear factor, conversion function or calibration file, and the permitted range.

    devices = device.new(
        ["lockbox__MOT__MHz", 0.05, -200, 200],
        [
            "AOM__MOT__transmission",
            conv.function_from_file(
                "resources/calibration/aom_calibration.dat",
                sep=r"\s+",
            ),
            0.0,
            1.0,
        ],
    )
Specify how you want your experiment to begin and end, using readable options and user-specific keywords. The initial and final states belong to no instant of the run, so they are placed before and after it, at −∞ and +∞.

    import math
    from wignertime import timeline as tl

    initial = tl.update(
        time=-math.inf,
        context="ADwin_LowInit",
        shutter__MOT=1,
        AOM__MOT=0,
        AOM__MOT__transmission=1.0,
        lockbox__MOT__MHz=0.0,
    )
    final = tl.update(
        time=math.inf,
        context="ADwin_Finish",
        shutter__MOT=1,
        AOM__MOT=0,
        AOM__MOT__transmission=1.0,
        lockbox__MOT__MHz=0.0,
    )

And any key processes…

MOT = tl.update(
            shutter__MOT=0,
            AOM__MOT=1,
            context="MOT",
        )
detuned_growth = tl.ramp(
                    lockbox__MOT__MHz=-5,
                    duration=10e-3,
        )
None of these is a timeline yet: each is a stage, written relative to its own beginning. Combine them in readable and modular fashion, and make a timeline of the result with to_timeline.

Due to the sensible defaults, each component, e.g. ramp, will automatically join onto the end of the previous operation in a causal chain.

tline = tl.to_timeline(
    tl.stack(
        initial,
        MOT,
        detuned_growth,
        final,
    )
)

The timeline is a pandas.DataFrame, so it can be edited and inspected directly: tline[tline["context"] == "MOT"] selects the rows of the MOT stage.

It can then be converted to an ADwin-compatible format. The cycle period has to be stated, in seconds, because it belongs to the program running on the ADwin rather than to the experiment.

    from wignertime.adwin import core as adwin

    adwin.convert(tline, connections, devices, cycle_period=5e-6)

Future?

  • Official support for NI systems
  • Graphical input
  • Feature requests. Post an issue!