{ "cells": [ { "cell_type": "markdown", "id": "cb3afcaa", "metadata": {}, "source": [ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/casangi/graphviper/blob/main/docs/graph_building_tutorial.ipynb)" ] }, { "cell_type": "markdown", "id": "ed122f09-ead5-4a22-a70b-7702d556e7fe", "metadata": {}, "source": [ "# GraphVIPER Tutorial\n", "\n", "This tutorial provides examples of how `GraphVIPER` can be used to build [Dask](https://www.dask.org/) graphs by mapping a dictionary-based container of [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) to [Dask](https://www.dask.org/) graph nodes, followed by a reduction step. The dictionary of [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) used in this tutorial is referred to as a [Processing Set](https://github.com/casangi/xradio/blob/main/src/xradio/vis/_processing_set.py), although any dictionary containing [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) can be used. Using the `GraphVIPER` [map](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/map/index.html#module-contents) and [reduce](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/reduce/index.html#module-contents) functions can be thought of as a generalization of [xarray.map_blocks](https://docs.xarray.dev/en/stable/generated/xarray.map_blocks.html) that can be applied to more than one [xarray.Dataset](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html). Both [map](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/map/index.html#module-contents) and [reduce](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/reduce/index.html#module-contents) build [Dask](https://www.dask.org/) graphs using [dask.delayed](https://docs.dask.org/en/stable/delayed.html).\n", "\n", "The following types of mapping are supported:\n", "\n", "- Partitions defined by any combination of the coordinates in the [Processing Set](https://github.com/casangi/xradio/blob/main/src/xradio/vis/_processing_set.py).\n", "- More than one [xarray.Dataset](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) can be assigned to a single mapping node.\n", "- [xarray.Dataset](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) partitions assigned to different nodes can have coordinates that overlap.\n", "\n", "The tutorial will cover the following examples:\n", "\n", "- Frequency Map Reduce: This example explains the concepts of `parallel_coords` and `node_task_data_mapping` that define parallelism.\n", "- Overlapping Frequency Map Reduce.\n", "- Baseline and Frequency Map Reduce.\n", "- Time Map Reduce.\n", "\n", "`GraphVIPER` provides improvements over the [CNGI prototype](https://cngi-prototype.readthedocs.io/en/stable/development.html):\n", "\n", "- There is a clear separation between the concurrency layer ([GraphVIPER](https://graphviper.readthedocs.io/en/latest/)) and the domain layer (science code, [AstroVIPER](https://github.com/casangi/astroviper)).\n", "- The memory backpressure issue was solved by incorporating the loading of data into the compute nodes. An example of the memory backpressure issue is cube imaging where large in-memory image cubes have to be created, which [Dask](https://www.dask.org/) is not aware of, causing [Dask](https://www.dask.org/) to be overeager in loading data from disk into memory. In the future, [Dask](https://www.dask.org/) might provide an alternative solution where graph nodes can be annotated with expected memory usage.\n", "- The number of graph nodes has been minimized; this was also solved by incorporating the loading of data into the compute nodes. When [Xarray](https://docs.xarray.dev/) backed [Dask](https://www.dask.org/) datasets are used, a node is created for each data variable, and since Radio Astronomy datasets have numerous data variables, it led to a bloated graph that impacted scaling performance.\n", "- Multiple [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) can be processed together with overlap. This cannot be done with the current [Xarray](https://docs.xarray.dev/) functionality, such as [xarray.map_blocks](https://docs.xarray.dev/en/stable/generated/xarray.map_blocks.html).\n", "- Using a [Dask plugin](https://distributed.dask.org/en/latest/plugins.html), the [Dask Scheduler](https://docs.dask.org/en/stable/scheduler-overview.html) has been modified so that data can be cached to a local disk when multiple passes over larger-than-memory data have to be done. This reduces clustered file system or binary object store access (see [GraphVIPER Client](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/dask/client/index.html)).\n" ] }, { "cell_type": "markdown", "id": "ffbf2b16", "metadata": {}, "source": [ "## Install GraphVIPER" ] }, { "cell_type": "code", "execution_count": 1, "id": "937bd155", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "GraphVIPER version 0.0.39 already installed.\n" ] } ], "source": [ "import os\n", "import dask\n", "import toolviper\n", "\n", "import numpy as np\n", "\n", "from importlib.metadata import version\n", "\n", "try:\n", " import graphviper\n", "\n", " print(\"GraphVIPER version\", version(\"graphviper\"), \"already installed.\")\n", "except ImportError as e:\n", " print(e)\n", " print(\"Installing GraphVIPER\")\n", "\n", " os.system(\"pip install graphviper\")\n", "\n", " import graphviper\n", "\n", " print(\"GraphVIPER version\", version(\"graphviper\"), \" installed.\")" ] }, { "cell_type": "markdown", "id": "2682bb1c-c377-47f2-9faa-271108954d8f", "metadata": {}, "source": [ "## Setup Dask Cluster\n", "To simplify things we are going to start of by just using a single process (everything will run in serial)." ] }, { "cell_type": "code", "execution_count": 2, "id": "f4e50179-3486-4454-9036-ff55d24346fc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[\u001b[38;2;128;05;128m2026-03-10 15:46:25,378\u001b[0m] \u001b[38;2;255;160;0m WARNING\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m It is recommended that the local cache directory be set using the \u001b[38;2;50;50;205mdask_local_dir\u001b[0m parameter. \n", "[\u001b[38;2;128;05;128m2026-03-10 15:46:25,378\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m Running client in synchronous mode. \n" ] } ], "source": [ "# Code to start a Dask cluster with two workers and 1 thread each.\n", "from toolviper.dask.client import local_client\n", "\n", "# viper_client = local_client(cores=2, memory_limit=\"4GB\",autorestrictor=True)\n", "viper_client = local_client(serial_execution=True)\n", "viper_client" ] }, { "cell_type": "markdown", "id": "e0aa8d3c-6fd9-474f-9223-6da8856ab948", "metadata": {}, "source": [ "## Download and Convert Dataset" ] }, { "cell_type": "code", "execution_count": 3, "id": "d4f2b674-78e3-4dbb-9079-2b46825e1f3a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[\u001b[38;2;128;05;128m2026-03-10 15:46:25,381\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m Initializing download... \n", "[\u001b[38;2;128;05;128m2026-03-10 15:46:25,382\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m File already exists: /Users/joshua/Development/graphviper/docs/Antennae_North.cal.lsrk.split.ms \n", "[\u001b[38;2;128;05;128m2026-03-10 15:46:27,886\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m Updated partition scheme used: ['DATA_DESC_ID', 'OBS_MODE', 'OBSERVATION_ID'] \n", "[\u001b[38;2;128;05;128m2026-03-10 15:46:27,888\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m Number of partitions: 4 \n", "[\u001b[38;2;128;05;128m2026-03-10 15:46:27,888\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m OBSERVATION_ID [0], DDI [0], STATE [23, 24, 25, 30, 31, 32, 33, 34, 37], FIELD [0, 1, 2], SCAN [9, 17, 21, 25], EPHEMERIS [None] \n", "[\u001b[38;2;128;05;128m2026-03-10 15:46:28,070\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m OBSERVATION_ID [1], DDI [0], STATE [23, 24, 25, 30, 31, 32, 33, 34, 37], FIELD [0, 1, 2], SCAN [26, 34, 38, 42], EPHEMERIS [None] \n", "[\u001b[38;2;128;05;128m2026-03-10 15:46:28,209\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m OBSERVATION_ID [2], DDI [0], STATE [32, 33, 34], FIELD [0, 1, 2], SCAN [43], EPHEMERIS [None] \n", "[\u001b[38;2;128;05;128m2026-03-10 15:46:28,321\u001b[0m] \u001b[38;2;50;50;205m INFO\u001b[0m\u001b[38;2;112;128;144m client: \u001b[0m OBSERVATION_ID [3], DDI [0], STATE [39, 40, 41, 46, 47, 48, 49, 50, 53], FIELD [0, 1, 2], SCAN [48, 56, 60, 64], EPHEMERIS [None] \n" ] } ], "source": [ "toolviper.utils.data.download(file=\"Antennae_North.cal.lsrk.split.ms\")\n", "\n", "from xradio.measurement_set.convert_msv2_to_processing_set import convert_msv2_to_processing_set\n", "\n", "# The chunksize on disk. Chunksize can be specified for any of the following dimensions :\n", "# time, baseline_id (interferometer) / antenna_id (single dish), frequency, and polarization.\n", "chunks_on_disk = {\"frequency\": 3}\n", "infile = \"Antennae_North.cal.lsrk.split.ms\"\n", "outfile = \"Antennae_North.cal.lsrk.split.ps.zarr\"\n", "convert_msv2_to_processing_set(\n", " in_file=infile,\n", " out_file=outfile,\n", " parallel_mode=\"none\",\n", " persistence_mode=\"w\",\n", " main_chunksize=chunks_on_disk,\n", ")" ] }, { "cell_type": "markdown", "id": "33a13d40-5654-4011-9467-d269fe601826", "metadata": {}, "source": [ "## Inspect the Processing Set\n", "\n", "The [open_processing_set](https://github.com/casangi/xradio/blob/main/src/xradio/measurement_set/open_processing_set.py) is a lazy function, so no data is loaded into memory; only metadata is loaded (the [load_processing_set](https://github.com/casangi/xradio/blob/main/src/xradio/measurement_set/load_processing_set.py) will load everything into memory). Metadata is defined as everything that is not an [xarray.DataArray](https://docs.xarray.dev/en/stable/generated/xarray.DataArray.html). Note that a [Processing Set](https://github.com/casangi/xradio/blob/main/src/xradio/measurement_set/processing_set.py) does not have to be used with `GraphVIPER`, and any dictionary of [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) can be used." ] }, { "cell_type": "code", "execution_count": 4, "id": "774b2011-8921-48fb-90dc-f56689862110", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namescan_intentsshapeexecution_block_UIDpolarizationscan_namespw_namespw_intentsfield_namesource_nameline_namefield_coordssession_reference_UIDscheduling_block_UIDproject_UIDstart_frequencyend_frequency
0Antennae_North.cal.lsrk.split_0[OBSERVE_TARGET#ON_SOURCE](50, 45, 8, 2)uid://A002/X1ff7b0/Xb[XX, YY][17, 21, 25, 9]spw_0[UNSPECIFIED][NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2][NGC4038 - Antennae North_0][]Multi-Phase-Center---uid://A002/X1fd4e7/X64dT.B.D.3.439281e+113.440067e+11
1Antennae_North.cal.lsrk.split_1[OBSERVE_TARGET#ON_SOURCE](50, 55, 8, 2)uid://A002/X207fe4/X3a[XX, YY][26, 34, 38, 42]spw_0[UNSPECIFIED][NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2][NGC4038 - Antennae North_0][]Multi-Phase-Center---uid://A002/X1fd4e7/X64dT.B.D.3.439281e+113.440067e+11
2Antennae_North.cal.lsrk.split_2[OBSERVE_TARGET#ON_SOURCE](15, 55, 8, 2)uid://A002/X207fe4/X3b9[XX, YY][43]spw_0[UNSPECIFIED][NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2][NGC4038 - Antennae North_0][]Multi-Phase-Center---uid://A002/X1fd4e7/X64dT.B.D.3.439281e+113.440067e+11
3Antennae_North.cal.lsrk.split_3[OBSERVE_TARGET#ON_SOURCE, CALIBRATE_WVR#ON_SOURCE](50, 77, 8, 2)uid://A002/X2181fb/X49[XX, YY][48, 56, 60, 64]spw_0[UNSPECIFIED][NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2][NGC4038 - Antennae North_0][]Multi-Phase-Center---uid://A002/X1fd4e7/X64dT.B.D.3.439281e+113.440067e+11
\n", "
" ], "text/plain": [ " name \\\n", "0 Antennae_North.cal.lsrk.split_0 \n", "1 Antennae_North.cal.lsrk.split_1 \n", "2 Antennae_North.cal.lsrk.split_2 \n", "3 Antennae_North.cal.lsrk.split_3 \n", "\n", " scan_intents shape \\\n", "0 [OBSERVE_TARGET#ON_SOURCE] (50, 45, 8, 2) \n", "1 [OBSERVE_TARGET#ON_SOURCE] (50, 55, 8, 2) \n", "2 [OBSERVE_TARGET#ON_SOURCE] (15, 55, 8, 2) \n", "3 [OBSERVE_TARGET#ON_SOURCE, CALIBRATE_WVR#ON_SOURCE] (50, 77, 8, 2) \n", "\n", " execution_block_UID polarization scan_name spw_name \\\n", "0 uid://A002/X1ff7b0/Xb [XX, YY] [17, 21, 25, 9] spw_0 \n", "1 uid://A002/X207fe4/X3a [XX, YY] [26, 34, 38, 42] spw_0 \n", "2 uid://A002/X207fe4/X3b9 [XX, YY] [43] spw_0 \n", "3 uid://A002/X2181fb/X49 [XX, YY] [48, 56, 60, 64] spw_0 \n", "\n", " spw_intents \\\n", "0 [UNSPECIFIED] \n", "1 [UNSPECIFIED] \n", "2 [UNSPECIFIED] \n", "3 [UNSPECIFIED] \n", "\n", " field_name \\\n", "0 [NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2] \n", "1 [NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2] \n", "2 [NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2] \n", "3 [NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2] \n", "\n", " source_name line_name field_coords \\\n", "0 [NGC4038 - Antennae North_0] [] Multi-Phase-Center \n", "1 [NGC4038 - Antennae North_0] [] Multi-Phase-Center \n", "2 [NGC4038 - Antennae North_0] [] Multi-Phase-Center \n", "3 [NGC4038 - Antennae North_0] [] Multi-Phase-Center \n", "\n", " session_reference_UID scheduling_block_UID project_UID start_frequency \\\n", "0 --- uid://A002/X1fd4e7/X64d T.B.D. 3.439281e+11 \n", "1 --- uid://A002/X1fd4e7/X64d T.B.D. 3.439281e+11 \n", "2 --- uid://A002/X1fd4e7/X64d T.B.D. 3.439281e+11 \n", "3 --- uid://A002/X1fd4e7/X64d T.B.D. 3.439281e+11 \n", "\n", " end_frequency \n", "0 3.440067e+11 \n", "1 3.440067e+11 \n", "2 3.440067e+11 \n", "3 3.440067e+11 " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import pandas as pd\n", "\n", "pd.options.display.max_colwidth = 100\n", "ps_store = \"Antennae_North.cal.lsrk.split.ps.zarr\"\n", "\n", "from xradio.measurement_set import open_processing_set\n", "\n", "fields = None\n", "ps_xdt = open_processing_set(\n", " ps_store=\"Antennae_North.cal.lsrk.split.ps.zarr\",\n", " scan_intents=[\"OBSERVE_TARGET#ON_SOURCE\"],\n", ")\n", "display(ps_xdt.xr_ps.summary())" ] }, { "cell_type": "markdown", "id": "20b44df1-613b-4eaf-9c04-55bad4c5ced3", "metadata": {}, "source": [ "## Inspect a single MS v4\n", "\n", "The [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) within a Processing Set are called [Measurement Set v4](https://docs.google.com/spreadsheets/d/14a6qMap9M5r_vjpLnaBKxsR9TF4azN5LVdOxLacOX-s/edit?usp=sharing) (MS v4)." ] }, { "cell_type": "code", "execution_count": 5, "id": "14c1b6da-4c81-4f97-9000-0f5784dfbc66", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "
<xarray.DataTree 'Antennae_North.cal.lsrk.split_0'>\n",
       "Group: /Antennae_North.cal.lsrk.split_0\n",
       "│   Dimensions:                     (time: 50, baseline_id: 45, frequency: 8,\n",
       "│                                    polarization: 2, uvw_label: 3)\n",
       "│   Coordinates:\n",
       "│     * time                        (time) float64 400B 1.307e+09 ... 1.307e+09\n",
       "│       field_name                  (time) <U46 9kB dask.array<chunksize=(50,), meta=np.ndarray>\n",
       "│       scan_name                   (time) <U21 4kB dask.array<chunksize=(50,), meta=np.ndarray>\n",
       "│     * baseline_id                 (baseline_id) int64 360B 0 1 2 3 ... 41 42 43 44\n",
       "│       baseline_antenna1_name      (baseline_id) <U9 2kB dask.array<chunksize=(45,), meta=np.ndarray>\n",
       "│       baseline_antenna2_name      (baseline_id) <U9 2kB dask.array<chunksize=(45,), meta=np.ndarray>\n",
       "│     * frequency                   (frequency) float64 64B 3.439e+11 ... 3.44e+11\n",
       "│     * polarization                (polarization) <U2 16B 'XX' 'YY'\n",
       "│     * uvw_label                   (uvw_label) <U1 12B 'u' 'v' 'w'\n",
       "│   Data variables:\n",
       "│       EFFECTIVE_INTEGRATION_TIME  (time, baseline_id) float64 18kB dask.array<chunksize=(50, 45), meta=np.ndarray>\n",
       "│       FLAG                        (time, baseline_id, frequency, polarization) bool 36kB dask.array<chunksize=(50, 45, 3, 2), meta=np.ndarray>\n",
       "│       TIME_CENTROID               (time, baseline_id) float64 18kB dask.array<chunksize=(50, 45), meta=np.ndarray>\n",
       "│       UVW                         (time, baseline_id, uvw_label) float64 54kB dask.array<chunksize=(50, 45, 3), meta=np.ndarray>\n",
       "│       VISIBILITY                  (time, baseline_id, frequency, polarization) complex128 576kB dask.array<chunksize=(50, 45, 3, 2), meta=np.ndarray>\n",
       "│       WEIGHT                      (time, baseline_id, frequency, polarization) float64 288kB dask.array<chunksize=(50, 45, 3, 2), meta=np.ndarray>\n",
       "│   Attributes:\n",
       "│       creation_date:     2026-03-10T06:46:27.899663+00:00\n",
       "│       creator:           {'software_name': 'xradio', 'version': '1.1.2'}\n",
       "│       data_groups:       {'base': {'correlated_data': 'VISIBILITY', 'date': '20...\n",
       "│       observation_info:  {'execution_block_UID': 'uid://A002/X1ff7b0/Xb', 'obse...\n",
       "│       processor_info:    {'sub_type': 'ALMA_CORRELATOR_MODE', 'type': 'CORRELAT...\n",
       "│       schema_version:    4.0.0\n",
       "│       type:              visibility\n",
       "├── Group: /Antennae_North.cal.lsrk.split_0/antenna_xds\n",
       "│       Dimensions:                 (antenna_name: 10, cartesian_pos_label: 3,\n",
       "│                                    receptor_label: 2)\n",
       "│       Coordinates:\n",
       "│         * antenna_name            (antenna_name) <U9 360B 'DV02_A015' ... 'PM03_J504'\n",
       "│           mount                   (antenna_name) <U6 240B dask.array<chunksize=(10,), meta=np.ndarray>\n",
       "│           station_name            (antenna_name) <U4 160B dask.array<chunksize=(10,), meta=np.ndarray>\n",
       "│           telescope_name          (antenna_name) <U4 160B dask.array<chunksize=(10,), meta=np.ndarray>\n",
       "│         * cartesian_pos_label     (cartesian_pos_label) <U1 12B 'x' 'y' 'z'\n",
       "│         * receptor_label          (receptor_label) <U5 40B 'pol_0' 'pol_1'\n",
       "│           polarization_type       (antenna_name, receptor_label) <U1 80B dask.array<chunksize=(10, 2), meta=np.ndarray>\n",
       "│       Data variables:\n",
       "│           ANTENNA_DISH_DIAMETER   (antenna_name) float64 80B dask.array<chunksize=(10,), meta=np.ndarray>\n",
       "│           ANTENNA_POSITION        (antenna_name, cartesian_pos_label) float64 240B dask.array<chunksize=(10, 3), meta=np.ndarray>\n",
       "│           ANTENNA_RECEPTOR_ANGLE  (antenna_name, receptor_label) float64 160B dask.array<chunksize=(10, 2), meta=np.ndarray>\n",
       "│       Attributes:\n",
       "│           overall_telescope_name:  ALMA\n",
       "│           relocatable_antennas:    True\n",
       "│           type:                    antenna\n",
       "├── Group: /Antennae_North.cal.lsrk.split_0/field_and_source_base_xds\n",
       "│       Dimensions:                       (field_name: 3, sky_dir_label: 2)\n",
       "│       Coordinates:\n",
       "│         * field_name                    (field_name) <U46 552B 'NGC4038 - Antennae ...\n",
       "│           source_name                   (field_name) <U46 552B dask.array<chunksize=(3,), meta=np.ndarray>\n",
       "│         * sky_dir_label                 (sky_dir_label) <U3 24B 'ra' 'dec'\n",
       "│       Data variables:\n",
       "│           FIELD_PHASE_CENTER_DIRECTION  (field_name, sky_dir_label) float64 48B dask.array<chunksize=(3, 2), meta=np.ndarray>\n",
       "│           SOURCE_DIRECTION              (field_name, sky_dir_label) float64 48B dask.array<chunksize=(3, 2), meta=np.ndarray>\n",
       "│       Attributes:\n",
       "│           type:     field_and_source\n",
       "└── Group: /Antennae_North.cal.lsrk.split_0/weather_xds\n",
       "        Dimensions:              (station_name: 2, time_weather: 259,\n",
       "                                  cartesian_pos_label: 3)\n",
       "        Coordinates:\n",
       "          * station_name         (station_name) <U10 80B 'Station_11' 'Station_12'\n",
       "          * time_weather         (time_weather) float64 2kB 1.307e+09 ... 1.307e+09\n",
       "          * cartesian_pos_label  (cartesian_pos_label) <U1 12B 'x' 'y' 'z'\n",
       "        Data variables:\n",
       "            DEW_POINT            (station_name, time_weather) float64 4kB dask.array<chunksize=(2, 259), meta=np.ndarray>\n",
       "            PRESSURE             (station_name, time_weather) float64 4kB dask.array<chunksize=(2, 259), meta=np.ndarray>\n",
       "            REL_HUMIDITY         (station_name, time_weather) float64 4kB dask.array<chunksize=(2, 259), meta=np.ndarray>\n",
       "            STATION_POSITION     (station_name, cartesian_pos_label) float64 48B dask.array<chunksize=(2, 3), meta=np.ndarray>\n",
       "            TEMPERATURE          (station_name, time_weather) float64 4kB dask.array<chunksize=(2, 259), meta=np.ndarray>\n",
       "            WIND_DIRECTION       (station_name, time_weather) float64 4kB dask.array<chunksize=(2, 259), meta=np.ndarray>\n",
       "            WIND_SPEED           (station_name, time_weather) float64 4kB dask.array<chunksize=(2, 259), meta=np.ndarray>\n",
       "        Attributes:\n",
       "            type:     weather
" ], "text/plain": [ "\n", "Group: /Antennae_North.cal.lsrk.split_0\n", "│ Dimensions: (time: 50, baseline_id: 45, frequency: 8,\n", "│ polarization: 2, uvw_label: 3)\n", "│ Coordinates:\n", "│ * time (time) float64 400B 1.307e+09 ... 1.307e+09\n", "│ field_name (time) \n", "│ scan_name (time) \n", "│ * baseline_id (baseline_id) int64 360B 0 1 2 3 ... 41 42 43 44\n", "│ baseline_antenna1_name (baseline_id) \n", "│ baseline_antenna2_name (baseline_id) \n", "│ * frequency (frequency) float64 64B 3.439e+11 ... 3.44e+11\n", "│ * polarization (polarization) \n", "│ FLAG (time, baseline_id, frequency, polarization) bool 36kB dask.array\n", "│ TIME_CENTROID (time, baseline_id) float64 18kB dask.array\n", "│ UVW (time, baseline_id, uvw_label) float64 54kB dask.array\n", "│ VISIBILITY (time, baseline_id, frequency, polarization) complex128 576kB dask.array\n", "│ WEIGHT (time, baseline_id, frequency, polarization) float64 288kB dask.array\n", "│ Attributes:\n", "│ creation_date: 2026-03-10T06:46:27.899663+00:00\n", "│ creator: {'software_name': 'xradio', 'version': '1.1.2'}\n", "│ data_groups: {'base': {'correlated_data': 'VISIBILITY', 'date': '20...\n", "│ observation_info: {'execution_block_UID': 'uid://A002/X1ff7b0/Xb', 'obse...\n", "│ processor_info: {'sub_type': 'ALMA_CORRELATOR_MODE', 'type': 'CORRELAT...\n", "│ schema_version: 4.0.0\n", "│ type: visibility\n", "├── Group: /Antennae_North.cal.lsrk.split_0/antenna_xds\n", "│ Dimensions: (antenna_name: 10, cartesian_pos_label: 3,\n", "│ receptor_label: 2)\n", "│ Coordinates:\n", "│ * antenna_name (antenna_name) \n", "│ station_name (antenna_name) \n", "│ telescope_name (antenna_name) \n", "│ * cartesian_pos_label (cartesian_pos_label) \n", "│ Data variables:\n", "│ ANTENNA_DISH_DIAMETER (antenna_name) float64 80B dask.array\n", "│ ANTENNA_POSITION (antenna_name, cartesian_pos_label) float64 240B dask.array\n", "│ ANTENNA_RECEPTOR_ANGLE (antenna_name, receptor_label) float64 160B dask.array\n", "│ Attributes:\n", "│ overall_telescope_name: ALMA\n", "│ relocatable_antennas: True\n", "│ type: antenna\n", "├── Group: /Antennae_North.cal.lsrk.split_0/field_and_source_base_xds\n", "│ Dimensions: (field_name: 3, sky_dir_label: 2)\n", "│ Coordinates:\n", "│ * field_name (field_name) \n", "│ * sky_dir_label (sky_dir_label) \n", "│ SOURCE_DIRECTION (field_name, sky_dir_label) float64 48B dask.array\n", "│ Attributes:\n", "│ type: field_and_source\n", "└── Group: /Antennae_North.cal.lsrk.split_0/weather_xds\n", " Dimensions: (station_name: 2, time_weather: 259,\n", " cartesian_pos_label: 3)\n", " Coordinates:\n", " * station_name (station_name) \n", " PRESSURE (station_name, time_weather) float64 4kB dask.array\n", " REL_HUMIDITY (station_name, time_weather) float64 4kB dask.array\n", " STATION_POSITION (station_name, cartesian_pos_label) float64 48B dask.array\n", " TEMPERATURE (station_name, time_weather) float64 4kB dask.array\n", " WIND_DIRECTION (station_name, time_weather) float64 4kB dask.array\n", " WIND_SPEED (station_name, time_weather) float64 4kB dask.array\n", " Attributes:\n", " type: weather" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ms_xds = ps_xdt[\n", " \"Antennae_North.cal.lsrk.split_0\"\n", "]\n", "\n", "ms_xds" ] }, { "cell_type": "markdown", "id": "9a2e880b", "metadata": {}, "source": [ "## Nomenclature\n", "\n", "- `input_data`: A dictionary of [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) or a [processing_set](https://github.com/casangi/xradio/blob/main/src/xradio/vis/_processing_set.py).\n", "- `n_datasets`: The number of [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html) in the input_data.\n", "- `i_dim`: The $\\text{i}^{\\text{th}}$ dimension name.\n", "- `n_dims`: The number of dimensions over which parallelism will occur.\n", "- `n_dim_i_chunks`: Number of chunks into which the dimension coordinate `dim_i` has been divided.\n", "- `n_nodes`: Number of nodes in the mapping stage of a MapReduce graph.\n", "- `_{}`: If curly brackets are preceded by an underscore, it indicates a subscript and not a dictionary value." ] }, { "cell_type": "markdown", "id": "aa7e11c7-18f8-4174-a713-a020a8f91273", "metadata": {}, "source": [ "## How Graph Parallelism is Specified: ```parallel_coords```\n", "\n", "The `parallel_coords` is a dictionary where the keys are dimensions over which parallelism will occur and can be any of the dimension coordinate names present in the input data. For the `MS v4` [xarray.Dataset](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html), the options include time, baseline_id (interferometer) / antenna_id (single dish), frequency, and polarization. Each dimension coordinate name is associated with a dictionary that describes the data selection for that dimension in each node of the mapping stage of the graph.\n", "\n", "The structure of the `parallel_coordinates`:\n", "```\n", " parallel_coords = {\n", " dim_0: {\n", " 'data': 1D list/np.ndarray of Number,\n", " 'data_chunks': {\n", " 0 : 1D list/np.ndarray of Number,\n", " ⋮\n", " n_dim_0_chunks-1 : ...,\n", " }\n", " 'data_chunk_edges': 1D list/np.ndarray of Number,\n", " 'dims': (dim_0,), \n", " 'attrs': measure attribute,\n", " }\n", " ⋮\n", " dim_{n_dims-1}: ...\n", " }\n", "```\n", "\n", "The `dim_i` dictionaries keys have the following meanings:\n", "\n", "- `data`: An array containing all the coordinate values associated with that dimension. These values do not necessarily have to match the values in the coordinates of the input data, as those are interpolated onto these values. The minimum and maximum values can be respectively larger or smaller than the values in the coordinates of individual [xarray.Datasets](https://docs.xarray.dev/en/stable/generated/xarray.Dataset.html); this will simply exclude that data from being processed. It's important to note that the `parallel_coords` and the input data coordinates must have the same measures attributes (reference frame, units, etc.).\n", "- `data_chunks`: A dictionary where the values are chunks of the data and the keys are integers. This chunking determines the parallelism of the graph. The values in the chunks can overlap.\n", "- `data_chunks_edges`: An array with the start and end values of each chunk.\n", "- `dims`: The dimension coordinate name.\n", "- `attrs`: The `XRADIO` measures attributes of the data (refer to [XRADIO documentation](https://docs.google.com/spreadsheets/d/14a6qMap9M5r_vjpLnaBKxsR9TF4azN5LVdOxLacOX-s/edit#gid=1504318014)).\n", "\n", "The combinations of all the chunks in `parallel_coords` determine the parallelism of the graph. For example, if you have `parallel_coords` with 5 `time` and 3 `frequency` chunks, you would have 15-way parallelism (5x3).\n", "\n", "This description may seem somewhat convoluted, but the following examples should help clarify things." ] }, { "cell_type": "markdown", "id": "86fa2f48", "metadata": {}, "source": [ "## Frequency Map Reduce" ] }, { "cell_type": "markdown", "id": "33094606-edf4-414b-800c-589146c4a55a", "metadata": {}, "source": [ "### Create Parallel Coordinates\n", "\n", "GraphVIPER offers a convenient function, [make_parallel_coord](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/coordinate_utils/index.html#graphviper.graph_tools.coordinate_utils.make_parallel_coord), that converts any [XRADIO measures](https://docs.google.com/spreadsheets/d/14a6qMap9M5r_vjpLnaBKxsR9TF4azN5LVdOxLacOX-s/edit#gid=1504318014) to a `parallel_coord`. In this case, we will use the frequency coordinate of one of the datasets in the [processing_set](https://github.com/casangi/xradio/blob/main/src/xradio/vis/_processing_set.py). It's worth noting that all datasets in this [processing_set](https://github.com/casangi/xradio/blob/main/src/xradio/vis/_processing_set.py) have the same frequency coordinates but differing time coordinates. This is the case because they represent the same spectral window but different fields in a Mosaic observation." ] }, { "cell_type": "code", "execution_count": 6, "id": "a33a781e-9b81-46c6-9c16-7510d0f4eae3", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
data: [3.43928097e+11 3.43939328e+11 3.43950560e+11 3.43961791e+11\n", " 3.43973023e+11 3.43984254e+11 3.43995486e+11 3.44006717e+11]
data_chunks
0: [3.43928097e+11 3.43939328e+11 3.43950560e+11]
1: [3.43961791e+11 3.43973023e+11 3.43984254e+11]
2: [3.43995486e+11 3.44006717e+11]
data_chunks_edges: [np.float64(343928096685.9587), np.float64(343950559663.9216), np.float64(343961791152.903), np.float64(343984254130.8659), np.float64(343995485619.84735), np.float64(344006717108.8288)]
data_chunk_slices
0: slice(0, 3, None)
1: slice(3, 6, None)
2: slice(6, 8, None)
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
" ], "text/plain": [ "" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.coordinate_utils import make_parallel_coord\n", "\n", "parallel_coords = {}\n", "n_chunks = 3\n", "parallel_coords[\"frequency\"] = make_parallel_coord(\n", " coord=ms_xds.frequency, n_chunks=n_chunks\n", ")\n", "\n", "toolviper.utils.display.DataDict.html(parallel_coords[\"frequency\"])" ] }, { "cell_type": "markdown", "id": "a679dd6a", "metadata": {}, "source": [ "The display of the frequency `parallel_coords` clearly shows how the data was split into 3 chunks. All the chunks must have the same number of values, except the last chunk, which can have fewer. `GraphVIPER` also has a convenience functions that can create [frequency](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/coordinate_utils/index.html#graphviper.graph_tools.coordinate_utils.make_frequency_coord) and [time](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/coordinate_utils/index.html#graphviper.graph_tools.coordinate_utils.make_time_coord) coordinate measures:" ] }, { "cell_type": "code", "execution_count": 7, "id": "078f9152-a57d-4c26-bd32-7736dffa850a", "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "
data: [3.43928097e+11 3.43939328e+11 3.43950560e+11 3.43961791e+11\n", " 3.43973023e+11 3.43984254e+11 3.43995486e+11 3.44006717e+11]
data_chunks
0: [3.43928097e+11 3.43939328e+11 3.43950560e+11]
1: [3.43961791e+11 3.43973023e+11 3.43984254e+11]
2: [3.43995486e+11 3.44006717e+11]
data_chunks_edges: [np.float64(343928096685.9587), np.float64(343950559663.9216), np.float64(343961791152.903), np.float64(343984254130.8659), np.float64(343995485619.84735), np.float64(344006717108.8288)]
data_chunk_slices
0: slice(0, 3, None)
1: slice(3, 6, None)
2: slice(6, 8, None)
dims: frequency
attrs
units: Hz
type: spectral_coord
velocity_frame: lsrk
" ], "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.coordinate_utils import make_frequency_coord\n", "\n", "n_chunks = 3\n", "\n", "coord = make_frequency_coord(\n", " freq_start=343928096685.9587,\n", " freq_delta=11231488.981445312,\n", " n_channels=8,\n", " velocity_frame=\"lsrk\",\n", ")\n", "parallel_coords[\"frequency\"] = make_parallel_coord(coord=coord, n_chunks=n_chunks)\n", "toolviper.utils.display.DataDict.html(parallel_coords[\"frequency\"])" ] }, { "cell_type": "markdown", "id": "f3b99bcd", "metadata": {}, "source": [ "### Create Node Task Data Mapping\n", "\n", "Now, the coordinates in the input data must be mapped onto the `parallel_coords`. This is achieved using the `interpolate_data_coords_onto_parallel_coords` function, which produces the `node_task_data_mapping`. It is a dictionary where each key is a node id of one of the nodes in the mapping stage of the graph.\n", "\n", "Structure of node_task_data_mapping:\n", "```\n", " node_task_data_mapping = {\n", " 0 : {\n", " 'chunk_indices': tuple of int,\n", " 'parallel_dims': (dim_0, ..., dim_{n_dims-1}),\n", " 'data_selection': {\n", " dataset_name_0: {\n", " dim_0: slice,\n", " ⋮\n", " dim_(n_dims-1): slice\n", " }\n", " ⋮\n", " dataset_name_{n_dataset-1}: ...\n", " }\n", " 'task_coords': \n", " dim_0:{\n", " 'data': list/np.ndarray of Number,\n", " 'dims': str,\n", " 'attrs': measure attribute,\n", " }\n", " ⋮\n", " dim_(n_dims-1): ...\n", " }\n", " ⋮\n", " n_nodes-1 : ...\n", " }\n", "```\n", "\n", "Each `node_id` dictionary has the keys with the following meaning:\n", "\n", "- `chunk_indices`: The indices assigned to the data chunks in the `parallel_coords`. There must be an index for each `parallel_dims`.\n", "- `parallel_dims`: The dimension coordinates over which parallelism will occur.\n", "- `data_selection`: A dictionary where the keys are the names of the datasets in the `processing_set`, and the values are dictionaries with the coordinates and accompanying slices. If a coordinate is not included, all values will be selected.\n", "- `task_coords`: The chunk of the parallel_coord that is assigned to this node." ] }, { "cell_type": "code", "execution_count": 8, "id": "4ffb8a9b", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
0
chunk_indices: (np.int64(0),)
parallel_dims: ['frequency']
data_selection
Antennae_North.cal.lsrk.split_0
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_1
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_2
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_3
frequency: slice(np.int64(0), np.int64(3), None)
task_coords
frequency
data: [3.43928097e+11 3.43939328e+11 3.43950560e+11]
dims: frequency
attrs
units: Hz
type: spectral_coord
velocity_frame: lsrk
slice: slice(0, 3, None)
1
chunk_indices: (np.int64(1),)
parallel_dims: ['frequency']
data_selection
Antennae_North.cal.lsrk.split_0
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_1
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_2
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_3
frequency: slice(np.int64(3), np.int64(6), None)
task_coords
frequency
data: [3.43961791e+11 3.43973023e+11 3.43984254e+11]
dims: frequency
attrs
units: Hz
type: spectral_coord
velocity_frame: lsrk
slice: slice(3, 6, None)
2
chunk_indices: (np.int64(2),)
parallel_dims: ['frequency']
data_selection
Antennae_North.cal.lsrk.split_0
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_1
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_2
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_3
frequency: slice(np.int64(6), np.int64(8), None)
task_coords
frequency
data: [3.43995486e+11 3.44006717e+11]
dims: frequency
attrs
units: Hz
type: spectral_coord
velocity_frame: lsrk
slice: slice(6, 8, None)
" ], "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.coordinate_utils import (\n", " interpolate_data_coords_onto_parallel_coords,\n", ")\n", "\n", "node_task_data_mapping = interpolate_data_coords_onto_parallel_coords(\n", " parallel_coords, ps_xdt\n", ")\n", "\n", "toolviper.utils.display.DataDict.html(node_task_data_mapping)" ] }, { "cell_type": "markdown", "id": "bbbea156-e5e0-45eb-8a40-ca1f11c97e1b", "metadata": {}, "source": [ "### Create a chunk function and map graph\n", "\n", "The [map](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/map/index.html#graphviper.graph_tools.map.map) function combines a `node_task_data_mapping` and a `node_task` to create the map portion of the graph. The `node_task` must be a function with a single dictionary input and a single output as is the `my_func` in the example below. The `map` function will pass the `input_params` dictionary to the `node_task` and add the following items from the `node_task_data_mapping`:\n", "\n", "- chunk_indices\n", "- parallel_dims\n", "- data_selection\n", "- task_coords\n", "- task_id\n", "\n", "If local caching is enabled the following will also be included with the `input_params` dictionary:\n", "\n", "- date_time\n", "- viper_local_dir" ] }, { "cell_type": "code", "execution_count": 9, "id": "93a5da8a-6b40-4912-903b-e9c9ee196fbd", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#Iff an error is given about graphviz not being installed, please install it using the following command:\n", "# conda install Graphviz\n", "\n", "%load_ext autoreload\n", "%autoreload 2\n", " \n", "from graphviper.graph_tools.map import map\n", "from graphviper.graph_tools.generate_dask_workflow import generate_dask_workflow\n", "\n", "def my_func(input_params):\n", " toolviper.utils.display.DataDict.html(input_params)\n", "\n", " print(\"*\" * 30)\n", " return input_params[\"test_input\"]\n", "\n", "\n", "input_params = {}\n", "input_params[\"test_input\"] = 42\n", "\n", "viper_graph = map(\n", " input_data=ps_xdt,\n", " node_task_data_mapping=node_task_data_mapping,\n", " node_task=my_func,\n", " input_params=input_params,\n", ")\n", "\n", "dask_graph = generate_dask_workflow(viper_graph)\n", "dask.visualize(dask_graph, filename=\"map_graph\")" ] }, { "cell_type": "code", "execution_count": 10, "id": "2dced77b", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
map
node_task:
input_params: [{'test_input': 42, 'chunk_indices': (np.int64(0),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(0), np.int64(3), None)}}, 'task_coords': {'frequency': {'data': array([3.43928097e+11, 3.43939328e+11, 3.43950560e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(0, 3, None)}}, 'task_id': 0, 'input_data': None, 'date_time': None}, {'test_input': 42, 'chunk_indices': (np.int64(1),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(3), np.int64(6), None)}}, 'task_coords': {'frequency': {'data': array([3.43961791e+11, 3.43973023e+11, 3.43984254e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(3, 6, None)}}, 'task_id': 1, 'input_data': None, 'date_time': None}, {'test_input': 42, 'chunk_indices': (np.int64(2),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(6), np.int64(8), None)}}, 'task_coords': {'frequency': {'data': array([3.43995486e+11, 3.44006717e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(6, 8, None)}}, 'task_id': 2, 'input_data': None, 'date_time': None}]
" ], "text/plain": [ "" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "toolviper.utils.display.DataDict.html(viper_graph)" ] }, { "cell_type": "code", "execution_count": 11, "id": "eadd60c8-e248-4356-9183-35fabdf27e7f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Delayed('my_func-f74a395c-84a3-42ac-9767-aa55c96bfe15'),\n", " Delayed('my_func-2f038a83-ed2a-4db2-8c75-5a37e6ae625c'),\n", " Delayed('my_func-8b396d27-3222-4397-a5ba-ee00f107d837')]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask_graph" ] }, { "cell_type": "markdown", "id": "5f509ded-23dd-4c08-aabc-d19678a7bbb8", "metadata": {}, "source": [ "### Run Map Graph" ] }, { "cell_type": "code", "execution_count": 12, "id": "252e010a-b04f-43dd-8e4f-798d31267b71", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "******************************\n", "******************************\n", "******************************\n" ] }, { "data": { "text/plain": [ "([42, 42, 42],)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask.compute(dask_graph)" ] }, { "cell_type": "markdown", "id": "291e9e20-8b36-4cb2-82b2-90fdfaf37a01", "metadata": {}, "source": [ "### Reduce Graph\n", "The [reduce](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/reduce/index.html#graphviper.graph_tools.reduce.reduce) function takes the graph created by the [map](https://graphviper.readthedocs.io/en/latest/_api/autoapi/graphviper/graph_tools/map/index.html#graphviper.graph_tools.map.map) function and adds a reduce graph that combines the outputs using one of two methods:\n", "\n", "- `single_node`: where the output from all `map` nodes is sent to a single node,\n", "- `tree`: where the outputs are combined using a binary tree reduction.\n", "\n", "The function that forms the nodes in the reduce portion of the graph must have two parameters: ``input_data`` and ``input_params``. The ``input_data`` represents the output from the mapping nodes, while ``input_params`` comes from the ``reduce`` parameter with the same name." ] }, { "cell_type": "code", "execution_count": 13, "id": "3286a1c5-cd4c-45e1-9ada-c14802538b82", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'map': {'node_task': , 'input_params': [{'test_input': 42, 'chunk_indices': (np.int64(0),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(0), np.int64(3), None)}}, 'task_coords': {'frequency': {'data': array([3.43928097e+11, 3.43939328e+11, 3.43950560e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(0, 3, None)}}, 'task_id': 0, 'input_data': None, 'date_time': None}, {'test_input': 42, 'chunk_indices': (np.int64(1),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(3), np.int64(6), None)}}, 'task_coords': {'frequency': {'data': array([3.43961791e+11, 3.43973023e+11, 3.43984254e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(3, 6, None)}}, 'task_id': 1, 'input_data': None, 'date_time': None}, {'test_input': 42, 'chunk_indices': (np.int64(2),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(6), np.int64(8), None)}}, 'task_coords': {'frequency': {'data': array([3.43995486e+11, 3.44006717e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(6, 8, None)}}, 'task_id': 2, 'input_data': None, 'date_time': None}]}, 'reduce': {'mode': 'single_node', 'node_task': , 'input_params': {'test_input': 5}}}\n" ] }, { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Single Node Reduce\n", "from graphviper.graph_tools import reduce\n", "\n", "def my_sum(graph_inputs, input_params):\n", " print(graph_inputs)\n", " return np.sum(graph_inputs / input_params[\"test_input\"])\n", "\n", "\n", "input_params = {}\n", "input_params[\"test_input\"] = 5\n", "viper_graph_reduce = reduce(\n", " viper_graph, my_sum, input_params, mode=\"single_node\"\n", ") # mode \"tree\",\"single_node\"\n", "\n", "print(viper_graph_reduce)\n", "\n", "dask_graph_reduce = generate_dask_workflow(viper_graph_reduce)\n", "dask.visualize(dask_graph_reduce)" ] }, { "cell_type": "code", "execution_count": 14, "id": "d1efd95c", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Tree Reduce\n", "from graphviper.graph_tools import reduce\n", "\n", "def my_sum(graph_inputs, input_params):\n", " print(graph_inputs)\n", " return np.sum(graph_inputs) + input_params[\"test_input\"]\n", "\n", "\n", "input_params = {}\n", "input_params[\"test_input\"] = 5\n", "viper_graph_reduce = reduce(\n", " viper_graph, my_sum, input_params, mode=\"tree\"\n", ") # mode \"tree\",\"single_node\"\n", "\n", "dask_graph_reduce = generate_dask_workflow(viper_graph_reduce)\n", "dask.visualize(dask_graph_reduce)" ] }, { "cell_type": "code", "execution_count": 15, "id": "530eafcf", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
map
node_task:
input_params: [{'test_input': 42, 'chunk_indices': (np.int64(0),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(0), np.int64(3), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(0), np.int64(3), None)}}, 'task_coords': {'frequency': {'data': array([3.43928097e+11, 3.43939328e+11, 3.43950560e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(0, 3, None)}}, 'task_id': 0, 'input_data': None, 'date_time': None}, {'test_input': 42, 'chunk_indices': (np.int64(1),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(3), np.int64(6), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(3), np.int64(6), None)}}, 'task_coords': {'frequency': {'data': array([3.43961791e+11, 3.43973023e+11, 3.43984254e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(3, 6, None)}}, 'task_id': 1, 'input_data': None, 'date_time': None}, {'test_input': 42, 'chunk_indices': (np.int64(2),), 'parallel_dims': ['frequency'], 'data_selection': {'Antennae_North.cal.lsrk.split_0': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_1': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_2': {'frequency': slice(np.int64(6), np.int64(8), None)}, 'Antennae_North.cal.lsrk.split_3': {'frequency': slice(np.int64(6), np.int64(8), None)}}, 'task_coords': {'frequency': {'data': array([3.43995486e+11, 3.44006717e+11]), 'dims': 'frequency', 'attrs': {'units': 'Hz', 'type': 'spectral_coord', 'velocity_frame': 'lsrk'}, 'slice': slice(6, 8, None)}}, 'task_id': 2, 'input_data': None, 'date_time': None}]
reduce
mode: tree
node_task:
input_params
test_input: 5
" ], "text/plain": [ "" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "toolviper.utils.display.DataDict.html(viper_graph)" ] }, { "cell_type": "markdown", "id": "717022f0-fed9-461a-9943-ea6f31714249", "metadata": {}, "source": [ "### Run Map Reduce Graph" ] }, { "cell_type": "code", "execution_count": 16, "id": "199eb981-3852-40d6-915c-70cc63818677", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "******************************\n", "******************************\n", "[42, 42]\n", "******************************\n", "[np.int64(89), 42]\n" ] }, { "data": { "text/plain": [ "(np.int64(136),)" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask.compute(dask_graph_reduce)" ] }, { "cell_type": "markdown", "id": "57673d4b-5e5e-4e1c-bf54-275ea526178e", "metadata": {}, "source": [ "## Overlapping Frequency Map Reduce" ] }, { "cell_type": "markdown", "id": "33871abd-78f6-48c1-9c7c-58388435d270", "metadata": {}, "source": [ "### Create Parallel Coordinates" ] }, { "cell_type": "code", "execution_count": 17, "id": "1278ba9b-9dcf-4a15-8265-b5e33c998d8c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
data: [343928096685.9587, 343939328174.9401, 343950559663.9216, 343961791152.903, 343973022641.88446, 343984254130.8659, 343995485619.84735, 344006717108.8288]
coords
frequency
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
data: [343928096685.9587, 343939328174.9401, 343950559663.9216, 343961791152.903, 343973022641.88446, 343984254130.8659, 343995485619.84735, 344006717108.8288]
name: frequency
data_chunks
0: [343928096685.9587, 343939328174.9401, 343950559663.9216, 343961791152.903]
1: [343961791152.903, 343973022641.88446, 343984254130.8659, 343995485619.84735]
2: [343973022641.88446, 343984254130.8659, 343995485619.84735, 344006717108.8288]
" ], "text/plain": [ "" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask.config.set(scheduler=\"synchronous\")\n", "\n", "from xradio.measurement_set import open_processing_set\n", "from IPython.display import HTML, display\n", "\n", "\n", "ps = open_processing_set(\n", " ps_store=\"Antennae_North.cal.lsrk.split.ps.zarr\",\n", " scan_intents=[\"OBSERVE_TARGET#ON_SOURCE\"],\n", ")\n", "ms_xds = ps[\"Antennae_North.cal.lsrk.split_0\"]\n", "n_chunks = 3\n", "\n", "parallel_coords = {}\n", "freq_coord = ms_xds.frequency.to_dict()\n", "# Here, we create overlapping data chunks. Currently, there is no convenience function available to assist with this.\n", "freq_coord[\"data_chunks\"] = {\n", " 0: freq_coord[\"data\"][0:4],\n", " 1: freq_coord[\"data\"][3:7],\n", " 2: freq_coord[\"data\"][4:8],\n", "}\n", "parallel_coords[\"frequency\"] = freq_coord\n", "\n", "toolviper.utils.display.DataDict.html(parallel_coords[\"frequency\"])" ] }, { "cell_type": "code", "execution_count": 18, "id": "6a6b2f44", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
namescan_intentsshapeexecution_block_UIDpolarizationscan_namespw_namespw_intentsfield_namesource_nameline_namefield_coordssession_reference_UIDscheduling_block_UIDproject_UIDstart_frequencyend_frequency
0Antennae_North.cal.lsrk.split_0[OBSERVE_TARGET#ON_SOURCE](50, 45, 8, 2)uid://A002/X1ff7b0/Xb[XX, YY][17, 21, 25, 9]spw_0[UNSPECIFIED][NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2][NGC4038 - Antennae North_0][]Multi-Phase-Center---uid://A002/X1fd4e7/X64dT.B.D.3.439281e+113.440067e+11
1Antennae_North.cal.lsrk.split_1[OBSERVE_TARGET#ON_SOURCE](50, 55, 8, 2)uid://A002/X207fe4/X3a[XX, YY][26, 34, 38, 42]spw_0[UNSPECIFIED][NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2][NGC4038 - Antennae North_0][]Multi-Phase-Center---uid://A002/X1fd4e7/X64dT.B.D.3.439281e+113.440067e+11
2Antennae_North.cal.lsrk.split_2[OBSERVE_TARGET#ON_SOURCE](15, 55, 8, 2)uid://A002/X207fe4/X3b9[XX, YY][43]spw_0[UNSPECIFIED][NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2][NGC4038 - Antennae North_0][]Multi-Phase-Center---uid://A002/X1fd4e7/X64dT.B.D.3.439281e+113.440067e+11
3Antennae_North.cal.lsrk.split_3[OBSERVE_TARGET#ON_SOURCE, CALIBRATE_WVR#ON_SOURCE](50, 77, 8, 2)uid://A002/X2181fb/X49[XX, YY][48, 56, 60, 64]spw_0[UNSPECIFIED][NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2][NGC4038 - Antennae North_0][]Multi-Phase-Center---uid://A002/X1fd4e7/X64dT.B.D.3.439281e+113.440067e+11
\n", "
" ], "text/plain": [ " name \\\n", "0 Antennae_North.cal.lsrk.split_0 \n", "1 Antennae_North.cal.lsrk.split_1 \n", "2 Antennae_North.cal.lsrk.split_2 \n", "3 Antennae_North.cal.lsrk.split_3 \n", "\n", " scan_intents shape \\\n", "0 [OBSERVE_TARGET#ON_SOURCE] (50, 45, 8, 2) \n", "1 [OBSERVE_TARGET#ON_SOURCE] (50, 55, 8, 2) \n", "2 [OBSERVE_TARGET#ON_SOURCE] (15, 55, 8, 2) \n", "3 [OBSERVE_TARGET#ON_SOURCE, CALIBRATE_WVR#ON_SOURCE] (50, 77, 8, 2) \n", "\n", " execution_block_UID polarization scan_name spw_name \\\n", "0 uid://A002/X1ff7b0/Xb [XX, YY] [17, 21, 25, 9] spw_0 \n", "1 uid://A002/X207fe4/X3a [XX, YY] [26, 34, 38, 42] spw_0 \n", "2 uid://A002/X207fe4/X3b9 [XX, YY] [43] spw_0 \n", "3 uid://A002/X2181fb/X49 [XX, YY] [48, 56, 60, 64] spw_0 \n", "\n", " spw_intents \\\n", "0 [UNSPECIFIED] \n", "1 [UNSPECIFIED] \n", "2 [UNSPECIFIED] \n", "3 [UNSPECIFIED] \n", "\n", " field_name \\\n", "0 [NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2] \n", "1 [NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2] \n", "2 [NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2] \n", "3 [NGC4038 - Antennae North_0, NGC4038 - Antennae North_1, NGC4038 - Antennae North_2] \n", "\n", " source_name line_name field_coords \\\n", "0 [NGC4038 - Antennae North_0] [] Multi-Phase-Center \n", "1 [NGC4038 - Antennae North_0] [] Multi-Phase-Center \n", "2 [NGC4038 - Antennae North_0] [] Multi-Phase-Center \n", "3 [NGC4038 - Antennae North_0] [] Multi-Phase-Center \n", "\n", " session_reference_UID scheduling_block_UID project_UID start_frequency \\\n", "0 --- uid://A002/X1fd4e7/X64d T.B.D. 3.439281e+11 \n", "1 --- uid://A002/X1fd4e7/X64d T.B.D. 3.439281e+11 \n", "2 --- uid://A002/X1fd4e7/X64d T.B.D. 3.439281e+11 \n", "3 --- uid://A002/X1fd4e7/X64d T.B.D. 3.439281e+11 \n", "\n", " end_frequency \n", "0 3.440067e+11 \n", "1 3.440067e+11 \n", "2 3.440067e+11 \n", "3 3.440067e+11 " ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\n", "ps = open_processing_set(\n", " ps_store=\"Antennae_North.cal.lsrk.split.ps.zarr\",\n", " scan_intents=[\"OBSERVE_TARGET#ON_SOURCE\"],\n", ")\n", "ps.xr_ps.summary()" ] }, { "cell_type": "markdown", "id": "364f37c4", "metadata": {}, "source": [ "### Create Node Task Data Mapping" ] }, { "cell_type": "code", "execution_count": 19, "id": "5f5e39ae", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
0
chunk_indices: (0,)
parallel_dims: ['frequency']
data_selection
Antennae_North.cal.lsrk.split_0
frequency: slice(np.int64(0), np.int64(4), None)
Antennae_North.cal.lsrk.split_1
frequency: slice(np.int64(0), np.int64(4), None)
Antennae_North.cal.lsrk.split_2
frequency: slice(np.int64(0), np.int64(4), None)
Antennae_North.cal.lsrk.split_3
frequency: slice(np.int64(0), np.int64(4), None)
task_coords
frequency
data: [343928096685.9587, 343939328174.9401, 343950559663.9216, 343961791152.903]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(None, None, None)
1
chunk_indices: (1,)
parallel_dims: ['frequency']
data_selection
Antennae_North.cal.lsrk.split_0
frequency: slice(np.int64(3), np.int64(7), None)
Antennae_North.cal.lsrk.split_1
frequency: slice(np.int64(3), np.int64(7), None)
Antennae_North.cal.lsrk.split_2
frequency: slice(np.int64(3), np.int64(7), None)
Antennae_North.cal.lsrk.split_3
frequency: slice(np.int64(3), np.int64(7), None)
task_coords
frequency
data: [343961791152.903, 343973022641.88446, 343984254130.8659, 343995485619.84735]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(None, None, None)
2
chunk_indices: (2,)
parallel_dims: ['frequency']
data_selection
Antennae_North.cal.lsrk.split_0
frequency: slice(np.int64(4), np.int64(8), None)
Antennae_North.cal.lsrk.split_1
frequency: slice(np.int64(4), np.int64(8), None)
Antennae_North.cal.lsrk.split_2
frequency: slice(np.int64(4), np.int64(8), None)
Antennae_North.cal.lsrk.split_3
frequency: slice(np.int64(4), np.int64(8), None)
task_coords
frequency
data: [343973022641.88446, 343984254130.8659, 343995485619.84735, 344006717108.8288]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(None, None, None)
" ], "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.coordinate_utils import (\n", " interpolate_data_coords_onto_parallel_coords,\n", ")\n", "\n", "node_task_data_mapping = interpolate_data_coords_onto_parallel_coords(\n", " parallel_coords, ps\n", ")\n", "\n", "toolviper.utils.display.DataDict.html(node_task_data_mapping)" ] }, { "cell_type": "code", "execution_count": 20, "id": "f9b2bdd0", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'dims': ('frequency',),\n", " 'attrs': {'channel_width': {'attrs': {'type': 'quantity', 'units': 'Hz'},\n", " 'data': 11231488.981445312,\n", " 'dims': []},\n", " 'observer': 'lsrk',\n", " 'reference_frequency': {'attrs': {'observer': 'lsrk',\n", " 'type': 'spectral_coord',\n", " 'units': 'Hz'},\n", " 'data': 343928096685.9587,\n", " 'dims': []},\n", " 'spectral_window_intents': ['UNSPECIFIED'],\n", " 'spectral_window_name': 'spw_0',\n", " 'type': 'spectral_coord',\n", " 'units': 'Hz'},\n", " 'data': [343928096685.9587,\n", " 343939328174.9401,\n", " 343950559663.9216,\n", " 343961791152.903,\n", " 343973022641.88446,\n", " 343984254130.8659,\n", " 343995485619.84735,\n", " 344006717108.8288],\n", " 'coords': {'frequency': {'dims': ('frequency',),\n", " 'attrs': {'channel_width': {'attrs': {'type': 'quantity', 'units': 'Hz'},\n", " 'data': 11231488.981445312,\n", " 'dims': []},\n", " 'observer': 'lsrk',\n", " 'reference_frequency': {'attrs': {'observer': 'lsrk',\n", " 'type': 'spectral_coord',\n", " 'units': 'Hz'},\n", " 'data': 343928096685.9587,\n", " 'dims': []},\n", " 'spectral_window_intents': ['UNSPECIFIED'],\n", " 'spectral_window_name': 'spw_0',\n", " 'type': 'spectral_coord',\n", " 'units': 'Hz'},\n", " 'data': [343928096685.9587,\n", " 343939328174.9401,\n", " 343950559663.9216,\n", " 343961791152.903,\n", " 343973022641.88446,\n", " 343984254130.8659,\n", " 343995485619.84735,\n", " 344006717108.8288]}},\n", " 'name': 'frequency',\n", " 'data_chunks': {0: [343928096685.9587,\n", " 343939328174.9401,\n", " 343950559663.9216,\n", " 343961791152.903],\n", " 1: [343961791152.903,\n", " 343973022641.88446,\n", " 343984254130.8659,\n", " 343995485619.84735],\n", " 2: [343973022641.88446,\n", " 343984254130.8659,\n", " 343995485619.84735,\n", " 344006717108.8288]},\n", " 'data_chunks_edges': [343928096685.9587,\n", " 343961791152.903,\n", " 343961791152.903,\n", " 343995485619.84735,\n", " 343973022641.88446,\n", " 344006717108.8288]}" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "{'dims': ('frequency',), 'attrs': {'channel_width': {'attrs': {'type': 'quantity', 'units': 'Hz'}, 'data': 11231488.981445312, 'dims': []}, 'observer': 'lsrk', 'reference_frequency': {'attrs': {'observer': 'lsrk', 'type': 'spectral_coord', 'units': 'Hz'}, 'data': 343928096685.9587, 'dims': []}, 'spectral_window_intents': ['UNSPECIFIED'], 'spectral_window_name': 'spw_0', 'type': 'spectral_coord', 'units': 'Hz'}, 'data': [343928096685.9587, 343939328174.9401, 343950559663.9216, 343961791152.903, 343973022641.88446, 343984254130.8659, 343995485619.84735, 344006717108.8288], 'coords': {'frequency': {'dims': ('frequency',), 'attrs': {'channel_width': {'attrs': {'type': 'quantity', 'units': 'Hz'}, 'data': 11231488.981445312, 'dims': []}, 'observer': 'lsrk', 'reference_frequency': {'attrs': {'observer': 'lsrk', 'type': 'spectral_coord', 'units': 'Hz'}, 'data': 343928096685.9587, 'dims': []}, 'spectral_window_intents': ['UNSPECIFIED'], 'spectral_window_name': 'spw_0', 'type': 'spectral_coord', 'units': 'Hz'}, \n", " 'data': [343928096685.9587, 343939328174.9401, 343950559663.9216, 343961791152.903, 343973022641.88446, 343984254130.8659, 343995485619.84735, 344006717108.8288]}}, 'name': 'frequency', \n", " 'data_chunks': {0: [343928096685.9587, 343939328174.9401, 343950559663.9216, 343961791152.903], 1: [343961791152.903, 343973022641.88446, 343984254130.8659, 343995485619.84735], 2: [343973022641.88446, 343984254130.8659, 343995485619.84735, 344006717108.8288]}, \n", " 'data_chunks_edges': [343928096685.9587, 343961791152.903, 343961791152.903, 343995485619.84735, 343973022641.88446, 344006717108.8288]}" ] }, { "cell_type": "markdown", "id": "4660e626-9561-4817-9bd5-cce3b371853e", "metadata": {}, "source": [ "### Map Graph" ] }, { "cell_type": "code", "execution_count": 21, "id": "59d7fd02-63e0-438f-b9a5-dde9789a8ac2", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.map import map\n", "from xradio.measurement_set import open_processing_set\n", "\n", "\n", "def my_func(input_params):\n", " toolviper.utils.display.DataDict.html(input_params)\n", "\n", " print(\"*\" * 30)\n", " return input_params[\"test_input\"]\n", "\n", "\n", "input_params = {}\n", "input_params[\"test_input\"] = 42\n", "\n", "ps = open_processing_set(\n", " ps_store=\"Antennae_North.cal.lsrk.split.ps.zarr\",\n", " scan_intents=[\"OBSERVE_TARGET#ON_SOURCE\"],\n", ")\n", "\n", "viper_graph = map(\n", " input_data=ps,\n", " node_task_data_mapping=node_task_data_mapping,\n", " node_task=my_func,\n", " input_params=input_params,\n", ")\n", "\n", "dask_graph = generate_dask_workflow(viper_graph)\n", "\n", "dask.visualize(dask_graph, filename=\"map_graph\")" ] }, { "cell_type": "markdown", "id": "1bbbc632-2744-4364-bac0-0c07670ff5d6", "metadata": {}, "source": [ "### Run Map Graph" ] }, { "cell_type": "code", "execution_count": 22, "id": "c08fd267-270e-410b-9a8e-122c351a70c9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "******************************\n", "******************************\n", "******************************\n" ] }, { "data": { "text/plain": [ "([42, 42, 42],)" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask.compute(dask_graph)" ] }, { "cell_type": "markdown", "id": "16514b4c-f138-48db-88e3-7d204747bff7", "metadata": {}, "source": [ "## Baseline and Frequency Map Reduce" ] }, { "cell_type": "markdown", "id": "e9f98381-34c8-400f-a7c0-b4fba291b330", "metadata": {}, "source": [ "### Create Parallel Coordinates" ] }, { "cell_type": "code", "execution_count": 23, "id": "54625049-166f-4748-ade2-2e0164d253b2", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
baseline_id
data: [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23\n", " 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44]
data_chunks
0: [ 0 1 2 3 4 5 6 7 8 9 10 11]
1: [12 13 14 15 16 17 18 19 20 21 22 23]
2: [24 25 26 27 28 29 30 31 32 33 34 35]
3: [36 37 38 39 40 41 42 43 44]
data_chunks_edges: [np.int64(0), np.int64(11), np.int64(12), np.int64(23), np.int64(24), np.int64(35), np.int64(36), np.int64(44)]
data_chunk_slices
0: slice(0, 12, None)
1: slice(12, 24, None)
2: slice(24, 36, None)
3: slice(36, 45, None)
dims: ('baseline_id',)
attrs
frequency
data: [3.43928097e+11 3.43939328e+11 3.43950560e+11 3.43961791e+11\n", " 3.43973023e+11 3.43984254e+11 3.43995486e+11 3.44006717e+11]
data_chunks
0: [3.43928097e+11 3.43939328e+11 3.43950560e+11]
1: [3.43961791e+11 3.43973023e+11 3.43984254e+11]
2: [3.43995486e+11 3.44006717e+11]
data_chunks_edges: [np.float64(343928096685.9587), np.float64(343950559663.9216), np.float64(343961791152.903), np.float64(343984254130.8659), np.float64(343995485619.84735), np.float64(344006717108.8288)]
data_chunk_slices
0: slice(0, 3, None)
1: slice(3, 6, None)
2: slice(6, 8, None)
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
" ], "text/plain": [ "" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.coordinate_utils import make_parallel_coord\n", "\n", "dask.config.set(scheduler=\"synchronous\")\n", "\n", "from xradio.measurement_set import open_processing_set\n", "\n", "intents = [\"OBSERVE_TARGET#ON_SOURCE\"]\n", "ps = open_processing_set(\n", " ps_store=\"Antennae_North.cal.lsrk.split.ps.zarr\",\n", " scan_intents=[\"OBSERVE_TARGET#ON_SOURCE\"],\n", ")\n", "\n", "ms_xds = ps[\"Antennae_North.cal.lsrk.split_0\"]\n", "\n", "parallel_coords = {}\n", "\n", "n_chunks = 4\n", "parallel_coords[\"baseline_id\"] = make_parallel_coord(\n", " coord=ms_xds.baseline_id, n_chunks=n_chunks\n", ")\n", "\n", "n_chunks = 3\n", "parallel_coords[\"frequency\"] = make_parallel_coord(\n", " coord=ms_xds.frequency, n_chunks=n_chunks\n", ")\n", "\n", "toolviper.utils.display.DataDict.html(parallel_coords)" ] }, { "cell_type": "markdown", "id": "00419ea6", "metadata": {}, "source": [ "### Create Node Task Data Mapping" ] }, { "cell_type": "code", "execution_count": 24, "id": "b0a04407", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
0
chunk_indices: (np.int64(0), np.int64(0))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(0), np.int64(3), None)
task_coords
baseline_id
data: [ 0 1 2 3 4 5 6 7 8 9 10 11]
dims: ('baseline_id',)
attrs
slice: slice(0, 12, None)
frequency
data: [3.43928097e+11 3.43939328e+11 3.43950560e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(0, 3, None)
1
chunk_indices: (np.int64(0), np.int64(1))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(3), np.int64(6), None)
task_coords
baseline_id
data: [ 0 1 2 3 4 5 6 7 8 9 10 11]
dims: ('baseline_id',)
attrs
slice: slice(0, 12, None)
frequency
data: [3.43961791e+11 3.43973023e+11 3.43984254e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(3, 6, None)
2
chunk_indices: (np.int64(0), np.int64(2))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(0), np.int64(12), None)
frequency: slice(np.int64(6), np.int64(8), None)
task_coords
baseline_id
data: [ 0 1 2 3 4 5 6 7 8 9 10 11]
dims: ('baseline_id',)
attrs
slice: slice(0, 12, None)
frequency
data: [3.43995486e+11 3.44006717e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(6, 8, None)
3
chunk_indices: (np.int64(1), np.int64(0))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(0), np.int64(3), None)
task_coords
baseline_id
data: [12 13 14 15 16 17 18 19 20 21 22 23]
dims: ('baseline_id',)
attrs
slice: slice(12, 24, None)
frequency
data: [3.43928097e+11 3.43939328e+11 3.43950560e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(0, 3, None)
4
chunk_indices: (np.int64(1), np.int64(1))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(3), np.int64(6), None)
task_coords
baseline_id
data: [12 13 14 15 16 17 18 19 20 21 22 23]
dims: ('baseline_id',)
attrs
slice: slice(12, 24, None)
frequency
data: [3.43961791e+11 3.43973023e+11 3.43984254e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(3, 6, None)
5
chunk_indices: (np.int64(1), np.int64(2))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(12), np.int64(24), None)
frequency: slice(np.int64(6), np.int64(8), None)
task_coords
baseline_id
data: [12 13 14 15 16 17 18 19 20 21 22 23]
dims: ('baseline_id',)
attrs
slice: slice(12, 24, None)
frequency
data: [3.43995486e+11 3.44006717e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(6, 8, None)
6
chunk_indices: (np.int64(2), np.int64(0))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(0), np.int64(3), None)
task_coords
baseline_id
data: [24 25 26 27 28 29 30 31 32 33 34 35]
dims: ('baseline_id',)
attrs
slice: slice(24, 36, None)
frequency
data: [3.43928097e+11 3.43939328e+11 3.43950560e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(0, 3, None)
7
chunk_indices: (np.int64(2), np.int64(1))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(3), np.int64(6), None)
task_coords
baseline_id
data: [24 25 26 27 28 29 30 31 32 33 34 35]
dims: ('baseline_id',)
attrs
slice: slice(24, 36, None)
frequency
data: [3.43961791e+11 3.43973023e+11 3.43984254e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(3, 6, None)
8
chunk_indices: (np.int64(2), np.int64(2))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(24), np.int64(36), None)
frequency: slice(np.int64(6), np.int64(8), None)
task_coords
baseline_id
data: [24 25 26 27 28 29 30 31 32 33 34 35]
dims: ('baseline_id',)
attrs
slice: slice(24, 36, None)
frequency
data: [3.43995486e+11 3.44006717e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(6, 8, None)
9
chunk_indices: (np.int64(3), np.int64(0))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(0), np.int64(3), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(0), np.int64(3), None)
task_coords
baseline_id
data: [36 37 38 39 40 41 42 43 44]
dims: ('baseline_id',)
attrs
slice: slice(36, 45, None)
frequency
data: [3.43928097e+11 3.43939328e+11 3.43950560e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(0, 3, None)
10
chunk_indices: (np.int64(3), np.int64(1))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(3), np.int64(6), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(3), np.int64(6), None)
task_coords
baseline_id
data: [36 37 38 39 40 41 42 43 44]
dims: ('baseline_id',)
attrs
slice: slice(36, 45, None)
frequency
data: [3.43961791e+11 3.43973023e+11 3.43984254e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(3, 6, None)
11
chunk_indices: (np.int64(3), np.int64(2))
parallel_dims: ['baseline_id', 'frequency']
data_selection
Antennae_North.cal.lsrk.split_0
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_1
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_2
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(6), np.int64(8), None)
Antennae_North.cal.lsrk.split_3
baseline_id: slice(np.int64(36), np.int64(45), None)
frequency: slice(np.int64(6), np.int64(8), None)
task_coords
baseline_id
data: [36 37 38 39 40 41 42 43 44]
dims: ('baseline_id',)
attrs
slice: slice(36, 45, None)
frequency
data: [3.43995486e+11 3.44006717e+11]
dims: ('frequency',)
attrs
channel_width
attrs
type: quantity
units: Hz
data: 11231488.981445312
dims: []
observer: lsrk
reference_frequency
attrs
observer: lsrk
type: spectral_coord
units: Hz
data: 343928096685.9587
dims: []
spectral_window_intents: ['UNSPECIFIED']
spectral_window_name: spw_0
type: spectral_coord
units: Hz
slice: slice(6, 8, None)
" ], "text/plain": [ "" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.coordinate_utils import (\n", " interpolate_data_coords_onto_parallel_coords,\n", ")\n", "\n", "node_task_data_mapping = interpolate_data_coords_onto_parallel_coords(\n", " parallel_coords, ps\n", ")\n", "\n", "toolviper.utils.display.DataDict.html(node_task_data_mapping)" ] }, { "cell_type": "markdown", "id": "b9a0e981-da9b-41f0-9b50-59d2731a57ce", "metadata": {}, "source": [ "### Map Graph" ] }, { "cell_type": "code", "execution_count": 25, "id": "4d3e3ec2-fc3b-43c2-a6d3-1d32a966a8e8", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.map import map\n", "\n", "def my_func(input_params):\n", " toolviper.utils.display.DataDict.html(input_params)\n", "\n", " print(\"*\" * 30)\n", " return input_params[\"test_input\"]\n", "\n", "\n", "# ['test_input', 'input_data_name', 'viper_local_dir', 'date_time', 'data_sel', 'chunk_coords', 'chunk_indx', 'chunk_id', 'parallel_dims']\n", "input_params = {}\n", "input_params[\"test_input\"] = 42\n", "\n", "viper_graph = map(\n", " input_data=ps,\n", " node_task_data_mapping=node_task_data_mapping,\n", " node_task=my_func,\n", " input_params=input_params,\n", ")\n", "\n", "dask_graph = generate_dask_workflow(viper_graph)\n", "dask.visualize(dask_graph, filename=\"map_graph\")" ] }, { "cell_type": "markdown", "id": "5bbe9975-65cb-43ad-859c-72cc35ad0557", "metadata": {}, "source": [ "### Run Map Graph" ] }, { "cell_type": "code", "execution_count": 26, "id": "b1dfee4c-4dcb-4bb9-ba12-2eca07dcea9c", "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "******************************\n", "******************************\n", "******************************\n", "******************************\n", "******************************\n", "******************************\n", "******************************\n", "******************************\n", "******************************\n", "******************************\n", "******************************\n", "******************************\n" ] }, { "data": { "text/plain": [ "([42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42],)" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask.compute(dask_graph)" ] }, { "cell_type": "markdown", "id": "f5b7f016-b05e-4773-aa2e-de8b397a0c59", "metadata": {}, "source": [ "## Time Map Reduce" ] }, { "cell_type": "markdown", "id": "b3a8a9fe-9376-461b-b472-83107d1c1c02", "metadata": {}, "source": [ "### Create Parallel Coordinates" ] }, { "cell_type": "code", "execution_count": 27, "id": "4e4acb1e-4a63-4e73-a342-30eb1013347e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
data: [1306547230.1759996, 1306547236.224, 1306547242.2720003, 1306547248.3199997, 1306547254.368, 1306547265.4560003, 1306547271.5039997, 1306547277.552, 1306547283.5999994, 1306547289.6479998, 1306547300.736, 1306547306.7840004, 1306547312.8319998, 1306547318.88, 1306547324.9279995, 1306548534.0480003, 1306548540.0959997, 1306548546.144, 1306548552.1919994, 1306548558.2399998, 1306548569.328, 1306548575.3760004, 1306548581.4240007, 1306548587.4720001, 1306548593.5199995, 1306548604.6079998, 1306548610.6560001, 1306548616.7040005, 1306548622.7519999, 1306548628.8000002, 1306549611.8400002, 1306549617.8879995, 1306549623.9359999, 1306549629.9840002, 1306549636.0320005, 1306549647.12, 1306549653.1680002, 1306549659.2159996, 1306549665.264, 1306549671.3120003, 1306549682.4000006, 1306549688.448, 1306549694.4960003, 1306549700.5439997, 1306549706.592, 1306550670.0959997, 1306550676.144, 1306550682.1919994, 1306550688.2399998, 1306550694.288, 1307136638.6879997, 1307136644.736, 1307136650.7839994, 1307136656.8319998, 1307136662.88, 1307136673.9680004, 1307136680.0159998, 1307136686.0640001, 1307136692.1119995, 1307136698.1599998, 1307136709.2480001, 1307136715.2960005, 1307136721.3439999, 1307136727.3920002, 1307136733.4400005, 1307137970.88, 1307137976.9279995, 1307137982.9759998, 1307137989.0240002, 1307137995.0719995, 1307138006.1599998, 1307138012.2080002, 1307138018.2560005, 1307138024.304, 1307138030.3519993, 1307138041.4399996, 1307138047.488, 1307138053.5360003, 1307138059.5840006, 1307138065.632, 1307139057.12, 1307139063.1680002, 1307139069.2159996, 1307139075.264, 1307139081.3119993, 1307139092.3999996, 1307139098.448, 1307139104.4960003, 1307139110.5440006, 1307139116.592, 1307139127.6799994, 1307139133.7279997, 1307139139.776, 1307139145.8240004, 1307139151.8719997, 1307140121.7600002, 1307140127.8079996, 1307140133.856, 1307140139.9039993, 1307140145.9519997, 1307147559.9359999, 1307147565.9840002, 1307147572.0319996, 1307147578.08, 1307147584.1280003, 1307147595.2160006, 1307147601.264, 1307147607.3120003, 1307147613.3600006, 1307147619.408, 1307147630.4960003, 1307147636.5439997, 1307147642.592, 1307147648.6400003, 1307147654.6880007]
data_chunks
0: [1.30654723e+09 1.30654724e+09 1.30654724e+09 1.30654725e+09\n", " 1.30654725e+09 1.30654727e+09 1.30654727e+09 1.30654728e+09\n", " 1.30654728e+09 1.30654729e+09 1.30654730e+09 1.30654731e+09\n", " 1.30654731e+09 1.30654732e+09 1.30654732e+09 1.30654853e+09\n", " 1.30654854e+09 1.30654855e+09 1.30654855e+09 1.30654856e+09\n", " 1.30654857e+09 1.30654858e+09 1.30654858e+09 1.30654859e+09\n", " 1.30654859e+09 1.30654860e+09 1.30654861e+09 1.30654862e+09\n", " 1.30654862e+09]
1: [1.30654863e+09 1.30654961e+09 1.30654962e+09 1.30654962e+09\n", " 1.30654963e+09 1.30654964e+09 1.30654965e+09 1.30654965e+09\n", " 1.30654966e+09 1.30654967e+09 1.30654967e+09 1.30654968e+09\n", " 1.30654969e+09 1.30654969e+09 1.30654970e+09 1.30654971e+09\n", " 1.30655067e+09 1.30655068e+09 1.30655068e+09 1.30655069e+09\n", " 1.30655069e+09 1.30713664e+09 1.30713664e+09 1.30713665e+09\n", " 1.30713666e+09 1.30713666e+09 1.30713667e+09 1.30713668e+09\n", " 1.30713669e+09]
2: [1.30713669e+09 1.30713670e+09 1.30713671e+09 1.30713672e+09\n", " 1.30713672e+09 1.30713673e+09 1.30713673e+09 1.30713797e+09\n", " 1.30713798e+09 1.30713798e+09 1.30713799e+09 1.30713800e+09\n", " 1.30713801e+09 1.30713801e+09 1.30713802e+09 1.30713802e+09\n", " 1.30713803e+09 1.30713804e+09 1.30713805e+09 1.30713805e+09\n", " 1.30713806e+09 1.30713807e+09 1.30713906e+09 1.30713906e+09\n", " 1.30713907e+09 1.30713908e+09 1.30713908e+09 1.30713909e+09\n", " 1.30713910e+09]
3: [1.30713910e+09 1.30713911e+09 1.30713912e+09 1.30713913e+09\n", " 1.30713913e+09 1.30713914e+09 1.30713915e+09 1.30713915e+09\n", " 1.30714012e+09 1.30714013e+09 1.30714013e+09 1.30714014e+09\n", " 1.30714015e+09 1.30714756e+09 1.30714757e+09 1.30714757e+09\n", " 1.30714758e+09 1.30714758e+09 1.30714760e+09 1.30714760e+09\n", " 1.30714761e+09 1.30714761e+09 1.30714762e+09 1.30714763e+09\n", " 1.30714764e+09 1.30714764e+09 1.30714765e+09 1.30714765e+09]
data_chunks_edges: [np.float64(1306547230.1759996), np.float64(1306548622.7519999), np.float64(1306548628.8000002), np.float64(1307136686.0640001), np.float64(1307136692.1119995), np.float64(1307139098.448), np.float64(1307139104.4960003), np.float64(1307147654.6880007)]
data_chunk_slices
0: slice(0, 29, None)
1: slice(29, 58, None)
2: slice(58, 87, None)
3: slice(87, 115, None)
dims: ('time',)
attrs
format: unix
integration_time
attrs
type: quantity
units: s
data: 6.048
dims: []
scale: utc
type: time
units: s
" ], "text/plain": [ "" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.coordinate_utils import make_parallel_coord\n", "\n", "dask.config.set(scheduler=\"synchronous\")\n", "\n", "from xradio.measurement_set import open_processing_set\n", "\n", "intents = [\"OBSERVE_TARGET#ON_SOURCE\"]\n", "ps = open_processing_set(\n", " ps_store=\"Antennae_North.cal.lsrk.split.ps.zarr\",\n", " scan_intents=[\"OBSERVE_TARGET#ON_SOURCE\"],\n", ")\n", "ms_xds = ps[\"Antennae_North.cal.lsrk.split_0\"]\n", "\n", "parallel_coords = {}\n", "\n", "import xarray as xr\n", "import numpy as np\n", "\n", "t0, t1, t2 = (ps[\"Antennae_North.cal.lsrk.split_1\"].time, ps[\"Antennae_North.cal.lsrk.split_0\"].time, ps[\"Antennae_North.cal.lsrk.split_2\"].time)\n", "time_coord = xr.concat([t0, t1, t2], dim=\"time\").sortby(\"time\").to_dict()\n", "n_chunks = 4\n", "parallel_coords[\"time\"] = make_parallel_coord(coord=time_coord, n_chunks=n_chunks)\n", "\n", "toolviper.utils.display.DataDict.html(parallel_coords[\"time\"])" ] }, { "cell_type": "markdown", "id": "5d2acace", "metadata": {}, "source": [ "### Create Node Task Data Mapping" ] }, { "cell_type": "code", "execution_count": 28, "id": "833a71ad", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
0
chunk_indices: (np.int64(0),)
parallel_dims: ['time']
data_selection
Antennae_North.cal.lsrk.split_0
time: slice(np.int64(0), np.int64(29), None)
task_coords
time
data: [1.30654723e+09 1.30654724e+09 1.30654724e+09 1.30654725e+09\n", " 1.30654725e+09 1.30654727e+09 1.30654727e+09 1.30654728e+09\n", " 1.30654728e+09 1.30654729e+09 1.30654730e+09 1.30654731e+09\n", " 1.30654731e+09 1.30654732e+09 1.30654732e+09 1.30654853e+09\n", " 1.30654854e+09 1.30654855e+09 1.30654855e+09 1.30654856e+09\n", " 1.30654857e+09 1.30654858e+09 1.30654858e+09 1.30654859e+09\n", " 1.30654859e+09 1.30654860e+09 1.30654861e+09 1.30654862e+09\n", " 1.30654862e+09]
dims: ('time',)
attrs
format: unix
integration_time
attrs
type: quantity
units: s
data: 6.048
dims: []
scale: utc
type: time
units: s
slice: slice(0, 29, None)
1
chunk_indices: (np.int64(1),)
parallel_dims: ['time']
data_selection
Antennae_North.cal.lsrk.split_0
time: slice(np.int64(29), np.int64(-1), None)
Antennae_North.cal.lsrk.split_1
time: slice(np.int64(0), np.int64(8), None)
task_coords
time
data: [1.30654863e+09 1.30654961e+09 1.30654962e+09 1.30654962e+09\n", " 1.30654963e+09 1.30654964e+09 1.30654965e+09 1.30654965e+09\n", " 1.30654966e+09 1.30654967e+09 1.30654967e+09 1.30654968e+09\n", " 1.30654969e+09 1.30654969e+09 1.30654970e+09 1.30654971e+09\n", " 1.30655067e+09 1.30655068e+09 1.30655068e+09 1.30655069e+09\n", " 1.30655069e+09 1.30713664e+09 1.30713664e+09 1.30713665e+09\n", " 1.30713666e+09 1.30713666e+09 1.30713667e+09 1.30713668e+09\n", " 1.30713669e+09]
dims: ('time',)
attrs
format: unix
integration_time
attrs
type: quantity
units: s
data: 6.048
dims: []
scale: utc
type: time
units: s
slice: slice(29, 58, None)
2
chunk_indices: (np.int64(2),)
parallel_dims: ['time']
data_selection
Antennae_North.cal.lsrk.split_1
time: slice(np.int64(8), np.int64(37), None)
task_coords
time
data: [1.30713669e+09 1.30713670e+09 1.30713671e+09 1.30713672e+09\n", " 1.30713672e+09 1.30713673e+09 1.30713673e+09 1.30713797e+09\n", " 1.30713798e+09 1.30713798e+09 1.30713799e+09 1.30713800e+09\n", " 1.30713801e+09 1.30713801e+09 1.30713802e+09 1.30713802e+09\n", " 1.30713803e+09 1.30713804e+09 1.30713805e+09 1.30713805e+09\n", " 1.30713806e+09 1.30713807e+09 1.30713906e+09 1.30713906e+09\n", " 1.30713907e+09 1.30713908e+09 1.30713908e+09 1.30713909e+09\n", " 1.30713910e+09]
dims: ('time',)
attrs
format: unix
integration_time
attrs
type: quantity
units: s
data: 6.048
dims: []
scale: utc
type: time
units: s
slice: slice(58, 87, None)
3
chunk_indices: (np.int64(3),)
parallel_dims: ['time']
data_selection
Antennae_North.cal.lsrk.split_1
time: slice(np.int64(37), np.int64(-1), None)
Antennae_North.cal.lsrk.split_2
time: slice(np.int64(0), np.int64(15), None)
task_coords
time
data: [1.30713910e+09 1.30713911e+09 1.30713912e+09 1.30713913e+09\n", " 1.30713913e+09 1.30713914e+09 1.30713915e+09 1.30713915e+09\n", " 1.30714012e+09 1.30714013e+09 1.30714013e+09 1.30714014e+09\n", " 1.30714015e+09 1.30714756e+09 1.30714757e+09 1.30714757e+09\n", " 1.30714758e+09 1.30714758e+09 1.30714760e+09 1.30714760e+09\n", " 1.30714761e+09 1.30714761e+09 1.30714762e+09 1.30714763e+09\n", " 1.30714764e+09 1.30714764e+09 1.30714765e+09 1.30714765e+09]
dims: ('time',)
attrs
format: unix
integration_time
attrs
type: quantity
units: s
data: 6.048
dims: []
scale: utc
type: time
units: s
slice: slice(87, 115, None)
" ], "text/plain": [ "" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from graphviper.graph_tools.coordinate_utils import (\n", " interpolate_data_coords_onto_parallel_coords,\n", ")\n", "\n", "node_task_data_mapping = interpolate_data_coords_onto_parallel_coords(\n", " parallel_coords, ps\n", ")\n", "\n", "toolviper.utils.display.DataDict.html(node_task_data_mapping)" ] }, { "cell_type": "markdown", "id": "1123fc67-58c1-4786-8546-a0fc47150dc6", "metadata": {}, "source": [ "### Map Graph" ] }, { "cell_type": "code", "execution_count": 29, "id": "d56f58e2-d02c-4950-9eab-9e9190660b0a", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import dask\n", "\n", "from graphviper.graph_tools.map import map\n", "\n", "def my_func(input_params):\n", " toolviper.utils.display.DataDict.html(input_params)\n", "\n", " print(\"*\" * 30)\n", " return input_params[\"test_input\"]\n", "\n", "\n", "# ['test_input', 'input_data_name', 'viper_local_dir', 'date_time', 'data_sel', 'chunk_coords', 'chunk_indx', 'chunk_id', 'parallel_dims']\n", "input_params = {}\n", "input_params[\"test_input\"] = 42\n", "\n", "viper_graph = map(\n", " input_data=ps,\n", " node_task_data_mapping=node_task_data_mapping,\n", " node_task=my_func,\n", " input_params=input_params,\n", ")\n", "\n", "dask_graph = generate_dask_workflow(viper_graph)\n", "dask.visualize(dask_graph, filename=\"map_graph\")" ] }, { "cell_type": "markdown", "id": "c030e0b0-f289-4c16-9c74-35aab1f7b011", "metadata": {}, "source": [ "### Run Map Graph" ] }, { "cell_type": "code", "execution_count": 30, "id": "a02e3704-6a3b-430a-a40a-457d6d751531", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "******************************\n", "******************************\n", "******************************\n", "******************************\n" ] }, { "data": { "text/plain": [ "([42, 42, 42, 42],)" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask.compute(dask_graph)" ] }, { "cell_type": "code", "execution_count": 31, "id": "19ac1f1f-4d49-449d-99f6-195e3737a99e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'0.0.16'" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "toolviper.__version__" ] }, { "cell_type": "code", "execution_count": null, "id": "4874d5a1-d119-42da-873e-e31e5d6ebd3e", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.15" } }, "nbformat": 4, "nbformat_minor": 5 }