diff --git a/.gitignore b/.gitignore index 9e47abe..330a467 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ doc/static .pytest_cache build dist -src/random_events.egg-info \ No newline at end of file +src/random_events.egg-info +*/__pycache__/ diff --git a/doc/index.rst b/doc/index.rst index 42d5d01..49ebf47 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -17,6 +17,9 @@ Install the package via pip install random-events +The formalism behind this package is the +`Product Measure `_. + Introduction ============ The package contains two modules. @@ -81,10 +84,13 @@ the event that the animal is either a dog or a cat. Furthermore, intersections of events are possible. The intersection is done variable wise. -The last class is the :class:`random_events.events.EncodedEvent` class, which converts events to +The next class is the :class:`random_events.events.EncodedEvent` class, which converts events to representations that are usable for indexing. For discrete variables, the values are converted to indices, while for continuous variables, the values are not changed. +The last class is the :class:`random_events.events.ComplexEvent` class. This class holds a list of disjoint +events. It can be seen as the result of operations where the result is not a single event, but a set of events. +Formally, it is the product outer measure. Examples ======== diff --git a/example/example.ipynb b/example/example.ipynb index 5cc95f1..dcaa6b9 100644 --- a/example/example.ipynb +++ b/example/example.ipynb @@ -5,6 +5,8 @@ "source": [ "# Quickstart\n", "\n", + "The tutorial walks through the most common used functions of this package.\n", + "\n", "First, import the necessary functionality." ], "metadata": { @@ -19,15 +21,26 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2023-10-25T11:20:48.308714267Z", - "start_time": "2023-10-25T11:20:48.257550261Z" + "end_time": "2024-03-15T13:14:44.222155Z", + "start_time": "2024-03-15T13:14:44.039779Z" } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": " \n " + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "from random_events.variables import Symbolic, Integer, Continuous\n", - "from random_events.events import Event, EncodedEvent\n", - "import portion" + "from random_events.events import Event, ComplexEvent\n", + "import portion\n", + "import plotly\n", + "import plotly.graph_objects as go\n", + "plotly.offline.init_notebook_mode()" ] }, { @@ -46,7 +59,7 @@ "outputs": [ { "data": { - "text/plain": "(Symbolic(name='symbol'), Integer(name='integer'), Continuous(name='real'))" + "text/plain": "(Symbolic(symbol), Integer(integer), Continuous(real))" }, "execution_count": 2, "metadata": {}, @@ -60,18 +73,14 @@ "symbol, integer, real" ], "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2023-10-25T11:20:48.354538469Z", - "start_time": "2023-10-25T11:20:48.311715019Z" - } + "collapsed": false }, "id": "49b07deb8aecb5d0" }, { "cell_type": "markdown", "source": [ - "The variables can be easily serialized using the pydantic way of converting objects to json and creating them from a json string." + "The variables can be easily serialized by converting objects to json and creating them from a json dict." ], "metadata": { "collapsed": false @@ -86,28 +95,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "{\"name\":\"symbol\",\"domain\":[\"Apple\",\"Dog\",\"Rain\"]}\n", - "name='symbol'\n", - "{\"name\":\"integer\",\"domain\":[1,2,5,6]}\n", - "name='integer'\n", - "{\"name\":\"real\",\"domain\":\"[[false, -Infinity, Infinity, false]]\"}\n", - "name='real'\n" + "{'name': 'symbol', 'type': 'random_events.variables.Symbolic', 'domain': ('Apple', 'Dog', 'Rain')}\n", + "Symbolic(symbol, ('Apple', 'Dog', 'Rain'))\n" ] } ], "source": [ - "print(symbol.model_dump_json())\n", - "print(Symbolic.model_validate_json(symbol.model_dump_json()))\n", - "print(integer.model_dump_json())\n", - "print(Integer.model_validate_json(integer.model_dump_json()))\n", - "print(real.model_dump_json())\n", - "print(Continuous.model_validate_json(real.model_dump_json()))" + "print(symbol.to_json())\n", + "print(Symbolic.from_json(symbol.to_json()))" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-25T11:20:48.354722087Z", - "start_time": "2023-10-25T11:20:48.354270523Z" + "end_time": "2024-03-15T13:14:44.262305Z", + "start_time": "2024-03-15T13:14:44.229945Z" } }, "id": "a3aed36278dab281" @@ -128,7 +129,7 @@ "outputs": [ { "data": { - "text/plain": "{Symbolic(name='symbol'): ('Rain',), Continuous(name='real'): (-inf,2)}" + "text/plain": "{Symbolic(symbol): ('Rain',), Continuous(real): (-inf,2)}" }, "execution_count": 4, "metadata": {}, @@ -142,8 +143,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-25T11:20:48.354846585Z", - "start_time": "2023-10-25T11:20:48.354366985Z" + "end_time": "2024-03-15T13:14:44.267754Z", + "start_time": "2024-03-15T13:14:44.263508Z" } }, "id": "a81cd22d80129c04" @@ -179,8 +180,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-25T11:20:48.355103739Z", - "start_time": "2023-10-25T11:20:48.354405117Z" + "end_time": "2024-03-15T13:14:44.276053Z", + "start_time": "2024-03-15T13:14:44.268462Z" } }, "id": "4975045ed9540d52" @@ -203,8 +204,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "{Continuous(name='real'): (1,2), Symbolic(name='symbol'): ('Rain',)}\n", - "{Continuous(name='real'): (1,2), Symbolic(name='symbol'): ('Rain',)}\n" + "{Symbolic(symbol): ('Rain',), Continuous(real): (1,2)}\n", + "{Symbolic(symbol): ('Rain',), Continuous(real): (1,2)}\n" ] } ], @@ -216,8 +217,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-25T11:20:48.355218483Z", - "start_time": "2023-10-25T11:20:48.354440779Z" + "end_time": "2024-03-15T13:14:44.286677Z", + "start_time": "2024-03-15T13:14:44.284174Z" } }, "id": "b4da0a20e367a1c0" @@ -225,7 +226,7 @@ { "cell_type": "markdown", "source": [ - "Similar, for unions, differences and complements:" + "For unions, differences and complements, the events generate complex events, since the result is a product outer space." ], "metadata": { "collapsed": false @@ -240,9 +241,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "{Continuous(name='real'): (-inf,4), Symbolic(name='symbol'): ('Apple', 'Rain')}\n", - "{Continuous(name='real'): [2,4), Symbolic(name='symbol'): ('Apple',)}\n", - "{Continuous(name='real'): [2,+inf), Symbolic(name='symbol'): ('Apple', 'Dog')}\n" + "{Symbolic(symbol): ('Rain',), Continuous(real): (-inf,2)} u {Symbolic(symbol): ('Apple',), Continuous(real): (1,4)} u {Symbolic(symbol): ('Rain',), Continuous(real): [2,4)}\n", + "{Symbolic(symbol): ('Apple',), Continuous(real): (1,4)} u {Symbolic(symbol): ('Rain',), Continuous(real): [2,4)}\n", + "{Symbolic(symbol): ('Apple', 'Dog'), Continuous(real): (-inf,+inf)} u {Continuous(real): [2,+inf), Symbolic(symbol): ('Rain',)}\n" ] } ], @@ -254,8 +255,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-25T11:20:48.355332510Z", - "start_time": "2023-10-25T11:20:48.354502686Z" + "end_time": "2024-03-15T13:14:44.329622Z", + "start_time": "2024-03-15T13:14:44.326152Z" } }, "id": "e8bb102d6d36e9a8" @@ -263,7 +264,7 @@ { "cell_type": "markdown", "source": [ - "At last the EncodedEvent converts from value assignments to indexed assignments. These can be easily used for array indexing and similar things. For continuous variables, the encoding does not change anything. " + "Next, the EncodedEvent converts from value assignments to indexed assignments. These can be easily used for array indexing and similar things. For continuous variables, the encoding does not change anything. " ], "metadata": { "collapsed": false @@ -278,8 +279,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "{Symbolic(name='symbol'): (2,), Continuous(name='real'): (-inf,2)}\n", - "{Symbolic(name='symbol'): (0, 2), Continuous(name='real'): (1,4)}\n" + "{Symbolic(symbol): (2,), Continuous(real): (-inf,2)}\n", + "{Symbolic(symbol): (0, 2), Continuous(real): (1,4)}\n" ] } ], @@ -290,11 +291,1875 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-25T11:20:48.355539342Z", - "start_time": "2023-10-25T11:20:48.354614193Z" + "end_time": "2024-03-15T13:14:44.411528Z", + "start_time": "2024-03-15T13:14:44.408918Z" } }, "id": "1463b40805ebaddd" + }, + { + "cell_type": "markdown", + "source": [ + "Events can also be plotted." + ], + "metadata": { + "collapsed": false + }, + "id": "d923870f7d4400a4" + }, + { + "cell_type": "code", + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + 0, + 0, + 1, + 1, + 0 + ], + "y": [ + 0, + 1, + 1, + 0, + 0 + ], + "type": "scatter" + } + ], + "layout": { + "xaxis": { + "title": { + "text": "x" + } + }, + "yaxis": { + "title": { + "text": "y" + } + }, + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + } + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x = Continuous(\"x\")\n", + "y = Continuous(\"y\")\n", + "event = Event({x: portion.open(0, 1), y: portion.open(0, 1)})\n", + "fig = go.Figure(event.plot(), event.plotly_layout())\n", + "fig.show()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-03-15T13:14:44.466827Z", + "start_time": "2024-03-15T13:14:44.453509Z" + } + }, + "id": "8c25f3846c860230", + "execution_count": 9 + }, + { + "cell_type": "markdown", + "source": [ + "Complex events can also be plotted." + ], + "metadata": { + "collapsed": false + }, + "id": "4e3a3a43ce75687f" + }, + { + "cell_type": "code", + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + -1, + -1, + 2, + 2, + -1 + ], + "y": [ + -1, + 0, + 0, + -1, + -1 + ], + "type": "scatter" + }, + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + -1, + -1, + 2, + 2, + -1 + ], + "y": [ + 1, + 2, + 2, + 1, + 1 + ], + "type": "scatter" + }, + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + -1, + -1, + 0, + 0, + -1 + ], + "y": [ + 0, + 1, + 1, + 0, + 0 + ], + "type": "scatter" + }, + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + 1, + 1, + 2, + 2, + 1 + ], + "y": [ + 0, + 1, + 1, + 0, + 0 + ], + "type": "scatter" + } + ], + "layout": { + "xaxis": { + "title": { + "text": "y" + } + }, + "yaxis": { + "title": { + "text": "x" + } + }, + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + } + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "complement = event.complement()\n", + "limiting_event = Event({x: portion.closed(-1, 2), y: portion.closed(-1, 2)})\n", + "result = complement.intersection(ComplexEvent([limiting_event]))\n", + "fig = go.Figure(result.plot(), result.plotly_layout())\n", + "fig.show()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-03-15T13:14:44.525560Z", + "start_time": "2024-03-15T13:14:44.519667Z" + } + }, + "id": "d22ef1b9c9b7c767", + "execution_count": 10 } ], "metadata": { diff --git a/example/independent_constraints.ipynb b/example/independent_constraints.ipynb index d2ee6dd..8e230e3 100644 --- a/example/independent_constraints.ipynb +++ b/example/independent_constraints.ipynb @@ -3,14 +3,15 @@ { "cell_type": "markdown", "source": [ - "# Understanding independent constraints\n", + "# Understanding product spaces\n", "\n", - "This tutorial aims to understand independent constraints as they are implemented in the `random_events` package.\n", + "This tutorial aims to understand product spaces as they are implemented in the `random_events` package.\n", "As mentioned in the introduction, research has shown that events that are described by independent constraints (rules) are most likely the only events where probability estimation is tractable.\n", + "Spaces that are constructed by independent constraints are called product spaces.\n", "\n", "Understanding the shape of such events is a key competence to building (new) tractable probabilistic models.\n", "\n", - "In this tutorial, we will work with some visualizations so make sure to install the following packages." + "In this tutorial, we will work with some visualizations of said events, to get a better understanding of their shape and behavior." ], "metadata": { "collapsed": false @@ -19,47 +20,19 @@ }, { "cell_type": "code", - "execution_count": 352, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: plotly in /home/tom_sch/.virtualenvs/random-events/lib/python3.9/site-packages (5.17.0)\r\n", - "Requirement already satisfied: tenacity>=6.2.0 in /home/tom_sch/.virtualenvs/random-events/lib/python3.9/site-packages (from plotly) (8.2.3)\r\n", - "Requirement already satisfied: packaging in /home/tom_sch/.virtualenvs/random-events/lib/python3.9/site-packages (from plotly) (23.2)\r\n", - "Requirement already satisfied: numpy in /home/tom_sch/.virtualenvs/random-events/lib/python3.9/site-packages (1.26.1)\r\n" - ] - } - ], - "source": [ - "!pip install plotly\n", - "!pip install numpy" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2023-10-26T10:12:00.006790929Z", - "start_time": "2023-10-26T10:11:58.360776210Z" - } - }, - "id": "98decef9c5c94439" - }, - { - "cell_type": "code", - "execution_count": 353, + "execution_count": 1, "id": "initial_id", "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2023-10-26T10:12:00.141534495Z", - "start_time": "2023-10-26T10:12:00.013931542Z" + "end_time": "2024-03-15T13:14:33.926843Z", + "start_time": "2024-03-15T13:14:33.754961Z" } }, "outputs": [ { "data": { - "text/html": " \n " + "text/html": " \n " }, "metadata": {}, "output_type": "display_data" @@ -67,13 +40,12 @@ ], "source": [ "from random_events.variables import Symbolic, Continuous, Integer\n", - "from random_events.events import Event\n", + "from random_events.events import Event, ComplexEvent\n", "import portion\n", "import plotly\n", "plotly.offline.init_notebook_mode()\n", "import plotly.graph_objects as go\n", - "import itertools\n", - "import numpy as np" + "import itertools" ] }, { @@ -88,13 +60,13 @@ }, { "cell_type": "code", - "execution_count": 354, + "execution_count": 2, "outputs": [ { "data": { - "text/plain": "{Symbolic(name='pets'): ('cat', 'dog'), Integer(name='age'): (5, 6, 7, 8, 9), Continuous(name='weight'): (5,50)}" + "text/plain": "{Symbolic(pets): ('cat', 'dog'), Integer(age): (5, 6, 7, 8, 9), Continuous(weight): (5,50)}" }, - "execution_count": 354, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -110,8 +82,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-26T10:12:00.142104693Z", - "start_time": "2023-10-26T10:12:00.097244111Z" + "end_time": "2024-03-15T13:14:33.932364Z", + "start_time": "2024-03-15T13:14:33.927966Z" } }, "id": "27ea010bc8892833" @@ -124,7 +96,7 @@ "- `age` is between 5 and 10\n", "- `weight` is between 5 and 50\n", "\n", - "There are no constraints possible such as `age > weight`, since these constraints are independent.\n", + "There are no constraints possible such as `age > weight`, since these constraints are dependent.\n", "In general, descriptions for events that only contain discrete information results in sets that are constructed via the cartesian product of every constraint. \n", "\n", "Let's investigate the product of the discrete information on the pets and age." @@ -136,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 355, + "execution_count": 3, "outputs": [ { "name": "stdout", @@ -162,8 +134,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-26T10:12:00.142193241Z", - "start_time": "2023-10-26T10:12:00.097342482Z" + "end_time": "2024-03-15T13:14:33.966347Z", + "start_time": "2024-03-15T13:14:33.933021Z" } }, "id": "3654b51ed6b0b7e2" @@ -191,7 +163,7 @@ }, { "cell_type": "code", - "execution_count": 356, + "execution_count": 4, "outputs": [ { "data": { @@ -200,7 +172,7 @@ { "fill": "toself", "mode": "lines", - "name": "rectangle", + "name": "Event", "x": [ 2, 2, @@ -219,6 +191,16 @@ } ], "layout": { + "xaxis": { + "title": { + "text": "x" + } + }, + "yaxis": { + "title": { + "text": "y" + } + }, "template": { "data": { "histogram2dcontour": [ @@ -1045,24 +1027,14 @@ } }, "title": { - "text": "Rectangle event" - }, - "xaxis": { - "title": { - "text": "x" - } - }, - "yaxis": { - "title": { - "text": "y" - } + "text": "Rectangle event in 2D" } }, "config": { "plotlyServerURL": "https://plot.ly" } }, - "text/html": "
" + "text/html": "
" }, "metadata": {}, "output_type": "display_data" @@ -1073,22 +1045,15 @@ "y = Continuous(\"y\")\n", "\n", "rectangle_event = Event({x: portion.closed(2, 3), y: portion.closed(10, 15)})\n", - "fig = go.Figure()\n", - "fig.add_trace(go.Scatter(x = [rectangle_event[x].lower] * 2 + [rectangle_event[x].upper] * 2 + [rectangle_event[x].lower],\n", - " y = [rectangle_event[y].lower] + [rectangle_event[y].upper] + [rectangle_event[y].upper] + [rectangle_event[y].lower] + [rectangle_event[y].lower],\n", - " mode = \"lines\",\n", - " name = \"rectangle\",\n", - " fill=\"toself\"))\n", - "fig.update_layout(title = \"Rectangle event\",\n", - " xaxis_title = \"x\",\n", - " yaxis_title = \"y\")\n", + "fig = go.Figure(rectangle_event.plot(), rectangle_event.plotly_layout())\n", + "fig.update_layout(title= \"Rectangle event in 2D\")\n", "fig.show()" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-26T10:12:00.142239053Z", - "start_time": "2023-10-26T10:12:00.097389498Z" + "end_time": "2024-03-15T13:14:33.978590Z", + "start_time": "2024-03-15T13:14:33.967321Z" } }, "id": "a86b223360f60725" @@ -1108,13 +1073,13 @@ }, { "cell_type": "code", - "execution_count": 357, + "execution_count": 5, "outputs": [ { "data": { - "text/plain": "{Continuous(name='x'): [2,3] | [4,5] | [6,7], Continuous(name='y'): [10,15] | [25,27]}" + "text/plain": "{Continuous(x): [2,3] | [4,5] | [6,7], Continuous(y): [10,15] | [25,27]}" }, - "execution_count": 357, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -1126,8 +1091,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-26T10:12:00.142373647Z", - "start_time": "2023-10-26T10:12:00.097613391Z" + "end_time": "2024-03-15T13:14:33.982121Z", + "start_time": "2024-03-15T13:14:33.979336Z" } }, "id": "86d42ecd896bef10" @@ -1144,7 +1109,7 @@ }, { "cell_type": "code", - "execution_count": 358, + "execution_count": 6, "outputs": [ { "data": { @@ -1153,7 +1118,7 @@ { "fill": "toself", "mode": "lines", - "name": "rectangle", + "name": "Event", "x": [ 2, 2, @@ -1173,7 +1138,7 @@ { "fill": "toself", "mode": "lines", - "name": "rectangle", + "name": "Event", "x": [ 2, 2, @@ -1193,7 +1158,7 @@ { "fill": "toself", "mode": "lines", - "name": "rectangle", + "name": "Event", "x": [ 4, 4, @@ -1213,7 +1178,7 @@ { "fill": "toself", "mode": "lines", - "name": "rectangle", + "name": "Event", "x": [ 4, 4, @@ -1233,7 +1198,7 @@ { "fill": "toself", "mode": "lines", - "name": "rectangle", + "name": "Event", "x": [ 6, 6, @@ -1253,7 +1218,7 @@ { "fill": "toself", "mode": "lines", - "name": "rectangle", + "name": "Event", "x": [ 6, 6, @@ -1272,6 +1237,16 @@ } ], "layout": { + "xaxis": { + "title": { + "text": "x" + } + }, + "yaxis": { + "title": { + "text": "y" + } + }, "template": { "data": { "histogram2dcontour": [ @@ -2105,57 +2080,22 @@ "plotlyServerURL": "https://plot.ly" } }, - "text/html": "
" + "text/html": "
" }, "metadata": {}, "output_type": "display_data" } ], "source": [ - "def plot_event(event: Event):\n", - " intervals = [value._intervals for value in event.values()]\n", - " simple_events = list(itertools.product(*intervals))\n", - " \n", - " x, y, z = 0, 1, 2\n", - " traces = []\n", - "\n", - " for simple_event in simple_events:\n", - "\n", - " \n", - " if len(simple_event) == 2:\n", - " \n", - " points = np.asarray(list(itertools.product(*[[axis.lower, axis.upper] for axis in simple_event])))\n", - " y_points = points[:, y]\n", - " y_points[len(y_points)//2:] = y_points[len(y_points)//2:][::-1]\n", - " traces.append(go.Scatter(x = np.append(points[:, x], points[0, x]), y = np.append(y_points, y_points[0]),\n", - " mode = \"lines\", name = \"rectangle\", fill=\"toself\"))\n", - " elif len(simple_event) == 3:\n", - " \n", - " # Create a Mesh3d trace for the rectangle\n", - " traces.append(go.Mesh3d(\n", - " \n", - " # 8 vertices of a cube\n", - " x=[simple_event[x].lower, simple_event[x].lower, simple_event[x].upper, simple_event[x].upper, simple_event[x].lower, simple_event[x].lower, simple_event[x].upper, simple_event[x].upper],\n", - " y=[simple_event[y].lower, simple_event[y].upper, simple_event[y].upper, simple_event[y].lower, simple_event[y].lower, simple_event[y].upper, simple_event[y].upper, simple_event[y].lower],\n", - " z=[simple_event[z].lower, simple_event[z].lower, simple_event[z].lower, simple_event[z].lower, simple_event[z].upper, simple_event[z].upper, simple_event[z].upper, simple_event[z].upper],\n", - "\n", - " # i, j and k give the vertices of triangles\n", - " i = [7, 0, 0, 0, 4, 4, 6, 6, 4, 0, 3, 2],\n", - " j = [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],\n", - " k = [0, 7, 2, 3, 6, 7, 1, 1, 5, 5, 7, 6],\n", - " ))\n", - " \n", - " return traces\n", - " \n", - "fig = go.Figure(plot_event(complex_event))\n", + "fig = go.Figure(complex_event.plot(), complex_event.plotly_layout())\n", "fig.update_layout(title= \"Complex event in 2D\")\n", "fig.show()" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-26T10:12:00.142651812Z", - "start_time": "2023-10-26T10:12:00.097690114Z" + "end_time": "2024-03-15T13:14:33.991792Z", + "start_time": "2024-03-15T13:14:33.982676Z" } }, "id": "7d81de549d20f02b" @@ -2172,13 +2112,14 @@ }, { "cell_type": "code", - "execution_count": 359, + "execution_count": 7, "outputs": [ { "data": { "application/vnd.plotly.v1+json": { "data": [ { + "flatshading": true, "i": [ 7, 0, @@ -2254,6 +2195,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2329,6 +2271,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2404,6 +2347,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2479,6 +2423,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2554,6 +2499,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2629,6 +2575,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2704,6 +2651,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2779,6 +2727,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2854,6 +2803,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -2929,6 +2879,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -3004,6 +2955,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -3079,6 +3031,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -3154,6 +3107,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -3229,6 +3183,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -3304,6 +3259,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -3379,6 +3335,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -3454,6 +3411,7 @@ "type": "mesh3d" }, { + "flatshading": true, "i": [ 7, 0, @@ -3530,6 +3488,23 @@ } ], "layout": { + "scene": { + "xaxis": { + "title": { + "text": "x" + } + }, + "yaxis": { + "title": { + "text": "y" + } + }, + "zaxis": { + "title": { + "text": "z" + } + } + }, "template": { "data": { "histogram2dcontour": [ @@ -4363,7 +4338,7 @@ "plotlyServerURL": "https://plot.ly" } }, - "text/html": "
" + "text/html": "
" }, "metadata": {}, "output_type": "display_data" @@ -4373,15 +4348,15 @@ "z = Continuous(\"z\")\n", "complex_event_3d = complex_event.copy()\n", "complex_event_3d[z] = portion.closed(1, 3) | portion.closed(4, 4.5) | portion.closed(10,11.5)\n", - "fig = go.Figure(plot_event(complex_event_3d))\n", + "fig = go.Figure(complex_event_3d.plot(), complex_event_3d.plotly_layout())\n", "fig.update_layout(title= \"Complex event in 3D\")\n", "fig.show()" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2023-10-26T10:12:00.142952444Z", - "start_time": "2023-10-26T10:12:00.097905829Z" + "end_time": "2024-03-15T13:14:34.007713Z", + "start_time": "2024-03-15T13:14:33.993829Z" } }, "id": "da2b51a986fa9d05" @@ -4396,6 +4371,2320 @@ "collapsed": false }, "id": "9300c1fdc0ac9ac2" + }, + { + "cell_type": "markdown", + "source": [ + "The final component to look at, is the outer space. When the complement of a rectangular event is created, the result is a set of rectangles that are not part of the original event.\n", + "This may look like this." + ], + "metadata": { + "collapsed": false + }, + "id": "2dd3f6212d340b49" + }, + { + "cell_type": "code", + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + -1, + -1, + 0, + 0, + -1 + ], + "y": [ + -1, + 2, + 2, + -1, + -1 + ], + "type": "scatter" + }, + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + 1, + 1, + 2, + 2, + 1 + ], + "y": [ + -1, + 2, + 2, + -1, + -1 + ], + "type": "scatter" + }, + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + 0, + 0, + 1, + 1, + 0 + ], + "y": [ + -1, + 0, + 0, + -1, + -1 + ], + "type": "scatter" + }, + { + "fill": "toself", + "mode": "lines", + "name": "Event", + "x": [ + 0, + 0, + 1, + 1, + 0 + ], + "y": [ + 1, + 2, + 2, + 1, + 1 + ], + "type": "scatter" + } + ], + "layout": { + "xaxis": { + "title": { + "text": "x" + } + }, + "yaxis": { + "title": { + "text": "y" + } + }, + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + } + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "event = Event({x: portion.open(0, 1), y: portion.open(0, 1)})\n", + "complement = event.complement()\n", + "limiting_event = Event({x: portion.closed(-1, 2), y: portion.closed(-1, 2)})\n", + "result = complement.intersection(ComplexEvent([limiting_event]))\n", + "fig = go.Figure(result.plot(), result.plotly_layout())\n", + "fig.show()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-03-15T13:14:34.073364Z", + "start_time": "2024-03-15T13:14:34.066945Z" + } + }, + "id": "97f6f80a61c8abaf", + "execution_count": 8 + }, + { + "cell_type": "markdown", + "source": [ + "In 3D, the outer event looks weird, but it is just the complement of the original event. You can use the interactive zoom functionality to see the missing inner event." + ], + "metadata": { + "collapsed": false + }, + "id": "bc351a7373a9ec7c" + }, + { + "cell_type": "code", + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "data": [ + { + "flatshading": true, + "i": [ + 7, + 0, + 0, + 0, + 4, + 4, + 6, + 6, + 4, + 0, + 3, + 2 + ], + "j": [ + 3, + 4, + 1, + 2, + 5, + 6, + 5, + 2, + 0, + 1, + 6, + 3 + ], + "k": [ + 0, + 7, + 2, + 3, + 6, + 7, + 1, + 1, + 5, + 5, + 7, + 6 + ], + "x": [ + -1, + -1, + 0, + 0, + -1, + -1, + 0, + 0 + ], + "y": [ + -1, + 2, + 2, + -1, + -1, + 2, + 2, + -1 + ], + "z": [ + -1, + -1, + -1, + -1, + 2, + 2, + 2, + 2 + ], + "type": "mesh3d" + }, + { + "flatshading": true, + "i": [ + 7, + 0, + 0, + 0, + 4, + 4, + 6, + 6, + 4, + 0, + 3, + 2 + ], + "j": [ + 3, + 4, + 1, + 2, + 5, + 6, + 5, + 2, + 0, + 1, + 6, + 3 + ], + "k": [ + 0, + 7, + 2, + 3, + 6, + 7, + 1, + 1, + 5, + 5, + 7, + 6 + ], + "x": [ + 1, + 1, + 2, + 2, + 1, + 1, + 2, + 2 + ], + "y": [ + -1, + 2, + 2, + -1, + -1, + 2, + 2, + -1 + ], + "z": [ + -1, + -1, + -1, + -1, + 2, + 2, + 2, + 2 + ], + "type": "mesh3d" + }, + { + "flatshading": true, + "i": [ + 7, + 0, + 0, + 0, + 4, + 4, + 6, + 6, + 4, + 0, + 3, + 2 + ], + "j": [ + 3, + 4, + 1, + 2, + 5, + 6, + 5, + 2, + 0, + 1, + 6, + 3 + ], + "k": [ + 0, + 7, + 2, + 3, + 6, + 7, + 1, + 1, + 5, + 5, + 7, + 6 + ], + "x": [ + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1 + ], + "y": [ + -1, + 2, + 2, + -1, + -1, + 2, + 2, + -1 + ], + "z": [ + -1, + -1, + -1, + -1, + 0, + 0, + 0, + 0 + ], + "type": "mesh3d" + }, + { + "flatshading": true, + "i": [ + 7, + 0, + 0, + 0, + 4, + 4, + 6, + 6, + 4, + 0, + 3, + 2 + ], + "j": [ + 3, + 4, + 1, + 2, + 5, + 6, + 5, + 2, + 0, + 1, + 6, + 3 + ], + "k": [ + 0, + 7, + 2, + 3, + 6, + 7, + 1, + 1, + 5, + 5, + 7, + 6 + ], + "x": [ + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1 + ], + "y": [ + -1, + 2, + 2, + -1, + -1, + 2, + 2, + -1 + ], + "z": [ + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2 + ], + "type": "mesh3d" + }, + { + "flatshading": true, + "i": [ + 7, + 0, + 0, + 0, + 4, + 4, + 6, + 6, + 4, + 0, + 3, + 2 + ], + "j": [ + 3, + 4, + 1, + 2, + 5, + 6, + 5, + 2, + 0, + 1, + 6, + 3 + ], + "k": [ + 0, + 7, + 2, + 3, + 6, + 7, + 1, + 1, + 5, + 5, + 7, + 6 + ], + "x": [ + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1 + ], + "y": [ + -1, + 0, + 0, + -1, + -1, + 0, + 0, + -1 + ], + "z": [ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1 + ], + "type": "mesh3d" + }, + { + "flatshading": true, + "i": [ + 7, + 0, + 0, + 0, + 4, + 4, + 6, + 6, + 4, + 0, + 3, + 2 + ], + "j": [ + 3, + 4, + 1, + 2, + 5, + 6, + 5, + 2, + 0, + 1, + 6, + 3 + ], + "k": [ + 0, + 7, + 2, + 3, + 6, + 7, + 1, + 1, + 5, + 5, + 7, + 6 + ], + "x": [ + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1 + ], + "y": [ + 1, + 2, + 2, + 1, + 1, + 2, + 2, + 1 + ], + "z": [ + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1 + ], + "type": "mesh3d" + } + ], + "layout": { + "scene": { + "xaxis": { + "title": { + "text": "x" + } + }, + "yaxis": { + "title": { + "text": "z" + } + }, + "zaxis": { + "title": { + "text": "y" + } + } + }, + "template": { + "data": { + "histogram2dcontour": [ + { + "type": "histogram2dcontour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "choropleth": [ + { + "type": "choropleth", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "histogram2d": [ + { + "type": "histogram2d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmap": [ + { + "type": "heatmap", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "heatmapgl": [ + { + "type": "heatmapgl", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "contourcarpet": [ + { + "type": "contourcarpet", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "contour": [ + { + "type": "contour", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "surface": [ + { + "type": "surface", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ] + } + ], + "mesh3d": [ + { + "type": "mesh3d", + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + ], + "scatter": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scatter" + } + ], + "parcoords": [ + { + "type": "parcoords", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolargl": [ + { + "type": "scatterpolargl", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "bar": [ + { + "error_x": { + "color": "#f2f5fa" + }, + "error_y": { + "color": "#f2f5fa" + }, + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "scattergeo": [ + { + "type": "scattergeo", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterpolar": [ + { + "type": "scatterpolar", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "scattergl": [ + { + "marker": { + "line": { + "color": "#283442" + } + }, + "type": "scattergl" + } + ], + "scatter3d": [ + { + "type": "scatter3d", + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattermapbox": [ + { + "type": "scattermapbox", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scatterternary": [ + { + "type": "scatterternary", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "scattercarpet": [ + { + "type": "scattercarpet", + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + } + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "baxis": { + "endlinecolor": "#A2B1C6", + "gridcolor": "#506784", + "linecolor": "#506784", + "minorgridcolor": "#506784", + "startlinecolor": "#A2B1C6" + }, + "type": "carpet" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#506784" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "header": { + "fill": { + "color": "#2a3f5f" + }, + "line": { + "color": "rgb(17,17,17)" + } + }, + "type": "table" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "rgb(17,17,17)", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ] + }, + "layout": { + "autotypenumbers": "strict", + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#f2f5fa" + }, + "hovermode": "closest", + "hoverlabel": { + "align": "left" + }, + "paper_bgcolor": "rgb(17,17,17)", + "plot_bgcolor": "rgb(17,17,17)", + "polar": { + "bgcolor": "rgb(17,17,17)", + "angularaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "radialaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "ternary": { + "bgcolor": "rgb(17,17,17)", + "aaxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "baxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + }, + "caxis": { + "gridcolor": "#506784", + "linecolor": "#506784", + "ticks": "" + } + }, + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "sequential": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1.0, + "#f0f921" + ] + ], + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ] + }, + "xaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "yaxis": { + "gridcolor": "#283442", + "linecolor": "#506784", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "#283442", + "automargin": true, + "zerolinewidth": 2 + }, + "scene": { + "xaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "yaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + }, + "zaxis": { + "backgroundcolor": "rgb(17,17,17)", + "gridcolor": "#506784", + "linecolor": "#506784", + "showbackground": true, + "ticks": "", + "zerolinecolor": "#C8D4E3", + "gridwidth": 2 + } + }, + "shapedefaults": { + "line": { + "color": "#f2f5fa" + } + }, + "annotationdefaults": { + "arrowcolor": "#f2f5fa", + "arrowhead": 0, + "arrowwidth": 1 + }, + "geo": { + "bgcolor": "rgb(17,17,17)", + "landcolor": "rgb(17,17,17)", + "subunitcolor": "#506784", + "showland": true, + "showlakes": true, + "lakecolor": "rgb(17,17,17)" + }, + "title": { + "x": 0.05 + }, + "updatemenudefaults": { + "bgcolor": "#506784", + "borderwidth": 0 + }, + "sliderdefaults": { + "bgcolor": "#C8D4E3", + "borderwidth": 1, + "bordercolor": "rgb(17,17,17)", + "tickwidth": 0 + }, + "mapbox": { + "style": "dark" + } + } + } + }, + "config": { + "plotlyServerURL": "https://plot.ly" + } + }, + "text/html": "
" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "event = Event({x: portion.closed(0, 1),\n", + " y: portion.closed(0, 1),\n", + " z: portion.closed(0, 1)})\n", + "complement = event.complement()\n", + "limiting_event = Event({x: portion.closed(-1, 2),\n", + " y: portion.closed(-1, 2),\n", + " z: portion.closed(-1, 2)})\n", + "result = complement.intersection(ComplexEvent([limiting_event]))\n", + "fig = go.Figure(result.plot(), result.plotly_layout())\n", + "fig.show()" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-03-15T13:14:34.170331Z", + "start_time": "2024-03-15T13:14:34.160312Z" + } + }, + "id": "9e47b2f884e4c9dd", + "execution_count": 9 } ], "metadata": { diff --git a/requirements.txt b/requirements.txt index 97fc0eb..b73d3c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,4 @@ portion>=2.4.1 +numpy>=1.24.4 +plotly>=5.20.0 +typing_extensions>=4.10.0 diff --git a/src/random_events/__init__.py b/src/random_events/__init__.py index 09964d6..3f39079 100644 --- a/src/random_events/__init__.py +++ b/src/random_events/__init__.py @@ -1 +1 @@ -__version__ = '1.2.5' +__version__ = '2.0.1' diff --git a/src/random_events/events.py b/src/random_events/events.py index 041bd26..be34780 100644 --- a/src/random_events/events.py +++ b/src/random_events/events.py @@ -1,13 +1,17 @@ from __future__ import annotations +import itertools from collections import UserDict -from typing import TYPE_CHECKING, Iterable -from typing import Union, Any +import numpy as np import portion +import plotly.graph_objects as go + +from typing_extensions import Set, Union, Any, TYPE_CHECKING, Iterable, List, Self, Dict from .variables import Variable, Continuous, Discrete + # Type hinting for Python 3.7 to 3.9 if TYPE_CHECKING: VariableMapType = UserDict[str, Variable] @@ -46,6 +50,9 @@ def __setitem__(self, key: Union[str, Variable], value: Any): raise TypeError(f"Key must be a Variable, not {type(key)}") super().__setitem__(key, value) + def __copy__(self): + return self.__class__({variable: value for variable, value in self.items()}) + # Type hinting for Python 3.7 to 3.9 if TYPE_CHECKING: @@ -54,195 +61,191 @@ def __setitem__(self, key: Union[str, Variable], value: Any): EventMapType = VariableMap -class Event(EventMapType): +class SupportsSetOperations: """ - A map of variables to values of their respective domains. + A class that supports set operations. """ - def check_same_type(self, other: Any): + def union(self, other: Self) -> Self: """ - Check that both self and other are of the same type. - - :param other: The other object + Form the union of this object with another object. """ - if type(self) is not type(other): - raise TypeError(f"Cannot use operation on {type(self)} with {type(other)}") - - def intersection(self, other: 'Event') -> 'Event': - """ - Get the intersection of this and another event. + raise NotImplementedError - If one variable is only in one of the events, it is assumed that the other does not constrain it. + def __or__(self, other: Self): + return self.union(other) - :return: The intersection + def intersection(self, other: Self) -> Self: """ + Form the intersection of this object with another object. + """ + raise NotImplementedError - self.check_same_type(other) - - result = self.__class__() - - variables = set(self.keys()) | set(other.keys()) - - for variable in variables: - - if isinstance(variable, Discrete): - - # get the entire domain - value = set(variable.domain) - - # intersect with the constraint of self - if variable in self: - value &= set(self[variable]) + def __and__(self, other): + return self.intersection(other) - # intersect with the constraint of other - if variable in other: - value &= set(other[variable]) + def difference(self, other: Self) -> Self: + """ + Form the difference of this object with another object. + """ + raise NotImplementedError - # convert back to tuple - value = tuple(sorted(value)) + def __sub__(self, other): + return self.difference(other) - # if the variable is continuous - elif isinstance(variable, Continuous): + def complement(self) -> Self: + """ + Form the complement of this object. + """ + raise NotImplementedError - # get the entire domain - value = variable.domain + def __invert__(self): + return self.complement() - # intersect with the constraint of self - if variable in self: - value &= self[variable] + def is_empty(self) -> bool: + """ + Check if this object is empty. + """ + raise NotImplementedError - # intersect with the constraint of other - if variable in other: - value &= other[variable] - else: - raise TypeError(f"Unknown variable type {type(variable)}") +class Event(SupportsSetOperations, EventMapType): + """ + A map of variables to values of their respective domains. + """ - result[variable] = value + def check_same_type(self, other: Any): + """ + Check that both self and other are of the same type. - return result + :param other: The other object + """ + if type(self) is not type(other): + raise TypeError(f"Cannot use operation on {type(self)} with {type(other)}") - __and__ = intersection - """Alias for intersection.""" + def intersection(self, other: EventType) -> EventType: - def union(self, other: 'Event') -> 'Event': - """ - Get the union of this and another event. + # if the other is a complex event + if isinstance(other, ComplexEvent): - If one variable is only in one of the events, the union is the corresponding element. - """ + # flip the call + return other.intersection(ComplexEvent([self])) self.check_same_type(other) - result = self.__class__() variables = set(self.keys()) | set(other.keys()) for variable in variables: + assignment1 = self.get(variable, variable.domain) + assignment2 = other.get(variable, variable.domain) + intersection = variable.intersection_of_assignments(assignment1, assignment2) + if len(intersection) == 0: + return self.__class__() + result[variable] = intersection - if isinstance(variable, Discrete): - - # get the entire domain - value = set() - - # intersect with the constraint of self - if variable in self: - value |= set(self[variable]) + return result - # intersect with the constraint of other - if variable in other: - value |= set(other[variable]) + def union(self, other: EventType) -> ComplexEvent: + # create complex event from self + complex_self = ComplexEvent([self]) - # convert back to tuple - value = tuple(sorted(value)) + # if the other is a complex event + if isinstance(other, ComplexEvent): - # if the variable is continuous - elif isinstance(variable, Continuous): + # flip the call + return other.union(complex_self) - # get the entire domain - value = portion.empty() + self.check_same_type(other) - # intersect with the constraint of self - if variable in self: - value |= self[variable] + # form the intersection + intersection = self.intersection(other) - # intersect with the constraint of other - if variable in other: - value |= other[variable] + # if the intersection of the two events is empty + if intersection.is_empty(): - else: - raise TypeError(f"Unknown variable type {type(variable)}") + # trivially mount it + complex_self.events.append(other) + return complex_self - result[variable] = value + # form complement of intersection + complement_of_intersection = intersection.complement() - return result + # intersect the other event with the complement of the intersection + fragments_of_other = complement_of_intersection.intersection(ComplexEvent([other])) - __or__ = union - """Alias for union.""" + # add the fragments of the other event + complex_self.events.extend(fragments_of_other.events) + return complex_self - def difference(self, other: 'Event') -> 'Event': - """ - Get the difference of this and another event. + def difference(self, other: EventType) -> ComplexEvent: + # if the other is a complex event + if isinstance(other, ComplexEvent): - If a variable appears only in `other`, it is assumed that `self` has the entire domain as default value. - """ + # flip the call + return other.complement().intersection(ComplexEvent([self])) self.check_same_type(other) - result = self.__class__() + # form the intersection + intersection = self.intersection(other) - variables = set(self.keys()) | set(other.keys()) + # if the intersection of the two events is empty + if intersection.is_empty(): + return ComplexEvent([self]) - for variable in variables: + # form complement of intersection + complement_of_intersection = intersection.complement() - if isinstance(variable, Discrete): + # construct intersection of complement + return ComplexEvent([event.intersection(self) for event in complement_of_intersection.events + if not event.is_empty()]) - # intersect with the constraint of self - if variable in self: - value = set(self[variable]) - else: - value = set(variable.domain) + def complement(self) -> ComplexEvent: + # initialize events + events = [] - # intersect with the constraint of other - if variable in other: - value -= set(other[variable]) + # get variables as set + variables: Set[Variable] = set(self.keys()) - # convert back to tuple - value = tuple(sorted(value)) + # memorize processed variables + processed_variables = [] - # if the variable is continuous - elif isinstance(variable, Continuous): + # for every assignment + for variable, value in self.items(): - # intersect with the constraint of self - if variable in self: - value = self[variable] - else: - value = portion.open(-portion.inf, portion.inf) - - # intersect with the constraint of other - if variable in other: - value -= other[variable] + # create the current complementary event + complement_event = self.__class__() - else: - raise TypeError(f"Unknown variable type {type(variable)}") + # invert this variables assignment + complement_event[variable] = variable.complement_of_assignment( + value, encoded=isinstance(self, EncodedEvent)) - result[variable] = value + # for every other variable + for other_variable in variables.difference({variable}): - return result + # if the other variable is already processed + if other_variable in processed_variables: + # add the assignment to the current event + complement_event[other_variable] = self[other_variable] + else: + # add the entire domain to the current event + other_domain = other_variable.domain - __sub__ = difference - """Alias for difference.""" + # encode if necessary + if isinstance(self, EncodedEvent): + other_domain = other_variable.encode_many(other_domain) + complement_event[other_variable] = other_domain - def complement(self) -> 'Event': - """ - Get the complement of this event. - """ - return self.__class__() - self + # add to processed variables + processed_variables.append(variable) - __invert__ = complement - """Alias for complement.""" + # add to complex event + if not complement_event.is_empty(): + events.append(complement_event) + return ComplexEvent(events) - def __eq__(self, other: 'Event') -> bool: + def __eq__(self, other: Self) -> bool: """ Check if two events are equal. @@ -313,7 +316,7 @@ def check_element(variable: Variable, element: Any) -> Union[tuple, portion.Inte raise TypeError(f"Unknown variable type {type(variable)}") def __setitem__(self, key: Union[str, Variable], value: Any): - super().__setitem__(key, self.check_element(key, value)) + EventMapType.__setitem__(self, key, self.check_element(key, value)) def encode(self) -> 'EncodedEvent': """ @@ -323,10 +326,95 @@ def encode(self) -> 'EncodedEvent': return EncodedEvent({variable: variable.encode_many(element) for variable, element in self.items()}) def is_empty(self) -> bool: + return any(len(value) == 0 for value in self.values()) or len(self.keys()) == 0 + + def plot(self) -> Union[List[go.Scatter], List[go.Mesh3d]]: """ - Check if this event is empty + Plot the event. """ - return any(len(value) == 0 for value in self.values()) + assert all(isinstance(variable, Continuous) for variable in self.keys()), "Can only plot continuous events" + if len(self.keys()) == 2: + return self.plot_2d() + elif len(self.keys()) == 3: + return self.plot_3d() + else: + raise ValueError("Can only plot 2D and 3D events") + + def plot_2d(self) -> List[go.Scatter]: + """ + Plot the event in 2D. + """ + + # form cartesian product of all intervals + intervals = [value._intervals for value in self.values()] + simple_events = list(itertools.product(*intervals)) + traces = [] + + # for every atomic interval + for simple_event in simple_events: + + # plot a rectangle + points = np.asarray(list(itertools.product(*[[axis.lower, axis.upper] for axis in simple_event]))) + y_points = points[:, 1] + y_points[len(y_points) // 2:] = y_points[len(y_points) // 2:][::-1] + traces.append(go.Scatter(x=np.append(points[:, 0], points[0, 0]), y=np.append(y_points, y_points[0]), + mode="lines", name="Event", fill="toself")) + return traces + + def plot_3d(self) -> List[go.Mesh3d]: + """ + Plot the event in 3D. + """ + + # form cartesian product of all intervals + intervals = [value._intervals for value in self.values()] + simple_events = list(itertools.product(*intervals)) + traces = [] + + # shortcut for the dimensions + x, y, z = 0, 1, 2 + + # for every atomic interval + for simple_event in simple_events: + + # Create a 3D mesh trace for the rectangle + traces.append(go.Mesh3d( + # 8 vertices of a cube + x=[simple_event[x].lower, simple_event[x].lower, simple_event[x].upper, simple_event[x].upper, + simple_event[x].lower, simple_event[x].lower, simple_event[x].upper, simple_event[x].upper], + y=[simple_event[y].lower, simple_event[y].upper, simple_event[y].upper, simple_event[y].lower, + simple_event[y].lower, simple_event[y].upper, simple_event[y].upper, simple_event[y].lower], + z=[simple_event[z].lower, simple_event[z].lower, simple_event[z].lower, simple_event[z].lower, + simple_event[z].upper, simple_event[z].upper, simple_event[z].upper, simple_event[z].upper], + # i, j and k give the vertices of triangles + i=[7, 0, 0, 0, 4, 4, 6, 6, 4, 0, 3, 2], + j=[3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3], + k=[0, 7, 2, 3, 6, 7, 1, 1, 5, 5, 7, 6], + flatshading=True + )) + return traces + + def plotly_layout(self) -> Dict: + """ + Create a layout for the plotly plot. + """ + variables = list(self.keys()) + if len(variables) == 2: + result = {"xaxis_title": variables[0].name, + "yaxis_title": variables[1].name} + elif len(variables) == 3: + result = dict(scene=dict( + xaxis_title=variables[0].name, + yaxis_title=variables[1].name, + zaxis_title=variables[2].name) + ) + else: + raise NotImplementedError("Can only plot 2D and 3D events") + + return result + + def __hash__(self): + return hash(tuple(sorted(self.items()))) class EncodedEvent(Event): @@ -370,6 +458,181 @@ def decode(self) -> Event: Decode the event to a normal event. :return: The decoded event """ - return Event( - {variable: variable.decode_many(value) for variable, value in - self.items()}) + return Event({variable: variable.decode_many(value) for variable, value in self.items()}) + + +class ComplexEvent(SupportsSetOperations): + """ + A complex event is a set of mutually exclusive events. + """ + events: List[Event] + + def __init__(self, events: Iterable[Event]): + self.events = list(event for event in events if not event.is_empty()) + + def union(self, other: Self) -> Self: + result = ComplexEvent(self.events + other.events) + return result.make_events_disjoint().simplify() + + def make_events_disjoint(self) -> Self: + """ + Make all events in this complex event disjoint. + + This is done by forming the intersection of all events recursively until no more intersections are found. + Then, the original events are decomposed into their disjoint components. + Finally, a complex event is formed from the disjoint components. Note that the result may not be the minimal + representation of the complex event, so it is advised to call ``simplify`` afterward. + """ + + # initialize previous intersections + previous_intersections = [] + + # for every pair of events + for index, event in enumerate(self.events): + for other_event in self.events[index + 1:]: + + # append intersection of pairwise events + intersection = event.intersection(other_event) + if not intersection.is_empty() and intersection not in previous_intersections: + previous_intersections.append(intersection) + + # if there are no intersections, skip the rest + if len(previous_intersections) == 0: + return self + + # while + while len(previous_intersections) > 0: + + # initialize new intersections + new_intersections = [] + + # form pairwise intersections of previous intersections + for index, intersection in enumerate(previous_intersections): + for other_intersection in previous_intersections[index + 1:]: + if not intersection.intersection(other_intersection).is_empty(): + new_intersections.append(intersection) + + if len(new_intersections) == 0: + break + + previous_intersections = new_intersections + + # sanity check + complex_event_of_intersections = ComplexEvent(previous_intersections) + assert complex_event_of_intersections.are_events_disjoint(), "Events are not disjoint" + + # initialize result + result = ComplexEvent(complex_event_of_intersections.events) + + # for every original event + for original_event in self.events: + + # initialize the difference of the original events with the intersection + decomposed_original_events = set() + + # for every atomic intersection in the disjoint intersections + for atomic_intersection in complex_event_of_intersections.events: + + # get the difference of the original event with the atomic intersection + original_event_disjoint_component = original_event.difference(atomic_intersection) + + # unify the differences + decomposed_original_events = decomposed_original_events.union( + set(original_event_disjoint_component.events)) + + # add the differences to the result + result.events.extend(decomposed_original_events) + return result + + def simplify(self) -> Self: + """ + Simplify the complex event such that sub-unions of events that can be expressed as a single events + are merged. + """ + + # for every pair of events + for index, event in enumerate(self.events): + for other_event in self.events[index + 1:]: + + # for every variable in the event + for variable, value in event.items(): + + # if the events match in this dimension + if other_event[variable] == value: + + # form the simpler union of the two events + unified_event = Event({variable: value}) + for variable_ in event.keys(): + if variable_ != variable: + unified_event[variable_] = variable.union_of_assignments(event[variable_], + other_event[variable_]) + # recurse into the simpler complex event + result = ComplexEvent([]) + result.events.append(unified_event) + result.events.extend([event__ for event__ in self.events if event__ != event + and event__ != other_event]) + return result.simplify() + + # if no simplification is possible, return the current complex event + return self.__copy__() + + def intersection(self, other: Self) -> Self: + intersections = [event.intersection(other_event) for other_event in other.events for event in self.events] + return ComplexEvent(intersections) + + def difference(self, other: Self) -> Self: + return self.intersection(other.complement()) + + def complement(self) -> Self: + result = self.events[0].complement() + for event in self.events[1:]: + current_complement = event.complement() + result = result.intersection(current_complement) + return result.make_events_disjoint().simplify() + + def are_events_disjoint(self) -> bool: + """ + Check if all events inside this complex event are disjoint. + """ + for index, event in enumerate(self.events): + for event_ in self.events[index + 1:]: + if not event.intersection(event_).is_empty(): + return False + return True + + def __str__(self): + return " u ".join(str(event) for event in self.events) + + def __repr__(self): + return f"Union of {len(self.events)} events" + + def __eq__(self, other: ComplexEvent) -> bool: + """ + Check if two complex events are equal. + """ + return (all(event in other.events for event in self.events) + and all(event in self.events for event in other.events)) + + def __copy__(self): + return self.__class__([event.copy() for event in self.events]) + + def plot(self) -> Union[List[go.Scatter], List[go.Mesh3d]]: + """ + Plot the complex event. + """ + traces = [] + for event in self.events: + traces.extend(event.plot()) + return traces + + def plotly_layout(self) -> Dict: + """ + Create a layout for the plotly plot. + """ + return self.events[0].plotly_layout() + + def is_empty(self) -> bool: + return len(self.events) == 0 + + +EventType = Union[Event, EncodedEvent, ComplexEvent] \ No newline at end of file diff --git a/src/random_events/variables.py b/src/random_events/variables.py index 4d07890..78e0230 100644 --- a/src/random_events/variables.py +++ b/src/random_events/variables.py @@ -1,9 +1,12 @@ from typing import Any, Iterable, Dict, Tuple import portion +from typing_extensions import Union from . import utils +AssignmentType = Union[portion.Interval, Tuple] + class Variable: """ @@ -15,7 +18,7 @@ class Variable: The name of the variable. The name is used for comparison and hashing. """ - domain: Any + domain: AssignmentType """ The set of possible events of the variable. """ @@ -112,6 +115,44 @@ def from_json(cls, data: Dict[str, Any]) -> 'Variable': raise ValueError("Unknown type for variable. Type is {}".format(data["type"])) + def complement_of_assignment(self, assignment: AssignmentType, encoded: bool = False) -> AssignmentType: + """ + Returns the complement of the assignment for the variable. + + :param assignment: The assignment + :param encoded: If the assignment is encoded + :return: The complement of the assignment + """ + raise NotImplementedError + + @staticmethod + def intersection_of_assignments(assignment1: AssignmentType, + assignment2: AssignmentType, + encoded: bool = False) -> AssignmentType: + """ + Returns the intersection of two assignments + + :param assignment1: The first assignment + :param assignment2: The second assignment + :param encoded: If the assignment is encoded + :return: The intersection of the assignments + """ + raise NotImplementedError + + @staticmethod + def union_of_assignments(assignment1: AssignmentType, + assignment2: AssignmentType, + encoded: bool = False) -> AssignmentType: + """ + Returns the union of two assignments + + :param assignment1: The first assignment + :param assignment2: The second assignment + :param encoded: If the assignment is encoded + :return: The union of the assignments + """ + raise NotImplementedError + class Continuous(Variable): """ @@ -131,6 +172,21 @@ def to_json(self) -> Dict[str, Any]: def _from_json(cls, data: Dict[str, Any]) -> 'Variable': return cls(name=data["name"], domain=portion.from_data(data["domain"])) + def complement_of_assignment(self, assignment: portion.Interval, encoded: bool = False) -> portion.Interval: + return self.domain - assignment + + @staticmethod + def intersection_of_assignments(assignment1: portion.Interval, + assignment2: portion.Interval, + encoded: bool = False) -> portion.Interval: + return assignment1 & assignment2 + + @staticmethod + def union_of_assignments(assignment1: portion.Interval, + assignment2: portion.Interval, + encoded: bool = False) -> portion.Interval: + return assignment1 | assignment2 + class Discrete(Variable): """ @@ -177,6 +233,25 @@ def decode_many(self, elements: Iterable[int]) -> Iterable[Any]: """ return tuple(map(self.decode, elements)) + def complement_of_assignment(self, assignment: Tuple, encoded: bool = False) -> Tuple: + if not encoded: + return tuple(sorted(set(self.domain) - set(assignment))) + else: + return tuple(sorted(set(range(len(self.domain))) - set(assignment))) + + @staticmethod + def intersection_of_assignments(assignment1: Tuple, + assignment2: Tuple, + encoded: bool = False) -> Tuple: + + return tuple(sorted(set(assignment1) & set(assignment2))) + + @staticmethod + def union_of_assignments(assignment1: Tuple, + assignment2: Tuple, + encoded: bool = False) -> Tuple: + return tuple(sorted(set(assignment1) | set(assignment2))) + class Symbolic(Discrete): """ diff --git a/test/test_events.py b/test/test_events.py index 66703e6..011ea78 100644 --- a/test/test_events.py +++ b/test/test_events.py @@ -2,9 +2,11 @@ import portion -from random_events.events import VariableMap, Event, EncodedEvent +from random_events.events import VariableMap, Event, EncodedEvent, ComplexEvent from random_events.variables import Continuous, Integer, Symbolic +import plotly.graph_objects as go + class VariableTestCase(unittest.TestCase): @@ -131,12 +133,7 @@ def test_empty_intersection(self): event_1 = Event() event_1[self.integer] = (1, 2) event_1[self.symbol] = ("c", ) - result = event_1.intersection(self.event) - - self.assertEqual(result["integer"], (1, )) - self.assertEqual(result["symbol"], tuple()) - self.assertEqual(result["real"], self.event["real"]) self.assertTrue(result.is_empty()) def test_intersection_alias(self): @@ -146,31 +143,20 @@ def test_intersection_alias(self): self.assertEqual(event_1 & self.event, event_1.intersection(self.event)) self.assertEqual(event_1 & self.event, self.event & event_1) - def test_union(self): - event_1 = Event() - event_1[self.integer] = (1, 2, 5) - event_1[self.symbol] = ("a", "b") - - result = self.event.union(event_1) - self.assertEqual(result["integer"], (1, 2, 5)) - self.assertEqual(result["symbol"], ("a", "b")) - self.assertEqual(result["real"], self.event["real"]) - - def test_union_alias(self): - event_1 = Event() - event_1[self.integer] = (1, 2, 5) - event_1[self.symbol] = ("a", "b") - self.assertEqual(event_1 | self.event, event_1.union(self.event)) - self.assertEqual(event_1 | self.event, self.event | event_1) + def test_union_without_intersection(self): + event1 = Event({self.integer: 1, self.symbol: "a", self.real: 2.0}) + union = event1.union(self.event) + self.assertIsInstance(union, ComplexEvent) + self.assertEqual(len(union.events), 2) + self.assertTrue(union.are_events_disjoint()) def test_difference(self): event_1 = Event() event_1[self.integer] = (1, 2, 5) event_1[self.symbol] = ("a", "b") result = event_1.difference(self.event) - self.assertEqual(result["integer"], (2, 5)) - self.assertEqual(result["symbol"], ("b", )) - self.assertEqual(result["real"], portion.open(-portion.inf, portion.inf) - self.event["real"]) + self.assertEqual(len(result.events), 3) + self.assertTrue(result.are_events_disjoint()) def test_difference_alias(self): event_1 = Event() @@ -248,8 +234,152 @@ def test_dict_like_creation(self): def test_set_operations_return_type(self): event = EncodedEvent(zip([self.integer, self.symbol], [1, 0])) self.assertEqual(type(event & event), EncodedEvent) - self.assertEqual(type(event | event), EncodedEvent) - self.assertEqual(type(event - event), EncodedEvent) + self.assertEqual(type(event | event), ComplexEvent) + self.assertEqual(type(event - event), ComplexEvent) + + +class ComplexEventTestCase(unittest.TestCase): + + x: Continuous = Continuous("x") + y: Continuous = Continuous("y") + z: Continuous = Continuous("z") + a: Symbolic = Symbolic("a", {"a1", "a2"}) + b: Symbolic = Symbolic("b", {"b1", "b2", "b3"}) + unit_interval = portion.closed(0, 1) + + def test_union(self): + event_1 = Event({self.x: self.unit_interval, self.y: self.unit_interval}) + event_2 = Event({self.x: portion.closed(0.5, 2), self.y: portion.closed(0.5, 2)}) + union = event_1.union(event_2) + self.assertIsInstance(union, ComplexEvent) + self.assertEqual(len(union.events), 3) + self.assertTrue(union.are_events_disjoint()) + # go.Figure(union.plot()).show() + + def test_union_of_complex_events(self): + event_1 = Event({self.x: self.unit_interval, self.y: self.unit_interval}) + event_2 = Event({self.x: portion.closed(0.5, 2), self.y: portion.closed(0.5, 2)}) + complex_event_1 = event_1.union(event_2) + event_3 = Event({self.x: portion.closed(0.5, 2), self.y: portion.closed(-0.5, 2)}) + complex_event_2 = event_1.union(event_3) + + result = complex_event_1.union(complex_event_2) + + self.assertIsInstance(result, ComplexEvent) + self.assertTrue(result.are_events_disjoint()) + + def test_make_events_disjoint_and_simplify(self): + event_1 = Event({self.x: self.unit_interval, self.y: self.unit_interval}) + event_2 = Event({self.x: portion.closed(0.5, 2), self.y: portion.closed(0.5, 2)}) + complex_event = ComplexEvent([event_1, event_2]) + self.assertFalse(complex_event.are_events_disjoint()) + complex_event = complex_event.make_events_disjoint() + self.assertEqual(len(complex_event.events), 5) + self.assertTrue(complex_event.are_events_disjoint()) + simplified_event = complex_event.simplify() + self.assertEqual(len(simplified_event.events), 3) + self.assertTrue(simplified_event.are_events_disjoint()) + + def test_are_events_disjoint(self): + event1 = Event({self.x: portion.closed(0, 1), self.y: portion.closed(0, 1)}) + event2 = Event({self.x: portion.closed(0.5, 2), self.y: portion.closed(0.5, 2)}) + event3 = Event({self.x: portion.closed(2, 3), self.y: portion.closed(2, 3)}) + + complex_event = ComplexEvent((event1, event2)) + self.assertFalse(complex_event.are_events_disjoint()) + + complex_event = ComplexEvent((event1, event3)) + self.assertTrue(complex_event.are_events_disjoint()) + + complex_event = ComplexEvent((event1, event3, event2,)) + self.assertFalse(complex_event.are_events_disjoint()) + + def test_from_continuous_complement(self): + event = Event({self.x: portion.closed(0, 1), self.y: portion.closed(0, 1)}) + complement = event.complement() + + self.assertEqual(len(complement.events), 2) + self.assertTrue(complement.are_events_disjoint()) + + c1 = complement.events[0] + self.assertEqual(c1[self.x], portion.open(-portion.inf, 0) | portion.open(1, portion.inf)) + self.assertEqual(c1[self.y], portion.open(-portion.inf, portion.inf)) + + c2 = complement.events[1] + self.assertEqual(c2[self.y], portion.open(-portion.inf, 0) | portion.open(1, portion.inf)) + self.assertEqual(c2[self.x], event[self.x]) + + for sub_event in complement.events: + self.assertTrue(sub_event.intersection(event).is_empty()) + + def test_complement_3d(self): + event = Event({self.x: portion.closed(0, 1), + self.y: portion.closed(0, 1), + self.z: portion.closed(0, 1)}) + complement = event.complement() + self.assertEqual(len(complement.events), 3) + self.assertTrue(complement.are_events_disjoint()) + + def test_from_discrete_complement(self): + event = Event({self.a: "a1", self.b: "b1"}) + complement = event.complement() + + self.assertEqual(len(complement.events), 2) + self.assertTrue(complement.are_events_disjoint()) + + c1 = complement.events[0] + self.assertEqual(c1[self.a], ("a2", )) + self.assertEqual(c1[self.b], self.b.domain) + + c2 = complement.events[1] + self.assertEqual(c2[self.a], ("a1", )) + self.assertEqual(c2[self.b], ("b2", "b3")) + + for sub_event in complement.events: + self.assertTrue(sub_event.intersection(event).is_empty()) + + def test_chained_complement(self): + event = Event({self.x: portion.closed(0, 1), self.y: portion.closed(0, 1)}) + complement = event.complement() + copied_event = complement.complement() + self.assertEqual(len(copied_event.events), 1) + self.assertEqual(copied_event.events[0], event) + + +class PlottingTestCase(unittest.TestCase): + x: Continuous = Continuous("x") + y: Continuous = Continuous("y") + z: Continuous = Continuous("z") + + def test_plot_2d(self): + event = Event({self.x: portion.closed(0, 1), self.y: portion.closed(0, 1)}) + fig = go.Figure(event.plot()) + # fig.show() + + def test_plot_3d(self): + event = Event({self.x: portion.closed(0, 1), self.y: portion.closed(0, 1), self.z: portion.closed(0, 1)}) + fig = go.Figure(event.plot()) + # fig.show() + + def test_plot_complex_event_2d(self): + event = Event({self.x: portion.closed(0, 1), self.y: portion.closed(0, 1)}) + complement = event.complement() + limiting_event = Event({self.x: portion.closed(-1, 2), self.y: portion.closed(-1, 2)}) + result = complement.intersection(ComplexEvent([limiting_event])) + fig = go.Figure(result.plot(), result.plotly_layout()) + # fig.show() + + def test_plot_complex_event_3d(self): + event = Event({self.x: portion.closed(0, 1), + self.y: portion.closed(0, 1), + self.z: portion.closed(0, 1)}) + complement = event.complement() + limiting_event = Event({self.x: portion.closed(-1, 2), + self.y: portion.closed(-1, 2), + self.z: portion.closed(-1, 2)}) + result = complement.intersection(ComplexEvent([limiting_event])) + fig = go.Figure(result.plot(), result.plotly_layout()) + # fig.show() if __name__ == '__main__': diff --git a/test/test_variables.py b/test/test_variables.py index 1c99268..e06a50f 100644 --- a/test/test_variables.py +++ b/test/test_variables.py @@ -92,6 +92,14 @@ def test_polymorphic_serialization(self): symbol = Variable.from_json(self.symbol.to_json()) self.assertEqual(symbol, self.symbol) + def test_complement_of_assignment(self): + """ + Test that the complement of an assignment is correct. + """ + self.assertEqual(self.real.complement_of_assignment(portion.closed(0, 1)), + portion.open(-portion.inf, 0) | portion.open(1, portion.inf)) + self.assertEqual(self.symbol.complement_of_assignment(("a",)), ("b", "c", )) + if __name__ == '__main__': unittest.main()