├── pyphysim ├── py.typed ├── progressbar │ └── __init__.py ├── pointprocess │ ├── __init__.py │ └── pointprocess.py ├── mimo │ └── __init__.py ├── c_extensions │ ├── README │ └── __init__.py ├── extra │ ├── __init__.py │ ├── MATLAB │ │ ├── __init__.py │ │ └── python2MATLAB.py │ └── pgfplotshelper.py ├── reference_signals │ ├── __init__.py │ ├── zadoffchu.py │ └── dmrs.py ├── modulators │ └── __init__.py ├── cell │ └── __init__.py ├── subspace │ ├── __init__.py │ ├── projections.py │ └── metrics.py ├── util │ ├── __init__.py │ └── serialize.py ├── __init__.py ├── channels │ ├── __init__.py │ ├── noise.py │ └── antennagain.py ├── ia │ └── __init__.py ├── comm │ ├── __init__.py │ └── waterfilling.py └── simulations │ └── __init__.py ├── apps ├── ia │ ├── __init__.py │ ├── ia_config_file.txt │ ├── greedy_config_file.txt │ ├── check_greedy_partial_results.py │ ├── test_ia_feasibility.py │ ├── simple_ia.py │ ├── ia_SINRs_and_capacity.py │ └── greedy_statistics.py ├── mimo │ ├── __init__.py │ └── .gitignore ├── ofdm │ ├── __init__.py │ ├── plot_ofdm_PSD.py │ └── ofdm_tdlchannel.py ├── awgn_modulators │ ├── __init__.py │ ├── simulate_qam.py │ └── simulate_bpsk.py ├── .gitignore ├── __init__.py ├── codebooks │ ├── codebook_results │ │ ├── codebook_128_precoders_in_G(2,1).mat │ │ ├── codebook_128_precoders_in_G(2,1).npz │ │ ├── codebook_12_precoders_in_G(2,1).mat │ │ ├── codebook_12_precoders_in_G(2,1).npz │ │ ├── codebook_12_precoders_in_G(3,1).mat │ │ ├── codebook_12_precoders_in_G(3,1).npz │ │ ├── codebook_12_precoders_in_G(3,2).mat │ │ ├── codebook_12_precoders_in_G(3,2).npz │ │ ├── codebook_16_precoders_in_G(2,1).mat │ │ ├── codebook_16_precoders_in_G(2,1).npz │ │ ├── codebook_16_precoders_in_G(3,1).mat │ │ ├── codebook_16_precoders_in_G(3,1).npz │ │ ├── codebook_16_precoders_in_G(3,2).mat │ │ ├── codebook_16_precoders_in_G(3,2).npz │ │ ├── codebook_16_precoders_in_G(4,1).mat │ │ ├── codebook_16_precoders_in_G(4,1).npz │ │ ├── codebook_16_precoders_in_G(4,2).mat │ │ ├── codebook_16_precoders_in_G(4,2).npz │ │ ├── codebook_20_precoders_in_G(2,1).mat │ │ ├── codebook_20_precoders_in_G(2,1).npz │ │ ├── codebook_20_precoders_in_G(3,1).mat │ │ ├── codebook_20_precoders_in_G(3,1).npz │ │ ├── codebook_20_precoders_in_G(3,2).mat │ │ ├── codebook_20_precoders_in_G(3,2).npz │ │ ├── codebook_24_precoders_in_G(2,1).mat │ │ ├── codebook_24_precoders_in_G(2,1).npz │ │ ├── codebook_24_precoders_in_G(3,1).mat │ │ ├── codebook_24_precoders_in_G(3,1).npz │ │ ├── codebook_24_precoders_in_G(3,2).mat │ │ ├── codebook_24_precoders_in_G(3,2).npz │ │ ├── codebook_28_precoders_in_G(2,1).mat │ │ ├── codebook_28_precoders_in_G(2,1).npz │ │ ├── codebook_28_precoders_in_G(3,1).mat │ │ ├── codebook_28_precoders_in_G(3,1).npz │ │ ├── codebook_28_precoders_in_G(3,2).mat │ │ ├── codebook_28_precoders_in_G(3,2).npz │ │ ├── codebook_32_precoders_in_G(2,1).mat │ │ ├── codebook_32_precoders_in_G(2,1).npz │ │ ├── codebook_32_precoders_in_G(3,1).mat │ │ ├── codebook_32_precoders_in_G(3,1).npz │ │ ├── codebook_32_precoders_in_G(3,2).mat │ │ ├── codebook_32_precoders_in_G(3,2).npz │ │ ├── codebook_32_precoders_in_G(4,1).mat │ │ ├── codebook_32_precoders_in_G(4,1).npz │ │ ├── codebook_36_precoders_in_G(2,1).mat │ │ ├── codebook_36_precoders_in_G(2,1).npz │ │ ├── codebook_36_precoders_in_G(3,1).mat │ │ ├── codebook_36_precoders_in_G(3,1).npz │ │ ├── codebook_36_precoders_in_G(3,2).mat │ │ ├── codebook_36_precoders_in_G(3,2).npz │ │ ├── codebook_3_precoders_in_G(3,1).mat │ │ ├── codebook_3_precoders_in_G(3,1).npz │ │ ├── codebook_40_precoders_in_G(2,1).mat │ │ ├── codebook_40_precoders_in_G(2,1).npz │ │ ├── codebook_40_precoders_in_G(3,1).mat │ │ ├── codebook_40_precoders_in_G(3,1).npz │ │ ├── codebook_40_precoders_in_G(3,2).mat │ │ ├── codebook_40_precoders_in_G(3,2).npz │ │ ├── codebook_44_precoders_in_G(2,1).mat │ │ ├── codebook_44_precoders_in_G(2,1).npz │ │ ├── codebook_44_precoders_in_G(3,1).mat │ │ ├── codebook_44_precoders_in_G(3,1).npz │ │ ├── codebook_44_precoders_in_G(3,2).mat │ │ ├── codebook_44_precoders_in_G(3,2).npz │ │ ├── codebook_48_precoders_in_G(2,1).mat │ │ ├── codebook_48_precoders_in_G(2,1).npz │ │ ├── codebook_48_precoders_in_G(3,1).mat │ │ ├── codebook_48_precoders_in_G(3,1).npz │ │ ├── codebook_48_precoders_in_G(3,2).mat │ │ ├── codebook_48_precoders_in_G(3,2).npz │ │ ├── codebook_52_precoders_in_G(2,1).mat │ │ ├── codebook_52_precoders_in_G(2,1).npz │ │ ├── codebook_52_precoders_in_G(3,1).mat │ │ ├── codebook_52_precoders_in_G(3,1).npz │ │ ├── codebook_52_precoders_in_G(3,2).mat │ │ ├── codebook_52_precoders_in_G(3,2).npz │ │ ├── codebook_56_precoders_in_G(2,1).mat │ │ ├── codebook_56_precoders_in_G(2,1).npz │ │ ├── codebook_56_precoders_in_G(3,1).mat │ │ ├── codebook_56_precoders_in_G(3,1).npz │ │ ├── codebook_56_precoders_in_G(3,2).mat │ │ ├── codebook_56_precoders_in_G(3,2).npz │ │ ├── codebook_60_precoders_in_G(2,1).mat │ │ ├── codebook_60_precoders_in_G(2,1).npz │ │ ├── codebook_60_precoders_in_G(3,1).mat │ │ ├── codebook_60_precoders_in_G(3,1).npz │ │ ├── codebook_60_precoders_in_G(3,2).mat │ │ ├── codebook_60_precoders_in_G(3,2).npz │ │ ├── codebook_64_precoders_in_G(2,1).mat │ │ ├── codebook_64_precoders_in_G(2,1).npz │ │ ├── codebook_64_precoders_in_G(3,1).mat │ │ ├── codebook_64_precoders_in_G(3,1).npz │ │ ├── codebook_64_precoders_in_G(3,2).mat │ │ ├── codebook_64_precoders_in_G(3,2).npz │ │ ├── codebook_8_precoders_in_G(2,1).mat │ │ ├── codebook_8_precoders_in_G(2,1).npz │ │ ├── codebook_8_precoders_in_G(3,1).mat │ │ ├── codebook_8_precoders_in_G(3,1).npz │ │ ├── codebook_8_precoders_in_G(3,2).mat │ │ ├── codebook_8_precoders_in_G(3,2).npz │ │ ├── codebook_8_precoders_in_G(4,1).mat │ │ └── codebook_8_precoders_in_G(4,1).npz │ ├── find_codebook_config.txt │ ├── minimum_distance_all_codebooks.py │ └── find_codebook_results ├── comp_BD │ └── bd_config_file.txt ├── testing_multiprocessing_progressbar.py ├── ber_plot_template.tikz ├── sum_capacity_template.tikz ├── simple_BD_with_whitening.py └── configobj_usage_example.py ├── poetry.toml ├── notebooks └── .gitignore ├── docs ├── _static │ └── README ├── _images │ ├── mimo_ic.pdf │ └── runsnakerun_screenshot-2.0.png ├── modules.rst ├── hand_written │ ├── README.md │ ├── packaging.rst │ ├── zreferences.rst │ ├── description.rst │ ├── typing_support.rst │ ├── writing_unittests.rst │ ├── simulators.rst │ └── writing_documentation.rst ├── pyphysim.c_extensions.rst ├── README.md ├── pyphysim.mimo.rst ├── pyphysim.progressbar.rst ├── pyphysim.pointprocess.rst ├── pyphysim.extra.MATLAB.rst ├── pyphysim.extra.rst ├── pyphysim.cell.rst ├── pyphysim.ia.rst ├── pyphysim.rst ├── pyphysim.comm.rst ├── pyphysim.subspace.rst ├── pyphysim.modulators.rst ├── pyphysim.util.rst ├── pyphysim.simulations.rst ├── pyphysim.reference_signals.rst ├── pyphysim.channels.rst ├── index.rst └── Mathjax │ └── PUT_MathJax_Here ├── .darglint ├── tests ├── F.npy ├── W.npy ├── big_H.npy ├── big_H2.npy ├── ipcluster2 ├── ipcluster3 ├── __init__.py ├── README ├── matlab_package_test.py ├── pointprocess_package_test.py └── subspace_package_test.py ├── lib └── README ├── .readthedocs.yml ├── ipython_notebooks ├── figs │ └── mimo_ic_channel.png ├── mimo_alamouti_config_file.txt ├── README └── METIS Simple Scenario 2.ipynb ├── .dir-locals.el ├── .projectile ├── .gitattributes ├── bin ├── py-physim ├── count_lines_of_code.sh ├── combine_results.py ├── run_python_coverage.sh └── split_into_partial_results.py ├── .style.yapf ├── .travis.yml ├── mypy.ini ├── .pre-commit-config.yaml ├── .gitignore ├── pyproject.toml ├── README.md └── uml └── Makefile /pyphysim/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/ia/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/mimo/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/ofdm/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/awgn_modulators/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/mimo/.gitignore: -------------------------------------------------------------------------------- 1 | *txt 2 | -------------------------------------------------------------------------------- /apps/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *txt 3 | -------------------------------------------------------------------------------- /poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | in-project = true 3 | -------------------------------------------------------------------------------- /notebooks/.gitignore: -------------------------------------------------------------------------------- 1 | rayleigh_qam_simulator.py 2 | *.txt 3 | -------------------------------------------------------------------------------- /pyphysim/progressbar/__init__.py: -------------------------------------------------------------------------------- 1 | from .progressbar import * 2 | -------------------------------------------------------------------------------- /docs/_static/README: -------------------------------------------------------------------------------- 1 | Static files are located in this folder. 2 | -------------------------------------------------------------------------------- /pyphysim/pointprocess/__init__.py: -------------------------------------------------------------------------------- 1 | from .pointprocess import * 2 | -------------------------------------------------------------------------------- /.darglint: -------------------------------------------------------------------------------- 1 | [darglint] 2 | docstring_style=numpy 3 | strictness=short 4 | -------------------------------------------------------------------------------- /tests/F.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/tests/F.npy -------------------------------------------------------------------------------- /tests/W.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/tests/W.npy -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | Put any C-language libraries here that are linked by some extension. 2 | -------------------------------------------------------------------------------- /pyphysim/mimo/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from .mimo import * 4 | -------------------------------------------------------------------------------- /tests/big_H.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/tests/big_H.npy -------------------------------------------------------------------------------- /pyphysim/c_extensions/README: -------------------------------------------------------------------------------- 1 | Cython extensions will be generated in this directory. 2 | -------------------------------------------------------------------------------- /tests/big_H2.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/tests/big_H2.npy -------------------------------------------------------------------------------- /docs/_images/mimo_ic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/docs/_images/mimo_ic.pdf -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | build: 2 | image: latest 3 | 4 | python: 5 | version: 3.8 6 | pip_install: true 7 | -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | pyphysim 2 | ======== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | pyphysim 8 | -------------------------------------------------------------------------------- /pyphysim/extra/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Package containing extra functionality. 4 | """ 5 | -------------------------------------------------------------------------------- /pyphysim/c_extensions/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Module containing compiled Cython extensions""" 3 | -------------------------------------------------------------------------------- /docs/_images/runsnakerun_screenshot-2.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/docs/_images/runsnakerun_screenshot-2.0.png -------------------------------------------------------------------------------- /ipython_notebooks/figs/mimo_ic_channel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/ipython_notebooks/figs/mimo_ic_channel.png -------------------------------------------------------------------------------- /pyphysim/reference_signals/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Package for Uplink Reference Signals related functionality.""" 3 | -------------------------------------------------------------------------------- /pyphysim/modulators/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Package containing modulators.""" 3 | 4 | from .fundamental import * 5 | from .ofdm import OFDM 6 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;;; Directory Local Variables 2 | ;;; For more information see (info "(emacs) Directory Variables") 3 | 4 | ((python-mode 5 | (mode . auto-revert))) 6 | -------------------------------------------------------------------------------- /tests/ipcluster2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2.7 2 | from ipyparallel.apps.ipclusterapp import launch_new_instance 3 | 4 | if __name__ == '__main__': 5 | launch_new_instance() 6 | -------------------------------------------------------------------------------- /tests/ipcluster3: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from ipyparallel.apps.ipclusterapp import launch_new_instance 3 | 4 | if __name__ == '__main__': 5 | launch_new_instance() 6 | -------------------------------------------------------------------------------- /apps/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Package with implementations of full simulators. 4 | 5 | Each file here (or folder) corresponds to a different simulator. 6 | """ 7 | -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_128_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_128_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_128_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_128_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_12_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_12_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_12_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_12_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_12_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_12_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_12_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_12_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_12_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_12_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_12_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_12_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(4,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(4,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(4,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(4,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(4,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(4,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_16_precoders_in_G(4,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_16_precoders_in_G(4,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_20_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_20_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_20_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_20_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_20_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_20_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_20_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_20_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_20_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_20_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_20_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_20_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_24_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_24_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_24_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_24_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_24_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_24_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_24_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_24_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_24_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_24_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_24_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_24_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_28_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_28_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_28_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_28_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_28_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_28_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_28_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_28_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_28_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_28_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_28_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_28_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_32_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_32_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_32_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_32_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_32_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_32_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_32_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_32_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_32_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_32_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_32_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_32_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_32_precoders_in_G(4,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_32_precoders_in_G(4,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_32_precoders_in_G(4,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_32_precoders_in_G(4,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_36_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_36_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_36_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_36_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_36_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_36_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_36_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_36_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_36_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_36_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_36_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_36_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_3_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_3_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_3_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_3_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_40_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_40_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_40_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_40_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_40_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_40_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_40_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_40_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_40_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_40_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_40_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_40_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_44_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_44_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_44_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_44_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_44_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_44_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_44_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_44_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_44_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_44_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_44_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_44_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_48_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_48_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_48_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_48_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_48_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_48_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_48_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_48_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_48_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_48_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_48_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_48_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_52_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_52_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_52_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_52_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_52_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_52_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_52_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_52_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_52_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_52_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_52_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_52_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_56_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_56_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_56_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_56_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_56_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_56_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_56_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_56_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_56_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_56_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_56_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_56_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_60_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_60_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_60_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_60_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_60_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_60_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_60_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_60_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_60_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_60_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_60_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_60_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_64_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_64_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_64_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_64_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_64_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_64_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_64_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_64_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_64_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_64_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_64_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_64_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_8_precoders_in_G(2,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_8_precoders_in_G(2,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_8_precoders_in_G(2,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_8_precoders_in_G(2,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_8_precoders_in_G(3,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_8_precoders_in_G(3,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_8_precoders_in_G(3,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_8_precoders_in_G(3,1).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_8_precoders_in_G(3,2).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_8_precoders_in_G(3,2).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_8_precoders_in_G(3,2).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_8_precoders_in_G(3,2).npz -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_8_precoders_in_G(4,1).mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_8_precoders_in_G(4,1).mat -------------------------------------------------------------------------------- /apps/codebooks/codebook_results/codebook_8_precoders_in_G(4,1).npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darcamo/pyphysim/HEAD/apps/codebooks/codebook_results/codebook_8_precoders_in_G(4,1).npz -------------------------------------------------------------------------------- /pyphysim/cell/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Package with cell and shapes related modules.""" 3 | 4 | # import shapes 5 | # import cell 6 | 7 | from . import cell, shapes 8 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Package with unittests for the packages in PyPhysim. 4 | 5 | There should be a python file for each package in PyPhysim. 6 | """ 7 | -------------------------------------------------------------------------------- /.projectile: -------------------------------------------------------------------------------- 1 | -*pyc 2 | -/build 3 | -/docs/_build 4 | -/__pycache__ 5 | -/.svn 6 | -*.so 7 | -*.pickle 8 | -*_bkp 9 | -*.ipynb 10 | -/.ropeproject 11 | -*.npz 12 | -*.mat 13 | -*ltxpng* 14 | -------------------------------------------------------------------------------- /docs/hand_written/README.md: -------------------------------------------------------------------------------- 1 | The files here are rst files written manually to be processed by sphinx. That 2 | is, they were not automatically generated by some tool from the python source 3 | code. 4 | -------------------------------------------------------------------------------- /pyphysim/subspace/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Subspace related stuff. 4 | 5 | This includes projections and metrics for subspaces. 6 | """ 7 | 8 | from . import metrics, projections 9 | -------------------------------------------------------------------------------- /pyphysim/util/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Package with several utility modules and classes. 4 | 5 | This includes the SimulationRunner class. 6 | """ 7 | 8 | from . import conversion, misc 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Notebooks in these folders are mean to be documentation demonstrating the 2 | # usage of features in pyphysim 3 | ipython_notebooks/* linguist-vendored 4 | notebooks/* linguist-vendored 5 | docs/* linguist-vendored 6 | -------------------------------------------------------------------------------- /bin/py-physim: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | cd ../ 4 | 5 | echo "xxxxxxxxxx Running nosetests xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 6 | nosetests tests/ 7 | echo "xxxxxxxxxx Done xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 8 | -------------------------------------------------------------------------------- /pyphysim/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | The Pyphysim library provides many useful classes and functions to 4 | perform Monte Carlo simulations of communications systems. 5 | """ 6 | 7 | # The short X.Y version. 8 | __version__ = "0.7.2" 9 | -------------------------------------------------------------------------------- /docs/pyphysim.c_extensions.rst: -------------------------------------------------------------------------------- 1 | pyphysim.c\_extensions package 2 | ============================== 3 | 4 | Module contents 5 | --------------- 6 | 7 | .. automodule:: pyphysim.c_extensions 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | -------------------------------------------------------------------------------- /pyphysim/channels/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pylint: disable=E1103 4 | """Module with implementation of channel related classes. 5 | """ 6 | 7 | __all__ = [ 8 | 'antennagain', 'fading_generators', 'fading', 'multiuser', 'noise', 9 | 'pathloss', 'singleuser' 10 | ] 11 | -------------------------------------------------------------------------------- /apps/codebooks/find_codebook_config.txt: -------------------------------------------------------------------------------- 1 | [Precoder] 2 | # Number of tx antennas 3 | Nt = 5 4 | # Number of streams 5 | Ns = 2 6 | # Number of precoders in the codebook 7 | K = 64 8 | 9 | [Simulation] 10 | # Number of repetitions per process 11 | rep_max = 100 12 | results_folder=results 13 | 14 | [Other] 15 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | From the parent folder run the command below to update sphinx documentation 2 | when if new modules/packages are created. 3 | 4 | `sphinx-apidoc -A "Darlan Cavalcante Moreira" -F -o docs pyphysim` 5 | 6 | After that you can running 7 | `sphinx-apidoc -o docs pyphysim` 8 | when new modules/packages are created. 9 | -------------------------------------------------------------------------------- /ipython_notebooks/mimo_alamouti_config_file.txt: -------------------------------------------------------------------------------- 1 | [Scenario] 2 | SNR = [ 0. 5. 10. 15. 20.] 3 | M = 16 4 | modulator = QAM 5 | NSymbs = 200 6 | Nr = 1 7 | Nt = 2 8 | [General] 9 | rep_max = 5000 10 | max_bit_errors = 3000 11 | unpacked_parameters = SNR, 12 | 13 | -------------------------------------------------------------------------------- /pyphysim/extra/MATLAB/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Package with MATLAB related functions. 3 | 4 | Examples: conversions for the print output from python to MATLAB and 5 | vice-verse. That way you can print a numpy array, copy the output and past 6 | in MATLAB for further analysis. 7 | 8 | """ 9 | 10 | from . import python2MATLAB 11 | -------------------------------------------------------------------------------- /pyphysim/ia/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Package with Interference Alignment (IA) algorithms. 4 | 5 | Note that all IA algorithms require the channel object and any change to 6 | the channel object must be performed before calling the `solve` method of 7 | the IA algorithm object. This includes generating the channel and setting 8 | the noise variance. 9 | """ 10 | 11 | from . import algorithms, iabase 12 | -------------------------------------------------------------------------------- /docs/pyphysim.mimo.rst: -------------------------------------------------------------------------------- 1 | pyphysim.mimo package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.mimo.mimo module 8 | ------------------------- 9 | 10 | .. automodule:: pyphysim.mimo.mimo 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | 16 | Module contents 17 | --------------- 18 | 19 | .. automodule:: pyphysim.mimo 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | -------------------------------------------------------------------------------- /docs/hand_written/packaging.rst: -------------------------------------------------------------------------------- 1 | Packaging 2 | --------- 3 | 4 | We use virtualenv and pip. 5 | 6 | In the main folder, create an enviroment with 7 | 8 | .. code-block:: bash 9 | 10 | $ virtualenv env 11 | 12 | This will create a `env` folder. Activate the environment with 13 | 14 | .. code-block:: bash 15 | 16 | $ source env/bin/activate 17 | 18 | Now you can install the python dependencies such as numpy, scipy, Cython, 19 | IPython, etc. 20 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style = pep8 3 | 4 | # Do not split consecutive brackets. Only relevant when 5 | # dedent_closing_brackets is set. For example: 6 | # 7 | # call_func_that_takes_a_dict( 8 | # { 9 | # 'key1': 'value1', 10 | # 'key2': 'value2', 11 | # } 12 | # ) 13 | # 14 | # would reformat to: 15 | # 16 | # call_func_that_takes_a_dict({ 17 | # 'key1': 'value1', 18 | # 'key2': 'value2', 19 | # }) 20 | coalesce_brackets=True 21 | -------------------------------------------------------------------------------- /ipython_notebooks/README: -------------------------------------------------------------------------------- 1 | 2 | The files in this folder are IPython notebooks. 3 | (See http://ipython.org/ipython-doc/dev/interactive/htmlnotebook.html) 4 | 5 | In order see and edit them, open a terminal in this folder and type 6 | "ipython notebook". The browser will be opened and you can select which 7 | notebook file to open. 8 | 9 | 10 | NOTE: The notebooks in this folder were created a long time ago. After review and updates they will be moved to the "notebooks" folder and this one will be deleted. 11 | -------------------------------------------------------------------------------- /docs/pyphysim.progressbar.rst: -------------------------------------------------------------------------------- 1 | pyphysim.progressbar package 2 | ============================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.progressbar.progressbar module 8 | --------------------------------------- 9 | 10 | .. automodule:: pyphysim.progressbar.progressbar 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | 16 | Module contents 17 | --------------- 18 | 19 | .. automodule:: pyphysim.progressbar 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | -------------------------------------------------------------------------------- /pyphysim/comm/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Package with communication related modules such as modulators, channels, 3 | etc. 4 | 5 | 1. summary 6 | 2. extended summary 7 | 3. routine listings 8 | 4. see also 9 | 5. notes 10 | 6. references 11 | 7. examples 12 | 13 | """ 14 | 15 | # import blockdiagonalization 16 | # import channels 17 | # import mimo 18 | # import modulators 19 | # import ofdm 20 | # import pathloss 21 | # import waterfilling 22 | 23 | from . import blockdiagonalization, waterfilling 24 | -------------------------------------------------------------------------------- /docs/pyphysim.pointprocess.rst: -------------------------------------------------------------------------------- 1 | pyphysim.pointprocess package 2 | ============================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.pointprocess.pointprocess module 8 | ----------------------------------------- 9 | 10 | .. automodule:: pyphysim.pointprocess.pointprocess 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | 16 | Module contents 17 | --------------- 18 | 19 | .. automodule:: pyphysim.pointprocess 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | -------------------------------------------------------------------------------- /docs/pyphysim.extra.MATLAB.rst: -------------------------------------------------------------------------------- 1 | pyphysim.extra.MATLAB package 2 | ============================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.extra.MATLAB.python2MATLAB module 8 | ------------------------------------------ 9 | 10 | .. automodule:: pyphysim.extra.MATLAB.python2MATLAB 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | 16 | Module contents 17 | --------------- 18 | 19 | .. automodule:: pyphysim.extra.MATLAB 20 | :members: 21 | :undoc-members: 22 | :show-inheritance: 23 | -------------------------------------------------------------------------------- /apps/ia/ia_config_file.txt: -------------------------------------------------------------------------------- 1 | [Scenario] 2 | SNR = [20 25 30] 3 | M = 4 4 | modulator = PSK 5 | NSymbs = 100 6 | K = 3 7 | Nr = 2 8 | Nt = 2 9 | Ns = 1 10 | [IA Algorithm] 11 | max_iterations = 1:5,5:5:121,200 12 | initialize_with = random,alt_min 13 | [General] 14 | max_bit_errors = 3000 15 | unpacked_parameters = SNR, max_iterations, initialize_with 16 | rep_max = 100 17 | [Plot] 18 | max_iterations_plot = 120 19 | initialize_with_plot = random 20 | -------------------------------------------------------------------------------- /docs/pyphysim.extra.rst: -------------------------------------------------------------------------------- 1 | pyphysim.extra package 2 | ====================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | pyphysim.extra.MATLAB 11 | 12 | Submodules 13 | ---------- 14 | 15 | pyphysim.extra.pgfplotshelper module 16 | ------------------------------------ 17 | 18 | .. automodule:: pyphysim.extra.pgfplotshelper 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: pyphysim.extra 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/hand_written/zreferences.rst: -------------------------------------------------------------------------------- 1 | References 2 | ========== 3 | 4 | 5 | .. [Cadambe2008] K. Gomadam, V. R. Cadambe, and S. A. Jafar, 6 | "Approaching the Capacity of Wireless Networks through Distributed 7 | Interference Alignment," in IEEE GLOBECOM 2008 - 2008 IEEE Global 8 | Telecommunications Conference, 2008, pp. 1-6. 9 | 10 | 11 | .. [Spencer2004] Q. H. Spencer, A. L. Swindlehurst, and M. Haardt, 12 | "Zero-Forcing Methods for Downlink Spatial Multiplexing 13 | in Multiuser MIMO Channels," IEEE Transactions on Signal 14 | Processing, vol. 52, no. 2, pp. 461-471, Feb. 2004. 15 | -------------------------------------------------------------------------------- /apps/comp_BD/bd_config_file.txt: -------------------------------------------------------------------------------- 1 | [Grid] 2 | cell_radius = 1.0 3 | num_cells = 3 4 | num_clusters = 1 5 | 6 | [Scenario] 7 | NSymbs = 500 8 | SNR = [ 0. 3. 6. 9. 12. 15. 18. 21. 24. 27. 30.] 9 | Pe_dBm = [-10. 0. 10.] 10 | Nr = 2 11 | Nt = 2 12 | N0 = -116.4 13 | ext_int_rank = 1 14 | user_positioning_method = Symmetric Far Away 15 | 16 | [Modulation] 17 | M = 4 18 | modulator = PSK 19 | packet_length = 60 20 | 21 | [General] 22 | rep_max = 5000 23 | unpacked_parameters = SNR, Pe_dBm 24 | -------------------------------------------------------------------------------- /docs/pyphysim.cell.rst: -------------------------------------------------------------------------------- 1 | pyphysim.cell package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.cell.cell module 8 | ------------------------- 9 | 10 | .. automodule:: pyphysim.cell.cell 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.cell.shapes module 16 | --------------------------- 17 | 18 | .. automodule:: pyphysim.cell.shapes 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: pyphysim.cell 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/pyphysim.ia.rst: -------------------------------------------------------------------------------- 1 | pyphysim.ia package 2 | =================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.ia.algorithms module 8 | ----------------------------- 9 | 10 | .. automodule:: pyphysim.ia.algorithms 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.ia.iabase module 16 | ------------------------- 17 | 18 | .. automodule:: pyphysim.ia.iabase 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: pyphysim.ia 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /bin/count_lines_of_code.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # This requires the cloc program to be installed. 4 | cd .. 5 | echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 6 | echo Lines of codes in Pyphysim library 7 | cloc pyphysim --exclude-lang="Bourne Shell,C" 8 | 9 | echo 10 | echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 11 | echo Lines of codes in simulators 12 | cloc apps --exclude-lang="Bourne Shell" 13 | 14 | echo 15 | echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 16 | echo Lines of code in test files 17 | cloc tests --exclude-lang="Bourne Shell" 18 | -------------------------------------------------------------------------------- /docs/pyphysim.rst: -------------------------------------------------------------------------------- 1 | pyphysim package 2 | ================ 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | pyphysim.c_extensions 11 | pyphysim.cell 12 | pyphysim.channels 13 | pyphysim.comm 14 | pyphysim.extra 15 | pyphysim.ia 16 | pyphysim.mimo 17 | pyphysim.modulators 18 | pyphysim.pointprocess 19 | pyphysim.progressbar 20 | pyphysim.reference_signals 21 | pyphysim.simulations 22 | pyphysim.subspace 23 | pyphysim.util 24 | 25 | Module contents 26 | --------------- 27 | 28 | .. automodule:: pyphysim 29 | :members: 30 | :undoc-members: 31 | :show-inheritance: 32 | -------------------------------------------------------------------------------- /docs/pyphysim.comm.rst: -------------------------------------------------------------------------------- 1 | pyphysim.comm package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.comm.blockdiagonalization module 8 | ----------------------------------------- 9 | 10 | .. automodule:: pyphysim.comm.blockdiagonalization 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.comm.waterfilling module 16 | --------------------------------- 17 | 18 | .. automodule:: pyphysim.comm.waterfilling 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: pyphysim.comm 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/pyphysim.subspace.rst: -------------------------------------------------------------------------------- 1 | pyphysim.subspace package 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.subspace.metrics module 8 | -------------------------------- 9 | 10 | .. automodule:: pyphysim.subspace.metrics 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.subspace.projections module 16 | ------------------------------------ 17 | 18 | .. automodule:: pyphysim.subspace.projections 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: pyphysim.subspace 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /docs/pyphysim.modulators.rst: -------------------------------------------------------------------------------- 1 | pyphysim.modulators package 2 | =========================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.modulators.fundamental module 8 | -------------------------------------- 9 | 10 | .. automodule:: pyphysim.modulators.fundamental 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.modulators.ofdm module 16 | ------------------------------- 17 | 18 | .. automodule:: pyphysim.modulators.ofdm 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: pyphysim.modulators 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /apps/codebooks/minimum_distance_all_codebooks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """module docstring""" 3 | 4 | import numpy as np 5 | 6 | Nt = 2 7 | Ns = 1 8 | # A script and a configuration file will be created for each value of K 9 | K = range(8, 65, 4) 10 | 11 | saved_data_file = "codebook_{0}_precoders_in_G({1},{2}).npz" 12 | 13 | np.set_printoptions(precision=4) 14 | min_dists = "" 15 | for k in K: 16 | # print(saved_data_file.format(k, Nt, Ns)) 17 | results = np.load(saved_data_file.format(k, Nt, Ns)) 18 | min_dists += " | {:0.4f}".format(results['best_dist'].item()) 19 | # Half part 20 | if k == 36: 21 | min_dists += " |\n" 22 | 23 | # print("{0} & {1}".format(k, results['best_dist'])) 24 | 25 | min_dists += " |" 26 | 27 | print(min_dists) 28 | -------------------------------------------------------------------------------- /docs/pyphysim.util.rst: -------------------------------------------------------------------------------- 1 | pyphysim.util package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.util.conversion module 8 | ------------------------------- 9 | 10 | .. automodule:: pyphysim.util.conversion 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.util.misc module 16 | ------------------------- 17 | 18 | .. automodule:: pyphysim.util.misc 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pyphysim.util.serialize module 24 | ------------------------------ 25 | 26 | .. automodule:: pyphysim.util.serialize 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | 32 | Module contents 33 | --------------- 34 | 35 | .. automodule:: pyphysim.util 36 | :members: 37 | :undoc-members: 38 | :show-inheritance: 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - 3.8 4 | # env: 5 | # global: 6 | # - secure: "" 7 | # - secure: "" 8 | before_install: 9 | - pip install poetry 10 | install: 11 | - poetry install 12 | script: 13 | # - poetry run flake8 my_package test 14 | # - poetry run coverage run --source=my_package -m unittest discover -b 15 | - nosetests --with-coverage --cover-package=pyphysim tests/ 16 | # before_deploy: 17 | # - poetry config repositories.mypypi http://mypypi.example.com/simple 18 | # - poetry config http-basic.mypypi $MYPYPI_USER $MYPYPI_PASS 19 | # - poetry build -f sdist 20 | # deploy: 21 | # provider: script 22 | # script: poetry publish -r mypypi 23 | # skip_cleanup: true 24 | # on: 25 | # tags: true 26 | 27 | ## Calculate coverage 28 | after_success: 29 | - coveralls 30 | -------------------------------------------------------------------------------- /pyphysim/simulations/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Module containing useful classes to implement Monte Carlo simulations. 4 | 5 | The main class for Monte Carlo simulations is the :class:`.runner.SimulationRunner` 6 | class, but a few other classes are also implemented to handle simulation 7 | parameters and simulation results. 8 | 9 | More specifically, the :mod:`.simulations` module implements the classes: 10 | - :class:`.runner.SimulationRunner` 11 | - :class:`.parameters.SimulationParameters` 12 | - :class:`.results.SimulationResults` 13 | - :class:`.results.Result` 14 | 15 | For a description of how to implement Monte Carlo simulations using the 16 | classes defined in the :mod:`.simulations` module see the section 17 | :ref:`implementing_monte_carlo_simulations`. 18 | """ 19 | 20 | from .parameters import * 21 | from .results import * 22 | from .runner import * 23 | -------------------------------------------------------------------------------- /pyphysim/channels/noise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pylint: disable=E1103 4 | from typing import cast 5 | 6 | import scipy.constants 7 | 8 | from ..util.conversion import linear2dBm 9 | 10 | 11 | def calc_thermal_noise_power_dBm(T: float, delta_f: float) -> float: 12 | """ 13 | Calculate the thermal noise power (in dBm) for the given room temperature 14 | `T` (in Cº) and bandwidth `delta_f` (in Hz). 15 | 16 | Parameters 17 | ---------- 18 | T : float 19 | Room temperature in Cesium degrees. 20 | delta_f : float 21 | Bandwidth in Hz. 22 | 23 | Returns 24 | ------- 25 | noise_var : float 26 | The noise power. 27 | """ 28 | # Boltzmann constant 29 | B = cast(float, scipy.constants.Boltzmann) 30 | K = T + 273.0 31 | noise_power = B * K * delta_f 32 | noise_power_dBm = linear2dBm(noise_power) 33 | return noise_power_dBm 34 | -------------------------------------------------------------------------------- /apps/ia/greedy_config_file.txt: -------------------------------------------------------------------------------- 1 | [Grid] 2 | cell_radius = 1.0 # Used in 'Random' scenario. Not use in 'NoPathLoss' scenario 3 | num_cells = 3 # Effectivelly the number os transmit/receive pairs 4 | num_clusters = 1 5 | [Scenario] 6 | SNR = 0:5:31 7 | M = 4 8 | modulator = PSK 9 | NSymbs = 200 10 | Nr = 3 11 | Nt = 3 12 | Ns = 3 # Maximum number of streams for each user 13 | N0 = -116.4 # Ignored in the NoPathLoss scenario 14 | scenario = NoPathLoss, Random # Either NoPathLoss or Random 15 | [IA Algorithm] 16 | max_iterations = 120 17 | initialize_with = random, # List can have: 'random', 'close_form', or 'alt_min' 18 | stream_sel_method = greedy, brute 19 | [General] 20 | max_bit_errors = 3000 21 | unpacked_parameters = SNR, initialize_with, stream_sel_method, scenario 22 | rep_max = 20 23 | [Plot] 24 | max_iterations_plot = 120 25 | initialize_with_plot = random 26 | -------------------------------------------------------------------------------- /docs/hand_written/description.rst: -------------------------------------------------------------------------------- 1 | PyPhysim organization 2 | ===================== 3 | 4 | 5 | The PyPhysim library is roughly organized in several packages with related 6 | modules. Most of the packages define functions and classes used to write 7 | physical layer simulators, but two packages are special: the `apps` package 8 | and the `tests` package. The `apps` package contains actual simulators that 9 | can be run, while the `tests` package, as the name suggests, have tests 10 | for the several packages in PyPhysim. 11 | 12 | At last, there is also a `bin` directory containing a few scripts. One 13 | useful script to be run while developing PyPhysim is the 14 | **run_python_corerage** script, which will run the python-coverage program 15 | in all the test files in the tests folder. This will give a good estimate 16 | of the test coverage in PyPhysim (see :doc:`writing_unittests` for details 17 | about writting unittests for PyPhysim). 18 | 19 | 20 | Packages in PyPhysim 21 | -------------------- 22 | 23 | A summary of the available packages in PyPhysim is shown below. 24 | 25 | .. toctree:: 26 | :maxdepth: 3 27 | 28 | ../pyphysim 29 | -------------------------------------------------------------------------------- /tests/README: -------------------------------------------------------------------------------- 1 | 2 | Put test scripts here. 3 | 4 | * Organization and naming conventions 5 | 6 | You should create one file with the tests for each package. This file 7 | should be called PACKAGENAME_package_test.py 8 | 9 | Note that some modules have doctests, therefore the first TestCase class in 10 | PACKAGENAME_package_test should simple run the doctests for each module in 11 | the PACKAGENAME package. Then you should create TestCases for each class in 12 | each module. 13 | 14 | * Running the tests 15 | 16 | Each package test script should have in its __main__ the line below 17 | : unittest.main() 18 | so that all tests in the script can be conveniently run. 19 | 20 | In order to easily run all the tests in all the scripts in this folder, 21 | install the python-nose package and then run the *nosetests* program in this 22 | folder. It will automatically run all the unittests in all the scripts 23 | here. 24 | 25 | If you don't want to install nose, then run the following command from this 26 | folder 27 | : python -m unittest discover --pattern=*.py 28 | See 29 | http://stackoverflow.com/questions/644821/python-how-to-run-unittest-main-for-all-source-files-in-a-subdirectory 30 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | warn_redundant_casts = True 3 | warn_unused_configs = True 4 | warn_unused_ignores = True 5 | warn_return_any = True 6 | warn_unreachable = True 7 | disallow_any_generics = True 8 | disallow_untyped_defs = True 9 | disallow_incomplete_defs = True 10 | no_implicit_optional = True 11 | strict_equality = True 12 | 13 | [mypy-cloudpickle.*] 14 | ignore_missing_imports = True 15 | 16 | [mypy-numpy.*] 17 | ignore_missing_imports = True 18 | 19 | [mypy-scipy.*] 20 | ignore_missing_imports = True 21 | 22 | [mypy-matplotlib.*] 23 | ignore_missing_imports = True 24 | 25 | [mypy-zmq.*] 26 | ignore_missing_imports = True 27 | 28 | [mypy-pandas.*] 29 | ignore_missing_imports = True 30 | 31 | [mypy-ipywidgets.*] 32 | ignore_missing_imports = True 33 | 34 | [mypy-ipyparallel.*] 35 | ignore_missing_imports = True 36 | 37 | [mypy-mpl_toolkits.*] 38 | ignore_missing_imports = True 39 | 40 | [mypy-configobj.*] 41 | ignore_missing_imports = True 42 | 43 | [mypy-validate.*] 44 | ignore_missing_imports = True 45 | 46 | [mypy-cPickle.*] 47 | ignore_missing_imports = True 48 | 49 | [mypy-h5py.*] 50 | ignore_missing_imports = True 51 | 52 | [mypy-IPython.*] 53 | ignore_missing_imports = True 54 | 55 | [mypy-numba.*] 56 | ignore_missing_imports = True 57 | -------------------------------------------------------------------------------- /apps/ofdm/plot_ofdm_PSD.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Plot the Power Spectral Density of OFDM modulated data""" 3 | 4 | import numpy as np 5 | from matplotlib import pylab 6 | from matplotlib import pyplot as plt 7 | 8 | from pyphysim.modulators.ofdm import OFDM 9 | 10 | if __name__ == '__main__': 11 | # xxxxxxxxxx Input generation (not part of OFDM) xxxxxxxxxxxxxxxxxxxxxx 12 | num_bits = 2500 13 | # generating 1's and 0's 14 | ip_bits = np.random.random_integers(0, 1, num_bits) 15 | # Number of modulated symbols 16 | num_mod_symbols = num_bits * 1 17 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 18 | 19 | # xxxxxxxxxxxxxxx BPSK modulation xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 20 | # bit0 --> -1 21 | # bit1 --> +1 22 | ip_mod = 2 * ip_bits - 1 23 | 24 | ofdm_obj = OFDM(64, 16, 52) 25 | ofdm_symbols = ofdm_obj.modulate(ip_mod) 26 | 27 | # Code to plot the power spectral density 28 | fsMHz = 20e6 29 | Pxx, W = pylab.psd(ofdm_symbols, NFFT=ofdm_obj.fft_size, Fs=fsMHz) 30 | # [Pxx,W] = pwelch(st,[],[],4096,20); 31 | plt.plot( 32 | W, 33 | # 10 * np.log10(np.fft.fftshift(Pxx)) 34 | 10 * np.log10(Pxx)) 35 | plt.xlabel('frequency, MHz') 36 | plt.ylabel('power spectral density') 37 | plt.title('Transmit spectrum OFDM (based on 802.11a)') 38 | plt.show() 39 | -------------------------------------------------------------------------------- /docs/pyphysim.simulations.rst: -------------------------------------------------------------------------------- 1 | pyphysim.simulations package 2 | ============================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.simulations.configobjvalidation module 8 | ----------------------------------------------- 9 | 10 | .. automodule:: pyphysim.simulations.configobjvalidation 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.simulations.parameters module 16 | -------------------------------------- 17 | 18 | .. automodule:: pyphysim.simulations.parameters 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pyphysim.simulations.results module 24 | ----------------------------------- 25 | 26 | .. automodule:: pyphysim.simulations.results 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pyphysim.simulations.runner module 32 | ---------------------------------- 33 | 34 | .. automodule:: pyphysim.simulations.runner 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pyphysim.simulations.simulationhelpers module 40 | --------------------------------------------- 41 | 42 | .. automodule:: pyphysim.simulations.simulationhelpers 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | 48 | Module contents 49 | --------------- 50 | 51 | .. automodule:: pyphysim.simulations 52 | :members: 53 | :undoc-members: 54 | :show-inheritance: 55 | -------------------------------------------------------------------------------- /docs/pyphysim.reference_signals.rst: -------------------------------------------------------------------------------- 1 | pyphysim.reference\_signals package 2 | =================================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.reference\_signals.channel\_estimation module 8 | ------------------------------------------------------ 9 | 10 | .. automodule:: pyphysim.reference_signals.channel_estimation 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.reference\_signals.dmrs module 16 | --------------------------------------- 17 | 18 | .. automodule:: pyphysim.reference_signals.dmrs 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pyphysim.reference\_signals.root\_sequence module 24 | ------------------------------------------------- 25 | 26 | .. automodule:: pyphysim.reference_signals.root_sequence 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pyphysim.reference\_signals.srs module 32 | -------------------------------------- 33 | 34 | .. automodule:: pyphysim.reference_signals.srs 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pyphysim.reference\_signals.zadoffchu module 40 | -------------------------------------------- 41 | 42 | .. automodule:: pyphysim.reference_signals.zadoffchu 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | 48 | Module contents 49 | --------------- 50 | 51 | .. automodule:: pyphysim.reference_signals 52 | :members: 53 | :undoc-members: 54 | :show-inheritance: 55 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | exclude: ^ipython_notebooks/* 4 | repos: 5 | - repo: https://github.com/pre-commit/pre-commit-hooks 6 | rev: v3.2.0 7 | hooks: 8 | - id: trailing-whitespace 9 | - id: end-of-file-fixer 10 | - id: check-added-large-files 11 | - id: check-case-conflict 12 | - id: check-docstring-first 13 | exclude: 'apps' 14 | - id: check-executables-have-shebangs 15 | - id: check-merge-conflict 16 | - id: check-symlinks 17 | - id: mixed-line-ending 18 | - id: fix-encoding-pragma 19 | args: ['--remove'] 20 | - id: check-toml 21 | - id: debug-statements 22 | # - repo: https://github.com/pre-commit/mirrors-mypy 23 | # rev: v0.761 24 | # hooks: 25 | # - id: mypy 26 | # - repo: https://github.com/saily/pre-commit-yapf-isort 27 | # rev: 598ff67cc598dddee43bc611d0bf4a07f4b08619 28 | # hooks: 29 | # - id: yapf-isort 30 | - repo: https://github.com/pre-commit/mirrors-yapf 31 | rev: 'v0.30.0' 32 | hooks: 33 | - id: yapf 34 | - repo: https://github.com/pre-commit/mirrors-isort 35 | rev: 'v5.4.2' 36 | hooks: 37 | - id: isort 38 | - repo: https://github.com/darcamo/version_check_hook.git 39 | rev: v1.2 40 | hooks: 41 | - id: version_check 42 | # # Currently darglint is slow and thus this might be commented out in the future 43 | # - repo: https://github.com/terrencepreilly/darglint 44 | # rev: master 45 | # hooks: 46 | # - id: darglint 47 | -------------------------------------------------------------------------------- /apps/testing_multiprocessing_progressbar.py: -------------------------------------------------------------------------------- 1 | import multiprocessing 2 | 3 | import numpy as np 4 | 5 | from pyphysim.progressbar import ProgressbarMultiProcessServer 6 | 7 | 8 | def func(rep_max, progressbar): 9 | for i in range(rep_max): 10 | a = np.random.randn(3, 3) 11 | b = np.random.randn(3, 3) 12 | c = np.linalg.inv(a @ b) 13 | progressbar.progress(i) 14 | return c 15 | 16 | 17 | if __name__ == '__main__': 18 | 19 | pb = ProgressbarMultiProcessServer(message="Running") 20 | 21 | num_process = 4 22 | 23 | rep_max = 100000 24 | 25 | procs = [] 26 | for i in range(num_process): 27 | proc_args = [ 28 | rep_max, 29 | pb.register_client_and_get_proxy_progressbar(rep_max) 30 | ] 31 | procs.append(multiprocessing.Process(target=func, args=proc_args)) 32 | 33 | # xxxxx Start all processes xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 34 | for proc in procs: 35 | proc.start() 36 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 37 | 38 | # xxxxx Start the processbar xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 39 | pb.start_updater() 40 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 41 | 42 | # xxxxx Join all processes xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 43 | for proc in procs: 44 | proc.join() 45 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 46 | 47 | # xxxxx Stop the processbar xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 48 | pb.stop_updater() 49 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 50 | -------------------------------------------------------------------------------- /docs/pyphysim.channels.rst: -------------------------------------------------------------------------------- 1 | pyphysim.channels package 2 | ========================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | pyphysim.channels.antennagain module 8 | ------------------------------------ 9 | 10 | .. automodule:: pyphysim.channels.antennagain 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pyphysim.channels.fading module 16 | ------------------------------- 17 | 18 | .. automodule:: pyphysim.channels.fading 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pyphysim.channels.fading\_generators module 24 | ------------------------------------------- 25 | 26 | .. automodule:: pyphysim.channels.fading_generators 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pyphysim.channels.multiuser module 32 | ---------------------------------- 33 | 34 | .. automodule:: pyphysim.channels.multiuser 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pyphysim.channels.noise module 40 | ------------------------------ 41 | 42 | .. automodule:: pyphysim.channels.noise 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | pyphysim.channels.pathloss module 48 | --------------------------------- 49 | 50 | .. automodule:: pyphysim.channels.pathloss 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | pyphysim.channels.singleuser module 56 | ----------------------------------- 57 | 58 | .. automodule:: pyphysim.channels.singleuser 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | 64 | Module contents 65 | --------------- 66 | 67 | .. automodule:: pyphysim.channels 68 | :members: 69 | :undoc-members: 70 | :show-inheritance: 71 | -------------------------------------------------------------------------------- /docs/hand_written/typing_support.rst: -------------------------------------------------------------------------------- 1 | Typing Support in PyPhysim 2 | ========================== 3 | 4 | PyPhysim has an increasing support for static typing checking. 5 | 6 | Ideally everything in PyPhysim should be type checked without errors by `mypy 7 | `_ and any new code should ideally have typing information 8 | as well. 9 | 10 | 11 | .. Note:: There are other type checkers that can be used, such as `pytype (from 12 | Google) `_ or `pyre (from Facebook) 13 | `_ 14 | 15 | 16 | Some useful information: 17 | ######################## 18 | 19 | - `What is the difference between TypeVar and NewType? `_ 20 | - `Covariance, Contravariance, and Invariance — The Ultimate Python Guide `_ 21 | - `Python Type Checking (Guide) `_ 22 | - Accepting any derived class: If the number of subclasses is fixed, just create 23 | a Union with all of them. If it is not and you truly want "any subclass of 24 | Base", then try "Generic[Base]" as the argument type. 25 | 26 | .. code-block:: python 27 | 28 | FadingGenerator = TypeVar('FadingGenerator', 29 | bound=FadingSampleGenerator) 30 | 31 | - Inspecting the type of variables: Use :code:`reveal_type(expr)` to see the 32 | type of :code:`expr`. It only works in `mypy` and you get a :code:`NameError` 33 | if you try to run code with it in Python. 34 | -------------------------------------------------------------------------------- /bin/combine_results.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Script that reads two files with saved SimulationResults and combine 4 | them into a new SimulationResults which is saved to a new file. 5 | """ 6 | 7 | import argparse 8 | 9 | from pyphysim.simulations.results import (SimulationResults, 10 | combine_simulation_results) 11 | from pyphysim.util.misc import replace_dict_values 12 | 13 | 14 | def main(): 15 | """Main function of the script. 16 | """ 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument('first', 19 | help="The name of the first SimulationResults file.") 20 | parser.add_argument('second', 21 | help="The name of the second SimulationResults file.") 22 | parser.add_argument( 23 | 'output', 24 | help=("The name that will be used to save the combined " 25 | "SimulationResults file."), 26 | nargs='?') 27 | 28 | args = parser.parse_args() 29 | 30 | first = SimulationResults.load_from_file(args.first) 31 | second = SimulationResults.load_from_file(args.second) 32 | union = combine_simulation_results(first, second) 33 | 34 | if args.output is None: 35 | output = replace_dict_values(first.original_filename, 36 | union.params.parameters, 37 | filename_mode=True) 38 | else: 39 | output = args.output 40 | 41 | if output == args.first or output == args.second: 42 | raise RuntimeError( 43 | "output filename must be different from the filename of either" 44 | " of the two SimulationResults.") 45 | 46 | # Finally save to the output file 47 | union.save_to_file(output) 48 | 49 | 50 | if __name__ == '__main__': 51 | main() 52 | -------------------------------------------------------------------------------- /pyphysim/pointprocess/pointprocess.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def generate_random_points_in_circle(num_points: int, 5 | max_radius: float, 6 | min_radius: float = 0.0) -> np.ndarray: 7 | """ 8 | Generate `numPoints` points uniformly inside a circle of radius `max_radius` 9 | and outside a circle with radius `min_radius` 10 | 11 | The circle center is at the origin. 12 | 13 | Parameters 14 | ---------- 15 | num_points 16 | The desired number of points 17 | max_radius 18 | The circle radius 19 | min_radius 20 | The minimum radius 21 | 22 | Returns 23 | ------- 24 | np.ndarray 25 | The random points inside the circle. 26 | """ 27 | radius_all_points = np.sqrt(np.random.random_sample( 28 | size=num_points)) * (max_radius - min_radius) + min_radius 29 | angles_all_points = np.random.random_sample(size=num_points) * 2 * np.pi 30 | 31 | return radius_all_points * np.exp(-1j * angles_all_points) 32 | 33 | 34 | def generate_random_points_in_rectangle(num_points: int, width: float, 35 | height: float) -> np.ndarray: 36 | """ 37 | Generate `num_points` points uniformly inside a rectangle. 38 | 39 | The rectangle center is at the origin. 40 | 41 | Parameters 42 | ---------- 43 | num_points 44 | The desired number of points 45 | width 46 | The rectangle width 47 | height 48 | The rectangle height 49 | 50 | Returns 51 | ------- 52 | np.ndarray 53 | The random points inside the circle. 54 | """ 55 | return width * (0.5 - np.random.random_sample(size=num_points)) + \ 56 | 1j * height * (0.5 - np.random.random_sample(size=num_points)) 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | *_cache 3 | __pycache__/ 4 | *.py[cod] 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | .venv 12 | env/ 13 | bin/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | pip-wheel-metadata/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | *.eggs 28 | 29 | # Installer logs 30 | pip-log.txt 31 | pip-delete-this-directory.txt 32 | 33 | # Unit test / coverage reports 34 | htmlcov/ 35 | .tox/ 36 | .coverage 37 | .cache 38 | nosetests.xml 39 | coverage.xml 40 | 41 | # Translations 42 | *.mo 43 | 44 | # Mr Developer 45 | .mr.developer.cfg 46 | .project 47 | .pydevproject 48 | 49 | # Rope 50 | .ropeproject 51 | 52 | # Django stuff: 53 | *.log 54 | *.pot 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | docs/autosummary_folder/ 59 | docs/Mathjax/* 60 | 61 | # Ignore some files created in the tests 62 | *.out 63 | 64 | # Ignore .c files created in the process of compiling any C extension. 65 | *_c.c 66 | 67 | # Ignore folder created by IPython. This folder is created when I start a 68 | # ipython notebook server in the ipython_notebooks/ folder and change one 69 | # of the notebooks there. 70 | ipython_notebooks/.ipynb_checkpoints 71 | .virtual_documents 72 | 73 | # Emacs related 74 | flycheck* 75 | *flymake* 76 | 77 | # Pickle files 78 | *.pickle 79 | 80 | # h5 files 81 | *.h5 82 | 83 | # Ignore text files with the progress created by the progressbar classes 84 | *_progress_*.txt 85 | 86 | # Ignore MATLAB .m files 87 | *.m 88 | 89 | # Ignore some non-interesting files in the apps folder 90 | apps/profile 91 | apps/texCode.pdf 92 | 93 | # Ignore folder created by IDEs 94 | .idea 95 | .vscode 96 | 97 | # Ignore a folder created by IPython to save checkpoints for the notebook 98 | .ipynb_checkpoints 99 | -------------------------------------------------------------------------------- /apps/awgn_modulators/simulate_qam.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Perform the simulation of the transmission of QAM symbols through an 4 | awgn channel. 5 | """ 6 | 7 | from apps.awgn_modulators.simulate_psk import VerySimplePskSimulationRunner 8 | from pyphysim.modulators import fundamental 9 | 10 | 11 | class VerySimpleQamSimulationRunner(VerySimplePskSimulationRunner): 12 | """Minimum code to perform a simulation of a QAM transmission through 13 | an AWGN channel. Only the modulator is changed from the 14 | VerySimplePskSimulationRunner. 15 | 16 | """ 17 | def __init__(self, ): 18 | super().__init__() 19 | M = 16 20 | 21 | SNR = np.array([0, 3, 6, 9, 12, 15, 18]) 22 | self.params.add('SNR', SNR) 23 | 24 | self.modulator = fundamental.QAM(M) 25 | self.progressbar_message = "{0}-QAM".format(M) + \ 26 | " Simulation - SNR: {SNR}" 27 | 28 | 29 | if __name__ == '__main__': 30 | # noinspection PyPackageRequirements 31 | from pylab import * 32 | sim = VerySimpleQamSimulationRunner() 33 | sim.simulate() 34 | SNR, ber, ser, theoretical_ber, theoretical_ser \ 35 | = sim.get_data_to_be_plotted() 36 | 37 | # Can only plot if we simulated for more then one value of SNR 38 | if SNR.size > 1: 39 | semilogy(SNR, ber, '--g*', label='BER') 40 | semilogy(SNR, ser, '--b*', label='SER') 41 | semilogy(SNR, theoretical_ber, '-g+', label='Theoretical BER') 42 | semilogy(SNR, theoretical_ser, '-b+', label='theoretical SER') 43 | 44 | xlabel('SNR') 45 | ylabel('Error') 46 | msg = 'BER and SER for {0} modulation in AWGN channel' 47 | title(msg.format(sim.modulator.name)) 48 | legend() 49 | 50 | grid(True, which='both', axis='both') 51 | show() 52 | 53 | print(sim.elapsed_time) 54 | -------------------------------------------------------------------------------- /apps/ber_plot_template.tikz: -------------------------------------------------------------------------------- 1 | \def\maxiterations{MAXITER} % Mude para o número máximo de iterações 2 | \pgfmathsetmacro{\rightaxisymax}{1.1*\maxiterations} 3 | \def\secondrightaxisytick{SECONDTICK} % The first tick is at 0. 4 | 5 | \begin{tikzpicture}[every pin/.append style={pin style}, trim axis left] 6 | \begin{semilogyaxis}[% 7 | width=\plotwidth, %\smallplotwidth, 8 | height=\plotheight, %\smallplotheight, 9 | scale only axis, 10 | xmin=-10, 11 | xmax=30, 12 | xtick={-10, -5, ..., 30}, 13 | xlabel={SNR (in dB)}, 14 | ymin=0.0000001, 15 | ymax=1, 16 | ylabel={BER}, 17 | legend style={draw=black,fill=white,legend cell align=left, legend pos=south west, font=\footnotesize}, 18 | grid=both, 19 | ] 20 | 21 | BER_ALL_ALGS 22 | 23 | \end{semilogyaxis} 24 | \begin{axis} 25 | [% 26 | width=\plotwidth, %\smallplotwidth, 27 | height=\plotheight, %\smallplotheight, 28 | scale only axis, 29 | xmin=-10, 30 | xmax=30, 31 | xtick=\empty, % We don't need xticks, since the ther axis already put them 32 | axis y line=right, 33 | ymin=0, 34 | ymax=\rightaxisymax, % Max iterations * 1.1 35 | ytick={0, \secondrightaxisytick, ..., \rightaxisymax}, 36 | ylabel={Mean Iterations}, 37 | %legend style={draw=black,fill=white,legend cell align=left, legend pos=south west}, 38 | %grid=both, 39 | ] 40 | 41 | ITER_ALL_ALGS 42 | 43 | \addplot[black] 44 | plot[] 45 | coordinates{(-10.0, \maxiterations) 46 | (-5.0, \maxiterations.0) 47 | (0.0, \maxiterations.0) 48 | (5.0, \maxiterations.0) 49 | (10.0, \maxiterations.0) 50 | (15.0, \maxiterations) 51 | (20.0, \maxiterations) 52 | (25.0, \maxiterations) 53 | (30.0, \maxiterations)}; 54 | 55 | % \node[coordinate,pin=110:{Maximum Number of iterations: \maxiterations}] 56 | % at (axis cs:28.0, \maxiterations) {}; 57 | 58 | \end{axis} 59 | \end{tikzpicture} 60 | -------------------------------------------------------------------------------- /docs/hand_written/writing_unittests.rst: -------------------------------------------------------------------------------- 1 | Writing Unittests for PyPhysim 2 | ============================== 3 | 4 | The standard :mod:`unittest` module is used to implement automated tests 5 | for the several packages in the PyPhysim library. The tests are located in 6 | the `tests` folder, which should contain a single test file for each 7 | package in PyPhysim. 8 | 9 | Each test file contains several *test case* classes, as usual in the 10 | unittests framework. The first test case class is always a *Doctests test 11 | case*, that is, a test case that simple run all the doctests in each module 12 | of the package. 13 | 14 | After that, a test case class must be implemented for each module in the 15 | package, which in turn should test all the classes and functions in that 16 | module. 17 | 18 | 19 | Test Coverage 20 | ------------- 21 | 22 | Ideally unittests should be implemented at the same time the actual code is 23 | implemented (or even before that). However, sometimes the tests are 24 | implemented the tested code and this may leave code untested. 25 | 26 | A good way to make sure that we have a good test coverage in PyPhysim is 27 | using the `python-coverage` program. With it we can run all the implemented 28 | unittests and get a report of the lines in any module in PyPhysim that was 29 | not run by any unittest (thus finding untested code). The `bin` folder 30 | contains a script called **run_python_coverage.sh** that will do exactly 31 | that. 32 | 33 | 34 | Code Quality 35 | ------------ 36 | 37 | A good way to ensure code quality, besides implementing unittests, is to 38 | employ code analisys tools such as pylint, pep8, pychecker, etc. 39 | 40 | For the quality of PyPhysim as a package, see the cheesecake_index tool. 41 | - http://pycheesecake.org/ 42 | - http://infinitemonkeycorps.net/docs/pph/ 43 | 44 | 45 | Notes 46 | ~~~~~ 47 | 48 | If you use pylint, this is the .pylintrc file I use (although I don't 49 | struggle to fix every pylint warning) 50 | 51 | .. literalinclude:: sample_dotpylintrc 52 | -------------------------------------------------------------------------------- /docs/hand_written/simulators.rst: -------------------------------------------------------------------------------- 1 | Implementing Simulators with PyPhysim 2 | ===================================== 3 | 4 | Several simulators are already implemented in the "`apps`" package, and can 5 | be used as examples of how to implement simulators with the PyPhysim 6 | library. The best complete example is probably the 7 | ":file:`apps/simulate_psk.py`" file. 8 | 9 | In general, the :mod:`.simulations` module provides a basic framework 10 | for implementing Monte Carlo Simulations and implementing a new simulator 11 | with PyPhysim starts by subclassing 12 | :class:`.SimulationRunner` and implementing the 13 | :meth:`.SimulationRunner._run_simulation` method with the code to simulate a 14 | single iteration for that specific simulator. 15 | 16 | 17 | A few other classes in the simulations module complete the framework by 18 | handling simulation parameters and simulation results. The classes in the 19 | framwork consist of 20 | 21 | - :class:`.SimulationRunner` 22 | - :class:`.SimulationParameters` 23 | - :class:`.SimulationResults` 24 | - :class:`.Result` 25 | 26 | For a description of how to implement Monte Carlo simulations using the 27 | clases defined in the :mod:`.simulations` module see 28 | :ref:`implementing_monte_carlo_simulations`. 29 | 30 | 31 | Getting simulation Parameters from a file 32 | ----------------------------------------- 33 | 34 | Python has the `ConfigParser library 35 | `_ 36 | (renamed to configparser in Python 3) to parse configuration 37 | parameters. You can use it in the `__init__` method of your simulator 38 | class (the simulatro main class that inherits from 39 | :class:`.SimulationRunner`) to read the simulation parameters from a file. 40 | 41 | Another good alternative is using the `configobj library `_, which provides an 42 | arguable easier to use API then configparser. Another advantage of using 43 | configobj is its ability to validate the configuration file. 44 | -------------------------------------------------------------------------------- /apps/awgn_modulators/simulate_bpsk.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Perform the simulation of the transmission of BPSK symbols through an 4 | awgn channel. 5 | """ 6 | 7 | import numpy as np 8 | 9 | from apps.awgn_modulators.simulate_psk import VerySimplePskSimulationRunner 10 | from pyphysim.modulators import fundamental 11 | 12 | 13 | class VerySimpleBpskSimulationRunner(VerySimplePskSimulationRunner): 14 | """Minimum code to perform a simulation of a BPSK transmission through 15 | an AWGN channel. Only the modulator is changed from the 16 | VerySimplePskSimulationRunner. 17 | 18 | """ 19 | def __init__(self): 20 | super().__init__() 21 | 22 | snr = np.array([0, 2, 4, 6, 8, 10]) 23 | self.params.add('SNR', snr) 24 | 25 | self.rep_max = 5000 26 | 27 | self.modulator = fundamental.BPSK() 28 | self.progressbar_message = "BPSK Simulation - SNR: {SNR}" 29 | 30 | 31 | if __name__ == '__main__': 32 | # noinspection PyPackageRequirements 33 | from pylab import * 34 | 35 | # noinspection PyUnresolvedReferences 36 | from apps.awgn_modulators.simulate_bpsk import \ 37 | VerySimpleBpskSimulationRunner 38 | 39 | sim = VerySimpleBpskSimulationRunner() 40 | sim.simulate() 41 | SNR, ber, ser, theoretical_ber, theoretical_ser \ 42 | = sim.get_data_to_be_plotted() 43 | 44 | # Can only plot if we simulated for more then one value of SNR 45 | if SNR.size > 1: 46 | semilogy(SNR, ber, '--g*', label='BER') 47 | semilogy(SNR, ser, '--b*', label='SER') 48 | semilogy(SNR, theoretical_ber, '-g+', label='Theoretical BER') 49 | semilogy(SNR, theoretical_ser, '-b+', label='theoretical SER') 50 | 51 | xlabel('SNR') 52 | ylabel('Error') 53 | title('BER and SER for {0} modulation in AWGN channel'.format( 54 | sim.modulator.name)) 55 | legend() 56 | 57 | grid(True, which='both', axis='both') 58 | show() 59 | 60 | print(sim.elapsed_time) 61 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. pyphysim documentation master file, created by 2 | sphinx-quickstart on Thu Sep 15 16:50:43 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to PyPhysim's documentation! 7 | ==================================== 8 | 9 | PyPhysim is a python library implementing functions, classes and simulators 10 | related to the physical layer of digital communications systems. 11 | 12 | 13 | .. _list_of_hand_written_doc: 14 | 15 | List of Documentation Articles 16 | ============================== 17 | 18 | PyPhysim organization is described in :doc:`hand_written/description`, where a summary 19 | of the several packages in PyPhysim is shown. For instructions on how to 20 | implement new simulations with PyPhysim see :doc:`hand_written/simulators`. 21 | 22 | The complete list of articles in the PyPhysim documentation is shown below. 23 | 24 | .. toctree:: 25 | :maxdepth: 1 26 | 27 | hand_written/description 28 | hand_written/simulators 29 | hand_written/monte_carlo 30 | hand_written/writing_documentation 31 | hand_written/writing_unittests 32 | hand_written/speedup 33 | hand_written/packaging 34 | hand_written/typing_support 35 | hand_written/zreferences 36 | 37 | .. _pyphysim_api: 38 | 39 | Packages and Modules in PyPhysim 40 | ================================ 41 | 42 | .. toctree:: 43 | :maxdepth: 3 44 | 45 | pyphysim 46 | 47 | .. The modules.rst file is created by sphinx-apidoc, but we dont want to 48 | include it, since we already included a toctree with the pyphysim 49 | subpackages above. Therefore, we include the modules.rst file as hidden here 50 | so that sphinx does not complain that it is not included in any toctree. 51 | .. toctree:: 52 | :maxdepth: 4 53 | :hidden: 54 | 55 | modules.rst 56 | 57 | 58 | .. 59 | Indices and tables 60 | ================== 61 | 62 | * :ref:`genindex` 63 | * :ref:`modindex` 64 | * :ref:`search` 65 | 66 | Todo List 67 | ========= 68 | 69 | .. todolist:: 70 | -------------------------------------------------------------------------------- /apps/sum_capacity_template.tikz: -------------------------------------------------------------------------------- 1 | \def\maxiterations{MAXITER} % Mude para o número máximo de iterações 2 | \pgfmathsetmacro{\rightaxisymax}{1.1*\maxiterations} 3 | \def\secondrightaxisytick{SECONDTICK} % The first tick is at 0. 4 | 5 | \def\lefttaxisymax{YMAX} 6 | 7 | \begin{tikzpicture}[every pin/.append style={pin style}, trim axis right] 8 | \begin{axis}[% 9 | width=\plotwidth, %\smallplotwidth, 10 | height=\plotheight, %\smallplotheight, 11 | scale only axis, 12 | xmin=-10, 13 | xmax=30, 14 | xtick={-10, -5, ..., 30}, 15 | xlabel={SNR (in dB)}, 16 | ymin=0, 17 | ymax=\lefttaxisymax, 18 | ylabel={Sum Capacity (bits/channel use)}, 19 | % legend style={draw=black,fill=white,legend cell align=left, at={(0.97,0.03)}, anchor=south east, font=\footnotesize}, 20 | legend style={draw=black,fill=white,legend cell align=left, at={(0.03,0.5)}, anchor=west, font=\footnotesize}, 21 | grid=both, 22 | ] 23 | 24 | SUM_CAPACITY_ALL_ALGS 25 | \end{axis} 26 | 27 | \begin{axis} 28 | [% 29 | width=\plotwidth, %\smallplotwidth, 30 | height=\plotheight, %\smallplotheight, 31 | scale only axis, 32 | xmin=-10, 33 | xmax=30, 34 | xtick=\empty, % We don't need xticks, since the ther axis already put them 35 | axis y line=right, 36 | ymin=0, 37 | ymax=\rightaxisymax, % Max iterations * 1.1 38 | ytick={0, \secondrightaxisytick, ..., \rightaxisymax}, 39 | ylabel={Mean Iterations}, 40 | %legend style={draw=black,fill=white,legend cell align=left, legend pos=south west}, 41 | %grid=both, 42 | ] 43 | 44 | ITER_ALL_ALGS 45 | 46 | \addplot[black] 47 | plot[] 48 | coordinates{(-10.0, \maxiterations) 49 | (-5.0, \maxiterations.0) 50 | (0.0, \maxiterations.0) 51 | (5.0, \maxiterations.0) 52 | (10.0, \maxiterations.0) 53 | (15.0, \maxiterations) 54 | (20.0, \maxiterations) 55 | (25.0, \maxiterations) 56 | (30.0, \maxiterations)}; 57 | 58 | % \node[coordinate,pin=80:{Maximum Number of iterations: \maxiterations}] 59 | % at (axis cs:0.0,\maxiterations) {}; 60 | 61 | \end{axis} 62 | 63 | \end{tikzpicture} 64 | -------------------------------------------------------------------------------- /apps/simple_BD_with_whitening.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """module docstring""" 3 | 4 | from time import time 5 | 6 | import numpy as np 7 | 8 | import pyphysim.channels.multiuser 9 | from pyphysim.comm import blockdiagonalization 10 | from pyphysim.modulators import fundamental 11 | from pyphysim.util import conversion 12 | 13 | tic = time() 14 | 15 | K = 3 16 | 17 | # Channel parameters 18 | Nt = 2 * np.ones(K) 19 | Nr = 2 * np.ones(K) 20 | Ns_BD = Nt 21 | 22 | # Modulator Parameters 23 | M = 4 24 | modulator = fundamental.PSK(M) 25 | 26 | # Transmission Parameters 27 | NSymbs = 500 # Number of symbols (per stream per user simulated at each 28 | # iteration 29 | SNR_dB = 15. 30 | N0_dBm = -116.4 # Noise power (in dBm) 31 | 32 | # External Interference Parameters 33 | Pe_dBm = -100 # transmit power (in dBm) of the ext. interference 34 | ext_int_rank = 1 # Rank of the external interference 35 | 36 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 37 | # xxxxxxxxxxxxxxx Dependent Variables xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 38 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 39 | noise_var = conversion.dBm2Linear(N0_dBm) 40 | snr = conversion.dB2Linear(SNR_dB) 41 | transmit_power = 1.0 # snr * noise_var 42 | # External interference power 43 | pe = conversion.dBm2Linear(Pe_dBm) 44 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 45 | 46 | # Generate the channel 47 | multiuser_channel = pyphysim.channels.multiuser.MultiUserChannelMatrixExtInt() 48 | multiuser_channel.randomize(Nr, Nt, K, ext_int_rank) 49 | 50 | # Generate input data and modulate it 51 | input_data = np.random.randint(0, M, [np.sum(Ns_BD), NSymbs]) 52 | symbols = modulator.modulate(input_data) 53 | 54 | BD = blockdiagonalization.BlockDiagonalizer(K, transmit_power, noise_var) 55 | enhancedBD = blockdiagonalization.EnhancedBD(K, transmit_power, noise_var, pe) 56 | 57 | (newH, 58 | Ms) = BD.block_diagonalize_no_waterfilling(multiuser_channel.big_H_no_ext_int) 59 | (newH_ext, Ms_ext, Ns_all_users 60 | ) = enhancedBD.block_diagonalize_no_waterfilling(multiuser_channel) 61 | -------------------------------------------------------------------------------- /apps/ia/check_greedy_partial_results.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pyphysim.simulations.results import SimulationResults 4 | 5 | if __name__ == '__main__': 6 | full_results_name = ("greedy_IA_stream_sel_results_[0.0_(5.0)_30.0]_" 7 | "4-PSK_3x3_(3)_MaxIter_120_(['random']).pickle") 8 | full_result = SimulationResults.load_from_file(full_results_name) 9 | full_params = full_result.params 10 | 11 | name = ("partial_results/greedy_IA_stream_sel_results_[0.0_(5.0)_30.0]_" 12 | "4-PSK_3x3_(3)_MaxIter_120_(['random'])_unpack_{:0>2d}.pickle") 13 | 14 | for i in range(28): 15 | result = SimulationResults.load_from_file(name.format(i)) 16 | params = result.params 17 | stream_sel_method = params['stream_sel_method'] 18 | scenario = params['scenario'] 19 | initialize_with = params['initialize_with'] 20 | SNR = params['SNR'] 21 | # print('Unpacked parameters') 22 | print(('scenario: {0:>10} | stream_sel_method: {1:>6s} | ' 23 | 'SNR: {2:>4}').format(scenario, stream_sel_method, SNR)) 24 | 25 | # if scenario == 'NoPathLoss' and stream_sel_method == 'brute': 26 | # print "SNR {0}: Ber {1}".format(SNR, result['ber']) 27 | 28 | # SNR 0.0: Ber [Result -> ber: 192516/6502400 -> 0.0296069143701] 29 | # SNR 5.0: Ber [Result -> ber: 32498/7090800 -> 0.00458312179162] 30 | # SNR 10.0: Ber [Result -> ber: 2108/10665200 -> 0.000197652177174] 31 | # SNR 15.0: Ber [Result -> ber: 11/11884000 -> 9.25614271289e-07] 32 | # SNR 20.0: Ber [Result -> ber: 0/12558000 -> 0.0] 33 | # SNR 25.0: Ber [Result -> ber: 0/13464400 -> 0.0] 34 | # SNR 30.0: Ber [Result -> ber: 0/13934400 -> 0.0] 35 | 36 | # print("\n\n") 37 | # params_list = full_params.get_unpacked_params_list() 38 | # for p in params_list: 39 | # stream_sel_method = p['stream_sel_method'] 40 | # scenario = p['scenario'] 41 | # initialize_with = p['initialize_with'] 42 | # SNR = p['SNR'] 43 | # print('scenario: {0:>10} | stream_sel_method: {1:>6s} | ' 44 | # 'SNR: {2:>4}').format(scenario, stream_sel_method, SNR) 45 | -------------------------------------------------------------------------------- /docs/Mathjax/PUT_MathJax_Here: -------------------------------------------------------------------------------- 1 | Download MathJax from 2 | http://www.mathjax.org/download/ 3 | and put its content here. 4 | 5 | After that, add the macros below in the config/local/local.js file in the 6 | MathJax folder. 7 | 8 | --8<---------------cut here---------------start------------->8--- 9 | // Matrices 10 | TEX.Macro("mtA", "\\mathbf{A}"); 11 | TEX.Macro("mtB", "\\mathbf{B}"); 12 | TEX.Macro("mtC", "\\mathbf{C}"); 13 | TEX.Macro("mtD", "\\mathbf{D}"); 14 | TEX.Macro("mtE", "\\mathbf{E}"); 15 | TEX.Macro("mtF", "\\mathbf{F}"); 16 | TEX.Macro("mtG", "\\mathbf{G}"); 17 | TEX.Macro("mtH", "\\mathbf{H}"); 18 | TEX.Macro("mtI", "\\mathbf{I}"); 19 | TEX.Macro("mtJ", "\\mathbf{J}"); 20 | TEX.Macro("mtK", "\\mathbf{K}"); 21 | TEX.Macro("mtL", "\\mathbf{L}"); 22 | TEX.Macro("mtM", "\\mathbf{M}"); 23 | TEX.Macro("mtN", "\\mathbf{N}"); 24 | TEX.Macro("mtO", "\\mathbf{P}"); 25 | TEX.Macro("mtP", "\\mathbf{P}"); 26 | TEX.Macro("mtQ", "\\mathbf{Q}"); 27 | TEX.Macro("mtR", "\\mathbf{R}"); 28 | TEX.Macro("mtS", "\\mathbf{S}"); 29 | TEX.Macro("mtT", "\\mathbf{T}"); 30 | TEX.Macro("mtU", "\\mathbf{U}"); 31 | TEX.Macro("mtV", "\\mathbf{V}"); 32 | TEX.Macro("mtW", "\\mathbf{W}"); 33 | TEX.Macro("mtX", "\\mathbf{X}"); 34 | TEX.Macro("mtY", "\\mathbf{Y}"); 35 | TEX.Macro("mtZ", "\\mathbf{Z}"); 36 | // Vectors 37 | TEX.Macro("vtB", "\\mathbf{b}"); 38 | TEX.Macro("vtC", "\\mathbf{c}"); 39 | TEX.Macro("vtD", "\\mathbf{d}"); 40 | TEX.Macro("vtE", "\\mathbf{e}"); 41 | TEX.Macro("vtF", "\\mathbf{f}"); 42 | TEX.Macro("vtG", "\\mathbf{g}"); 43 | TEX.Macro("vtH", "\\mathbf{h}"); 44 | TEX.Macro("vtI", "\\mathbf{i}"); 45 | TEX.Macro("vtJ", "\\mathbf{j}"); 46 | TEX.Macro("vtK", "\\mathbf{k}"); 47 | TEX.Macro("vtL", "\\mathbf{l}"); 48 | TEX.Macro("vtM", "\\mathbf{m}"); 49 | TEX.Macro("vtN", "\\mathbf{n}"); 50 | TEX.Macro("vtO", "\\mathbf{p}"); 51 | TEX.Macro("vtP", "\\mathbf{p}"); 52 | TEX.Macro("vtQ", "\\mathbf{q}"); 53 | TEX.Macro("vtR", "\\mathbf{r}"); 54 | TEX.Macro("vtS", "\\mathbf{s}"); 55 | TEX.Macro("vtT", "\\mathbf{t}"); 56 | TEX.Macro("vtU", "\\mathbf{u}"); 57 | TEX.Macro("vtV", "\\mathbf{v}"); 58 | TEX.Macro("vtW", "\\mathbf{w}"); 59 | TEX.Macro("vtX", "\\mathbf{x}"); 60 | TEX.Macro("vtY", "\\mathbf{y}"); 61 | TEX.Macro("vtZ", "\\mathbf{z}"); 62 | --8<---------------cut here---------------end--------------->8--- 63 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "pyphysim" 3 | version = "0.7.2" 4 | description = "Simulation of digital communication (physical layer) in python" 5 | authors = ["Darlan Cavalcante Moreira "] 6 | license = "GPL-2.0-or-later" 7 | readme = "README.md" 8 | repository = "https://github.com/darcamo/pyphysim" 9 | keywords = ["physical layer", "QAM", "PSK", "QPSK", "BPSK", "OFDM", "Modulation", "Monte Carlo", "simulation", "MIMO", "Tapped Delay Line"] 10 | classifiers = [ 11 | "Development Status :: 5 - Production/Stable", 12 | "Intended Audience :: Telecommunications Industry", 13 | "Intended Audience :: Education", 14 | "Intended Audience :: Science/Research", 15 | "License :: OSI Approved :: GNU General Public License (GPL)", 16 | "Programming Language :: Python :: 3", 17 | "Topic :: Scientific/Engineering" 18 | ] 19 | include = ["LICENSE.md", "pyphysim/py.typed"] 20 | # build = 'build.py' 21 | 22 | [tool.poetry.dependencies] 23 | python = "^3.7" 24 | numpy = "*" 25 | scipy = "*" 26 | matplotlib = "*" 27 | configobj = "*" 28 | numba = "*" 29 | 30 | # Required to run simulations in parallel 31 | ipyparallel = {version = "^6.3.0", optional = true} 32 | ipython = {version = "^7.16.1", optional = true} 33 | 34 | # Required to use the 'ipython' style for progressbar (only works inside a notebook) 35 | ipywidgets = {version = "^7.5.1", optional = true} 36 | 37 | # Required to use the `to_dataframe` method of SimulationResults 38 | pandas = {version = "^1.0.5", optional = true} 39 | cloudpickle = {version = "^1.5.0", optional = true} 40 | 41 | [tool.poetry.dev-dependencies] 42 | nose = "^1.3.7" 43 | mypy = "^0.782" 44 | pre-commit = "^2.6.0" 45 | pylint = "^2.5.3" 46 | sphinx = "^3.1.2" 47 | sphinx_rtd_theme = "^0.5.0" 48 | yapf = "^0.30.0" 49 | coverage = "^5.2" 50 | coveralls = "^2.1.1" 51 | pudb = "^2019.2" 52 | darglint = "^1.5.1" 53 | importmagic = "^0.1.7" 54 | jupyterlab = "^2.2.0" 55 | jupyterlab_code_formatter = "^1.3.5" 56 | jupyter-lsp = "^0.8.0" 57 | ipympl = "^0.5.7" 58 | pip = "^20.1.1" 59 | python-language-server = {extras = ["all"], version = "^0.34.1"} 60 | epc = "^0.0.5" 61 | 62 | [build-system] 63 | # See https://github.com/chrahunt/rtd-poetry/issues/3 64 | requires = ["poetry>=1.0", "rtd-poetry-tmp"] 65 | build-backend = "rtd_poetry" 66 | # requires = ["poetry>=0.12"] 67 | # build-backend = "poetry.masonry.api" 68 | -------------------------------------------------------------------------------- /bin/run_python_coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # Example in http://nedbatchelder.com/code/coverage/cmd.html 4 | 5 | # You need to install the coverage package (available in the Ubuntu 6 | # repositories) to run this script. 7 | 8 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 9 | # This script run coverage on each test file here and append the 10 | # data to the .coverage file. Then it generates a coverage report. 11 | # 12 | # Note that the coverage report omits stuff on the /usr folder to avoid 13 | # reporting information about built-in modules. 14 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 15 | 16 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 17 | # Observation: Ideally it is better to run coverage on a single test 18 | # file and generate the report only for that test file. In the report one 19 | # would care only about the coverage of the tested module. 20 | # 21 | # That is because the way it is done here (accumulating coverage data from 22 | # all test files) may not spot some methods that are not properly tested 23 | # simple because they are used in other test files. 24 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 25 | 26 | # Remove any previous information from coverage. 27 | rm -f .coverage 28 | 29 | cd ../tests 30 | rm -f .coverage 31 | for file in *test.py; 32 | do 33 | echo "xxxxxxxxxx Running coverage on file $file" 34 | coverage run -a --omit "/usr/*","*/pyphysim/extra/*" --source "../pyphysim" $file > /dev/null; 35 | echo "xxxxxxxxxx Done xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 36 | done 37 | 38 | # Generate the report 39 | echo "xxxxxxxxxx Creating Coverage Report xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 40 | coverage report -m --omit "/usr/*","/*__init__*","/*apps/*" 41 | echo "xxxxxxxxxx Done xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 42 | 43 | 44 | # Note: Test coverage measured over running a whole unit test suite is not 45 | # necessarily a good idea. Measuring a whole run means some things get 46 | # marked as being tested even though they just happen to get called, and 47 | # get called only in some particular way. That means that maybe you should 48 | # run the coverage and the report on each test *.test file instead of 49 | # appending all of the coverage data to the same .coverage file for latter 50 | # report. 51 | -------------------------------------------------------------------------------- /apps/ia/test_ia_feasibility.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """module docstring""" 3 | 4 | import numpy as np 5 | 6 | from pyphysim.channels.multiuser import MultiUserChannelMatrix 7 | from pyphysim.ia import algorithms 8 | from pyphysim.modulators.fundamental import PSK 9 | from pyphysim.util.conversion import dB2Linear 10 | 11 | if __name__ == '__main__': 12 | K = 3 13 | Nr = np.ones(K) * 4 14 | Nt = np.ones(K) * 4 15 | Ns = np.array([2, 2, 2]) # np.ones(K) * 2 16 | 17 | multiuserchannel = MultiUserChannelMatrix() 18 | modulator = PSK(4) 19 | 20 | SNR = 40 21 | noise_var = 1 / dB2Linear(SNR) 22 | print("SNR: {0}".format(SNR)) 23 | print("noise_var: {0}".format(noise_var)) 24 | 25 | multiuserchannel.randomize(Nr, Nt, K) 26 | multiuserchannel.noise_var = noise_var 27 | 28 | ia_solver = algorithms.AlternatingMinIASolver(multiuserchannel) 29 | ia_solver2 = algorithms.MMSEIASolver(multiuserchannel) 30 | ia_solver3 = algorithms.MaxSinrIASolver(multiuserchannel) 31 | 32 | ia_solver4 = algorithms.AlternatingMinIASolver(multiuserchannel) 33 | 34 | # ia_solver.initialize_with_closed_form = True 35 | # ia_solver2.initialize_with_closed_form = True 36 | # ia_solver3.initialize_with_closed_form = True 37 | 38 | ia_solver.randomizeF(Ns) 39 | ia_solver.max_iterations = 400 40 | ia_solver.solve(Ns) 41 | 42 | ia_solver2.randomizeF(Ns) 43 | ia_solver2.max_iterations = 100 44 | ia_solver2.solve(Ns) 45 | 46 | ia_solver3.randomizeF(Ns) 47 | ia_solver3.max_iterations = 100 48 | ia_solver3.solve(Ns) 49 | 50 | ia_solver4.randomizeF(Ns) 51 | ia_solver4.max_iterations = 100 52 | ia_solver4.solve(Ns) 53 | 54 | print("Final_Cost: {0}\n".format(ia_solver.get_cost())) 55 | 56 | # all_possibilities = itertools.product(range(K), range(K)) 57 | # for ij in all_possibilities: 58 | # i, j = ij 59 | # print "Hij: H{0}{1}".format(i, j) 60 | # Hij = multiuserchannel.get_Hkl(i, j) 61 | # Hij_eff = np.dot( 62 | # ia_solver.full_W_H[i], np.dot(Hij, ia_solver.full_F[j])) 63 | # print "Eigenvalues: {0}".format(np.linalg.svd(Hij_eff)[1].round(6)[0]) 64 | # print "Eigenvector: {0}".format( 65 | # np.linalg.svd(Hij_eff)[0].round(6)[0][0]) 66 | # print 67 | 68 | print("Sum Capacity (Alt Min): {0}".format( 69 | np.sum(np.log2(np.hstack(1.0 + ia_solver.calc_SINR()))))) 70 | print("Sum Capacity (Alt Min): {0}".format( 71 | np.sum(np.log2(np.hstack(1.0 + ia_solver4.calc_SINR()))))) 72 | print("Sum Capacity (MMSE): {0}".format( 73 | np.sum(np.log2(np.hstack(1.0 + ia_solver2.calc_SINR()))))) 74 | print("Sum Capacity (Max SINR): {0}".format( 75 | np.sum(np.log2(np.hstack(1.0 + ia_solver3.calc_SINR()))))) 76 | 77 | # print linear2dB(np.hstack(ia_solver.calc_SINR())) 78 | # print linear2dB(np.hstack(ia_solver2.calc_SINR())) 79 | # print linear2dB(np.hstack(ia_solver3.calc_SINR())) 80 | -------------------------------------------------------------------------------- /tests/matlab_package_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pylint: disable=E1101 4 | """ 5 | Tests for the modules in the MATLAB package. 6 | 7 | Each module has doctests for its functions and all we need to do is run all 8 | of them. 9 | """ 10 | 11 | import doctest 12 | import unittest 13 | 14 | import numpy as np 15 | 16 | from pyphysim.extra.MATLAB import python2MATLAB 17 | 18 | 19 | # noinspection PyMethodMayBeStatic 20 | class MATLABDoctestsTestCase(unittest.TestCase): 21 | """Test case that run all the doctests in the modules of the MATLAB 22 | package. 23 | 24 | """ 25 | def test_python2MATLAB(self) -> None: 26 | """Run python2MATLAB doctests""" 27 | doctest.testmod(python2MATLAB) 28 | 29 | 30 | # noinspection PyMethodMayBeStatic 31 | class MATLABFunctionsTestCase(unittest.TestCase): 32 | def test_mmat(self) -> None: 33 | # Test 1D numpy array 34 | a = np.arange(10) + (10 - np.arange(10)) * 1j 35 | 36 | conv_a = python2MATLAB.to_mat_str(a) 37 | expected_conv_a = ('[+0.000000000000e+00+1.000000000000e+01j, ' 38 | '+1.000000000000e+00+9.000000000000e+00j, ' 39 | '+2.000000000000e+00+8.000000000000e+00j, ' 40 | '+3.000000000000e+00+7.000000000000e+00j, ' 41 | '+4.000000000000e+00+6.000000000000e+00j, ' 42 | '+5.000000000000e+00+5.000000000000e+00j, ' 43 | '+6.000000000000e+00+4.000000000000e+00j, ' 44 | '+7.000000000000e+00+3.000000000000e+00j, ' 45 | '+8.000000000000e+00+2.000000000000e+00j, ' 46 | '+9.000000000000e+00+1.000000000000e+00j]') 47 | self.assertEqual(conv_a, expected_conv_a) 48 | 49 | # Test a 2D numpy array with a single column (a column vector) 50 | b = np.arange(1, 5) - np.arange(1, 5) * 1j 51 | b.shape = (4, 1) 52 | conv_b = python2MATLAB.to_mat_str(b) 53 | expected_conv_b = ('[+1.000000000000e+00-1.000000000000e+00j; ' 54 | '+2.000000000000e+00-2.000000000000e+00j; ' 55 | '+3.000000000000e+00-3.000000000000e+00j; ' 56 | '+4.000000000000e+00-4.000000000000e+00j]') 57 | self.assertEqual(conv_b, expected_conv_b) 58 | 59 | # Test a 2D real numpy array 60 | c = np.arange(1, 10) 61 | c.shape = (3, 3) 62 | conv_c = python2MATLAB.to_mat_str(c) 63 | expected_conv_c = ( 64 | '[+1.000000000000e+00, +2.000000000000e+00, +3.000000000000e+00; ' 65 | '+4.000000000000e+00, +5.000000000000e+00, +6.000000000000e+00; ' 66 | '+7.000000000000e+00, +8.000000000000e+00, +9.000000000000e+00]') 67 | self.assertEqual(conv_c, expected_conv_c) 68 | 69 | 70 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 71 | if __name__ == "__main__": 72 | unittest.main() 73 | -------------------------------------------------------------------------------- /bin/split_into_partial_results.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Script that reads a SimulationResults file and save the corresponding 4 | partial results. 5 | """ 6 | 7 | import argparse 8 | import os 9 | 10 | from pyphysim.simulations.results import SimulationResults 11 | from pyphysim.simulations.runner import get_partial_results_filename 12 | from pyphysim.util.misc import replace_dict_values 13 | 14 | 15 | # TODO: Test if the partial results saved by this script are correct 16 | def main(): 17 | """Main function of the script. 18 | """ 19 | parser = argparse.ArgumentParser() 20 | parser.add_argument('name', help="The name of the SimulationResults file.") 21 | parser.add_argument('folder', 22 | help="The name of the second SimulationResults file.", 23 | nargs='?') 24 | 25 | args = parser.parse_args() 26 | 27 | name = args.name 28 | folder = args.folder # This will be None, if not provided 29 | 30 | results = SimulationResults.load_from_file(name) 31 | 32 | original_filename = results.original_filename 33 | 34 | # xxxxxxxxxx Remove the .pickle file extension xxxxxxxxxxxxxxxxxxxxxxxx 35 | # If partial_filename has a 'pickle' extension we remove it from the name 36 | original_filename_no_ext, ext = os.path.splitext(original_filename) 37 | if ext == '.pickle': 38 | original_filename = original_filename_no_ext 39 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 40 | 41 | unpacked_params_list = results.params.get_unpacked_params_list() 42 | all_results_names = results.get_result_names() 43 | 44 | for i, p in enumerate(results.params.get_unpacked_params_list()): 45 | partial_filename = get_partial_results_filename( 46 | original_filename, p, folder) 47 | partial_filename_with_replacements = replace_dict_values( 48 | partial_filename, results.params.parameters, filename_mode=True) 49 | 50 | if partial_filename_with_replacements == name: 51 | raise RuntimeError('invalid name') 52 | 53 | partial_param = unpacked_params_list[i] 54 | partial_result = SimulationResults() 55 | partial_result.set_parameters(partial_param) 56 | 57 | for result_name in all_results_names: 58 | partial_result.add_result(results[result_name][i]) 59 | 60 | partial_result.current_rep = results.runned_reps[i] 61 | 62 | # Try to save the partial results. If we get an error 63 | try: 64 | # If we get an IOError exception here, maybe we are trying to 65 | # save to a folder and the folder does not exist yet. 66 | partial_result.save_to_file(partial_filename_with_replacements) 67 | except IOError as e: 68 | if folder is not None: 69 | # Lets create the folder... 70 | os.mkdir(folder) 71 | # ... and try to save the file again. 72 | partial_result.save_to_file(partial_filename_with_replacements) 73 | else: 74 | raise e 75 | 76 | 77 | if __name__ == '__main__': 78 | main() 79 | -------------------------------------------------------------------------------- /apps/codebooks/find_codebook_results: -------------------------------------------------------------------------------- 1 | 10.000 repetições 2 | 3 | REAL CODEBOOKS 4 | 5 | In [1]: run find_codebook.py 6 | -------- Search for 4 precoders in G(3,1) -------- 7 | Maximum minimum distance is: 0.92 8 | Principal angles are (radians): [ 1.17803756] 9 | Principal angles are (degrees): [ 67.49658025] 10 | 11 | 12 | In [2]: run find_codebook.py 13 | -------- Search for 4 precoders in G(3,1) -------- 14 | Maximum minimum distance is: 0.90 15 | Principal angles are (radians): [ 1.12809212] 16 | Principal angles are (degrees): [ 64.63491715] 17 | 18 | 19 | In [3]: run find_codebook.py 20 | -------- Search for 8 precoders in G(3,1) -------- 21 | Maximum minimum distance is: 0.62 22 | Principal angles are (radians): [ 0.67248083] 23 | Principal angles are (degrees): [ 38.53031337] 24 | 25 | 26 | In [4]: run find_codebook.py 27 | -------- Search for 8 precoders in G(3,1) -------- 28 | Maximum minimum distance is: 0.61 29 | Principal angles are (radians): [ 0.6528877] 30 | Principal angles are (degrees): [ 37.40770967] 31 | 32 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 33 | COMPLEX CODEBOOKS 34 | 35 | In [5]: run find_codebook.py 36 | -------- Search for 8 precoders in G(3,1) -------- 37 | Maximum minimum distance is: 0.69 38 | Principal angles are (radians): [ 0.76054388] 39 | Principal angles are (degrees): [ 43.57595454] 40 | 41 | 42 | In [6]: run find_codebook.py 43 | -------- Search for 8 precoders in G(3,1) -------- 44 | Maximum minimum distance is: 0.68 45 | Principal angles are (radians): [ 0.75063794] 46 | Principal angles are (degrees): [ 43.00838611] 47 | 48 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 49 | COMPLEX QEGT CODEBOOKS 50 | 51 | In [7]: run find_codebook.py 52 | -------- Search for 8 precoders in G(3,1) -------- 53 | Maximum minimum distance is: 0.53 54 | Principal angles are (radians): [ 0.55493027] 55 | Principal angles are (degrees): [ 31.79516231] 56 | 57 | In [8]: run find_codebook.py 58 | -------- Search for 8 precoders in G(3,1) -------- 59 | Maximum minimum distance is: 0.58 60 | Principal angles are (radians): [ 0.62277553] 61 | Principal angles are (degrees): [ 35.68240927] 62 | 63 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 64 | 100.000 repetições -> COMPLEX CODEBOOKS 65 | 66 | In [9]: run find_codebook.py 67 | -------- Search for 8 precoders in G(3,1) -------- 68 | Maximum minimum distance is: 0.70 69 | Principal angles are (radians): [ 0.7782351] 70 | Principal angles are (degrees): [ 44.58958687] 71 | 72 | In [10]: run find_codebook.py 73 | -------- Search for 8 precoders in G(3,1) -------- 74 | Maximum minimum distance is: 0.73 75 | Principal angles are (radians): [ 0.81293265] 76 | Principal angles are (degrees): [ 46.57760965] 77 | 78 | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 | 10.000 repetições COMPLEX CODEBOOKS 80 | 81 | In [11]: run find_codebook.py 82 | ------- Search for 16 precoders in G(3,1) -------- 83 | Maximum minimum distance is: 0.51 84 | Principal angles are (radians): [ 0.53008703] 85 | Principal angles are (degrees): [ 30.37174981] 86 | 87 | In [12]: run find_codebook.py 88 | ------- Search for 16 precoders in G(3,1) -------- 89 | Maximum minimum distance is: 0.55 90 | Principal angles are (radians): [ 0.58281008] 91 | Principal angles are (degrees): [ 33.3925579] 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Testing](https://travis-ci.org/darcamo/pyphysim.svg?branch=master) 2 | [![Coverage Status](https://coveralls.io/repos/github/darcamo/pyphysim/badge.svg?branch=master)](https://coveralls.io/github/darcamo/pyphysim?branch=master) 3 | [![Documentation Status](https://readthedocs.org/projects/pyphysim/badge/?version=latest)](http://pyphysim.readthedocs.io/en/latest/?badge=latest) 4 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) 5 | 6 | PyPhysim 7 | ======== 8 | 9 | Simulation of Digital Communication (physical layer) in Python. 10 | 11 | This includes classes related to digital modulation (M-QAM, M-PSK, etc), AWGN 12 | channel, Rayleigh and tapped delay line channel models, channel estimation, 13 | MIMO, OFDM, etc.. It also includes classes related to multiuser transmission, 14 | such as block diagonalization, interference alignment, etc., as well as classes 15 | representing access nodes and users for easily creating physical layer 16 | simulations. 17 | 18 | Furthermore, a framework for implementing Monte Carlo simulations is also 19 | implemented (see the pyphysim.simulations package) to help with creating 20 | simulators that run many independent realizations and average the results. 21 | 22 | 23 | Installation 24 | ------------ 25 | 26 | Pyphysim is available in [Pypi](https://pypi.org/project/pyphysim/) and can be 27 | installed with pip or similar tools. If you want to install from the git 28 | repository, then install [poetry](https://python-poetry.org/) first, clone the 29 | repository, and run the command `poetry install` from the cloned folder to 30 | install pyphysim and its dependencies in a virtual environment (created by 31 | poetry). After that, just use `poetry shell` to activate the environment and you 32 | should be able to import pyphysim from python started in that shell. 33 | 34 | 35 | Examples 36 | ======== 37 | 38 | There are a few notebooks in the 39 | [notebooks](https://github.com/darcamo/pyphysim/tree/master/notebooks) folder in 40 | the GitHub repository. A list of some of the notebooks with a small description 41 | is shown below for quick access. 42 | 43 | - [Transmission_with_AWGN_channel.ipynb](https://github.com/darcamo/pyphysim/blob/master/notebooks/Transmission_with_AWGN_channel.ipynb): 44 | Illustrates the use of digital modulation to transmit through an AWGN channel 45 | and the usage of classes that help creating Monte Carlo simulations 46 | - [Transmission_with_Rayleigh_and_AWGN_channels.ipynb](https://github.com/darcamo/pyphysim/blob/master/notebooks/Transmission_with_Rayleigh_and_AWGN_channels.ipynb): Extends the previous notebook to transmission through a Rayleigh channel and discuss running simulation 47 | - [TDL_and_OFDM.ipynb](https://github.com/darcamo/pyphysim/blob/master/notebooks/TDL_and_OFDM.ipynb): Simulation of transmission through a frequency selective channel that varies in time using OFDM 48 | - [PathLoss classes.ipynb](https://github.com/darcamo/pyphysim/blob/master/notebooks/PathLoss_classes.ipynb): Illustrate some path loss classes 49 | - [Alamouti.ipynb](https://github.com/darcamo/pyphysim/blob/master/notebooks/Alamouti.ipynb): Illustrates how to simulate using multiple antennas and how to use configuration files 50 | -------------------------------------------------------------------------------- /pyphysim/extra/pgfplotshelper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pylint: disable-all 4 | """ 5 | This module contains useful functions to create pgfplots code (latex 6 | code using the pgfplots package) from python data. 7 | 8 | One example of tex code for a plot using pgfplots is show below 9 | 10 | .. code-block:: latex 11 | 12 | \\begin{tikzpicture} 13 | \\begin{axis}[axis options] 14 | \\addplot [plot options] 15 | plot [options] 16 | coordinates { 17 | (0, 0) 18 | (1, 1) 19 | (2, 4) 20 | (3, 9)}; 21 | \\addlegendentry{Legend of the last line}; 22 | \\end{axis} 23 | \\end{tikzpicture} 24 | 25 | """ 26 | 27 | from typing import Collection, Optional 28 | 29 | import numpy as np 30 | 31 | 32 | def generate_pgfplots_plotline( 33 | x: Collection[float], # pragma: no cover 34 | y: Collection[float], 35 | errors: Optional[np.ndarray] = None, 36 | options: Optional[str] = None, 37 | legend: Optional[str] = None) -> str: 38 | """ 39 | This function generates the code corresponding to the "addplot" command 40 | in a pgfplots plot for the coordinates given in `x` and `y`. 41 | 42 | If the parameter `errors` is also provided then error bars will be 43 | added in the `y` direction, while options to the addplot command can be 44 | passed as a string in the `options` argument. 45 | 46 | Parameters 47 | ---------- 48 | x : np.ndarray | list[float] | list[int] 49 | The data for the 'x' axis in the plot. 50 | y : np.ndarray | list[float] | list[int] 51 | The data for the 'x' axis in the plot 52 | errors : np.ndarray, optional 53 | The error for plotting the errorbars. 54 | options : str 55 | pgfplot options for the plot line. 56 | Ex: "color=red, 57 | solid, 58 | mark=square, 59 | mark options={solid}" 60 | legend : str 61 | The legend for the plot line. 62 | """ 63 | import itertools 64 | 65 | # xxxxxxxxxx Creates the coordinates part of the plot line xxxxxxxxxxxx 66 | points = zip(x, y) 67 | num_points = min(len(x), len(y)) 68 | if errors is None: 69 | points_string = "\n".join([str(p) for p in points]) 70 | plot_line = "plot[]\ncoordinates{{{0}}};".format(points_string) 71 | else: 72 | error_points = zip(itertools.repeat(0.0, num_points), errors / 2.0) 73 | points_and_errors_list = [ 74 | "{0} +- {1}".format(a, b) for a, b in zip(points, error_points) 75 | ] 76 | points_string = "\n".join(points_and_errors_list) 77 | plot_line = ("plot[error bars/.cd, y dir = both, y explicit]\n" 78 | "coordinates{{{0}}};").format(points_string) 79 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 80 | 81 | # xxxxxxxxxx Get the whole addplot line xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 82 | if options is None: 83 | addplot_line = "\\addplot[]\n{0}".format(plot_line) 84 | else: 85 | addplot_line = "\\addplot[{1}]\n{0}".format(plot_line, options) 86 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 87 | 88 | if legend is not None: 89 | legend_line = "\n\\addlegendentry{{{0}}};".format(legend) 90 | addplot_line += legend_line 91 | 92 | return addplot_line 93 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 94 | -------------------------------------------------------------------------------- /pyphysim/extra/MATLAB/python2MATLAB.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Module with functions to easily moving data from python to MATLAB.""" 3 | 4 | import numpy as np 5 | 6 | __all__ = ["to_mat_str"] 7 | 8 | 9 | def to_mat_str( # type:ignore 10 | x: np.ndarray, format_string: str = '+.12e') -> str: 11 | """Convert the ndarray 'x' to a string corresponding to the MATLAB 12 | representation of `x`. 13 | 14 | The to_mat_str function formats numpy arrays of arbitrary dimension in 15 | a way which can easily copied and pasted into an interactive MATLAB 16 | session 17 | 18 | Parameters 19 | ---------- 20 | x : numpy array 21 | The numpy array to be represented as a MATLAB type. 22 | format_string : str, optional 23 | The format_string string to convert each element in `x`. 24 | 25 | Returns 26 | ------- 27 | converted_string : str 28 | A string that represents the converted numpy array. You can copy 29 | this string and past it into a MATLAB session. 30 | 31 | Examples 32 | -------- 33 | >>> a=np.arange(1,10) 34 | >>> a.shape=(3,3) 35 | >>> # Print as a numpy matrix 36 | >>> print(a) 37 | [[1 2 3] 38 | [4 5 6] 39 | [7 8 9]] 40 | >>> # Call to_mat_str(a) to print the string representation of the 41 | >>> # converted matrix 42 | >>> print(to_mat_str(a)) 43 | [\ 44 | +1.000000000000e+00, +2.000000000000e+00, +3.000000000000e+00; \ 45 | +4.000000000000e+00, +5.000000000000e+00, +6.000000000000e+00; \ 46 | +7.000000000000e+00, +8.000000000000e+00, +9.000000000000e+00] 47 | 48 | """ 49 | 50 | # noinspection PyShadowingNames 51 | def convert_row_or_col(numpy_array: np.array, 52 | format_string: str, 53 | separator: str = ', ') -> str: 54 | """Convert a one-dimensional numpy array to its MATLAB 55 | representation 56 | 57 | Parameters 58 | ---------- 59 | numpy_array : numpy array 60 | The array to be converted. 61 | format_string : str 62 | The format string for the conversion. 63 | separator : str 64 | The separator for 2 elements. 65 | 66 | """ 67 | # {0:+.12e} 68 | # +.12e 69 | output = [] 70 | if numpy_array.dtype == 'complex': 71 | format_string = "{{0:{0}}}{{1:{0}}}j".format( # type: ignore 72 | format_string, format_string) 73 | for i in numpy_array: 74 | output.append(format_string.format(i.real, i.imag)) 75 | else: 76 | format_string = '{{0:{0}}}'.format(format_string) 77 | for i in numpy_array: 78 | output.append(format_string.format(i)) 79 | return separator.join(output) 80 | 81 | if x.ndim == 1: 82 | # 1d input 83 | return f'[{convert_row_or_col(x, format_string)}]' 84 | 85 | if x.ndim == 2: 86 | if x.shape[1] == 1: 87 | # This is a Column vector 88 | return f"[{convert_row_or_col(np.reshape(x, x.size), format_string, separator='; ')}]" 89 | else: 90 | # This is not a column vector 91 | output = [] 92 | for row in x: 93 | output.append(convert_row_or_col(row, format_string)) 94 | return f"[{'; '.join(output)}]" 95 | 96 | if x.ndim > 2: # pragma: no cover 97 | raise NotImplementedError('This case is not implemented') 98 | -------------------------------------------------------------------------------- /pyphysim/comm/waterfilling.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Implements a waterfilling method. 3 | 4 | The doWF method performs the waterfilling algorithm. 5 | """ 6 | 7 | from typing import Tuple 8 | 9 | import numpy as np 10 | 11 | __all__ = ['doWF'] 12 | 13 | 14 | # noinspection PyUnresolvedReferences 15 | def doWF(vtChannels: np.ndarray, 16 | dPt: float, 17 | noiseVar: float = 1.0, 18 | Es: float = 1.0) -> Tuple[np.ndarray, float]: 19 | """ 20 | Performs the Waterfilling algorithm and returns the optimum power and 21 | water level. 22 | 23 | Parameters 24 | ---------- 25 | vtChannels : np.ndarray 26 | Numpy array with the channel POWER gains (power of the parallel 27 | AWGN channels). 28 | dPt : float 29 | Total available power. 30 | noiseVar : float 31 | Noise variance (power in linear scale). 32 | Es : float 33 | Symbol energy (in linear scale). 34 | 35 | Returns 36 | ------- 37 | (vtOptP, mu) : (np.ndarray, float) 38 | A tuple with vtOptP and mu, where vtOptP are the optimum powers, 39 | while mu is the water level. 40 | """ 41 | # Sort Channels (descending order) 42 | vtChannelsSortIndexes = np.argsort(vtChannels)[::-1] 43 | vtChannelsSorted = vtChannels[vtChannelsSortIndexes] 44 | assert isinstance(vtChannelsSorted, np.ndarray) 45 | 46 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 47 | # Calculates the water level that touches the worst channel (the higher 48 | # one) and therefore transmits zero power in this worst channel. After 49 | # that, calculates the power in each channel (the vector 'Ps') for this 50 | # water level. If the sum of all of these powers in 'Ps' is less then 51 | # the total available power, then all we need to do is divide the 52 | # remaining power equally among all the channels (increase the water 53 | # level). On the other hand, if the sum of all of these powers in 'Ps' 54 | # is greater then the total available power then we remove the worst 55 | # channel and repeat the process. 56 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 57 | # Calculates minimum water-level $\mu$ required to use all channels 58 | dNChannels = vtChannels.size 59 | dRemoveChannels = 0 60 | 61 | minMu = float(noiseVar) / ( 62 | Es * vtChannelsSorted[dNChannels - dRemoveChannels - 1]) 63 | Ps = (minMu - float(noiseVar) / 64 | (Es * vtChannelsSorted[np.arange(0, dNChannels - dRemoveChannels)])) 65 | 66 | # Ps should be a numpy array 67 | assert isinstance(Ps, np.ndarray) 68 | 69 | while (sum(Ps) > dPt) and (dRemoveChannels < dNChannels): 70 | dRemoveChannels += 1 71 | minMu = float(noiseVar) / ( 72 | Es * vtChannelsSorted[dNChannels - dRemoveChannels - 1]) 73 | Ps = ( 74 | minMu - float(noiseVar) / 75 | (Es * vtChannelsSorted[np.arange(0, dNChannels - dRemoveChannels)]) 76 | ) 77 | 78 | # Distributes the remaining power among the all the remaining channels 79 | dPdiff = dPt - np.sum(Ps) 80 | vtOptPaux = dPdiff / (dNChannels - dRemoveChannels) + Ps 81 | 82 | # Put optimum power in the original channel order 83 | vtOptP = np.zeros([ 84 | vtChannels.size, 85 | ]) 86 | vtOptP[vtChannelsSortIndexes[np.arange(0, dNChannels - 87 | dRemoveChannels)]] = vtOptPaux 88 | mu = vtOptPaux[0] + float(noiseVar) / vtChannelsSorted[0] 89 | 90 | return vtOptP, mu 91 | -------------------------------------------------------------------------------- /uml/Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile uses pyreverse (part of pylint) to generate class diagrams 2 | # for the classes in the packages of PyPhySim. 3 | # 4 | # Each package in PyPhySim should have two corresponding targets here: 5 | # "PACKAGE_diagrams" and a "PACKAGE_diagrams_simple" targets. 6 | # 7 | # Each of these targets generate two PDF files, "classes_something.pdf" and 8 | # "packages_something.pdf". The first one has the class diagram while the 9 | # second has the package diagram. At last, in the "simple" target the class 10 | # diagrams only have the class names. 11 | # 12 | # 13 | # Extending this Makefile 14 | # ----------------------- 15 | # 16 | # If new packages are added to PyPhySim, simple add the corresponding 17 | # dependence in the regular_diagrams and simple_diagrams targets. 18 | # 19 | # For instance suppose a new package called "cool" was added to 20 | # PyPhySim. You should edit this Makefile and add "classes_cool.pdf" as a 21 | # dependence in the regular_diagrams target and "classes_simple_cool.pdf" 22 | # as a dependence in the simple_diagrams target. 23 | # 24 | # 25 | # Notes 26 | # ----- 27 | # 28 | # Module functions are not shown in any of these diagrams. 29 | 30 | 31 | COMMON_COMPILE_PART:= pyreverse -o pdf -f ALL 32 | COMMON_SIMPLE_COMPILE_PART:= pyreverse -o pdf -k 33 | 34 | all: regular_diagrams simple_diagrams 35 | 36 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 37 | # Regular version diagrams for all targets 38 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 39 | regular_diagrams: classes_cell.pdf classes_comm.pdf classes_ia.pdf classes_MATLAB.pdf classes_plot.pdf classes_simulations.pdf classes_subspace.pdf classes_util.pdf 40 | @echo All regular diagrams generated 41 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 42 | 43 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 44 | # Simple version diagrams for all targets 45 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 46 | simple_diagrams: classes_simple_cell.pdf classes_simple_comm.pdf classes_simple_ia.pdf classes_simple_MATLAB.pdf classes_simple_plot.pdf classes_simple_simulations.pdf classes_simple_subspace.pdf classes_simple_util.pdf 47 | @echo All simple diagrams generated 48 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 49 | 50 | 51 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 52 | # General Rule to generate the regular diagrams 53 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 54 | classes_%.pdf: ../pyphysim/%/*.py 55 | @echo Generating regular diagrams for the $(subst .pdf,,$(subst classes_,,$@)) package 56 | @$(COMMON_COMPILE_PART) ../pyphysim/$(subst .pdf,,$(subst classes_,,$@)) -p $(subst .pdf,,$(subst classes_,,$@)) > /dev/null 57 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 58 | 59 | 60 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 61 | # General Rule to generate the simple diagrams 62 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 63 | classes_simple_%.pdf: ../pyphysim/%/*.py 64 | @echo Generating simple diagrams for the $(subst .pdf,,$(subst classes_simple_,,$@)) package 65 | @$(COMMON_SIMPLE_COMPILE_PART) ../pyphysim/$(subst .pdf,,$(subst classes_simple_,,$@)) -p simple_$(subst .pdf,,$(subst classes_simple_,,$@)) > /dev/null 66 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 67 | 68 | 69 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 70 | # Remove all diagrams from the folder 71 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 72 | clean: 73 | @rm -f *.pdf 74 | -------------------------------------------------------------------------------- /apps/ofdm/ofdm_tdlchannel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import numpy as np 4 | from matplotlib import pyplot as plt 5 | 6 | from pyphysim.channels.fading import COST259_TUx 7 | from pyphysim.channels.fading_generators import JakesSampleGenerator 8 | from pyphysim.channels.singleuser import SuChannel 9 | from pyphysim.modulators import OFDM, QPSK 10 | from pyphysim.modulators.ofdm import OfdmOneTapEqualizer 11 | 12 | """Simulate an OFDM transmission through a Tapped Delay Line channel. """ 13 | 14 | if __name__ == '__main__': 15 | 16 | # xxxxxxxxxx Simulation parameters xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 17 | num_symbols = 10000 18 | # OFDM parametersConfiguration 19 | fft_size = 512 20 | num_used_subcarriers = 300 21 | cp_size = 52 22 | # Jakes sample generator parameters 23 | Fd = 5 24 | Ts = 2.0e-7 25 | L = 20 26 | # Channel Profile 27 | channel_profile = COST259_TUx.get_discretize_profile(Ts) 28 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 29 | 30 | # xxxxxxxxxx Input generation (not part of OFDM) xxxxxxxxxxxxxxxxxxxxxx 31 | # generate input data 32 | input_data = np.random.random_integers(0, 4 - 1, num_symbols) 33 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 34 | 35 | # xxxxxxxxxx QPSK and OFDM Modulators xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 36 | qpsk_obj = QPSK() 37 | ofdm_obj = OFDM(fft_size=fft_size, 38 | cp_size=cp_size, 39 | num_used_subcarriers=num_used_subcarriers) 40 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 41 | 42 | # xxxxxxxxxx Modulate data xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 43 | modulated_data = qpsk_obj.modulate(input_data) 44 | transmit_data = ofdm_obj.modulate(modulated_data) 45 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 46 | 47 | # xxxxxxxxxx Transmit data through the channel xxxxxxxxxxxxxxxxxxxxxxxx 48 | jakes = JakesSampleGenerator(Fd, Ts, L) 49 | channel = SuChannel(jakes, channel_profile) 50 | channel_memory = channel.num_taps_with_padding - 1 51 | received_data = channel.corrupt_data(transmit_data) 52 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 53 | 54 | # xxxxxxxxxx OFDM Demodulate data xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 55 | ofdm_received_data = ofdm_obj.demodulate(received_data[:-channel_memory]) 56 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 57 | 58 | # xxxxxxxxxx One-Tap Equalization xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 59 | # First we need to get the frequency response 60 | impulse_response = channel.get_last_impulse_response() 61 | freq_response = impulse_response.get_freq_response(fft_size) 62 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 63 | 64 | # xxxxxxxxxx Equalization xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 65 | equalizer = OfdmOneTapEqualizer(ofdm_obj) 66 | equalized_ofdm_received_data = equalizer.equalize_data( 67 | ofdm_received_data, impulse_response) 68 | equalized_ofdm_received_data = equalized_ofdm_received_data[:num_symbols] 69 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 70 | 71 | # xxxxxxxxxx Plot demodulated symbols and original symbols xxxxxxxxxxxx 72 | plt.scatter(equalized_ofdm_received_data.real, 73 | equalized_ofdm_received_data.imag, 74 | color='r') 75 | plt.scatter(modulated_data.real, modulated_data.imag) 76 | plt.show() 77 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 78 | 79 | # xxxxxxxxxx QPSK Demodulate data xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 80 | demodulated_data = qpsk_obj.demodulate(equalized_ofdm_received_data) 81 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 82 | 83 | # print(sum(input_data == demodulated_data[:10000])) 84 | -------------------------------------------------------------------------------- /pyphysim/reference_signals/zadoffchu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Module containing Zadoff-chu related functions. 4 | """ 5 | 6 | import numpy as np 7 | 8 | __all__ = ['get_shifted_root_seq', 'calcBaseZC', 'get_extended_ZF'] 9 | 10 | 11 | def calcBaseZC(Nzc: int, u: int, q: complex = 0) -> np.ndarray: 12 | """ 13 | Calculate the root sequence of Zadoff-Chu sequences. 14 | 15 | Parameters 16 | ---------- 17 | Nzc : int 18 | The size of the root Zadoff-Chu sequence. 19 | u : int 20 | The root sequence index. 21 | q : complex 22 | Any complex number. Usually this is just zero. 23 | 24 | Returns 25 | ------- 26 | a_u : np.ndarray 27 | The root Zadoff-Chu sequence. 28 | """ 29 | # $X = e^\frac{-j \pi u n (n+1)}{\text{Nzc}}$ 30 | # In fact, 'u' must be lower than the largest prime number below or 31 | # equal to Nzc 32 | assert (u < Nzc) 33 | 34 | n = np.arange(Nzc) 35 | a_u = np.exp((-1j * np.pi * u * n * (n + 1 + 2 * q)) / Nzc) 36 | return a_u 37 | 38 | 39 | def get_shifted_root_seq(root_seq: np.ndarray, n_cs: int, 40 | denominator: int) -> np.ndarray: 41 | """ 42 | Get the shifted root sequence suitable as the SRS sequence or the 43 | DMRS sequence of a user (depend on the `denominator` parameter). 44 | 45 | Parameters 46 | ---------- 47 | root_seq : np.ndarray 48 | The root sequence to be shifted. This is a complex numpy array. 49 | n_cs : int 50 | The desired cyclic shift number. This should be an integer from 51 | 0 to `denominator`-1, where 0 will just return the base 52 | sequence, 1 gives the first shift, and so on. 53 | denominator : int 54 | The denominator in the cyclic shift formula. This should be 8 for 55 | SRS and 12 for DMRS. 56 | 57 | Returns 58 | ------- 59 | np.ndarray 60 | The shifted root sequence (a complex numpy array). 61 | 62 | See Also 63 | -------- 64 | get_srs_seq, get_dmrs_seq 65 | """ 66 | assert (abs(n_cs) >= 0) 67 | assert (abs(n_cs) < denominator) 68 | 69 | all_index_values = np.arange(root_seq.size) 70 | alpha_m = 2 * np.pi * n_cs / denominator 71 | shifted_seq = np.exp(1j * alpha_m * all_index_values) * root_seq 72 | return shifted_seq 73 | 74 | 75 | def get_extended_ZF(root_seq: np.ndarray, size: int) -> np.ndarray: 76 | """ 77 | Cyclic Extend the Zadoff-Chu root sequence to have size equal to 78 | `size`. 79 | 80 | Parameters 81 | ---------- 82 | root_seq : np.ndarray 83 | The root Zadoff-Chu sequence. This is a complex numpy array. 84 | size : int 85 | The size that the sequence should be extended to. 86 | 87 | Returns 88 | ------- 89 | output : np.ndarray 90 | The extended root sequence. 91 | 92 | Examples 93 | -------- 94 | >>> root_seq = np.array([1, 2, 3, 4, 5]) 95 | >>> get_extended_ZF(root_seq, 8) 96 | array([1, 2, 3, 4, 5, 1, 2, 3]) 97 | """ 98 | root_seq_size = root_seq.size 99 | if size - root_seq_size > root_seq_size: 100 | stack_list = [root_seq] 101 | num_full_repeats = (size // root_seq_size) 102 | # Repeat the full sequence by this amount 103 | stack_list *= num_full_repeats 104 | 105 | current_size = root_seq_size * num_full_repeats 106 | 107 | # Append remaining element to achieve the required size 108 | stack_list.append(root_seq[0:size - current_size]) 109 | 110 | output = np.hstack(stack_list) 111 | else: 112 | output = np.hstack([root_seq, root_seq[0:size - root_seq_size]]) 113 | 114 | return output 115 | 116 | 117 | # if __name__ == '__main__': 118 | # np.set_printoptions(precision=4) 119 | # a_u = calcBaseZC(23, 4) 120 | # r1 = get_srs_seq(a_u, 2) 121 | # print(r1) 122 | -------------------------------------------------------------------------------- /apps/ia/simple_ia.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """module docstring""" 3 | 4 | import numpy as np 5 | 6 | import pyphysim.channels.multiuser 7 | from pyphysim.ia import algorithms 8 | from pyphysim.progressbar import ProgressbarText 9 | from pyphysim.util.conversion import dB2Linear, linear2dB 10 | 11 | # calc_capacity = lambda sirn: np.sum(np.log2(1 + sirn)) 12 | 13 | 14 | def calc_capacity(sinr): 15 | """ 16 | Calculate the Sum capacity. 17 | 18 | Parameters 19 | ---------- 20 | sinr : np.ndarray 21 | The sinr (in linear scale) of all streams of all users. This is a 1D 22 | numpy array of 1D arrays. 23 | 24 | Returns 25 | ------- 26 | capacity : np.ndarray 27 | The capacity of each user. 28 | """ 29 | 30 | capacity = np.array( 31 | [np.sum(np.log2(1 + user_sinrs)) for user_sinrs in sinr]) 32 | return capacity 33 | 34 | 35 | def main(): 36 | """Main function. 37 | """ 38 | K = 3 39 | Nr = 4 40 | Nt = 4 41 | Ns = 2 42 | SNR = 30.0 43 | P = 1.0 44 | 45 | # Dependent parameters 46 | noise_var = 1 / dB2Linear(SNR) 47 | 48 | RepMax = 1 49 | mmse_sinrs = np.empty([RepMax, K, Ns], dtype=float) 50 | max_sinr_sinrs = np.empty([RepMax, K, Ns], dtype=float) 51 | mmse_capacity = np.empty(RepMax, dtype=float) 52 | max_sinr_capacity = np.empty(RepMax, dtype=float) 53 | 54 | pbar = ProgressbarText(RepMax, 55 | message="Simulating for SNR: {0}".format(SNR)) 56 | 57 | for rep in range(RepMax): 58 | # Create the channel 59 | multiUserChannel = pyphysim.channels.multiuser.MultiUserChannelMatrix() 60 | multiUserChannel.randomize(Nr, Nt, K) 61 | multiUserChannel.noise_var = noise_var 62 | 63 | # Create the IA solver object 64 | mmse_ia_solver = algorithms.MMSEIASolver(multiUserChannel) 65 | max_sinr_ia_solver = algorithms.MaxSinrIASolver(multiUserChannel) 66 | 67 | mmse_ia_solver.randomizeF(Ns, P) 68 | 69 | mmse_ia_solver.initialize_with = 'fix' 70 | max_sinr_ia_solver.initialize_with = 'fix' 71 | # noinspection PyProtectedMember 72 | max_sinr_ia_solver._F = mmse_ia_solver._F 73 | 74 | #mmse_ia_solver.initialize_with = 'fix' 75 | 76 | # We wouldn't need to explicitly set ia_solver.noise_var 77 | # variable if the multiUserChannel object had the correct value at 78 | # this point. 79 | # mmse_ia_solver.noise_var = noise_var 80 | mmse_ia_solver.max_iterations = 200 81 | mmse_ia_solver.solve(Ns) 82 | 83 | # max_sinr_ia_solver.noise_var = noise_var 84 | max_sinr_ia_solver.max_iterations = 200 85 | 86 | max_sinr_ia_solver.solve(Ns) 87 | 88 | mmse_sinrs[rep] = list(map(linear2dB, mmse_ia_solver.calc_SINR())) 89 | max_sinr_sinrs[rep] = list( 90 | map(linear2dB, max_sinr_ia_solver.calc_SINR())) 91 | 92 | mmse_capacity[rep] = np.sum(calc_capacity(mmse_ia_solver.calc_SINR())) 93 | max_sinr_capacity[rep] = np.sum( 94 | calc_capacity(max_sinr_ia_solver.calc_SINR())) 95 | 96 | # print "MMSE Alt. SINRs:\n{0}".format(np.vstack(mmse_sinrs[rep])) 97 | # print "Max SINR Alg. SINRs:\n{0}".format(np.vstack(max_sinr_sinrs[rep])) 98 | 99 | # print "MMSE Alt. Capacity: {0}".format(np.sum(calc_capacity(mmse_sinrs[rep]))) 100 | # print "Max SINR Alg. Capacity: {0}".format(np.sum(calc_capacity(max_sinr_sinrs[rep]))) 101 | # print 102 | 103 | pbar.progress(rep) 104 | 105 | print("MMSE Average SINRs:\n{0}".format(mmse_sinrs.mean(0))) 106 | print("Max SINR Average SINRs:\n{0}".format(max_sinr_sinrs.mean(0))) 107 | print("MMSE Average Capacity: {0}".format(mmse_capacity.mean())) 108 | print("Max SINR Average Capacity: {0}".format(max_sinr_capacity.mean())) 109 | 110 | print("\nEnd!") 111 | 112 | 113 | if __name__ == '__main__': 114 | main() 115 | -------------------------------------------------------------------------------- /pyphysim/reference_signals/dmrs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Module with Sounding Reference Signal (SRS) related functions""" 3 | 4 | from typing import Optional, cast 5 | 6 | import numpy as np 7 | 8 | from pyphysim.reference_signals.srs import UeSequence 9 | 10 | from .root_sequence import RootSequence 11 | from .zadoffchu import get_shifted_root_seq 12 | 13 | __all__ = ['get_dmrs_seq', 'DmrsUeSequence'] 14 | 15 | 16 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 17 | # xxxxxxxxxxxxxxx Module Functions xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 18 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 19 | def get_dmrs_seq(root_seq: np.ndarray, n_cs: int) -> np.ndarray: 20 | """ 21 | Get the shifted root sequence suitable as the DMRS sequence of a user. 22 | 23 | Parameters 24 | ---------- 25 | root_seq : np.ndarray 26 | The root sequence to shift. 27 | n_cs : int 28 | The desired cyclic shift number. This should be an integer from 29 | 0 to 11, where 0 will just return the base sequence, 1 gives the 30 | first shift, and so on. 31 | 32 | Returns 33 | ------- 34 | np.ndarray 35 | The shifted root sequence. 36 | 37 | See Also 38 | -------- 39 | .zadoffchu.get_shifted_root_seq, .srs.get_srs_seq 40 | """ 41 | return get_shifted_root_seq(root_seq, n_cs, 12) 42 | 43 | 44 | class DmrsUeSequence(UeSequence): 45 | """ 46 | DMRS sequence of a single user. 47 | 48 | Parameters 49 | ---------- 50 | root_seq : RootSequence 51 | The DMRS root sequence of the base station the user is 52 | associated to. This should be an object of the RootSequence 53 | class. 54 | n_cs : int 55 | The shift index of the user. This can be an integer from 0 to 11. 56 | cover_code : np.ndarray, optional 57 | Cover Code used by the UE. As an example, consider the cover code 58 | `np.array([1, -1])`. In that case, if the regular DMRS sequence 59 | (without the cover code) is `seq`, than the actual DMRS sequence 60 | with cover code will be a 2D numpy array equivalent with 61 | `seq_occ[0]==seq` and `seq_occ[1]==-seq`. 62 | normalize : bool 63 | True if the reference signal should be normalized. False otherwise. 64 | """ 65 | def __init__(self, 66 | root_seq: RootSequence, 67 | n_cs: int, 68 | cover_code: Optional[np.ndarray] = None, 69 | normalize: bool = False) -> None: 70 | root_seq_array = root_seq.seq_array() 71 | user_seq_array = get_dmrs_seq(root_seq_array, n_cs) 72 | 73 | # Orthogonal Cover Code. This is stored in an attribute only for 74 | # visualization purposes, since the stored user_seq_array will 75 | # already include its effect. 76 | self._occ = cover_code 77 | 78 | if cover_code is not None: 79 | assert (isinstance(self._occ, np.ndarray)) 80 | self._occ.flags.writeable = False 81 | user_seq_array = user_seq_array * cover_code[:, np.newaxis] 82 | 83 | super().__init__(root_seq, n_cs, user_seq_array, normalize=normalize) 84 | 85 | @property 86 | def cover_code(self) -> np.ndarray: 87 | """Return the cover code.""" 88 | return self._occ 89 | 90 | @property 91 | def size(self) -> int: 92 | """ 93 | Return the size of the reference signal sequence. 94 | 95 | Returns 96 | ------- 97 | size : int 98 | The size of the user's reference signal sequence. 99 | """ 100 | if self._occ is None: 101 | return cast(int, self._user_seq_array.shape[0]) 102 | 103 | return cast(int, self._user_seq_array.shape[1]) 104 | 105 | def __repr__(self) -> str: 106 | """ 107 | Get the representation of the object. 108 | 109 | Returns 110 | ------- 111 | str 112 | The representation of the object. 113 | """ 114 | return "<{0}(root_index={1}, n_cs={2}, cover_code={3})>".format( 115 | self.__class__.__name__, self._root_index, self._n_cs, self._occ) 116 | -------------------------------------------------------------------------------- /apps/configobj_usage_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # See the link below for an "argparse + configobj" option 4 | # http://mail.scipy.org/pipermail/numpy-discussion/2011-November/059332.html 5 | 6 | from configobj import ConfigObj, flatten_errors 7 | from validate import Validator 8 | 9 | from pyphysim.simulations.configobjvalidation import real_numpy_array_check 10 | 11 | if __name__ == '__main__': 12 | config_file_name = 'psk_simulation_config.txt' 13 | 14 | # Spec could be also the name of a file containing the string below 15 | spec = """[Scenario] 16 | SNR=real_numpy_array(default=15) 17 | modulator=option('PSK', 'QAM', 'BPSK', default="PSK") 18 | M=integer(min=4, max=512, default=4) 19 | NSymbs=integer(min=10, max=1000000, default=200) 20 | K=integer(min=2,default=3) 21 | Nr=integer(min=2,default=2) 22 | Nt=integer(min=2,default=2) 23 | Ns=integer(min=1,default=1) 24 | [IA Algorithm] 25 | max_iterations=integer(min=1, default=60) 26 | [General] 27 | rep_max=integer(min=1, default=2000) 28 | max_bit_errors=integer(min=1, default=3000) 29 | unpacked_parameters=string_list(default=list('SNR')) 30 | """.split("\n") 31 | 32 | conf_file_parser = ConfigObj(config_file_name, 33 | list_values=True, 34 | configspec=spec) 35 | 36 | #conf_file_parser.write() 37 | 38 | # Dictionary with custom validation functions 39 | fdict = {'real_numpy_array': real_numpy_array_check} 40 | validator = Validator(fdict) 41 | 42 | # The 'copy' argument indicates that if we save the ConfigObj object to 43 | # a file after validating, the default values will also be written to 44 | # the file. 45 | result = conf_file_parser.validate(validator, 46 | preserve_errors=True, 47 | copy=True) 48 | 49 | # Note that if there was no parsing errors, then "result" will be True. 50 | # It there was an error, then result will be a dictionary with each 51 | # parameter as a key. The value of each key will be either 'True' if 52 | # that parameter was parsed without error or a "validate.something" 53 | # object (since we set preserve_errors to True) describing the error. 54 | 55 | # if result != True: 56 | # print 'Config file validation failed!' 57 | # sys.exit(1) 58 | 59 | # xxxxxxxxxx Test if there was some error in parsing the file xxxxxxxxx 60 | # The flatten_errors function will return only the parameters whose 61 | # parsing failed. 62 | errors_list = flatten_errors(conf_file_parser, result) 63 | 64 | if len(errors_list) != 0: 65 | first_error = errors_list[0] 66 | # The exception will only describe the error for the first 67 | # incorrect parameter. 68 | if first_error[2] is False: 69 | raise Exception( 70 | "Parameter '{0}' in section '{1}' must be provided.".format( 71 | first_error[1], first_error[0][0])) 72 | else: 73 | raise Exception( 74 | "Parameter '{0}' in section '{1}' is invalid. {2}".format( 75 | first_error[1], first_error[0][0], 76 | first_error[2].message.capitalize())) 77 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 78 | 79 | print('Filename: {0}'.format(config_file_name)) 80 | print("Valid_config_file: {0}".format(result)) 81 | 82 | # Simulation Section 83 | SimulationSection = conf_file_parser['Scenario'] 84 | SNR = SimulationSection['SNR'] 85 | M = SimulationSection['M'] 86 | NSymbs = SimulationSection['NSymbs'] 87 | modulator_name = SimulationSection['modulator'] 88 | 89 | # IA Section 90 | IASection = conf_file_parser['IA Algorithm'] 91 | max_iterations = IASection['max_iterations'] 92 | 93 | # General Section 94 | GeneralSection = conf_file_parser['General'] 95 | rep_max = GeneralSection['rep_max'] 96 | max_bit_errors = GeneralSection['max_bit_errors'] 97 | unpacked_parameters = GeneralSection['unpacked_parameters'] 98 | 99 | print("Parameters: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 100 | print("SNR: {0}".format(SNR)) 101 | print("M: {0}".format(M)) 102 | print("Modulator: {0}".format(modulator_name)) 103 | print("NSymbs: {0}".format(NSymbs)) 104 | print("rep_max: {0}".format(rep_max)) 105 | print("max_bit_errors: {0}".format(max_bit_errors)) 106 | print("unpacked_parameters: {0}".format(unpacked_parameters)) 107 | 108 | conf_file_parser.write() 109 | -------------------------------------------------------------------------------- /pyphysim/channels/antennagain.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from typing import Optional, TypeVar 4 | 5 | import numpy as np 6 | 7 | from ..util.conversion import dB2Linear 8 | 9 | # See http://www.qtc.jp/3GPP/Specs/25996-a00.pdf 10 | 11 | NumberOrArray = TypeVar("NumberOrArray", np.ndarray, float) 12 | 13 | 14 | class AntGainBase: # pragma: no cover 15 | """Base class for antenna models. 16 | """ 17 | def get_antenna_gain(self, angle: NumberOrArray) -> NumberOrArray: 18 | """ 19 | Get the antenna gain for the given angle. 20 | 21 | Parameters 22 | ---------- 23 | angle : float | np.ndarray 24 | Angle between the direction of interest and the boresight of 25 | the antenna. This can also be a numpy array with angles. 26 | 27 | Returns 28 | ------- 29 | float | np.ndarray 30 | The gain (in linear scale) for the provided angle. 31 | """ 32 | raise NotImplementedError("Implement in a subclass") 33 | 34 | 35 | class AntGainOmni(AntGainBase): 36 | """ 37 | Class for Omnidirectional antenna gain model. 38 | 39 | Parameters 40 | ---------- 41 | ant_gain : float, optional 42 | The antenna gain (in dBi). If not provided then 0dBi will be assumed. 43 | """ 44 | def __init__(self, ant_gain: Optional[float] = None): 45 | super().__init__() 46 | self.ant_gain: float 47 | if ant_gain is None: 48 | self.ant_gain = 1.0 49 | else: 50 | self.ant_gain = dB2Linear(ant_gain) 51 | 52 | def get_antenna_gain(self, angle: NumberOrArray) -> NumberOrArray: 53 | """ 54 | Get the antenna gain for the given angle. 55 | 56 | Parameters 57 | ---------- 58 | angle : float | np.ndarray 59 | Angle between the direction of interest and the boresight of 60 | the antenna. This can also be a numpy array with angles. 61 | 62 | Returns 63 | ------- 64 | float | np.ndarray 65 | The gain (in linear scale) for the provided angle. 66 | """ 67 | if isinstance(angle, np.ndarray): 68 | return self.ant_gain * np.ones(angle.shape) # type: ignore 69 | 70 | return self.ant_gain 71 | 72 | 73 | class AntGainBS3GPP25996(AntGainBase): 74 | """ 75 | Class for antenna model defined by 3GPP in the 25996 norm for sectorized 76 | Base Stations. 77 | 78 | The antenna gain (in dBi) will depend on the number of sectors of the 79 | Base Station. 80 | 81 | NOTE: The antenna pattern here is targeted for diversity-oriented 82 | implementations (i.e. large inter-element spacings). For beamforming 83 | applications that require small spacings, alternative antenna designs 84 | may have to be considered leading to a different antenna pattern. 85 | 86 | Parameters 87 | ---------- 88 | number_of_sectors : int 89 | The number of sectors of the base station. It can be either 3 or 6. 90 | """ 91 | def __init__(self, number_of_sectors: int = 3): 92 | super().__init__() 93 | 94 | self.ant_gain: float 95 | 96 | if number_of_sectors == 3: 97 | self.theta_3db = 70. # Defined in the norm 98 | self.Am = 20. # Maximum attenuation in dB 99 | self.ant_gain = dB2Linear(14.) # Antenna gain (in dBi) 100 | elif number_of_sectors == 6: 101 | self.theta_3db = 35. # Defined in the norm 102 | self.Am = 23. # Maximum attenuation in dB 103 | self.ant_gain = dB2Linear(17.) # Antenna gain (in dBi) 104 | else: 105 | raise ValueError( 106 | "Invalid number of sectors: {0}".format(number_of_sectors)) 107 | 108 | # noinspection PyPep8 109 | def get_antenna_gain(self, angle: NumberOrArray) -> NumberOrArray: 110 | """ 111 | Get the antenna gain for the given angle. 112 | 113 | Parameters 114 | ---------- 115 | angle : float | np.ndarray 116 | Angle between the direction of interest and the boresight of 117 | the antenna. This can also be a numpy array with angles. 118 | 119 | Returns 120 | ------- 121 | float | np.ndarray 122 | The gain (in linear scale) for the provided angle. 123 | """ 124 | # For the antenna gain model defined in 3GPP 25996 the gain (in dB) 125 | # is given by 126 | #\(-\min\left[ 12\left( \frac{\theta}{\theta_{3dB}} \right)^2, A_m \right]\), where \(-180 \geq \theta \geq 180\) 127 | ant_pattern_gain = dB2Linear( 128 | -np.minimum(12 * (angle / self.theta_3db)**2, self.Am)) 129 | return self.ant_gain * ant_pattern_gain # type: ignore 130 | -------------------------------------------------------------------------------- /docs/hand_written/writing_documentation.rst: -------------------------------------------------------------------------------- 1 | Writing Documentation for PyPhysim 2 | ================================== 3 | 4 | The handwritten rst files should be listed in the toctree in the 5 | :ref:`list_of_hand_written_doc` section of the ``index.rst`` file, while the 6 | documentation for each package is automatically generated using 7 | ``sphinx-apidoc`` and should be listed in the toctree in the 8 | ``description.rst`` file. 9 | 10 | In order to generate the rst files for each package with ``sphinx-apidoc`` call 11 | the command ``sphinx-apidoc -o docs pyphysim`` from outside the docs folder. 12 | 13 | Note that ``sphinx-apidoc`` will generate a corresponding `.rst` file for each 14 | package, that uses the autodoc feature of sphinx to include the docstrings of 15 | the python files in the actual documentation. For instance, the ``.rst`` file of 16 | the ``comm`` module (whose name is ``pyphysim.comm.rst``) is shown below. 17 | 18 | .. literalinclude:: ../pyphysim.comm.rst 19 | :language: rst 20 | 21 | Notice the use of "``.. automodule::``" to get the documentation of the comm 22 | package modules from the source code. 23 | 24 | Therefore, you only need to run ``sphinx-apidoc`` when new modules are created 25 | in PyPhysim. Everything else will be automatically done by sphinx with the help 26 | of the autodoc extension. 27 | 28 | 29 | Writing math in the documentation 30 | --------------------------------- 31 | 32 | The sphinx documentation generation system is configured to allow the 33 | inclusion of math snippets with LaTeX syntax in the documentation. Simply 34 | put a directive like ``:math:`x+y = \frac{1}{2}``` 35 | in the documentation and it will be rendered as :math:`x+y = \frac{1}{2}` 36 | in the final HTML documentation. 37 | 38 | In order for this to work you need to download MathJax from 39 | `http://www.mathjax.org/download/` 40 | and put it in the `docs/Mathjax/` folder. 41 | 42 | Also, in order to make writing math easier, a few extra LaTeX macros should 43 | be defined. In special, we use bold to indicate matrices. Usually one would 44 | need to write ``:math:`\mathbf{H}_{jk}``` to write :math:`\mathbf{H}_{jk}`, 45 | but the macro "``\mtH``" is used instead for a bold "H" such that we can 46 | write the same think with ``:math:`\mtH_{jk}``` and this should be 47 | configured in Mathjax. Likewise, equivalent macros must also be defined for 48 | the other letters as well as ``\vtH`` and similar to represent vectors 49 | (lower case bold letters). 50 | 51 | The after downloading Mathjax to the `docs/Mathjax/` edit the 52 | `docs/Mathjax/config/local/local.js` file and add the following macros 53 | there. 54 | 55 | .. code-block:: javascript 56 | 57 | // Matrices 58 | TEX.Macro("mtA", "\\mathbf{A}"); 59 | TEX.Macro("mtB", "\\mathbf{B}"); 60 | TEX.Macro("mtC", "\\mathbf{C}"); 61 | TEX.Macro("mtD", "\\mathbf{D}"); 62 | TEX.Macro("mtE", "\\mathbf{E}"); 63 | TEX.Macro("mtF", "\\mathbf{F}"); 64 | TEX.Macro("mtG", "\\mathbf{G}"); 65 | TEX.Macro("mtH", "\\mathbf{H}"); 66 | TEX.Macro("mtI", "\\mathbf{I}"); 67 | TEX.Macro("mtJ", "\\mathbf{J}"); 68 | TEX.Macro("mtK", "\\mathbf{K}"); 69 | TEX.Macro("mtL", "\\mathbf{L}"); 70 | TEX.Macro("mtM", "\\mathbf{M}"); 71 | TEX.Macro("mtN", "\\mathbf{N}"); 72 | TEX.Macro("mtO", "\\mathbf{P}"); 73 | TEX.Macro("mtP", "\\mathbf{P}"); 74 | TEX.Macro("mtQ", "\\mathbf{Q}"); 75 | TEX.Macro("mtR", "\\mathbf{R}"); 76 | TEX.Macro("mtS", "\\mathbf{S}"); 77 | TEX.Macro("mtT", "\\mathbf{T}"); 78 | TEX.Macro("mtU", "\\mathbf{U}"); 79 | TEX.Macro("mtV", "\\mathbf{V}"); 80 | TEX.Macro("mtW", "\\mathbf{W}"); 81 | TEX.Macro("mtX", "\\mathbf{X}"); 82 | TEX.Macro("mtY", "\\mathbf{Y}"); 83 | TEX.Macro("mtZ", "\\mathbf{Z}"); 84 | // Vectors 85 | TEX.Macro("vtB", "\\mathbf{b}"); 86 | TEX.Macro("vtC", "\\mathbf{c}"); 87 | TEX.Macro("vtD", "\\mathbf{d}"); 88 | TEX.Macro("vtE", "\\mathbf{e}"); 89 | TEX.Macro("vtF", "\\mathbf{f}"); 90 | TEX.Macro("vtG", "\\mathbf{g}"); 91 | TEX.Macro("vtH", "\\mathbf{h}"); 92 | TEX.Macro("vtI", "\\mathbf{i}"); 93 | TEX.Macro("vtJ", "\\mathbf{j}"); 94 | TEX.Macro("vtK", "\\mathbf{k}"); 95 | TEX.Macro("vtL", "\\mathbf{l}"); 96 | TEX.Macro("vtM", "\\mathbf{m}"); 97 | TEX.Macro("vtN", "\\mathbf{n}"); 98 | TEX.Macro("vtO", "\\mathbf{p}"); 99 | TEX.Macro("vtP", "\\mathbf{p}"); 100 | TEX.Macro("vtQ", "\\mathbf{q}"); 101 | TEX.Macro("vtR", "\\mathbf{r}"); 102 | TEX.Macro("vtS", "\\mathbf{s}"); 103 | TEX.Macro("vtT", "\\mathbf{t}"); 104 | TEX.Macro("vtU", "\\mathbf{u}"); 105 | TEX.Macro("vtV", "\\mathbf{v}"); 106 | TEX.Macro("vtW", "\\mathbf{w}"); 107 | TEX.Macro("vtX", "\\mathbf{x}"); 108 | TEX.Macro("vtY", "\\mathbf{y}"); 109 | TEX.Macro("vtZ", "\\mathbf{z}"); 110 | -------------------------------------------------------------------------------- /ipython_notebooks/METIS Simple Scenario 2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Simulation of the METIS scenario with rooms in one floor" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "This notebook simulates a scenario with different access point densities. The access points are placed in every other room and each room has walls that causes some signal power loss." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "Note: In order for `%matplotlib widget` to work in jupyterlab you need to install the `ipympl` package as well as two jupyterlab extensions.\n", 22 | "The package can be installed with \n", 23 | " \n", 24 | "```bash\n", 25 | "conda install -c conda-forge ipympl\n", 26 | "```\n", 27 | "\n", 28 | "The extensions can be installed with\n", 29 | " \n", 30 | "```bash\n", 31 | "jupyter labextension install @jupyter-widgets/jupyterlab-manager\n", 32 | "jupyter labextension install jupyter-matplotlib\n", 33 | "```" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 7, 39 | "metadata": { 40 | "collapsed": false, 41 | "jupyter": { 42 | "outputs_hidden": false 43 | } 44 | }, 45 | "outputs": [], 46 | "source": [ 47 | "%matplotlib widget\n", 48 | "\n", 49 | "# xxxxxxxxxx Add the parent folder to the python path. xxxxxxxxxxxxxxxxxxxx\n", 50 | "import sys\n", 51 | "import os\n", 52 | "sys.path.append('../')\n", 53 | "# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", 54 | "from matplotlib import pyplot as plt\n", 55 | "import numpy as np\n", 56 | "\n", 57 | "from ipywidgets.widgets import interact, interactive, fixed\n", 58 | "from IPython.html import widgets\n", 59 | "from IPython.display import display_latex\n", 60 | "\n", 61 | "# Import the simulation runner\n", 62 | "from apps.metis_scenarios.simulate_metis_scenario2 import *" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "## Simulation Configuration" 70 | ] 71 | }, 72 | { 73 | "cell_type": "markdown", 74 | "metadata": {}, 75 | "source": [ 76 | "Now we set the simulation configuration." 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 5, 82 | "metadata": { 83 | "collapsed": false, 84 | "jupyter": { 85 | "outputs_hidden": false 86 | } 87 | }, 88 | "outputs": [], 89 | "source": [ 90 | "scenario_params = {\n", 91 | " 'side_length': 10, # 10 meters side length\n", 92 | " 'single_wall_loss_dB': 5,\n", 93 | " 'num_rooms_per_side': 12,\n", 94 | " 'ap_decimation': 2}\n", 95 | "\n", 96 | "power_params = {\n", 97 | " 'Pt_dBm': 20, # 20 dBm transmit power\n", 98 | " 'noise_power_dBm': -300 # Very low noise power\n", 99 | "}" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "## Perform the Simulation" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "calculate the SINRs and capacity" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 8, 119 | "metadata": { 120 | "collapsed": false, 121 | "jupyter": { 122 | "outputs_hidden": false 123 | } 124 | }, 125 | "outputs": [ 126 | { 127 | "name": "stdout", 128 | "output_type": "stream", 129 | "text": [ 130 | "\n", 131 | "Min/Mean/Max SINR value (METIS PS7):\n", 132 | " -2.8825177951152354\n", 133 | " 24.786521205502535\n", 134 | " 54.89042989166305\n", 135 | "\n", 136 | "Min/Mean/Max Capacity value (METIS PS7):\n", 137 | " 0.24557410367953877\n", 138 | " 4.053835497453433\n", 139 | " 14.500528637845253\n" 140 | ] 141 | }, 142 | { 143 | "data": { 144 | "application/vnd.jupyter.widget-view+json": { 145 | "model_id": "3c0218b8700c4aa2a1ff032259c294cf", 146 | "version_major": 2, 147 | "version_minor": 0 148 | }, 149 | "text/plain": [ 150 | "Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …" 151 | ] 152 | }, 153 | "metadata": {}, 154 | "output_type": "display_data" 155 | } 156 | ], 157 | "source": [ 158 | "out = perform_simulation(scenario_params, power_params)\n", 159 | "sinr_array_pl_metis_ps7_dB, capacity_metis_ps7 = out" 160 | ] 161 | } 162 | ], 163 | "metadata": { 164 | "kernelspec": { 165 | "display_name": "Python 3", 166 | "language": "python", 167 | "name": "python3" 168 | }, 169 | "language_info": { 170 | "codemirror_mode": { 171 | "name": "ipython", 172 | "version": 3 173 | }, 174 | "file_extension": ".py", 175 | "mimetype": "text/x-python", 176 | "name": "python", 177 | "nbconvert_exporter": "python", 178 | "pygments_lexer": "ipython3", 179 | "version": "3.7.6" 180 | } 181 | }, 182 | "nbformat": 4, 183 | "nbformat_minor": 4 184 | } 185 | -------------------------------------------------------------------------------- /tests/pointprocess_package_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pylint: disable=E1101,E0611 4 | """ 5 | Tests for the modules in the pointprocess package. 6 | """ 7 | 8 | import unittest 9 | 10 | import numpy as np 11 | from matplotlib import pyplot as plt 12 | 13 | from pyphysim import pointprocess as pp 14 | 15 | 16 | class PointProcessTestCase(unittest.TestCase): 17 | def setUp(self) -> None: 18 | """Called before each test.""" 19 | pass 20 | 21 | def test_generate_random_points_in_circle(self) -> None: 22 | # This test will test the `generate_random_points_in_circle` function 23 | # when only a maximum radius is passed 24 | num_points = 500000 25 | radius = 15.0 26 | 27 | points = pp.generate_random_points_in_circle(num_points, radius) 28 | self.assertEqual(points.size, num_points) 29 | self.assertLessEqual(np.max(np.abs(points)), radius) 30 | 31 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 32 | # Compute how many points are inside the circle with radius 1 centered 33 | # at the origin 34 | small_radius1 = 1.0 35 | num_smaller_area_points1 = np.sum(np.abs(points) < small_radius1) 36 | expected_num_smaller_area_points1 = int( 37 | num_points * (small_radius1 * small_radius1) / (radius * radius)) 38 | ratio1 = num_smaller_area_points1 / expected_num_smaller_area_points1 39 | self.assertAlmostEqual(ratio1, 1.0, 1) 40 | 41 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 42 | # Compute how many points are inside the circle with radius 2.5 centered 43 | # at the origin 44 | small_radius2 = 2.5 45 | num_smaller_area_points2 = np.sum(np.abs(points) < small_radius2) 46 | expected_num_smaller_area_points2 = int( 47 | num_points * (small_radius2 * small_radius2) / (radius * radius)) 48 | ratio2 = num_smaller_area_points2 / expected_num_smaller_area_points2 49 | self.assertAlmostEqual(ratio2, 1.0, 1) 50 | 51 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 52 | # Compute how many points are inside the circle with radius 2.5 centered 53 | # at the origin, but now the big circle is centered at 2.5+0i 54 | small_radius3 = 2.5 55 | num_smaller_area_points3 = np.sum(np.abs(points + 2.5) < small_radius3) 56 | expected_num_smaller_area_points3 = int( 57 | num_points * (small_radius3 * small_radius3) / (radius * radius)) 58 | ratio3 = num_smaller_area_points3 / expected_num_smaller_area_points3 59 | self.assertAlmostEqual(ratio3, 1.0, 1) 60 | 61 | def test_generate_random_points_in_circle2(self) -> None: 62 | # This test will test the `generate_random_points_in_circle` function 63 | # when both a maximum and a minimum radius are passed 64 | num_points = 500000 65 | radius = 5.0 66 | min_radius = 1.0 67 | 68 | points = pp.generate_random_points_in_circle(num_points, radius, 69 | min_radius) 70 | self.assertEqual(points.size, num_points) 71 | self.assertLessEqual(np.max(np.abs(points)), radius) 72 | self.assertGreaterEqual(np.min(np.abs(points)), min_radius) 73 | 74 | small_radius1 = min_radius # With this there should be no points inside this circle 75 | num_smaller_area_points1 = np.sum(np.abs(points) < small_radius1) 76 | self.assertEqual(num_smaller_area_points1, 0) 77 | 78 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 79 | # Compute how many points are inside the circle with radius 2.5 centered 80 | # at the origin 81 | small_radius2 = 2.5 82 | num_smaller_area_points2 = np.sum(np.abs(points) < small_radius2) 83 | expected_num_smaller_area_points2 = int( 84 | num_points * ((small_radius2 - min_radius)**2) / 85 | ((radius - min_radius)**2)) 86 | ratio2 = num_smaller_area_points2 / expected_num_smaller_area_points2 87 | self.assertAlmostEqual(ratio2, 1.0, 1) 88 | 89 | def test_generate_random_points_in_rectangle(self) -> None: 90 | num_points = 50000 91 | width = 3.0 92 | height = 2.0 93 | 94 | points = pp.generate_random_points_in_rectangle( 95 | num_points, width, height) 96 | self.assertEqual(points.size, num_points) 97 | self.assertLessEqual(np.max(points.real), width / 2.0) 98 | self.assertLessEqual(np.max(points.imag), height / 2.0) 99 | self.assertGreaterEqual(np.min(points.real), -width / 2.0) 100 | self.assertGreaterEqual(np.min(points.imag), -height / 2.0) 101 | 102 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 103 | # Compute how many points are inside the circle with radius 1 centered 104 | # at the origin 105 | small_radius1 = 1.0 106 | num_smaller_area_points1 = np.sum(np.abs(points) < small_radius1) 107 | # The expected number of points is proportional to the ratio of the areas 108 | expected_num_smaller_area_points1 = int( 109 | num_points * np.pi * (small_radius1 * small_radius1) / 110 | (width * height)) 111 | ratio1 = num_smaller_area_points1 / expected_num_smaller_area_points1 112 | self.assertAlmostEqual(ratio1, 1.0, 1) 113 | 114 | 115 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 116 | if __name__ == "__main__": 117 | unittest.main() 118 | -------------------------------------------------------------------------------- /apps/ia/ia_SINRs_and_capacity.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """module docstring""" 3 | 4 | try: 5 | import cPickle as pickle 6 | except ImportError as e: # pragma: no cover 7 | import pickle 8 | 9 | import numpy as np 10 | from matplotlib import pyplot as plt 11 | from pandas import DataFrame 12 | 13 | import pyphysim.channels.multiuser 14 | from pyphysim.ia.algorithms import (AlternatingMinIASolver, MaxSinrIASolver, 15 | MMSEIASolver) 16 | from pyphysim.progressbar import ProgressbarText 17 | from pyphysim.util.conversion import dB2Linear 18 | 19 | 20 | def calc_SINRs_and_capacity(solver): 21 | """ 22 | Calculates the SINRs. 23 | 24 | Parameters 25 | ---------- 26 | solver : T < IASolverBaseClass 27 | The IA solver. 28 | """ 29 | SINRs = solver.calc_SINR_in_dB() 30 | sinrs = solver.calc_SINR() 31 | 32 | calc_capacity = lambda sirn: np.sum(np.log2(1 + sirn)) 33 | 34 | capacity = np.array(list(map(calc_capacity, sinrs))) 35 | sum_capacity = np.sum(capacity) 36 | 37 | return SINRs, capacity, sum_capacity 38 | 39 | 40 | if __name__ == '__main__': 41 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 42 | K = 3 43 | Nr = 4 44 | Nt = 4 45 | Ns = 2 46 | SNR = 5 47 | max_iterations = 2000 48 | P = 1.0 49 | initialize_with = 'alt_min' 50 | # --------------------------------------------------------------------- 51 | noise_var = 1. / dB2Linear(SNR) 52 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 53 | 54 | multiuserchannel = pyphysim.channels.multiuser.MultiUserChannelMatrix() 55 | multiuserchannel.randomize(Nr, Nt, K) 56 | multiuserchannel.noise_var = noise_var 57 | 58 | alt_min_solver = AlternatingMinIASolver(multiuserchannel) 59 | alt_min_solver.max_iterations = max_iterations 60 | # alt_min_solver.noise_var = noise_var 61 | 62 | max_sinr_solver = MaxSinrIASolver(multiuserchannel) 63 | max_sinr_solver.max_iterations = max_iterations 64 | # max_sinr_solver.noise_var = noise_var 65 | max_sinr_solver.initialize_with = 'alt_min' 66 | 67 | mmse_solver = MMSEIASolver(multiuserchannel) 68 | mmse_solver.max_iterations = max_iterations 69 | # mmse_solver.noise_var = noise_var 70 | mmse_solver.initialize_with = 'alt_min' 71 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 72 | 73 | calc_capacity = lambda sirn: np.sum(np.log2(1 + sirn)) 74 | 75 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 76 | 77 | rep_max = 100 78 | 79 | alt_min_SINRs = np.empty(rep_max, dtype=np.ndarray) 80 | max_sinr_SINRs = np.empty(rep_max, dtype=np.ndarray) 81 | mmse_SINRs = np.empty(rep_max, dtype=np.ndarray) 82 | alt_min_capacity = np.empty(rep_max, dtype=np.ndarray) 83 | max_sinr_capacity = np.empty(rep_max, dtype=np.ndarray) 84 | mmse_capacity = np.empty(rep_max, dtype=np.ndarray) 85 | alt_min_sum_capacity = np.empty(rep_max, dtype=float) 86 | max_sinr_sum_capacity = np.empty(rep_max, dtype=float) 87 | mmse_sum_capacity = np.empty(rep_max, dtype=float) 88 | 89 | alt_min_runned_iterations = np.empty(rep_max, dtype=int) 90 | max_sinr_runned_iterations = np.empty(rep_max, dtype=int) 91 | mmse_runned_iterations = np.empty(rep_max, dtype=int) 92 | 93 | pbar = ProgressbarText(rep_max, 94 | message="Simulating for SNR: {0}".format(SNR)) 95 | for rep in range(rep_max): 96 | multiuserchannel.randomize(Nr, Nt, K) 97 | 98 | alt_min_solver.clear() 99 | max_sinr_solver.clear() 100 | mmse_solver.clear() 101 | 102 | alt_min_runned_iterations[rep] = alt_min_solver.solve(Ns, P) 103 | max_sinr_runned_iterations[rep] = max_sinr_solver.solve(Ns, P) 104 | mmse_runned_iterations[rep] = mmse_solver.solve(Ns, P) 105 | 106 | mmse_solver.calc_sum_capacity() 107 | 108 | # print "Alt Min" 109 | (alt_min_SINRs[rep], alt_min_capacity[rep], 110 | alt_min_sum_capacity[rep]) = calc_SINRs_and_capacity(alt_min_solver) 111 | # print "SINRs:\n{0}".format(alt_min_SINRs[rep]) 112 | # print "Capacity:\n{0}".format(alt_min_capacity[rep]) 113 | # print "Sum_Capacity: {0}".format(alt_min_sum_capacity[rep]) 114 | 115 | # print "\nMax SINR" 116 | (max_sinr_SINRs[rep], max_sinr_capacity[rep], 117 | max_sinr_sum_capacity[rep]) = calc_SINRs_and_capacity(max_sinr_solver) 118 | # print "SINRs:\n{0}".format(max_sinr_SINRs[rep]) 119 | # print "Capacity:\n{0}".format(max_sinr_capacity[rep]) 120 | # print "Sum_Capacity: {0}".format(max_sinr_sum_capacity[rep]) 121 | 122 | # print "\nMMSE" 123 | (mmse_SINRs[rep], mmse_capacity[rep], 124 | mmse_sum_capacity[rep]) = calc_SINRs_and_capacity(mmse_solver) 125 | # print "SINRs:\n{0}".format(mmse_SINRs[rep]) 126 | # print "Capacity:\n{0}".format(mmse_capacity[rep]) 127 | # print "Sum_Capacity: {0}".format(mmse_sum_capacity[rep]) 128 | 129 | pbar.progress(rep) 130 | 131 | df = DataFrame({ 132 | 'Min. Leakage': alt_min_sum_capacity, 133 | 'Max SINR': max_sinr_sum_capacity, 134 | 'MMSE': mmse_sum_capacity 135 | }) 136 | df.to_csv( 137 | 'sum_capacity_{Nr}x{Nt}_{Ns}_SNR_{SNR}_{initialize_with}_init.txt'. 138 | format(Nr=Nr, Ns=Ns, Nt=Nt, SNR=SNR, initialize_with=initialize_with), 139 | index_label="Index") 140 | 141 | plt.plot([sum(alt_min_capacity[a]) for a in range(50)]) 142 | plt.plot([sum(max_sinr_capacity[a]) for a in range(50)]) 143 | plt.plot([sum(mmse_capacity[a]) for a in range(50)]) 144 | plt.legend(["Min Leakage", "Max SINR", "MMSE"]) 145 | -------------------------------------------------------------------------------- /tests/subspace_package_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pylint: disable=E1101 4 | """ 5 | Tests for the modules in the subspace package. 6 | 7 | Each module has doctests for its functions and all we need to do is run all 8 | of them them. 9 | """ 10 | 11 | import doctest 12 | import unittest 13 | 14 | import numpy as np 15 | 16 | from pyphysim.subspace import metrics, projections 17 | from pyphysim.util.misc import randn_c 18 | 19 | 20 | # noinspection PyMethodMayBeStatic 21 | class SubspaceDoctestsTestCase(unittest.TestCase): 22 | """ 23 | Test case that run all the doctests in the modules of the subspace package. 24 | """ 25 | def test_metrics(self) -> None: 26 | """Run metrics doctests""" 27 | doctest.testmod(metrics) 28 | 29 | def test_projections(self) -> None: 30 | """Run projections doctests""" 31 | doctest.testmod(projections) 32 | 33 | 34 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 35 | # xxxxxxxxxxxxxxx Projections Module xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 36 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 37 | class ProjectionsTestCase(unittest.TestCase): 38 | """Unittests for the Projection class in the projections module.""" 39 | def setUp(self) -> None: 40 | """Called before each test.""" 41 | self.A = np.array([[1 + 1j, 2 - 2j], [3 - 2j, 0], [-1 - 1j, 2 - 3j]]) 42 | self.P_obj = projections.Projection(self.A) 43 | self.v = np.array([1, 2, 3]) 44 | 45 | def test_calcProjectionMatrix(self) -> None: 46 | Q = projections.calcProjectionMatrix(self.A) 47 | 48 | expected_Q = np.array([ 49 | [0.5239436 - 0.j, 0.0366197 + 0.3295774j, 0.3661971 + 0.0732394j], 50 | [0.0366197 - 0.3295774j, 0.7690140 + 0.j, -0.0788732 + 0.2478873j], 51 | [0.3661971 - 0.0732394j, -0.0788732 - 0.2478873j, 0.7070422 - 0j] 52 | ]) 53 | np.testing.assert_array_almost_equal(Q, expected_Q) 54 | 55 | def test_calcOrthogonalProjectionMatrix(self) -> None: 56 | Q = projections.calcProjectionMatrix(self.A) 57 | oQ = projections.calcOrthogonalProjectionMatrix(self.A) 58 | expected_oQ = np.eye(3) - Q 59 | np.testing.assert_array_almost_equal(oQ, expected_oQ) 60 | 61 | def test_project(self) -> None: 62 | v_proj = self.P_obj.project(self.v) 63 | expected_v_proj = np.array([ 64 | 1.69577465 + 0.87887324j, 1.33802817 + 0.41408451j, 65 | 2.32957746 - 0.56901408j 66 | ]) 67 | np.testing.assert_array_almost_equal(v_proj, expected_v_proj) 68 | 69 | def test_oProject(self) -> None: 70 | v_oproj = self.P_obj.oProject(self.v) 71 | expected_v_oproj = np.array([ 72 | -0.69577465 - 0.87887324j, 0.66197183 - 0.41408451j, 73 | 0.67042254 + 0.56901408j 74 | ]) 75 | np.testing.assert_array_almost_equal(v_oproj, expected_v_oproj) 76 | 77 | def test_reflect(self) -> None: 78 | v_reflec = self.P_obj.reflect(self.v) 79 | expected_v_reflec = np.array([ 80 | -2.39154930 - 1.75774648j, -0.67605634 - 0.82816901j, 81 | -1.65915493 + 1.13802817j 82 | ]) 83 | np.testing.assert_array_almost_equal(v_reflec, expected_v_reflec) 84 | 85 | 86 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 87 | # xxxxxxxxxxxxxxx Metrics Module xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 88 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 89 | class MetricsTestCase(unittest.TestCase): 90 | def setUp(self) -> None: 91 | """Called before each test.""" 92 | self.A = np.arange(1, 9.) 93 | self.A.shape = (4, 2) 94 | self.B = np.array([[1.2, 2.1], [2.9, 4.3], [5.2, 6.1], [6.8, 8.1]]) 95 | 96 | def test_calc_principal_angles(self) -> None: 97 | np.testing.assert_array_almost_equal( 98 | metrics.calc_principal_angles(self.A, self.B), 99 | np.array([0.00796407, 0.49360193])) 100 | 101 | # Example from the Matrix computations book 102 | A = np.array([1, 2, 3, 4, 5, 6]) 103 | A.shape = (3, 2) 104 | B = np.array([1, 5, 3, 7, 5, -1]) 105 | B.shape = (3, 2) 106 | np.testing.assert_array_almost_equal( 107 | metrics.calc_principal_angles(A, B), np.array([0., 0.54312217])) 108 | 109 | def test_calculating_the_chordal_distance(self) -> None: 110 | expected_chord_dist = 0.473867859572 111 | 112 | # Test calcChordalDistance 113 | self.assertAlmostEqual(metrics.calc_chordal_distance(self.A, self.B), 114 | expected_chord_dist) 115 | 116 | # Test calcChordalDistance2 117 | self.assertAlmostEqual(metrics.calc_chordal_distance_2(self.A, self.B), 118 | expected_chord_dist) 119 | 120 | # Test 121 | principal_angles = metrics.calc_principal_angles(self.A, self.B) 122 | self.assertAlmostEqual( 123 | metrics.calc_chordal_distance_from_principal_angles( 124 | principal_angles), expected_chord_dist) 125 | 126 | # xxxxxxxxxx Now let's test with complex values xxxxxxxxxxxxxxxxxxx 127 | A = randn_c(3, 2) 128 | B = randn_c(3, 2) 129 | principal_angles2 = metrics.calc_principal_angles(A, B) 130 | dist1 = metrics.calc_chordal_distance(A, B) 131 | dist2 = metrics.calc_chordal_distance_2(A, B) 132 | dist3 = metrics.calc_chordal_distance_from_principal_angles( 133 | principal_angles2) 134 | 135 | self.assertAlmostEqual(dist1, dist2) 136 | self.assertAlmostEqual(dist3, dist2) 137 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 138 | 139 | 140 | # xxxxxxxxxx Doctests xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 141 | if __name__ == "__main__": 142 | unittest.main() 143 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 144 | -------------------------------------------------------------------------------- /pyphysim/subspace/projections.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Module related to subspace projection.""" 3 | 4 | import numpy as np 5 | 6 | 7 | class Projection: 8 | """ 9 | Class to calculate the projection, orthogonal projection and 10 | reflection of a given matrix in a Subspace `S` spanned by the 11 | columns of a matrix `A`. 12 | 13 | The matrix A is provided in the constructor and after that the 14 | functions `project`, `oProject` and `reflect` can be called with M 15 | as an argument. 16 | 17 | Parameters 18 | ---------- 19 | A : np.ndarray 20 | The matrix whose columns form a basis for the projected 21 | subspace. 22 | 23 | Examples 24 | -------- 25 | >>> A = np.array([[1 + 1j, 2 - 2j], [3 - 2j, 0], [-1 - 1j, 2 - 3j]]) 26 | >>> v = np.array([1, 2, 3]) 27 | >>> P = Projection(A) 28 | >>> P.project(v) 29 | array([1.69577465+0.87887324j, 1.33802817+0.41408451j, 30 | 2.32957746-0.56901408j]) 31 | >>> P.oProject(v) 32 | array([-0.69577465-0.87887324j, 0.66197183-0.41408451j, 33 | 0.67042254+0.56901408j]) 34 | >>> P.reflect(v) 35 | array([-2.3915493 -1.75774648j, -0.67605634-0.82816901j, 36 | -1.65915493+1.13802817j]) 37 | """ 38 | def __init__(self, A: np.ndarray): 39 | self._A = A 40 | self.Q = Projection.calcProjectionMatrix(A) 41 | 42 | # Matrix to project in the orthogonal subspace. Note that self.Q is 43 | # always a square matrix 44 | self.oQ = Projection.calcOrthogonalProjectionMatrix(A) 45 | 46 | def project(self, M: np.ndarray) -> np.ndarray: 47 | """ 48 | Project the matrix (or vector) M in the desired subspace. 49 | 50 | Parameters 51 | ---------- 52 | M : np.ndarray 53 | The matrix to be projected. 54 | 55 | Returns 56 | ------- 57 | np.ndarray 58 | The projection of `M` into the desired subspace. 59 | """ 60 | return self.Q.dot(M) 61 | 62 | def oProject(self, M: np.ndarray) -> np.ndarray: 63 | """ 64 | Project the matrix (or vector) M the subspace ORTHOGONAL to the 65 | subspace projected with `project`. 66 | 67 | Parameters 68 | ---------- 69 | M : np.ndarray 70 | The matrix to be projected. 71 | 72 | Returns 73 | ------- 74 | np.ndarray 75 | The projection of `M` into the orthogonal subspace. 76 | """ 77 | return self.oQ.dot(M) 78 | 79 | def reflect(self, M: np.ndarray) -> np.ndarray: 80 | """Find the reflection of the matrix in the subspace spanned by 81 | the columns of `A` 82 | 83 | Parameters 84 | ---------- 85 | M : np.ndarray 86 | The matrix to be projected. 87 | 88 | Returns 89 | ------- 90 | np.ndarray 91 | The reflection of `M` in the subspace. 92 | """ 93 | return (np.eye(self.Q.shape[0]) - 2 * self.Q).dot(M) 94 | 95 | @staticmethod 96 | def calcProjectionMatrix(A: np.ndarray) -> np.ndarray: 97 | """ 98 | Calculates the projection matrix that projects a vector (or a 99 | matrix) into the signal space spanned by the columns of `A`. 100 | 101 | Parameters 102 | ---------- 103 | A : np.ndarray 104 | A matrix whose columns form a basis for the desired subspace. 105 | 106 | Returns 107 | ------- 108 | np.ndarray 109 | The projection matrix that can be used to project a vector or a 110 | matrix into the subspace spanned by the columns of `A` 111 | 112 | See also 113 | -------- 114 | calcOrthogonalProjectionMatrix 115 | 116 | Examples 117 | -------- 118 | >>> A = np.array([[1 + 1j, 2 - 2j], [3 - 2j, 0], \ 119 | [-1 - 1j, 2 - 3j]]) 120 | >>> # Matrix that projects into the subspace spanned by the columns 121 | >>> # of A 122 | >>> Q = calcProjectionMatrix(A) 123 | >>> np.allclose(Q.round(4), np.array( \ 124 | [[ 0.5239+0.j, 0.0366+0.3296j, 0.3662+0.0732j], \ 125 | [ 0.0366-0.3296j, 0.7690+0.j, -0.0789+0.2479j], \ 126 | [ 0.3662-0.0732j, -0.0789-0.2479j, 0.7070-0.j]])) 127 | True 128 | """ 129 | # MATLAB version: A/(A'*A)*A'; 130 | A_H = A.conjugate().transpose() 131 | return (A.dot(np.linalg.inv(A_H.dot(A)))).dot(A_H) 132 | 133 | @staticmethod 134 | def calcOrthogonalProjectionMatrix(A: np.ndarray) -> np.ndarray: 135 | """ 136 | Calculates the projection matrix that projects a vector (or a 137 | matrix) into the signal space orthogonal to the signal space 138 | spanned by the columns of M. 139 | 140 | Parameters 141 | ---------- 142 | A : np.ndarray 143 | A matrix whose columns form a basis for the "desired subspace". 144 | 145 | Returns 146 | ------- 147 | np.ndarray 148 | The projection matrix that can be used to project a vector or a 149 | matrix into the subspace orthogonal to the subspace spanned by 150 | the columns of `A` 151 | 152 | See also 153 | -------- 154 | calcProjectionMatrix 155 | 156 | Examples 157 | -------- 158 | >>> A = np.array([[1, 2], [2, 2], [4, 3]]) 159 | >>> # Matrix that projects into the subspace orthogonal to the 160 | >>> # subspace spanned by the columns of A 161 | >>> oQ = calcOrthogonalProjectionMatrix(A) 162 | >>> print(oQ) 163 | [[ 0.12121212 -0.3030303 0.12121212] 164 | [-0.3030303 0.75757576 -0.3030303 ] 165 | [ 0.12121212 -0.3030303 0.12121212]] 166 | """ 167 | Q = Projection.calcProjectionMatrix(A) 168 | return np.eye(Q.shape[0]) - Q 169 | 170 | 171 | # xxxxx Alias for the static methods of Projection class xxxxxxxxxxxxxxxxxx 172 | calcProjectionMatrix = Projection.calcProjectionMatrix 173 | calcOrthogonalProjectionMatrix = Projection.calcOrthogonalProjectionMatrix 174 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 175 | -------------------------------------------------------------------------------- /apps/ia/greedy_statistics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # pylint: disable=C0325 4 | """ 5 | Script to read the result files created by the simulate_greedy_ia.py 6 | simulator and create a table with the stream statistics. 7 | """ 8 | 9 | import itertools 10 | 11 | import numpy as np 12 | 13 | from pyphysim.simulations.parameters import SimulationParameters 14 | from pyphysim.simulations.results import SimulationResults 15 | from pyphysim.util import misc 16 | 17 | 18 | def get_result_from_file(): 19 | """ 20 | Load the SimulationResults object from the file and return it. 21 | """ 22 | config_file = 'greedy_config_file.txt' 23 | 24 | # xxxxxxxxxx Config spec for the config file xxxxxxxxxxxxxxxxxxxxxxxxxx 25 | spec = """[Grid] 26 | cell_radius=float(min=0.01, default=1.0) 27 | num_cells=integer(min=3,default=3) 28 | num_clusters=integer(min=1,default=1) 29 | [Scenario] 30 | NSymbs=integer(min=10, max=1000000, default=200) 31 | SNR=real_numpy_array(min=-50, max=100, default=0:5:31) 32 | M=integer(min=4, max=512, default=4) 33 | modulator=option('QPSK', 'PSK', 'QAM', 'BPSK', default="PSK") 34 | Nr=integer_scalar_or_integer_numpy_array_check(min=2,default=3) 35 | Nt=integer_scalar_or_integer_numpy_array_check(min=2,default=3) 36 | Ns=integer_scalar_or_integer_numpy_array_check(min=1,default=3) 37 | N0=float(default=-116.4) 38 | scenario=string_list(default=list('Random', 'NoPathLoss')) 39 | [IA Algorithm] 40 | max_iterations=integer(min=1, default=120) 41 | initialize_with=string_list(default=list('random')) 42 | stream_sel_method=string_list(default=list('greedy', 'brute')) 43 | [General] 44 | rep_max=integer(min=1, default=2000) 45 | max_bit_errors=integer(min=1, default=3000) 46 | unpacked_parameters=string_list(default=list('SNR','stream_sel_method','scenario','initialize_with')) 47 | """.split("\n") 48 | 49 | params = SimulationParameters.load_from_config_file(config_file, spec) 50 | 51 | # xxxxx Results base name xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 52 | base_name = ("IA_stream_sel_results_{SNR}_{M}-{modulator}_{Nr}x{Nt}_({Ns})" 53 | "_MaxIter_{max_iterations}_({initialize_with})") 54 | base_name = misc.replace_dict_values(base_name, params.parameters, True) 55 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 56 | 57 | # xxxxxxxxxx Get the SimulationResults objects xxxxxxxxxxxxxxxxxxxxxxxx 58 | results = SimulationResults.load_from_file( 59 | 'greedy_{0}.pickle'.format(base_name)) 60 | # xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 61 | 62 | return results 63 | 64 | 65 | def get_pretty_statistic_table(statistics, Ns=None, multiply=100): 66 | """ 67 | Get a pretty table with the statistics for each stream. 68 | 69 | Parameters 70 | ---------- 71 | statistics : 1D numpy array 72 | The array with the statistics of each combination. 73 | Ns : 1D numpy array 74 | The maximum number of streams for each user. If it is None it 75 | defaults to [3, 3, 3]. 76 | multiply : int (default = 100) 77 | Value to multiply by each statistic value. If the values in 78 | `statistic_matrix` are average over N, set `multiply` to N to use 79 | the absolute values instead of the percentage. 80 | """ 81 | if Ns is None: 82 | Ns = [3, 3, 3] 83 | 84 | my_range = lambda x: range(1, x + 1) 85 | 86 | all_ranges = map(my_range, Ns) 87 | all_combinations = itertools.product(*all_ranges) 88 | 89 | statistic_table = zip(all_combinations, np.round(statistics * multiply, 2)) 90 | 91 | return statistic_table 92 | 93 | 94 | def print_statistics_table(statistic_table): 95 | """ 96 | Print the statistics table in a pretty way. 97 | 98 | Parameters 99 | ---------- 100 | statistic_table : TYPE 101 | """ 102 | # Remove rows in statistic_table which have a zero percentage 103 | filtered_table = [(a, b) for (a, b) in statistic_table if b != 0] 104 | for line in filtered_table: 105 | print('{0}: {1}%'.format(line[0], line[1])) 106 | 107 | 108 | if __name__ == '__main__': 109 | results = get_result_from_file() 110 | 111 | greedy_nopl_stream_statistics = results.get_result_values_list( 112 | 'stream_statistics', 113 | fixed_params={ 114 | 'initialize_with': 'random', 115 | 'stream_sel_method': 'greedy', 116 | 'scenario': 'NoPathLoss' 117 | }) 118 | greedy_withpl_stream_statistics = results.get_result_values_list( 119 | 'stream_statistics', 120 | fixed_params={ 121 | 'initialize_with': 'random', 122 | 'stream_sel_method': 'greedy', 123 | 'scenario': 'Random' 124 | }) 125 | brute_force_nopl_stream_statistics = results.get_result_values_list( 126 | 'stream_statistics', 127 | fixed_params={ 128 | 'initialize_with': 'random', 129 | 'stream_sel_method': 'brute', 130 | 'scenario': 'NoPathLoss' 131 | }) 132 | brute_force_withpl_stream_statistics = results.get_result_values_list( 133 | 'stream_statistics', 134 | fixed_params={ 135 | 'initialize_with': 'random', 136 | 'stream_sel_method': 'brute', 137 | 'scenario': 'Random' 138 | }) 139 | 140 | SNR = results.params['SNR'] 141 | 142 | print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 143 | print("xxxxxxxxxxxxxxx Greedy Results xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 144 | print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 145 | for idx, snr in enumerate(SNR): 146 | print("SNR: {0}".format(snr)) 147 | statistic_table = get_pretty_statistic_table( 148 | greedy_withpl_stream_statistics[idx]) 149 | print_statistics_table(statistic_table) 150 | print() 151 | print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 152 | print("xxxxxxxxxxxxxxx Brute Force Results xxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 153 | print("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 154 | for idx, snr in enumerate(SNR): 155 | print("SNR: {0}".format(snr)) 156 | statistic_table = get_pretty_statistic_table( 157 | brute_force_withpl_stream_statistics[idx]) 158 | print_statistics_table(statistic_table) 159 | -------------------------------------------------------------------------------- /pyphysim/subspace/metrics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Implement several metrics for subspaces.""" 3 | 4 | import math 5 | from typing import cast 6 | 7 | import numpy as np 8 | 9 | from .projections import calcProjectionMatrix 10 | 11 | __all__ = [ 12 | "calc_principal_angles", "calc_chordal_distance_from_principal_angles", 13 | "calc_chordal_distance", "calc_chordal_distance_2" 14 | ] 15 | 16 | 17 | # TODO: I think calc_principal_angles is not correct when matrix1 e matrix2 18 | # have different sizes. At least obtaining the chordal distance from the 19 | # principal angles does not work when matrix1 and matrix2 have different 20 | # shapes. 21 | def calc_principal_angles(matrix1: np.ndarray, 22 | matrix2: np.ndarray) -> np.ndarray: 23 | """ 24 | Calculates the principal angles between `matrix1` and `matrix2`. 25 | 26 | Parameters 27 | ---------- 28 | matrix1 : np.ndarray 29 | A 2D numpy array. 30 | matrix2 : np.ndarray 31 | A 2D numpy array. 32 | 33 | Returns 34 | ------- 35 | np.ndarray 36 | The principal angles between `matrix1` and `matrix2`. This is a 37 | 1D numpy array. 38 | 39 | See also 40 | -------- 41 | calc_chordal_distance_from_principal_angles 42 | 43 | Examples 44 | -------- 45 | >>> A = np.array([[1, 2], [3, 4], [5, 6]]) 46 | >>> B = np.array([[1, 5], [3, 7], [5, -1]]) 47 | >>> print(calc_principal_angles(A, B)) 48 | [0. 0.54312217] 49 | """ 50 | # First we need to find the orthogonal basis for matrix1 and 51 | # matrix2. This can be done with the QR decomposition. Note that if 52 | # matrix1 has 'n' columns then its orthogonal basis is given by the 53 | # first 'n' columns of the 'Q' matrix from its QR decomposition. 54 | Q1 = np.linalg.qr(matrix1)[0] 55 | Q2 = np.linalg.qr(matrix2)[0] 56 | 57 | # TODO: Test who has more columns. Q1 must have dimension grater than 58 | # or equal to Q2 so that the SVD can be calculated in the order below. 59 | # 60 | # See the algorithm in 61 | # http://sensblogs.wordpress.com/2011/09/07/matlab-codes-for-principal-angles-also-termed-as-canonical-correlation-between-any-arbitrary-subspaces-redirected-from-jen-mei-changs-dissertation/ 62 | S = np.linalg.svd(Q1.conjugate().transpose().dot(Q2), 63 | full_matrices=False)[1] 64 | 65 | # The singular values of S vary between 0 and 1, but due to 66 | # computational impressions there can be some value above 1 (by a very 67 | # small value). Below we change values greater then 1 to be equal to 1 68 | # to avoid problems with the arc-cos call later. 69 | S[S > 1] = 1 # Change values greater then 1 to 1 70 | 71 | # The singular values in the matrix S are equal to the cosine of the 72 | # principal angles. We can calculate the arc-cosine of each element 73 | # then. 74 | return np.arccos(S) 75 | 76 | 77 | # noinspection PyPep8 78 | def calc_chordal_distance_from_principal_angles( 79 | principalAngles: np.ndarray) -> float: 80 | """ 81 | Calculates the chordal distance from the principal angles. 82 | 83 | It is given by the square root of the sum of the squares of the sin of 84 | the principal angles. 85 | 86 | Parameters 87 | ---------- 88 | principalAngles : np.ndarray 89 | Numpy array with the principal angles. This is a 1D numpy array. 90 | 91 | Returns 92 | ------- 93 | chord_dist : float 94 | The chordal distance. 95 | 96 | See also 97 | -------- 98 | calc_principal_angles, 99 | calc_chordal_distance, 100 | calc_chordal_distance_2 101 | 102 | Examples 103 | -------- 104 | >>> A = np.arange(1, 9.) 105 | >>> A.shape = (4, 2) 106 | >>> B = np.array([[1.2, 2.1], [2.9, 4.3], [5.2, 6.1], [6.8, 8.1]]) 107 | >>> princ_angles = calc_principal_angles(A, B) 108 | >>> print(round(calc_chordal_distance_from_principal_angles(princ_angles), 8)) 109 | 0.47386786 110 | """ 111 | # noinspection PyTypeChecker 112 | summation = (np.sum(np.sin(principalAngles)**2)).item() 113 | return math.sqrt(summation) 114 | 115 | 116 | def calc_chordal_distance(matrix1: np.ndarray, matrix2: np.ndarray) -> float: 117 | """ 118 | Calculates the chordal distance between the two matrices 119 | 120 | Parameters 121 | ---------- 122 | matrix1 : np.ndarray 123 | A 2D numpy array. 124 | matrix2 : np.ndarray 125 | A 2D numpy array. 126 | 127 | Returns 128 | ------- 129 | chord_dist : float 130 | The chordal distance. 131 | 132 | Notes 133 | ----- 134 | Same as :func:`calc_chordal_distance_2`, but implemented differently. 135 | 136 | See also 137 | -------- 138 | calc_chordal_distance_2, 139 | calc_chordal_distance_from_principal_angles 140 | 141 | Examples 142 | -------- 143 | >>> A = np.arange(1, 9.) 144 | >>> A.shape = (4, 2) 145 | >>> B = np.array([[1.2, 2.1], [2.9, 4.3], [5.2, 6.1], [6.8, 8.1]]) 146 | >>> print(round(calc_chordal_distance(A, B), 8)) 147 | 0.47386786 148 | """ 149 | Q1 = np.linalg.qr(matrix1)[0] 150 | Q2 = np.linalg.qr(matrix2)[0] 151 | 152 | # ncols = matrix1.shape[1] # Must be equal to matrix2.shape[1]. 153 | 154 | # The first ncols columns of Q1 and Q2 make orthogonal basis of 155 | # ran(matrix1) and ran(matrix2), respectively 156 | Q1_sqr = Q1.dot(Q1.conjugate().transpose()) 157 | Q2_sqr = Q2.dot(Q2.conjugate().transpose()) 158 | return cast(float, np.linalg.norm(Q1_sqr - Q2_sqr, 'fro') / math.sqrt(2.)) 159 | 160 | 161 | def calc_chordal_distance_2(matrix1: np.ndarray, matrix2: np.ndarray) -> float: 162 | """ 163 | Calculates the chordal distance between the two matrices 164 | 165 | Parameters 166 | ---------- 167 | matrix1 : np.ndarray 168 | A 2D numpy array. 169 | matrix2 : np.ndarray 170 | A 2D numpy array. 171 | 172 | Returns 173 | ------- 174 | chord_dist : float 175 | The chordal distance. 176 | 177 | Notes 178 | ----- 179 | Same as :func:`calc_chordal_distance`, but implemented differently. 180 | 181 | See also 182 | -------- 183 | calc_chordal_distance, 184 | calc_chordal_distance_from_principal_angles 185 | 186 | Examples 187 | -------- 188 | >>> A = np.arange(1, 9.) 189 | >>> A.shape = (4, 2) 190 | >>> B = np.array([[1.2, 2.1], [2.9, 4.3], [5.2, 6.1], [6.8, 8.1]]) 191 | >>> print(round(calc_chordal_distance_2(A, B), 8)) 192 | 0.47386786 193 | """ 194 | return cast( 195 | float, # 196 | (np.linalg.norm( 197 | calcProjectionMatrix(matrix1) - calcProjectionMatrix(matrix2), 198 | 'fro') / math.sqrt(2)) # 199 | ) 200 | -------------------------------------------------------------------------------- /pyphysim/util/serialize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Module containing function related to serialization. 4 | """ 5 | 6 | import json 7 | from typing import Any, Dict, Union, cast 8 | 9 | import numpy as np 10 | 11 | Serializable = Union[np.ndarray, np.int32, np.int64, np.float32, np.float64, 12 | np.float128, set] 13 | 14 | # A type corresponding to the JSON representation of the object. For a lack of 15 | # a better option we use Any 16 | JsonRepresentation = Any 17 | 18 | 19 | class NumpyOrSetEncoder(json.JSONEncoder): 20 | """ 21 | JSON encoder for numpy arrays. 22 | 23 | Pass this class to json.dumps when converting a dictionary to json so 24 | that any field which with a numpy array as value will be properly 25 | converted. 26 | 27 | This encoder will also handle numpy scalars and the native python set 28 | types. 29 | 30 | When you need to convert the json representation back, use the 31 | `json_numpy_or_set_obj_hook` function. 32 | 33 | See Also 34 | -------- 35 | json_numpy_or_set_obj_hook 36 | """ 37 | def default(self, obj: Serializable) -> JsonRepresentation: 38 | """ 39 | If input object is an ndarray it will be converted into a dict holding 40 | data, dtype, _is_numpy_array and shape. 41 | 42 | Parameters 43 | ---------- 44 | obj : Serializable 45 | 46 | Returns 47 | ------- 48 | Serialized Data 49 | """ 50 | # Case for numpy arrays 51 | if isinstance(obj, np.ndarray): 52 | return { 53 | 'data': obj.tolist(), 54 | 'dtype': str(obj.dtype), 55 | '_is_numpy_array': True, 56 | 'shape': obj.shape 57 | } 58 | # Case for numpy scalars 59 | if isinstance(obj, (np.int32, np.int64)): 60 | return int(obj) 61 | if isinstance(obj, (np.float32, np.float64, np.float128)): 62 | return int(obj) 63 | 64 | # Case for built-in Python sets 65 | if isinstance(obj, set): 66 | return {'data': list(obj), '_is_set': True} 67 | 68 | # If it is not a numpy array we fall back to base class encoder 69 | return json.JSONEncoder(self, obj) # type: ignore 70 | 71 | 72 | def json_numpy_or_set_obj_hook( 73 | dct: Dict[str, JsonRepresentation]) -> Serializable: 74 | """ 75 | Decodes a previously encoded numpy array. 76 | 77 | Parameters 78 | ---------- 79 | dct : dict 80 | The JSON encoded numpy array. 81 | 82 | Returns 83 | ------- 84 | np.ndarray | set | dict, optional 85 | The decoded numpy array or None if the encoded json data was not an 86 | encoded numpy array. 87 | 88 | See Also 89 | -------- 90 | NumpyOrSetEncoder 91 | """ 92 | if isinstance(dct, dict) and '_is_numpy_array' in dct: 93 | if dct['_is_numpy_array'] is True: 94 | data = dct['data'] 95 | return np.array(data) 96 | 97 | raise ValueError( # pragma: no cover 98 | 'Json representation contains the "_is_numpy_array" key ' 99 | 'indicating that the object should be a numpy array, but it ' 100 | 'was set to False, which is not valid.') 101 | if isinstance(dct, dict) and '_is_set' in dct: 102 | if dct['_is_set'] is True: 103 | data = dct['data'] 104 | return set(data) 105 | 106 | raise ValueError( # pragma: no cover 107 | 'Json representation contains the "_is_set" key ' 108 | 'indicating that the object should be python set, but it ' 109 | 'was set to False, which is not valid.') 110 | return dct 111 | 112 | 113 | class JsonSerializable: 114 | """ 115 | Base class for classes you want to be JSON serializable (convert 116 | to/from JSON). 117 | 118 | You can call the methods `to_json` and `from_json` methods (the later 119 | is a staticmethod). 120 | 121 | Note that a subclass must implement the `_to_dict` and `_from_dict` methods. 122 | """ 123 | def _to_dict(self) -> Any: 124 | """ 125 | Convert the object to a dictionary representation. 126 | 127 | Returns 128 | ------- 129 | dict 130 | The dictionary representation of the object. 131 | """ 132 | raise NotImplementedError( 133 | "Implement in a subclass") # pragma: no cover 134 | 135 | def to_dict(self) -> Dict[str, Any]: 136 | """ 137 | Convert the object to a dictionary representation. 138 | 139 | Returns 140 | ------- 141 | dict 142 | The dictionary representation of the object. 143 | """ 144 | return cast(Dict[str, Any], self._to_dict()) 145 | 146 | @staticmethod 147 | def _from_dict(d: Any) -> Any: 148 | """ 149 | Convert from a dictionary to an object. 150 | 151 | Parameters 152 | ---------- 153 | d : dict 154 | The dictionary representing the object. 155 | 156 | Returns 157 | ------- 158 | Result 159 | The converted object. 160 | """ 161 | raise NotImplementedError( 162 | "Implement in a subclass") # pragma: no cover 163 | 164 | @classmethod 165 | def from_dict(cls, d: Dict[str, Any]) -> Any: 166 | """ 167 | Convert from a dictionary to an object. 168 | 169 | Parameters 170 | ---------- 171 | d : dict 172 | The dictionary representing the Result. 173 | 174 | Returns 175 | ------- 176 | Result 177 | The converted object. 178 | """ 179 | return cls._from_dict(d) 180 | 181 | def to_json(self) -> JsonRepresentation: 182 | """ 183 | Convert the object to JSON. 184 | 185 | Returns 186 | ------- 187 | str 188 | JSON representation of the object. 189 | """ 190 | return json.dumps(self._to_dict(), cls=NumpyOrSetEncoder) 191 | 192 | @classmethod 193 | def from_json(cls, data: JsonRepresentation) -> Any: 194 | """ 195 | Convert a JSON representation of the object to an actual object. 196 | 197 | Parameters 198 | ---------- 199 | data : str 200 | The JSON representation of the object. 201 | 202 | Returns 203 | ------- 204 | any 205 | The actual object 206 | """ 207 | d = json.loads(data, object_hook=json_numpy_or_set_obj_hook) 208 | return cls._from_dict(d) 209 | 210 | 211 | # # xxxxxxxxxx Test and Example Usage xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 212 | # if __name__ == '__main__': 213 | # expected = np.arange(100, dtype=np.float) 214 | # dumped = json.dumps(expected, cls=NumpyOrSetEncoder) 215 | # result = json.loads(dumped, object_hook=json_numpy_or_set_obj_hook) 216 | # print(type(result)) 217 | # print(result) 218 | --------------------------------------------------------------------------------