{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Computation\n", "\n", "## General concepts and mechanisms\n", "\n", "### Overview\n", "\n", "Binary operations between data arrays or datasets behave as follows:\n", "\n", "| Property | Action |\n", "| --- | --- |\n", "| coord | compare, abort on mismatch |\n", "| data | apply operation |\n", "| mask | combine with `or` |\n", "| attr | typically ignored or dropped |\n", "\n", "In the special case of in-place operations such as `+=` or `*=` scipp preserves existing attributes and ignores attributes of the right-hand-side.\n", "\n", "### Dimension matching and transposing\n", "\n", "Operations \"align\" variables based on their dimension labels.\n", "That is, an operation between two variables that have a transposed memory layout behave correctly:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scipp as sc\n", "\n", "a = sc.Variable(values=np.random.rand(2, 4),\n", " variances=np.random.rand(2, 4),\n", " dims=['x', 'y'],\n", " unit=sc.units.m)\n", "b = sc.Variable(values=np.random.rand(4, 2),\n", " variances=np.random.rand(4, 2),\n", " dims=['y', 'x'],\n", " unit=sc.units.s)\n", "a/b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Propagation of uncertainties\n", "\n", "If variables have variances, operations correctly propagate uncertainties (the variances), in contrast to a naive implementation using numpy:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result = a/b\n", "result.values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a.values/np.transpose(b.values)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "result.variances" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a.variances/np.transpose(b.variances)" ] }, { "cell_type": "markdown", "metadata": { "pycharm": { "name": "#%% md\n" } }, "source": [ "The implementation assumes uncorrelated data and is otherwise based on, e.g., [Wikipedia: Propagation of uncertainty](https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae).\n", "See also [Propagation of uncertainties](../reference/error-propagation.rst) for the concrete equations used for error propagation.\n", "\n", "### Broadcasting\n", "\n", "Missing dimensions in the operands are automatically broadcast.\n", "Consider:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "var_xy = sc.Variable(dims=['x', 'y'], values=np.arange(6).reshape((2,3)))\n", "print(var_xy.values)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "var_y = sc.Variable(dims=['y'], values=np.arange(3))\n", "print(var_y.values)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "var_xy -= var_y\n", "print(var_xy.values)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since `var_y` did not depend on dimension `'x'` it is considered as \"constant\" along that dimension.\n", "That is, the *same* `var_y` values are subtracted *from all slices of dimension* `'x'` in `var_xy`.\n", "\n", "Coming back to our original variables `a` and `b`, we see that broadcasting integrates seamlessly with slicing and transposing:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a.values" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "a -= a['x', 1]\n", "a.values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Both operands may be broadcast, creating an output with the combination of input dimensions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sc.show(a['x', 1])\n", "sc.show(a['y', 1])\n", "sc.show(a['x', 1] + a['y', 1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that in-place operations such as `+=` will never change the shape of the left-hand-side.\n", "That is only the right-hand-side operation can be broadcast, and the operation fails of a broadcast of the left-hand-side would be required.\n", "\n", "### Units\n", "\n", "Units are required to be compatible:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " a + b\n", "except Exception as e:\n", " print(str(e))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Coordinate and name matching\n", "\n", "In operations with datasets, data items are paired based on their names when applying operations to datasets.\n", "Operations fail if names do not match:\n", "\n", "- In-place operations such as `+=` accept a right-hand-side operand that omits items that the left-hand-side has.\n", " If the right-hand-side contains items that are not in the left-hand-side the operation fails.\n", "- Non-in-place operations such as `+` return a new dataset with items from the intersection of the inputs.\n", "\n", "Coords are compared in operations with datasets or data arrays (or items of datasets).\n", "Operations fail if there is any mismatch in coord or label values." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d1 = sc.Dataset(\n", " data={'a': sc.Variable(dims=['x', 'y'], values=np.random.rand(2, 3)),\n", " 'b': sc.Variable(dims=['y', 'x'], values=np.random.rand(3, 2)),\n", " 'c': sc.Variable(dims=['x'], values=np.random.rand(2)),\n", " 'd': sc.scalar(value=1.0)},\n", " coords={\n", " 'x': sc.Variable(dims=['x'], values=np.arange(2.0), unit=sc.units.m),\n", " 'y': sc.Variable(dims=['y'], values=np.arange(3.0), unit=sc.units.m)})\n", "d2 = sc.Dataset(\n", " data={'a': sc.Variable(dims=['x', 'y'], values=np.random.rand(2, 3)),\n", " 'b': sc.Variable(dims=['y', 'x'], values=np.random.rand(3, 2))},\n", " coords={\n", " 'x': sc.Variable(dims=['x'], values=np.arange(2.0), unit=sc.units.m),\n", " 'y': sc.Variable(dims=['y'], values=np.arange(3.0), unit=sc.units.m)})" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d1 += d2" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "try:\n", " d2 += d1\n", "except Exception as e:\n", " print(str(e))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d3 = d1 + d2\n", "for name in d3:\n", " print(name)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "d3['a'] -= d3['b'] # transposing\n", "d3['a'] -= d3['x', 1]['b'] # broadcasting\n", "try:\n", " d3['a'] -= d3['x', 1:2]['b'] # fail due to coordinate mismatch\n", "except Exception as e:\n", " print(str(e))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arithmetics\n", "\n", "The arithmetic operations `+`, `-`, `*`, and `/` and their in-place variants `+=`, `-=`, `*=`, and `/=` are available for variables, data arrays, and datasets.\n", "They can also be combined with [slicing](slicing.ipynb)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Trigonometrics\n", "\n", "Trigonometric functions like `sin` are supported for variables.\n", "Units for angles provide a safeguard and ensure correct operation when working with either degree or radian:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "rad = 3.141593*sc.units.rad\n", "deg = 180.0*sc.units.deg\n", "print(sc.sin(rad))\n", "print(sc.sin(deg))\n", "try:\n", " rad + deg\n", "except Exception as e:\n", " print(str(e))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Other\n", "\n", "See the [list of free functions](../reference/api.rst#free-functions) for an overview of other available operations." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.10" } }, "nbformat": 4, "nbformat_minor": 4 }