diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..7e897f0 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,55 @@ +name: Deploy docs + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Poetry + uses: snok/install-poetry@v1 + + - name: Install dependencies + run: poetry install + + - name: Build docs + run: poetry run mkdocs build + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 494207f..a3176b0 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,12 @@ This is currently an alpha release. Usable, but subject to breaking changes. We Tests can be run from the root folder with ```bash poetry run pytest +``` +and documentation can be generated locally using +``` bash +poetry run mkdocs serve ``` # Getting Started diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..2e780f3 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,4 @@ +# API Reference + +::: wigner_time + diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..195d45b --- /dev/null +++ b/docs/index.md @@ -0,0 +1,213 @@ +# Wigner_Time +Timeline creation and management for open-loop control in AMO experiments and beyond. Keep scrolling for a quick overview. + +## Status +This is currently an alpha release. Usable, but subject to breaking changes. We will release the first stable version soon. + + +## 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. + - `parallel_processing`: Installs `polars` for parallel dataframe manipulation. (WARNING: This is currently not used, but will be in the future) + +## Developer Notes +Tests can be run from the root folder with +```bash +poetry run pytest +``` + +# Getting Started + +1. [Abstract](#orgfb88e4f) +2. [Why not ADbasic?](#org231a69c) +3. [Why Wigner Time?](#org5e1d714) + 1. [Easy (separation of concerns)](#org154d75b) + 2. [’Simple’](#org2528e95) + 3. [Flexible](#org1877e40) + 4. [Portable](#org99c74fd) + 5. [Robust](#orgb155d13) + 6. [Graphical display](#org6bc654b) +4. [How it works?](#org95dac3d) +5. [Example (For ADwin systems)](#orge8cea69) +6. [Future?](#orge0a7f00) +7. [Status](#org48d7fda) + + + + + +# 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.](resources/timeline--example.png "A timeline generated and displayed by the package.") + + + + +# 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 digitally control an optical shutter and AOM. + +For digital channels, simply *name* the ADwin ports using standard Python lists. These keep track of the physical connections. + +``` python + from wigner_time.adwin import connection as adcon + from wigner_time import device + from wigner_time import conversion as conv + + connections = adcon.new( + ["shutter_MOT", 1, 11], + ["AOM_MOT", 1, 1]) +``` +For analogue connections, do the same, but specify a linear factor, conversion function or calibration file. + +``` python + 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. + +N.B. The use of *pandas.DataFrame* for convenient edits. + +``` python + import timeline as tl + + initial = tl.create( + t=1e-6, + context="ADwin_LowInit", + + shutter_MOT= 1 + AOM_MOT=0, + ) + final = init + final['context']="ADwin_Finish" +``` + +And any key processes… + +``` python +MOT = tl.update( + shutter_MOT= 0 + AOM_MOT=1, + context="MOT", + ) +detuned_growth = tl.ramp( + lockbox_MOT__MHz=-5, + duration=10e-3, + ), +``` +Then combine it all together in readable and modular fashion. + +Due to the (hopefully) sensible defaults, each component, e.g. \`ramp\`, will automatically join onto the end of the previous operation in a causal chain. + +``` python +tline = tl.stack( + initial, + MOT, + detuned_growth, + final +) + +``` + + +The timeline can then be exported to an ADwin-compatible format. + +``` python + from wigner_time.adwin import core as adwin + + adwin.to_data(tline) +``` + + + +# Future? + +- Official support for NI systems +- Graphical input +- Feature requests. Post an issue! diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..a942e82 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,26 @@ +site_name: Wigner Time + +theme: + name: material + +plugins: + - search + - mkdocstrings: + handlers: + python: + options: + docstring_style: numpy + show_source: true + show_submodules: true + +nav: + - Home: index.md + - API: api.md + +markdown_extensions: + - pymdownx.highlight + - pymdownx.superfences + +extra: + version: + provider: mike diff --git a/pyproject.toml b/pyproject.toml index d6b8c90..865974d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,9 @@ parallel_processing = ["polars"] jupyter = "^1.1.1" black = "^24.10.0" pytest = "^7.3.1" +mkdocstrings = {version = "^1.0.3", extras = ["python"]} +mkdocs-material = "^9.7.6" +mike = "^2.1.4" [build-system] requires = ["poetry-core"] diff --git a/wigner_time/timeline.py b/wigner_time/timeline.py index 0967224..812df88 100644 --- a/wigner_time/timeline.py +++ b/wigner_time/timeline.py @@ -10,7 +10,6 @@ """ from copy import deepcopy -import inspect from typing import Callable import funcy