├── .github └── workflows │ ├── python-publish.yml │ └── test.yml ├── .gitignore ├── .pylintrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── examples ├── README.md ├── broadcasting │ ├── README.md │ ├── broadcasting.ipynb │ ├── config.py │ ├── main.py │ ├── nada-project.toml │ ├── src │ │ └── broadcasting.py │ ├── target │ │ └── .gitignore │ └── tests │ │ └── broadcasting.yaml ├── common │ └── utils.py ├── dot_product │ ├── README.md │ ├── config.py │ ├── dot_product.ipynb │ ├── main.py │ ├── nada-project.toml │ ├── src │ │ └── dot_product.py │ ├── target │ │ └── .gitignore │ └── tests │ │ └── dot_product.yaml ├── linear_regression │ ├── README.md │ ├── cleartext.ipynb │ ├── linear_regression.ipynb │ ├── main.py │ ├── nada-project.toml │ ├── src │ │ ├── determinant.py │ │ ├── gauss_jordan.py │ │ ├── linear_regression.py │ │ ├── linear_regression_256.py │ │ ├── matrix_inverse.py │ │ └── modular_inverse.py │ ├── target │ │ └── .gitignore │ └── tests │ │ ├── determinant_1.yaml │ │ ├── determinant_2.yaml │ │ ├── determinant_3.yaml │ │ ├── gauss_jordan.yaml │ │ ├── linear_regression.yaml │ │ ├── linear_regression_1.yaml │ │ ├── linear_regression_2.yaml │ │ ├── linear_regression_256_1.yaml │ │ ├── linear_regression_256_2.yaml │ │ ├── matrix_inverse.py │ │ └── modular_inverse.yaml ├── matrix_multiplication │ ├── README.md │ ├── config.py │ ├── main.py │ ├── matrix_multiplication.ipynb │ ├── nada-project.toml │ ├── src │ │ └── matrix_multiplication.py │ ├── target │ │ └── .gitignore │ └── tests │ │ └── matrix_multiplication.yaml └── rational_numbers │ ├── README.md │ ├── main.py │ ├── nada-project.toml │ ├── rational_numbers.ipynb │ ├── src │ └── rational_numbers.py │ ├── target │ └── .gitignore │ └── tests │ └── rational_numbers.yaml ├── mypy.ini ├── nada_numpy ├── __init__.py ├── array.py ├── client.py ├── context.py ├── funcs.py ├── legacy_client.py ├── nada_typing.py ├── types.py └── utils.py ├── poetry.lock ├── pyproject.toml └── tests ├── __init__.py ├── nada-tests ├── nada-project.toml ├── src │ ├── array_attributes.py │ ├── array_comparison.py │ ├── array_matrix_inverse.py │ ├── array_statistics.py │ ├── base.py │ ├── broadcasting_div.py │ ├── broadcasting_mul.py │ ├── broadcasting_rationals.py │ ├── broadcasting_sub.py │ ├── broadcasting_sum.py │ ├── broadcasting_vec.py │ ├── chained_rational_operations.py │ ├── cleartext_matrix_inverse.ipynb │ ├── dot_product.py │ ├── dot_product_rational.py │ ├── functional_operations.py │ ├── fxpmath_arrays.py │ ├── fxpmath_funcs.py │ ├── fxpmath_methods.py │ ├── gauss_jordan.py │ ├── generate_array.py │ ├── get_attr.py │ ├── get_item.py │ ├── get_vec.py │ ├── hstack.py │ ├── logistic_regression.py │ ├── logistic_regression_rational.py │ ├── matrix_inverse.py │ ├── matrix_multiplication.py │ ├── matrix_multiplication_rational.py │ ├── matrix_multiplication_rational_multidim.py │ ├── new_array.py │ ├── private_inverse.py │ ├── random_array.py │ ├── rational_advanced.py │ ├── rational_arithmetic.ipynb │ ├── rational_arithmetic.py │ ├── rational_array.py │ ├── rational_comparison.py │ ├── rational_if_else.py │ ├── rational_operability.py │ ├── rational_scaling.py │ ├── reveal.py │ ├── secret_rational_arithmetic.py │ ├── secret_rational_comparison.py │ ├── set_item.py │ ├── shape.py │ ├── shuffle.py │ ├── sum.py │ ├── supported_operations.py │ ├── supported_operations_return_types.py │ ├── type_guardrails.py │ ├── unsigned_matrix_inverse.py │ └── vstack.py ├── target │ └── .gitignore └── tests │ ├── array_attributes.yaml │ ├── array_comparison.yaml │ ├── array_statistics.yaml │ ├── base.yaml │ ├── broadcasting_div.yaml │ ├── broadcasting_mul.yaml │ ├── broadcasting_rationals.yaml │ ├── broadcasting_sub.yaml │ ├── broadcasting_sum.yaml │ ├── broadcasting_vec.yaml │ ├── chained_rational_operations.yaml │ ├── dot_product.yaml │ ├── dot_product_rational.yaml │ ├── functional_operations.yaml │ ├── fxpmath_arrays.yaml │ ├── fxpmath_funcs.yaml │ ├── fxpmath_methods.yaml │ ├── gauss_jordan.yaml │ ├── generate_array.yaml │ ├── get_attr.yaml │ ├── get_item.yaml │ ├── get_vec.yaml │ ├── hstack.yaml │ ├── logistic_regression.yaml │ ├── logistic_regression_rational.yaml │ ├── matrix_multiplication.yaml │ ├── matrix_multiplication_rational.yaml │ ├── matrix_multiplication_rational_multidim.yaml │ ├── new_array.yaml │ ├── private_inverse.yaml │ ├── random_array.yaml │ ├── rational_advanced.yaml │ ├── rational_arithmetic.yaml │ ├── rational_array.yaml │ ├── rational_comparison.yaml │ ├── rational_if_else.yaml │ ├── rational_operability.yaml │ ├── rational_scaling.yaml │ ├── reveal.yaml │ ├── secret_rational_arithmetic.yaml │ ├── secret_rational_comparison.yaml │ ├── set_item.yaml │ ├── shape.yaml │ ├── shuffle.yaml │ ├── sum.yaml │ ├── supported_operations.yaml │ ├── supported_operations_return_types.yaml │ ├── type_guardrails.yaml │ ├── unsigned_matrix_inverse.yaml │ ├── unsigned_matrix_inverse_2.yaml │ └── vstack.yaml ├── python-tests ├── test_client.py └── test_client_legacy.py └── test_all.py /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | 3 | name: Upload Python Package 4 | 5 | on: 6 | release: 7 | types: [published] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | deploy: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up Python 20 | uses: actions/setup-python@v3 21 | with: 22 | python-version: '3.12' 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install poetry 27 | poetry install --with dev 28 | - name: Build package 29 | run: poetry build 30 | - name: Publish package 31 | run: | 32 | poetry config pypi-token.pypi ${{ secrets.NILLION_PYPI_TOKEN }} 33 | poetry publish 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Test Nada Numpy 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "*" ] 11 | 12 | jobs: 13 | check-linting: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up Python 3.12 18 | uses: actions/setup-python@v3 19 | with: 20 | python-version: "3.12" 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install poetry 25 | poetry install --with dev 26 | - name: Run pylint 27 | run: poetry run pylint nada_numpy 28 | - name: Run mypy 29 | run: poetry run mypy nada_numpy 30 | 31 | build: 32 | needs: check-linting 33 | runs-on: ubuntu-latest 34 | strategy: 35 | fail-fast: false 36 | matrix: 37 | python-version: ["3.10", "3.11", "3.12"] 38 | 39 | steps: 40 | - uses: actions/checkout@v4 41 | - name: Install Nillion SDK 42 | run: | 43 | echo "$HOME/.nilup/bin/" >> $GITHUB_PATH 44 | curl https://nilup.nilogy.xyz/install.sh | bash 45 | - name: Setup Nillion SDK 46 | run: | 47 | nilup init 48 | nilup install latest 49 | nilup use latest 50 | - name: Set up Python ${{ matrix.python-version }} 51 | uses: actions/setup-python@v3 52 | with: 53 | python-version: ${{ matrix.python-version }} 54 | - name: Install dependencies 55 | run: | 56 | python -m pip install --upgrade pip 57 | pip install poetry 58 | poetry install --with dev 59 | - name: Run tests 60 | run: poetry run pytest -vv 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.nada.bin 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .env* 125 | .venv 126 | env/ 127 | venv/ 128 | ENV/ 129 | env.bak/ 130 | venv.bak/ 131 | 132 | # Spyder project settings 133 | .spyderproject 134 | .spyproject 135 | 136 | # Rope project settings 137 | .ropeproject 138 | 139 | # mkdocs documentation 140 | /site 141 | 142 | # mypy 143 | .mypy_cache/ 144 | .dmypy.json 145 | dmypy.json 146 | 147 | # Pyre type checker 148 | .pyre/ 149 | 150 | # pytype static type analyzer 151 | .pytype/ 152 | 153 | # Cython debug symbols 154 | cython_debug/ 155 | 156 | # PyCharm 157 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 158 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 159 | # and can be added to the global gitignore or merged into this file. For a more nuclear 160 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 161 | #.idea/ 162 | 163 | *.joblib 164 | tmp.* 165 | .DS_Store 166 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `nada-numpy` 2 | 3 | Thank you for considering contributing to `nada-numpy`! There are two ways to contribute to `nada-numpy`: 4 | 5 | 1. [Open issues](#open-issues) to report bugs and typos, or to suggest new ideas. 6 | 2. [Submit a PR](#submit-a-pull-request) with a new feature or improvement. 7 | 8 | To ensure a consistent development process, please follow the guidelines outlined below. 9 | 10 | ## Code quality and type checking 11 | 12 | - All contributions must adhere to the project's coding standards. We enforce these standards using `pylint` for code quality and `mypy` for type checking. 13 | - Before submitting your contributions, ensure that your code passes both `pylint` and `mypy` checks. 14 | - These tools are also integrated into our CI/CD pipeline, and any PR will be automatically validated against these checks. 15 | 16 | ## Development 17 | 18 | We recommend continuously running your code through `pylint` and `mypy` during the development process. These tools help identify potential issues early, enforce coding standards, and maintain type safety. 19 | 20 | ### Installation 21 | 22 | 1. Install [black](https://pypi.org/project/black/) and [isort](https://pycqa.github.io/isort/) for code formating 23 | ```bash 24 | pip3 install black && isort 25 | ``` 26 | 2. Fork the [repo](https://github.com/NillionNetwork/nada-numpy.git) 27 | 3. Install from source the `nada-numpy` library: 28 | ```bash 29 | cd nada-numpy 30 | pip3 install -e . 31 | ``` 32 | 33 | ### Adding tests 34 | 35 | The [`tests/nada-tests`](https://github.com/NillionNetwork/nada-numpy/tree/main/tests/nada-tests) folder contains the testing infrastructure for `nada_numpy`. You will need to create one or more scripts to test your functionality. You can read the [docs](https://docs.nillion.com/nada#generate-a-test-file) for more info about testing. Follow these steps for testing: 36 | 37 | 1. Add a script to [`tests/nada-tests/nada-project.toml`](https://github.com/NillionNetwork/nada-numpy/blob/main/tests/nada-tests/nada-project.toml). 38 | 39 | 2. Place your test script in [`tests/nada-tests/src/`](https://github.com/NillionNetwork/nada-numpy/blob/main/tests/nada-tests/src), where it will verify the expected behavior. 40 | 41 | 3. Generate the test file by running 42 | ```bash 43 | nada generate-test --test-name 44 | ``` 45 | and placing it in [`tests/nada-tests/tests/`](https://github.com/NillionNetwork/nada-numpy/blob/main/tests/nada-tests/tests). 46 | 47 | 4. Finally, add the script to the `TESTS` array in [`tests/test_all.py`](https://github.com/NillionNetwork/nada-numpy/blob/dd112a09835c2354cbf7ecc89ad2714ca7171b20/tests/test_all.py#L6) to integrate it with the CI/CD pipeline. 48 | 49 | ### Hints to add new features 50 | 51 | Below we provide some hints on how to add new features. We give two examples: adding a `NadaArray` method and adding a `Rational` method. 52 | 53 | #### New `NadaArray` method 54 | 55 | As an example, we use the `variance` operation to describe the development flow: 56 | 57 | 1. **Integrate the variance function:** 58 | - In [`nada_numpy/array.py`](https://github.com/NillionNetwork/nada-numpy/blob/main/nada_numpy/array.py), integrate the `variance` method inside the `NadaArray` class as a new member function. This will allow the `variance` to be called as `array.var()`. 59 | 60 | 2. **Add a Wrapper in `nada_numpy/funcs.py`:** 61 | - In `nada-numpy`, functions can also be called in the form `na.var(array)`. To support this, add a wrapper in [`nada_numpy/funcs.py`](https://github.com/NillionNetwork/nada-numpy/blob/main/nada_numpy/funcs.py). You can refer to the existing functions in this file to see how they simply wrap around `array.var()` in this context. 62 | 63 | #### New `Rational` method 64 | 65 | As an example, we use the exponential `exp` function to describe the development flow: 66 | 67 | 1. **Integrate the exp function with Rational:** 68 | - In [`nada_numpy/types.py`](https://github.com/NillionNetwork/nada-numpy/blob/main/nada_numpy/types.py), integrate the `exp` method inside both the `Rational` and `SecretRational` classes as a new member function. This will allow the `exp` to be called as `value.exp()`. 69 | 70 | 2. **Integrate the exp function with NadaArray:** 71 | - In [`nada_numpy/array.py`](https://github.com/NillionNetwork/nada-numpy/blob/main/nada_numpy/array.py), integrate the `exp` method inside the `NadaArray` class as a new member function. This will allow the `exp` to be called as `array.exp()`. 72 | 73 | 3. **Add a Wrapper in `nada_numpy/funcs.py`:** 74 | - In `nada-numpy`, functions can also be called in the form `na.exp(array)`. To support this, add a wrapper in [`nada_numpy/funcs.py`](https://github.com/NillionNetwork/nada-numpy/blob/main/nada_numpy/funcs.py). You can refer to the existing functions in this file to see how they simply wrap around `array.exp()` in this context. 75 | 76 | ## Submit a Pull Request 77 | 78 | We actively welcome your pull requests. Please follow these steps to successfully submit a PR: 79 | 80 | 1. Fork the [repo](https://github.com/NillionNetwork/nada-numpy.git) and create your branch from `main`. 81 | 2. If you've added code that should be tested, add tests as explained [above](#adding-tests). 82 | 3. Ensure that the test suite passes. Under [`tests/nada-tests/tests/`](https://github.com/NillionNetwork/nada-numpy/blob/main/tests/nada-tests/tests) run 83 | ```bash 84 | nada test 85 | ``` 86 | 4. Run from the root directory both 87 | ```bash 88 | black . && isort . 89 | ``` 90 | 5. Ensure that your code passes both `pylint` and `mypy` checks: 91 | ```bash 92 | poetry run pylint 93 | poetry run mypy 94 | ``` 95 | 96 | ## Open Issues 97 | 98 | We use [GitHub issues](https://github.com/NillionNetwork/nada-numpy/issues/new/choose) to report bugs and typos, or to suggest new ideas. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. 99 | 100 | ## License 101 | By contributing to `nada-numpy`, you agree that your contributions will be licensed under the [LICENSE](./LICENSE) file in the root directory of this source tree. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nada-Numpy 2 | 3 | ![GitHub License](https://img.shields.io/github/license/NillionNetwork/nada-numpy?style=for-the-badge&logo=apache&logoColor=white&color=%23D22128&link=https%3A%2F%2Fgithub.com%2FNillionNetwork%2Fnada-numpy%2Fblob%2Fmain%2FLICENSE&link=https%3A%2F%2Fgithub.com%2FNillionNetwork%2Fnada-numpy%2Fblob%2Fmain%2FLICENSE) 4 | ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/NillionNetwork/nada-numpy/test.yml?style=for-the-badge&logo=python&logoColor=white&link=https%3A%2F%2Fgithub.com%2FNillionNetwork%2Fnada-numpy%2Factions%2Fworkflows%2Ftest.yml&link=https%3A%2F%2Fgithub.com%2FNillionNetwork%2Fnada-numpy%2Factions%2Fworkflows%2Ftest.yml) 5 | ![GitHub Release](https://img.shields.io/github/v/release/NillionNetwork/nada-numpy?sort=date&display_name=release&style=for-the-badge&logo=dependabot&label=LATEST%20RELEASE&color=0000FE&link=https%3A%2F%2Fpypi.org%2Fproject%2Fnada-numpy&link=https%3A%2F%2Fpypi.org%2Fproject%2Fnada-numpy) 6 | 7 | ## Features 8 | 9 | ### Use Numpy Array Features 10 | 11 | - **Dot Product**: Compute the dot product between two NadaArray objects. 12 | - **Element-wise Operations**: Perform element-wise addition, subtraction, multiplication, and division with broadcasting support. 13 | - **Stacking**: Horizontally and vertically stack arrays. 14 | 15 | ### Use additional Array helpers 16 | 17 | - **Shuffling**: Our implementation shuffles a 1D array using the Benes network, which rearranges elements in a deterministic yet seemingly random order. The Benes network is commonly used in sorting and switching circuits and requires the input array length to be a power of two (e.g., 2, 4, 8, 16). The shuffled array contains the same elements as the input, just in a different order. 18 | 19 | ### Use Decimal Numbers in Nada 20 | 21 | - **Rational Number Support**: Our implementation of `Rational` and `SecretRational` allows the use of simplified implementations of decimal numbers on top of Nillion. 22 | 23 | ## Installation 24 | 25 | ### Using pip 26 | 27 | ```bash 28 | pip install nada-numpy 29 | ``` 30 | 31 | ### From Sources 32 | 33 | You can install the nada-numpy library using Poetry: 34 | 35 | ```bash 36 | git clone https://github.com/NillionNetwork/nada-numpy.git 37 | pip3 install ./nada-numpy 38 | ``` 39 | 40 | ## Testing 41 | 42 | To test that the version installed works as expected, you can use poetry as follows: 43 | 44 | ```bash 45 | poetry install 46 | poetry run pytest 47 | ``` 48 | 49 | ## Join Nillion community and contribute 50 | 51 | Please join the Nillion community on [Discord](https://discord.com/invite/nillionnetwork). 52 | 53 | See the [CONTRIBUTING](./CONTRIBUTING.md) file for how to contribute to `nada-numpy`. 54 | 55 | ## License 56 | 57 | This project is licensed under the Apache2 License. See the LICENSE file for details. 58 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | The following are the currently available examples: 4 | 5 | - **Dot Product** [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/dot_product/dot_product.ipynb) 6 | - **Matrix Multiplication** [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/matrix_multiplication/matrix_multiplication.ipynb) 7 | - **Broadcasting** [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/broadcasting/broadcasting.ipynb) 8 | - **Rational Numbers** [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/rational_numbers/rational_numbers.ipynb) 9 | - **[Advanced] Linear Regression Closed-Form Training** [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/linear_regression/linear_regression.ipynb) -------------------------------------------------------------------------------- /examples/broadcasting/README.md: -------------------------------------------------------------------------------- 1 | # Broadcasting Tutorial 2 | 3 | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/broadcasting/broadcasting.ipynb) 4 | 5 | This tutorial shows how to efficiently use broadcasting in Nada using Nada Numpy. 6 | 7 | ## 🚨 Limitations 8 | The choice for blind computing implies certain trade-offs in comparison to conventional computing. What you gain in privacy, you pay in extra computational overhead & capacity constraints. 9 | 10 | Therefore, you will notice that large-scale computational workloads may lead to long compilation and/or execution times or hitting network capacity guardrails. 11 | 12 | That said, the Nillion team is working around the clock to push the boundaries of this technology and bring the potential of blind computing to reality 🚀 13 | 14 | 👉 This example has been tested on local devnet with arrays of up to 1000 elements. 15 | 16 | ## ➡️ Stay in touch 17 | If you want to get involved in the blind computing community and be the first to know all big updates, join our Discord 18 | 19 | [![Discord](https://img.shields.io/badge/Discord-nillionnetwork-%235865F2?logo=discord)](https://discord.gg/nillionnetwork) 20 | 21 | And if you want to contribute to the blind computing revolution, we welcome open-source contributors! 22 | 23 | [![GitHub Discussions](https://img.shields.io/badge/GitHub_Discussions-NillionNetwork-%23181717?logo=github)](https://github.com/orgs/NillionNetwork/discussions) 24 | -------------------------------------------------------------------------------- /examples/broadcasting/config.py: -------------------------------------------------------------------------------- 1 | """Configuration variables""" 2 | 3 | DIM = 3 4 | -------------------------------------------------------------------------------- /examples/broadcasting/main.py: -------------------------------------------------------------------------------- 1 | """Broadcasting example script""" 2 | 3 | import asyncio 4 | import os 5 | 6 | import numpy as np 7 | import pytest 8 | from config import DIM 9 | from dotenv import load_dotenv 10 | from nillion_client import (InputPartyBinding, Network, NilChainPayer, 11 | NilChainPrivateKey, OutputPartyBinding, 12 | Permissions, PrivateKey, SecretInteger, VmClient) 13 | 14 | import nada_numpy.client as na_client 15 | 16 | home = os.getenv("HOME") 17 | load_dotenv(f"{home}/.config/nillion/nillion-devnet.env") 18 | 19 | 20 | # 1 Party running simple addition on 1 stored secret and 1 compute time secret 21 | async def main(): 22 | # Use the devnet configuration generated by `nillion-devnet` 23 | network = Network.from_config("devnet") 24 | 25 | # Create payments config and set up Nillion wallet with a private key to pay for operations 26 | nilchain_key: str = os.getenv("NILLION_NILCHAIN_PRIVATE_KEY_0") # type: ignore 27 | payer = NilChainPayer( 28 | network, 29 | wallet_private_key=NilChainPrivateKey(bytes.fromhex(nilchain_key)), 30 | gas_limit=10000000, 31 | ) 32 | 33 | # Use a random key to identify ourselves 34 | signing_key = PrivateKey() 35 | client = await VmClient.create(signing_key, network, payer) 36 | party_names = na_client.parties(3) 37 | program_name = "broadcasting" 38 | program_mir_path = f"./target/{program_name}.nada.bin" 39 | 40 | ##### STORE PROGRAM 41 | print("-----STORE PROGRAM") 42 | 43 | # Store program 44 | program_mir = open( 45 | os.path.join(os.path.dirname(os.path.abspath(__file__)), program_mir_path), "rb" 46 | ).read() 47 | program_id = await client.store_program(program_name, program_mir).invoke() 48 | 49 | # Print details about stored program 50 | print(f"Stored program_id: {program_id}") 51 | 52 | ##### STORE SECRETS 53 | print("-----STORE SECRETS Party 0") 54 | 55 | # Create a secret 56 | A = na_client.array(np.ones([DIM]), "A", SecretInteger) 57 | C = na_client.array(np.ones([DIM]), "C", SecretInteger) 58 | 59 | # Create a permissions object to attach to the stored secret 60 | permissions = Permissions.defaults_for_user(client.user_id).allow_compute( 61 | client.user_id, program_id 62 | ) 63 | 64 | # Store a secret, passing in the receipt that shows proof of payment 65 | values_A = await client.store_values( 66 | A, ttl_days=5, permissions=permissions 67 | ).invoke() 68 | 69 | print("Stored values_A: ", values_A) 70 | 71 | values_C = await client.store_values( 72 | C, ttl_days=5, permissions=permissions 73 | ).invoke() 74 | 75 | print("Stored values_C: ", values_C) 76 | print("-----STORE SECRETS Party 1") 77 | 78 | # Create a secret 79 | B = na_client.array(np.ones([DIM]), "B", SecretInteger) 80 | D = na_client.array(np.ones([DIM]), "D", SecretInteger) 81 | 82 | # Create a permissions object to attach to the stored secret 83 | permissions = Permissions.defaults_for_user(client.user_id).allow_compute( 84 | client.user_id, program_id 85 | ) 86 | 87 | # Store a secret, passing in the receipt that shows proof of payment 88 | values_B = await client.store_values( 89 | B, ttl_days=5, permissions=permissions 90 | ).invoke() 91 | 92 | print("Stored values_B: ", values_B) 93 | 94 | values_D = await client.store_values( 95 | D, ttl_days=5, permissions=permissions 96 | ).invoke() 97 | 98 | print("Stored values_D: ", values_D) 99 | 100 | ##### COMPUTE 101 | print("-----COMPUTE") 102 | 103 | # Bind the parties in the computation to the client to set input and output parties 104 | input_bindings = [ 105 | InputPartyBinding(party_names[0], client.user_id), 106 | InputPartyBinding(party_names[1], client.user_id), 107 | ] 108 | output_bindings = [OutputPartyBinding(party_names[2], [client.user_id])] 109 | 110 | # Create a computation time secret to use 111 | compute_time_values = { 112 | # "my_int2": SecretInteger(10) 113 | } 114 | 115 | # Compute, passing in the compute time values as well as the previously uploaded value. 116 | print( 117 | f"Invoking computation using program {program_id} and values id {values_A}, {values_B}, {values_C}, {values_D}" 118 | ) 119 | compute_id = await client.compute( 120 | program_id, 121 | input_bindings, 122 | output_bindings, 123 | values=compute_time_values, 124 | value_ids=[values_A, values_B, values_C, values_D], 125 | ).invoke() 126 | 127 | # Print compute result 128 | print(f"The computation was sent to the network. compute_id: {compute_id}") 129 | result = await client.retrieve_compute_results(compute_id).invoke() 130 | print(f"✅ Compute complete for compute_id {compute_id}") 131 | print(f"🖥️ The result is {result}") 132 | return result 133 | 134 | 135 | if __name__ == "__main__": 136 | asyncio.run(main()) 137 | -------------------------------------------------------------------------------- /examples/broadcasting/nada-project.toml: -------------------------------------------------------------------------------- 1 | name = "broadcasting" 2 | version = "0.1.0" 3 | authors = [""] 4 | 5 | [[programs]] 6 | path = "src/broadcasting.py" 7 | prime_size = 128 8 | -------------------------------------------------------------------------------- /examples/broadcasting/src/broadcasting.py: -------------------------------------------------------------------------------- 1 | """Broadcasting example program""" 2 | 3 | from typing import List 4 | 5 | from config import DIM 6 | from nada_dsl import Output, SecretInteger 7 | 8 | # Step 0: Nada Numpy is imported with this line 9 | import nada_numpy as na 10 | 11 | 12 | def nada_main() -> List[Output]: 13 | """ 14 | Main Nada program. 15 | 16 | Returns: 17 | List[Output]: List of Nada outputs. 18 | """ 19 | # Step 1: We use Nada Numpy wrapper to create "Party0", "Party1" and "Party2" 20 | parties = na.parties(3) 21 | 22 | # Step 2: Party0 creates an array of dimension (3, ) with name "A" 23 | a = na.array([DIM], parties[0], "A", SecretInteger) 24 | 25 | # Step 3: Party1 creates an array of dimension (3, ) with name "B" 26 | b = na.array([DIM], parties[1], "B", SecretInteger) 27 | 28 | # Step 4: Party0 creates an array of dimension (3, ) with name "C" 29 | c = na.array([DIM], parties[0], "C", SecretInteger) 30 | 31 | # Step 5: Party1 creates an array of dimension (3, ) with name "D" 32 | d = na.array([DIM], parties[1], "D", SecretInteger) 33 | 34 | # Step 4: The result is of computing SIMD operations on top of the elements of the array 35 | # SIMD operations are performed on all the elements of the array. 36 | # The equivalent would be: for i in range(3): result += a[i] + b[i] - c[i] * d[i] 37 | result = a + b - c * d 38 | # Step 5: We can use result.output() to produce the output for Party2 39 | # and variable name "my_output" 40 | return result.output(parties[2], "my_output") 41 | -------------------------------------------------------------------------------- /examples/broadcasting/target/.gitignore: -------------------------------------------------------------------------------- 1 | # This directory is kept purposely, so that no compilation errors arise. 2 | # Ignore everything in this directory 3 | * 4 | # Except this file 5 | !.gitignore -------------------------------------------------------------------------------- /examples/broadcasting/tests/broadcasting.yaml: -------------------------------------------------------------------------------- 1 | program: broadcasting 2 | inputs: 3 | B_1: 3 4 | A_0: 3 5 | C_1: 3 6 | B_0: 3 7 | D_0: 3 8 | C_2: 3 9 | C_0: 3 10 | A_2: 3 11 | A_1: 3 12 | D_1: 3 13 | B_2: 3 14 | D_2: 3 15 | expected_outputs: 16 | my_output_1: -3 17 | my_output_2: -3 18 | my_output_0: -3 19 | -------------------------------------------------------------------------------- /examples/dot_product/README.md: -------------------------------------------------------------------------------- 1 | # Dot Product Tutorial 2 | 3 | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/dot_product/dot_product.ipynb) 4 | 5 | This tutorial shows how to efficiently use dot product in Nada using Nada Numpy. 6 | 7 | ## 🚨 Limitations 8 | The choice for blind computing implies certain trade-offs in comparison to conventional computing. What you gain in privacy, you pay in extra computational overhead & capacity constraints. 9 | 10 | Therefore, you will notice that large-scale computational workloads may lead to long compilation and/or execution times or hitting network capacity guardrails. 11 | 12 | That said, the Nillion team is working around the clock to push the boundaries of this technology and bring the potential of blind computing to reality 🚀 13 | 14 | 👉 This example has been tested on local devnet with arrays of up to 2500 elements. 15 | 16 | ## ➡️ Stay in touch 17 | If you want to get involved in the blind computing community and be the first to know all big updates, join our Discord 18 | 19 | [![Discord](https://img.shields.io/badge/Discord-nillionnetwork-%235865F2?logo=discord)](https://discord.gg/nillionnetwork) 20 | 21 | And if you want to contribute to the blind computing revolution, we welcome open-source contributors! 22 | 23 | [![GitHub Discussions](https://img.shields.io/badge/GitHub_Discussions-NillionNetwork-%23181717?logo=github)](https://github.com/orgs/NillionNetwork/discussions) 24 | -------------------------------------------------------------------------------- /examples/dot_product/config.py: -------------------------------------------------------------------------------- 1 | """Configuration variables""" 2 | 3 | DIM = 3 4 | -------------------------------------------------------------------------------- /examples/dot_product/main.py: -------------------------------------------------------------------------------- 1 | """Dot Product example script""" 2 | 3 | import asyncio 4 | import os 5 | 6 | import numpy as np 7 | import pytest 8 | from config import DIM 9 | from dotenv import load_dotenv 10 | from nillion_client import (InputPartyBinding, Network, NilChainPayer, 11 | NilChainPrivateKey, OutputPartyBinding, 12 | Permissions, PrivateKey, SecretInteger, VmClient) 13 | 14 | import nada_numpy.client as na_client 15 | 16 | home = os.getenv("HOME") 17 | load_dotenv(f"{home}/.config/nillion/nillion-devnet.env") 18 | 19 | 20 | # 1 Party running simple addition on 1 stored secret and 1 compute time secret 21 | async def main(): 22 | # Use the devnet configuration generated by `nillion-devnet` 23 | network = Network.from_config("devnet") 24 | 25 | # Create payments config and set up Nillion wallet with a private key to pay for operations 26 | nilchain_key: str = os.getenv("NILLION_NILCHAIN_PRIVATE_KEY_0") # type: ignore 27 | payer = NilChainPayer( 28 | network, 29 | wallet_private_key=NilChainPrivateKey(bytes.fromhex(nilchain_key)), 30 | gas_limit=10000000, 31 | ) 32 | 33 | # Use a random key to identify ourselves 34 | signing_key = PrivateKey() 35 | client = await VmClient.create(signing_key, network, payer) 36 | party_names = na_client.parties(3) 37 | program_name = "dot_product" 38 | program_mir_path = f"./target/{program_name}.nada.bin" 39 | 40 | ##### STORE PROGRAM 41 | print("-----STORE PROGRAM") 42 | 43 | # Store program 44 | program_mir = open( 45 | os.path.join(os.path.dirname(os.path.abspath(__file__)), program_mir_path), "rb" 46 | ).read() 47 | program_id = await client.store_program(program_name, program_mir).invoke() 48 | 49 | # Print details about stored program 50 | print(f"Stored program_id: {program_id}") 51 | 52 | ##### STORE SECRETS 53 | print("-----STORE SECRETS Party 0") 54 | 55 | # Create a secret 56 | A = na_client.array(np.ones([DIM]), "A", SecretInteger) 57 | 58 | # Create a permissions object to attach to the stored secret 59 | permissions = Permissions.defaults_for_user(client.user_id).allow_compute( 60 | client.user_id, program_id 61 | ) 62 | 63 | # Store a secret, passing in the receipt that shows proof of payment 64 | values_A = await client.store_values( 65 | A, ttl_days=5, permissions=permissions 66 | ).invoke() 67 | 68 | print("Stored values_A: ", values_A) 69 | 70 | print("-----STORE SECRETS Party 1") 71 | 72 | # Create a secret 73 | B = na_client.array(np.ones([DIM]), "B", SecretInteger) 74 | 75 | # Create a permissions object to attach to the stored secret 76 | permissions = Permissions.defaults_for_user(client.user_id).allow_compute( 77 | client.user_id, program_id 78 | ) 79 | 80 | # Store a secret, passing in the receipt that shows proof of payment 81 | values_B = await client.store_values( 82 | B, ttl_days=5, permissions=permissions 83 | ).invoke() 84 | 85 | print("Stored values_B: ", values_B) 86 | 87 | ##### COMPUTE 88 | print("-----COMPUTE") 89 | 90 | # Bind the parties in the computation to the client to set input and output parties 91 | input_bindings = [ 92 | InputPartyBinding(party_names[0], client.user_id), 93 | InputPartyBinding(party_names[1], client.user_id), 94 | ] 95 | output_bindings = [OutputPartyBinding(party_names[2], [client.user_id])] 96 | 97 | # Create a computation time secret to use 98 | compute_time_values = { 99 | # "my_int2": SecretInteger(10) 100 | } 101 | 102 | # Compute, passing in the compute time values as well as the previously uploaded value. 103 | print( 104 | f"Invoking computation using program {program_id} and values id {values_A}, {values_B}" 105 | ) 106 | compute_id = await client.compute( 107 | program_id, 108 | input_bindings, 109 | output_bindings, 110 | values=compute_time_values, 111 | value_ids=[values_A, values_B], 112 | ).invoke() 113 | 114 | # Print compute result 115 | print(f"The computation was sent to the network. compute_id: {compute_id}") 116 | result = await client.retrieve_compute_results(compute_id).invoke() 117 | print(f"✅ Compute complete for compute_id {compute_id}") 118 | print(f"🖥️ The result is {result}") 119 | return result 120 | 121 | 122 | if __name__ == "__main__": 123 | asyncio.run(main()) 124 | -------------------------------------------------------------------------------- /examples/dot_product/nada-project.toml: -------------------------------------------------------------------------------- 1 | name = "dot_product" 2 | version = "0.1.0" 3 | authors = [""] 4 | 5 | [[programs]] 6 | path = "src/dot_product.py" 7 | prime_size = 128 8 | -------------------------------------------------------------------------------- /examples/dot_product/src/dot_product.py: -------------------------------------------------------------------------------- 1 | """Main Nada program""" 2 | 3 | from typing import List 4 | 5 | from config import DIM 6 | from nada_dsl import Output, SecretInteger 7 | 8 | # Step 0: Nada Numpy is imported with this line 9 | import nada_numpy as na 10 | 11 | 12 | def nada_main() -> List[Output]: 13 | """ 14 | Main dot product Nada program. 15 | 16 | Returns: 17 | List[Output]: List of program outputs. 18 | """ 19 | # Step 1: We use Nada Numpy wrapper to create "Party0", "Party1" and "Party2" 20 | parties = na.parties(3) 21 | 22 | # Step 2: Party0 creates an array of dimension (3, ) with name "A" 23 | a = na.array([DIM], parties[0], "A", SecretInteger) 24 | 25 | # Step 3: Party1 creates an array of dimension (3, ) with name "B" 26 | b = na.array([DIM], parties[1], "B", SecretInteger) 27 | 28 | # Step 4: The result is of computing the dot product between the two 29 | result = a.dot(b) 30 | 31 | # Step 5: We can use result.output() to produce 32 | # the output for Party2 and variable name "my_output" 33 | return na.output(result, parties[2], "my_output") 34 | -------------------------------------------------------------------------------- /examples/dot_product/target/.gitignore: -------------------------------------------------------------------------------- 1 | # This directory is kept purposely, so that no compilation errors arise. 2 | # Ignore everything in this directory 3 | * 4 | # Except this file 5 | !.gitignore -------------------------------------------------------------------------------- /examples/dot_product/tests/dot_product.yaml: -------------------------------------------------------------------------------- 1 | program: dot_product 2 | inputs: 3 | A_0: 3 4 | A_1: 3 5 | A_2: 3 6 | B_0: 3 7 | B_1: 3 8 | B_2: 3 9 | expected_outputs: 10 | my_output: 27 11 | -------------------------------------------------------------------------------- /examples/linear_regression/README.md: -------------------------------------------------------------------------------- 1 | # Linear Regression Closed-Form Solution Tutorial 2 | 3 | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/linear_regression/linear_regression.ipynb) 4 | 5 | This tutorial shows how to train a linear regression privately based on the closed-form solution to linear regression. 6 | 7 | This is an implementation on the [paper by Blom et al.](https://eprint.iacr.org/2019/773.pdf) 8 | 9 | ## 🚨 Limitations 10 | The choice for blind computing implies certain trade-offs in comparison to conventional computing. What you gain in privacy, you pay in extra computational overhead & capacity constraints. 11 | 12 | Therefore, you will notice that large-scale computational workloads may lead to long compilation and/or execution times or hitting network capacity guardrails. 13 | 14 | That said, the Nillion team is working around the clock to push the boundaries of this technology and bring the potential of blind computing to reality 🚀 15 | 16 | 👉 This example has been tested on local devnet with the current given dimension of 3 x 3. 17 | 18 | ## ➡️ Stay in touch 19 | If you want to get involved in the blind computing community and be the first to know all big updates, join our Discord 20 | 21 | [![Discord](https://img.shields.io/badge/Discord-nillionnetwork-%235865F2?logo=discord)](https://discord.gg/nillionnetwork) 22 | 23 | And if you want to contribute to the blind computing revolution, we welcome open-source contributors! 24 | 25 | [![GitHub Discussions](https://img.shields.io/badge/GitHub_Discussions-NillionNetwork-%23181717?logo=github)](https://github.com/orgs/NillionNetwork/discussions) 26 | -------------------------------------------------------------------------------- /examples/linear_regression/cleartext.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 13, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from sklearn.linear_model import Ridge\n", 10 | "import numpy as np\n", 11 | "\n", 12 | "LAMBDA = 1\n", 13 | "LOG_SCALE = 8\n", 14 | "SCALE = 1 << LOG_SCALE\n", 15 | "PRIME_64 = 18446744072637906947\n", 16 | "PRIME_128 = 340282366920938463463374607429104828419\n", 17 | "PRIME_256 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98C00003\n", 18 | "PRIME = PRIME_256" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 16, 24 | "metadata": {}, 25 | "outputs": [ 26 | { 27 | "name": "stdout", 28 | "output_type": "stream", 29 | "text": [ 30 | "1.0 10.0\n" 31 | ] 32 | }, 33 | { 34 | "data": { 35 | "text/plain": [ 36 | "(array([[-1. , -0.77777778, -0.55555556],\n", 37 | " [-0.33333333, -0.11111111, 0.11111111],\n", 38 | " [ 0.33333333, 0.55555556, 1. ]]),\n", 39 | " array([-1. , -0.77777778, -0.55555556]))" 40 | ] 41 | }, 42 | "execution_count": 16, 43 | "metadata": {}, 44 | "output_type": "execute_result" 45 | } 46 | ], 47 | "source": [ 48 | "n_samples, n_features = 3, 3\n", 49 | "rng = np.random.RandomState(0)\n", 50 | "y = np.array([1.0, 2.0, 3.0])\n", 51 | "X = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 10.0]])\n", 52 | "# X = np.random.rand(n_samples, n_features)\n", 53 | "# y = np.random.rand(n_samples)\n", 54 | "dmin = np.min([np.min(X), np.min(y)])\n", 55 | "dmax = np.max([np.max(X), np.max(y)])\n", 56 | "print(dmin, dmax)\n", 57 | "minmax = lambda x: ((x - dmin) / (dmax - dmin)) * 2 - 1\n", 58 | "y = minmax(y)\n", 59 | "X = minmax(X)\n", 60 | "X, y" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 17, 66 | "metadata": {}, 67 | "outputs": [ 68 | { 69 | "name": "stdout", 70 | "output_type": "stream", 71 | "text": [ 72 | " A_0_0:\n", 73 | " SecretInteger: \"-256\"\n", 74 | " A_0_1:\n", 75 | " SecretInteger: \"-199\"\n", 76 | " A_0_2:\n", 77 | " SecretInteger: \"-142\"\n", 78 | " A_1_0:\n", 79 | " SecretInteger: \"-85\"\n", 80 | " A_1_1:\n", 81 | " SecretInteger: \"-28\"\n", 82 | " A_1_2:\n", 83 | " SecretInteger: \"28\"\n", 84 | " A_2_0:\n", 85 | " SecretInteger: \"85\"\n", 86 | " A_2_1:\n", 87 | " SecretInteger: \"142\"\n", 88 | " A_2_2:\n", 89 | " SecretInteger: \"256\"\n", 90 | " b_0:\n", 91 | " SecretInteger: \"-256\"\n", 92 | " b_1:\n", 93 | " SecretInteger: \"-199\"\n", 94 | " b_2:\n", 95 | " SecretInteger: \"-142\"\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "def array_to_str(arr, prefix):\n", 101 | " if len(arr.shape) == 1:\n", 102 | " return \"\\n\".join([f\" {prefix}_{i}:\\n SecretInteger: \\\"{round(x * SCALE)}\\\"\" for i, x in enumerate(arr)]) \n", 103 | " else:\n", 104 | " return \"\\n\".join([array_to_str(arr[i], f\"{prefix}_{i}\") for i in range(arr.shape[0])]) + \"\\n\"\n", 105 | "\n", 106 | "print(array_to_str(X, \"A\") + array_to_str(y, \"b\"))" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 18, 112 | "metadata": {}, 113 | "outputs": [ 114 | { 115 | "name": "stdout", 116 | "output_type": "stream", 117 | "text": [ 118 | "This is the input to the program:\n", 119 | "A: [[-256 -199 -142]\n", 120 | " [ -85 -28 28]\n", 121 | " [ 85 142 256]]\n", 122 | "b: [-256 -199 -142]\n", 123 | "lambda: 65536\n", 124 | "EXPECTED OUTPUT: [ 3.33333333e+00 -3.00000000e+00 -2.09082751e-13] 0.0\n", 125 | "For the output, we print the results:\n", 126 | "w: [-1.30064019 -0.91010335 -3.95961349]\n", 127 | "z: 7496337347136\n" 128 | ] 129 | } 130 | ], 131 | "source": [ 132 | "# LINEAR REGRESSION:\n", 133 | "clf = Ridge(alpha=0, fit_intercept=False)\n", 134 | "clf.fit(X, y)\n", 135 | "# print(X, y)\n", 136 | "print(\"This is the input to the program:\")\n", 137 | "print(\"A:\", (X * SCALE).astype(np.int64))\n", 138 | "print(\"b:\", (y * SCALE).astype(np.int64))\n", 139 | "print(\"lambda: \", LAMBDA * SCALE**2)\n", 140 | "print(\"EXPECTED OUTPUT: \", clf.coef_, clf.intercept_)\n", 141 | "\n", 142 | "print(\"For the output, we print the results:\")\n", 143 | "w_0 = -9750037619520\n", 144 | "w_1 = -6822441714528\n", 145 | "w_2 = -29682598492800\n", 146 | "b_0 = 7496337347136\n", 147 | "\n", 148 | "w = np.array([w_0, w_1, w_2]) / b_0\n", 149 | "print(\"w:\", w)\n", 150 | "print(\"z:\", b_0)" 151 | ] 152 | } 153 | ], 154 | "metadata": { 155 | "kernelspec": { 156 | "display_name": "Python 3", 157 | "language": "python", 158 | "name": "python3" 159 | }, 160 | "language_info": { 161 | "codemirror_mode": { 162 | "name": "ipython", 163 | "version": 3 164 | }, 165 | "file_extension": ".py", 166 | "mimetype": "text/x-python", 167 | "name": "python", 168 | "nbconvert_exporter": "python", 169 | "pygments_lexer": "ipython3", 170 | "version": "3.12.3" 171 | } 172 | }, 173 | "nbformat": 4, 174 | "nbformat_minor": 2 175 | } 176 | -------------------------------------------------------------------------------- /examples/linear_regression/main.py: -------------------------------------------------------------------------------- 1 | """Linear Regression example script""" 2 | 3 | import asyncio 4 | import os 5 | 6 | import numpy as np 7 | import pytest 8 | from dotenv import load_dotenv 9 | from nillion_client import (InputPartyBinding, Network, NilChainPayer, 10 | NilChainPrivateKey, OutputPartyBinding, 11 | Permissions, PrivateKey, SecretInteger, VmClient) 12 | from sklearn.linear_model import Ridge 13 | 14 | import nada_numpy.client as na_client 15 | 16 | home = os.getenv("HOME") 17 | load_dotenv(f"{home}/.config/nillion/nillion-devnet.env") 18 | 19 | 20 | # 1 Party running simple addition on 1 stored secret and 1 compute time secret 21 | async def main(): 22 | # Use the devnet configuration generated by `nillion-devnet` 23 | network = Network.from_config("devnet") 24 | 25 | # Create payments config and set up Nillion wallet with a private key to pay for operations 26 | nilchain_key: str = os.getenv("NILLION_NILCHAIN_PRIVATE_KEY_0") # type: ignore 27 | payer = NilChainPayer( 28 | network, 29 | wallet_private_key=NilChainPrivateKey(bytes.fromhex(nilchain_key)), 30 | gas_limit=10000000, 31 | ) 32 | 33 | # Use a random key to identify ourselves 34 | signing_key = PrivateKey() 35 | client = await VmClient.create(signing_key, network, payer) 36 | party_names = na_client.parties(3) 37 | program_name = "linear_regression_256" 38 | program_mir_path = f"target/{program_name}.nada.bin" 39 | 40 | ##### STORE PROGRAM 41 | print("-----STORE PROGRAM") 42 | 43 | # Store program 44 | program_mir = open( 45 | os.path.join(os.path.dirname(os.path.abspath(__file__)), program_mir_path), "rb" 46 | ).read() 47 | program_id = await client.store_program(program_name, program_mir).invoke() 48 | 49 | # Print details about stored program 50 | print(f"Stored program_id: {program_id}") 51 | 52 | ##### STORE SECRETS 53 | print("-----STORE SECRETS Party 0") 54 | 55 | # Create a secret 56 | A = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 10.0]]) 57 | b = np.array([1.0, 2.0, 3.0]) 58 | 59 | # Minmax normalization of data 60 | dmin = np.min([np.min(A), np.min(b)]) 61 | dmax = np.max([np.max(A), np.max(b)]) 62 | minmax = lambda x: ((x - dmin) / (dmax - dmin)) * 2 - 1 63 | LOG_SCALE = 8 64 | SCALE = 1 << LOG_SCALE 65 | # Minmax normalization of data 66 | A = minmax(A) 67 | b = minmax(b) 68 | 69 | clf = Ridge(alpha=0, fit_intercept=False) 70 | clf.fit(A, b) 71 | 72 | # Scale data into (-SCALE, SCALE) range 73 | A = np.round(A * SCALE).astype(np.int64) 74 | b = np.round(b * SCALE).astype(np.int64) 75 | 76 | A = na_client.array(A, "A", SecretInteger) 77 | b = na_client.array(b, "b", SecretInteger) 78 | # Create a permissions object to attach to the stored secret 79 | permissions = Permissions.defaults_for_user(client.user_id).allow_compute( 80 | client.user_id, program_id 81 | ) 82 | 83 | # Store a secret, passing in the receipt that shows proof of payment 84 | values_A = await client.store_values( 85 | A, ttl_days=5, permissions=permissions 86 | ).invoke() 87 | 88 | print("Stored values_A: ", values_A) 89 | 90 | # Store a secret, passing in the receipt that shows proof of payment 91 | values_B = await client.store_values( 92 | b, ttl_days=5, permissions=permissions 93 | ).invoke() 94 | 95 | print("Stored values_B: ", values_B) 96 | 97 | ##### COMPUTE 98 | print("-----COMPUTE") 99 | 100 | # Bind the parties in the computation to the client to set input and output parties 101 | input_bindings = [ 102 | InputPartyBinding(party_names[0], client.user_id), 103 | InputPartyBinding(party_names[1], client.user_id), 104 | ] 105 | output_bindings = [OutputPartyBinding(party_names[2], [client.user_id])] 106 | 107 | # Create a computation time secret to use 108 | compute_time_values = { 109 | # "my_int2": SecretInteger(10) 110 | } 111 | 112 | # Compute, passing in the compute time values as well as the previously uploaded value. 113 | print( 114 | f"Invoking computation using program {program_id} and values id {values_A}, {values_B}" 115 | ) 116 | compute_id = await client.compute( 117 | program_id, 118 | input_bindings, 119 | output_bindings, 120 | values=compute_time_values, 121 | value_ids=[values_A, values_B], 122 | ).invoke() 123 | 124 | # Print compute result 125 | print(f"The computation was sent to the network. compute_id: {compute_id}") 126 | result = await client.retrieve_compute_results(compute_id).invoke() 127 | 128 | w_0 = result["w_0"].value 129 | w_1 = result["w_1"].value 130 | w_2 = result["w_2"].value 131 | b = result["b"].value 132 | 133 | coeff = np.array([w_0, w_1, w_2]) / b 134 | 135 | print("--------------------") 136 | print("🔍 Expected Coefficients: ", clf.coef_) 137 | print("✅ Coefficients: ", coeff) 138 | return coeff 139 | 140 | 141 | if __name__ == "__main__": 142 | asyncio.run(main()) 143 | -------------------------------------------------------------------------------- /examples/linear_regression/nada-project.toml: -------------------------------------------------------------------------------- 1 | name = "linear-regression" 2 | version = "0.1.0" 3 | authors = [""] 4 | 5 | [test_framework.nada-test] 6 | command = "nada-test ./tests" 7 | 8 | [[programs]] 9 | path = "src/linear_regression.py" 10 | prime_size = 64 11 | 12 | [[programs]] 13 | path = "src/linear_regression_256.py" 14 | prime_size = 256 15 | 16 | 17 | [[programs]] 18 | path = "src/modular_inverse.py" 19 | prime_size = 64 20 | 21 | [[programs]] 22 | path = "src/determinant.py" 23 | prime_size = 64 24 | 25 | 26 | [[programs]] 27 | path = "src/gauss_jordan.py" 28 | prime_size = 64 29 | 30 | 31 | [[programs]] 32 | path = "src/matrix_inverse.py" 33 | prime_size = 64 -------------------------------------------------------------------------------- /examples/linear_regression/src/determinant.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | from nada_numpy.array import NadaArray 6 | 7 | 8 | def get_minor(matrix, i, j): 9 | """Return the Minor matrix after removing the i-th row and j-th column""" 10 | row_removed = matrix[:i].vstack(matrix[i + 1 :]) 11 | return row_removed[:, :j].hstack(row_removed[:, j + 1 :]) 12 | 13 | 14 | def determinant(A: NadaArray): 15 | """ 16 | Recursively calculate the determinant of a matrix 17 | 18 | Parameters: 19 | - `A` (numpy.ndarray): The input matrix to calculate the determinant of. 20 | - `modulo` (int): The modulo representing the field `Z_n` 21 | 22 | Returns: 23 | int: The determinant of the input matrix. 24 | """ 25 | if A.shape[0] != A.shape[1]: 26 | raise ValueError("Matrix must be square") 27 | 28 | # Base case for 2x2 matrix 29 | if A.shape == (2, 2): 30 | return (A[0][0] * A[1][1]) - (A[0][1] * A[1][0]) 31 | det = Integer(0) 32 | for c in range(A.shape[0]): 33 | det += Integer((-1) ** c) * A[0][c] * determinant(get_minor(A, 0, c)) 34 | return det 35 | 36 | 37 | def nada_main(): 38 | parties = na.parties(3) 39 | 40 | X = na.array([3, 3], parties[0], "A", SecretInteger) 41 | X = X.to_public() 42 | detX = determinant(X) 43 | 44 | return na.output(detX, parties[2], "my_output") 45 | -------------------------------------------------------------------------------- /examples/linear_regression/src/gauss_jordan.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from modular_inverse import PRIME, public_modular_inverse 3 | from nada_dsl import * 4 | 5 | import nada_numpy as na 6 | 7 | # from nada_crypto import random_lu_matrix, public_modular_inverse 8 | 9 | 10 | def gauss_jordan_zn(mat: na.NadaArray, modulo: int): 11 | """ 12 | Perform Gauss-Jordan elimination on Z_n on a given matrix. 13 | 14 | Parameters: 15 | - `matrix` (numpy.ndarray): The input matrix to perform Gauss-Jordan elimination on. 16 | - `modulo` (int): The modulo representing the field `Z_n` 17 | 18 | Returns: 19 | numpy.ndarray: The reduced row echelon form of the input matrix. 20 | """ 21 | 22 | # Make a copy of the matrix to avoid modifying the original 23 | rows = mat.inner.shape[0] 24 | cols = mat.inner.shape[1] 25 | 26 | # Forward elimination 27 | for i in range(rows): 28 | 29 | # Scale pivot row to have leading 1 30 | diagonal_element = mat[i][i] 31 | pivot_inv = public_modular_inverse(diagonal_element, modulo) 32 | 33 | mat[i] = mat[i] * pivot_inv 34 | 35 | # Perform row operations to eliminate entries below pivot 36 | for j in range(i + 1, rows): 37 | factor = mat[j][i] 38 | mat[j] -= mat[i] * factor 39 | 40 | # Backward elimination 41 | for i in range(rows - 1, -1, -1): 42 | for j in range(i - 1, -1, -1): 43 | factor = mat[j][i] 44 | mat[j] -= mat[i] * factor 45 | 46 | return mat 47 | 48 | 49 | def nada_main(): 50 | parties = na.parties(3) 51 | 52 | A = na.array([3, 3], parties[0], "A", nada_type=SecretInteger) 53 | 54 | A = A.to_public() 55 | A_inv = gauss_jordan_zn(A, PRIME) 56 | outputs = na.output(A_inv, parties[2], "my_output") 57 | 58 | return outputs 59 | -------------------------------------------------------------------------------- /examples/linear_regression/src/linear_regression.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from determinant import determinant 3 | from gauss_jordan import gauss_jordan_zn 4 | from modular_inverse import (PRIME, private_modular_inverse, 5 | public_modular_inverse) 6 | from nada_dsl import * 7 | 8 | import nada_numpy as na 9 | from nada_numpy.array import NadaArray 10 | 11 | 12 | def create_random_upper_triangular_matrix(n: int) -> NadaArray: 13 | """ 14 | Create a random upper triangular matrix with the specified dimensions. 15 | 16 | Args: 17 | n (int): The size of the matrix. 18 | party (Party): The party object representing the current party. 19 | prefix (str): A prefix string to be used for generating random values. 20 | 21 | Returns: 22 | np.ndarray: A NumPy array representing a random upper triangular matrix. 23 | 24 | """ 25 | # return np.triu(create_random_array([n, n], party, prefix)) 26 | return NadaArray( 27 | np.array( 28 | [ 29 | [SecretInteger.random() if i <= j else Integer(0) for j in range(n)] 30 | for i in range(n) 31 | ] 32 | ) 33 | ) 34 | 35 | 36 | def create_random_lower_triangular_matrix(n: int) -> NadaArray: 37 | """ 38 | Create a random lower triangular matrix with the specified dimensions. 39 | 40 | Args: 41 | n (int): The size of the matrix. 42 | party (Party): The party object representing the current party. 43 | prefix (str): A prefix string to be used for generating random values. 44 | 45 | Returns: 46 | np.ndarray: A NumPy array representing a random lower triangular matrix. 47 | 48 | """ 49 | # return np.tril(create_random_array([n, n], party, prefix)) 50 | return NadaArray( 51 | np.array( 52 | [ 53 | [ 54 | ( 55 | SecretInteger.random() 56 | if i > j 57 | else Integer(1) if i == j else Integer(0) 58 | ) 59 | for j in range(n) 60 | ] 61 | for i in range(n) 62 | ] 63 | ) 64 | ) 65 | 66 | 67 | def random_lu_matrix(n: int) -> NadaArray: 68 | """ 69 | Generates a random LU matrix of size n x n. 70 | 71 | Parameters: 72 | - `n` (int): The size of the matrix. 73 | 74 | Returns: 75 | - `tuple`: A tuple containing the LU matrix and the determinant of the upper triangular matrix. 76 | """ 77 | upper = create_random_upper_triangular_matrix(n) 78 | lower = create_random_lower_triangular_matrix(n) 79 | detU = upper.inner.diagonal().prod() 80 | return lower @ upper, private_modular_inverse(detU, PRIME) 81 | 82 | 83 | def linsol(A: NadaArray, b: NadaArray, modulo: int): 84 | 85 | if A.shape[0] != A.shape[1]: 86 | raise Exception("Invalid input shape: Expected equal squared matrix") 87 | n = A.shape[0] # (n,n) matrix 88 | 89 | R, detR_inv = random_lu_matrix( 90 | n 91 | ) # (n, n) random matrix R with inverse determinant detR_inv 92 | 93 | # Revealing matrix RA 94 | RA = (R @ A).to_public() # (n, n) revealed matrix 95 | 96 | # Computing Rb as a secret matrix multiplication of R and b 97 | Rb = R @ b # (n, n) @ (n,) = (n,) 98 | 99 | # Concatenating RA and Rb 100 | RAR = RA.hstack(Rb.reshape(n, 1)) 101 | 102 | # Performing Gauss-Jordan elimination 103 | A_invb = gauss_jordan_zn(RAR, modulo) 104 | A_invb = A_invb[:, n:].reshape( 105 | -1, 106 | ) # (n, 1) -> (n,) # After elimination, the right half of the matrix is the inverse 107 | detRA = determinant(RA) # Determinant of RA 108 | detA = detRA * detR_inv 109 | # raise Exception(type(A_invb), type(detA)) 110 | adjAb = A_invb * detA 111 | return adjAb, detA 112 | 113 | 114 | def linear_regression_zn( 115 | X: na.NadaArray, y: na.NadaArray, modulo: int, lambda_: PublicInteger 116 | ): 117 | """ 118 | Calculate the linear regression of a dataset using Z_n field. 119 | 120 | Parameters: 121 | - `X` (na.NadaArray): The input features having shape (n, d). 122 | - `y` (na.NadaArray): The target values having shape (n,). 123 | - `modulo` (int): The modulo representing the field `Z_n`. 124 | 125 | """ 126 | n, d = X.shape 127 | identity = na.eye(d, nada_type=Integer) 128 | A = X.T @ X + identity * lambda_ # (n,d) @ (d,n) = (d,d) 129 | # A = SCALE * SCALE + SCALE 130 | b = X.T @ y # (d,n) @ (n,) = (d,) 131 | adjAb, detA = linsol(A, b, modulo) 132 | detAw = adjAb 133 | return detAw, detA 134 | 135 | 136 | def nada_main(): 137 | parties = na.parties(3) 138 | 139 | DIM = 3 140 | NUM_FEATURES = 3 141 | X = na.array([DIM, NUM_FEATURES], parties[0], "A", nada_type=SecretInteger) 142 | y = na.array([NUM_FEATURES], parties[1], "b", nada_type=SecretInteger) 143 | # lambda_ = na.array([1], parties[0], "lambda", nada_type=PublicInteger) 144 | lambda_ = Integer(0) 145 | (w, b) = linear_regression_zn(X, y, PRIME, lambda_) 146 | 147 | return w.output(parties[2], "w") + na.output(b, parties[2], "b") 148 | -------------------------------------------------------------------------------- /examples/linear_regression/src/modular_inverse.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | LOG_SCALE = 16 7 | SCALE = 1 << LOG_SCALE 8 | PRIME_64 = 18446744072637906947 9 | PRIME_128 = 340282366920938463463374607429104828419 10 | PRIME_256 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98C00003 11 | PRIME = PRIME_64 12 | 13 | 14 | def public_modular_inverse( 15 | value: PublicInteger | Integer, modulo: int 16 | ) -> PublicInteger | Integer: 17 | """ 18 | Calculates the modular inverse of a public value with respect to a prime modulus. 19 | 20 | Args: 21 | `value`: The value for which the modular inverse is to be calculated. 22 | `modulo`: The prime modulo with respect to which the modular inverse is to be calculated. 23 | 24 | Returns: 25 | The modular inverse of the value with respect to the modulo. 26 | 27 | Raises: 28 | Exception: If the input type is not a `PublicInteger` or `Integer`. 29 | """ 30 | # We cannot do `value ** Integer(modulo - 2)` because the value of modulo overflows the limit of an Integer 31 | # We do instead: value ** (modulo - 2) == value ** ((modulo // 2) - 1) * value ** ((modulo // 2) - 1) 32 | # We multiply once more (value) # if modulo is odd 33 | mod, rem = ( 34 | modulo // 2, 35 | modulo % 2, 36 | ) # Unless it is prime 2, it is going to be odd, but we check in any case 37 | power = value ** Integer( 38 | mod - 1 39 | ) # value ** modulo = value ** (modulo // 2) * modulo ** (modulo // 2) 40 | power = power * power * (value if rem else Integer(1)) # value ** mo 41 | return power 42 | 43 | 44 | def private_modular_inverse(secret: SecretInteger, modulo: int) -> SecretInteger: 45 | """ 46 | Calculate the modular inverse of a secret value with respect to a prime modulo. 47 | 48 | Args: 49 | secret (SecretInteger): The secret value for which the modular inverse is to be calculated. 50 | modulo (int): The prime modulo with respect to which the modular inverse is to be calculated. 51 | 52 | Returns: 53 | SecretInteger: The modular inverse of the secret value with respect to the modulo. 54 | """ 55 | r = SecretInteger.random() 56 | 57 | ra = r * secret # Masking our secret 58 | ra_revealed = ra.to_public() # Revealing the masked secret 59 | 60 | ra_inv = public_modular_inverse( 61 | ra_revealed, modulo 62 | ) # Compute the inverse of the masked secret 63 | 64 | a_inv = ra_inv * r # Unmask the secret with the random shares 65 | 66 | return a_inv 67 | 68 | 69 | def nada_main(): 70 | parties = na.parties(3) 71 | 72 | a = na.array([1], parties[0], "A", nada_type=SecretInteger) 73 | a_inv = private_modular_inverse(a[0], PRIME) 74 | 75 | result = a_inv * a[0] 76 | # A_inv = na.random([3, 3], nada_type=SecretInteger) 77 | # A_inv = [[SecretInteger.random() for i in range(3)] for j in range(3)] 78 | # outputs = na.output(A_inv, parties[2], "my_output") 79 | 80 | return na.output(result, parties[2], "my_output") 81 | -------------------------------------------------------------------------------- /examples/linear_regression/target/.gitignore: -------------------------------------------------------------------------------- 1 | # This directory is kept purposely, so that no compilation errors arise. 2 | # Ignore everything in this directory 3 | * 4 | # Except this file 5 | !.gitignore -------------------------------------------------------------------------------- /examples/linear_regression/tests/determinant_1.yaml: -------------------------------------------------------------------------------- 1 | program: determinant 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 2 5 | A_0_2: 3 6 | A_1_0: 4 7 | A_1_1: 5 8 | A_1_2: 6 9 | A_2_0: 7 10 | A_2_1: 8 11 | A_2_2: 10 12 | expected_outputs: 13 | my_output: -3 14 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/determinant_2.yaml: -------------------------------------------------------------------------------- 1 | program: determinant 2 | inputs: 3 | A_0_1: 3 4 | A_2_2: 3 5 | A_1_0: 3 6 | A_1_2: 3 7 | A_2_0: 3 8 | A_0_0: 3 9 | A_1_1: 3 10 | A_2_1: 3 11 | A_0_2: 3 12 | expected_outputs: 13 | my_output: 0 14 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/determinant_3.yaml: -------------------------------------------------------------------------------- 1 | program: determinant 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 0 5 | A_0_2: 0 6 | A_1_0: 0 7 | A_1_1: 2 8 | A_1_2: 0 9 | A_2_0: 0 10 | A_2_1: 0 11 | A_2_2: 3 12 | expected_outputs: 13 | my_output: 6 14 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/gauss_jordan.yaml: -------------------------------------------------------------------------------- 1 | program: gauss_jordan 2 | inputs: 3 | A_0_0: 2 4 | A_0_1: 4 5 | A_0_2: 6 6 | A_1_0: 1 7 | A_1_1: 3 8 | A_1_2: 5 9 | A_2_0: 3 10 | A_2_1: 1 11 | A_2_2: 2 12 | B: 456 13 | expected_outputs: 14 | my_output_0_0: 1 15 | my_output_0_1: 0 16 | my_output_0_2: 0 17 | my_output_1_0: 0 18 | my_output_1_1: 1 19 | my_output_1_2: 0 20 | my_output_2_0: 0 21 | my_output_2_1: 0 22 | my_output_2_2: 1 23 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/linear_regression.yaml: -------------------------------------------------------------------------------- 1 | program: linear_regression 2 | inputs: 3 | A_0_0: -256 4 | A_0_1: -199 5 | A_0_2: -142 6 | A_1_0: -85 7 | A_1_1: -28 8 | A_1_2: 28 9 | A_2_0: 85 10 | A_2_1: 142 11 | A_2_2: 256 12 | b_0: -256 13 | b_1: -199 14 | b_2: -142 15 | expected_outputs: 16 | b: 330643400256 17 | w_0: 1102041164640 18 | w_1: -993683999568 19 | w_2: 1868226984 20 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/linear_regression_1.yaml: -------------------------------------------------------------------------------- 1 | program: linear_regression 2 | inputs: 3 | A_0_0: -256 4 | A_0_1: -199 5 | A_0_2: -142 6 | A_1_0: -85 7 | A_1_1: -28 8 | A_1_2: 28 9 | A_2_0: 85 10 | A_2_1: 142 11 | A_2_2: 256 12 | b_0: -256 13 | b_1: -199 14 | b_2: -142 15 | expected_outputs: 16 | b: 330643400256 17 | w_0: 1102041164640 18 | w_1: -993683999568 19 | w_2: 1868226984 20 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/linear_regression_2.yaml: -------------------------------------------------------------------------------- 1 | program: linear_regression 2 | inputs: 3 | A_0_0: 116 4 | A_0_1: 206 5 | A_0_2: -115 6 | A_1_0: -256 7 | A_1_1: -168 8 | A_1_2: 152 9 | A_2_0: 3 10 | A_2_1: -142 11 | A_2_2: -33 12 | b_0: 117 13 | b_1: -116 14 | b_2: 256 15 | expected_outputs: 16 | b: 7496337347136 17 | w_0: -9750037619520 18 | w_1: -6822441714528 19 | w_2: -29682598492800 20 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/linear_regression_256_1.yaml: -------------------------------------------------------------------------------- 1 | program: linear_regression_256 2 | inputs: 3 | A_0_0: -256 4 | A_0_1: -199 5 | A_0_2: -142 6 | A_1_0: -85 7 | A_1_1: -28 8 | A_1_2: 28 9 | A_2_0: 85 10 | A_2_1: 142 11 | A_2_2: 256 12 | b_0: -256 13 | b_1: -199 14 | b_2: -142 15 | expected_outputs: 16 | b: 330643400256 17 | w_0: 1102041164640 18 | w_1: -993683999568 19 | w_2: 1868226984 20 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/linear_regression_256_2.yaml: -------------------------------------------------------------------------------- 1 | program: linear_regression_256 2 | inputs: 3 | A_0_0: 116 4 | A_0_1: 206 5 | A_0_2: -115 6 | A_1_0: -256 7 | A_1_1: -168 8 | A_1_2: 152 9 | A_2_0: 3 10 | A_2_1: -142 11 | A_2_2: -33 12 | b_0: 117 13 | b_1: -116 14 | b_2: 256 15 | expected_outputs: 16 | b: 7496337347136 17 | w_0: -9750037619520 18 | w_1: -6822441714528 19 | w_2: -29682598492800 20 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/matrix_inverse.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import numpy as np 4 | from nada_test import NadaTest, nada_test 5 | 6 | import nada_numpy.client as na 7 | 8 | 9 | # Functional style test 10 | @nada_test(program="matrix_inverse") 11 | def my_test(): 12 | n = 10 13 | m = np.random.rand(n, n) 14 | mx = np.sum(np.abs(m), axis=1) 15 | np.fill_diagonal(m, mx) 16 | A = na.array(m * (1 << 16), "A", nada_type=int) 17 | print("INPUTS:", A, file=sys.stderr) 18 | outputs = yield A 19 | print(outputs, file=sys.stderr) 20 | for output, value in outputs.items(): 21 | output = output.split("_") 22 | if output[-1] == output[-2]: 23 | assert value == 1, f"Expected 1 {output}, got {value}" 24 | else: 25 | assert value == 0, f"Expected 0 {output}, got {value}" 26 | 27 | # assert outputs["my_output"] == a + b 28 | -------------------------------------------------------------------------------- /examples/linear_regression/tests/modular_inverse.yaml: -------------------------------------------------------------------------------- 1 | program: modular_inverse 2 | inputs: 3 | A_0: 982554 4 | expected_outputs: 5 | my_output: 1 6 | -------------------------------------------------------------------------------- /examples/matrix_multiplication/README.md: -------------------------------------------------------------------------------- 1 | # Matrix Multiplication Tutorial 2 | 3 | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/matrix_multiplication/matrix_multiplication.ipynb) 4 | 5 | This tutorial shows how to efficiently use matrix multiplication in Nada using Nada Numpy. 6 | 7 | ## 🚨 Limitations 8 | The choice for blind computing implies certain trade-offs in comparison to conventional computing. What you gain in privacy, you pay in extra computational overhead & capacity constraints. 9 | 10 | Therefore, you will notice that large-scale computational workloads may lead to long compilation and/or execution times or hitting network capacity guardrails. 11 | 12 | That said, the Nillion team is working around the clock to push the boundaries of this technology and bring the potential of blind computing to reality 🚀 13 | 14 | 👉 This example has been tested on local devnet with arrays with dimensions up to (15, 15). 15 | 16 | ## ➡️ Stay in touch 17 | If you want to get involved in the blind computing community and be the first to know all big updates, join our Discord 18 | 19 | [![Discord](https://img.shields.io/badge/Discord-nillionnetwork-%235865F2?logo=discord)](https://discord.gg/nillionnetwork) 20 | 21 | And if you want to contribute to the blind computing revolution, we welcome open-source contributors! 22 | 23 | [![GitHub Discussions](https://img.shields.io/badge/GitHub_Discussions-NillionNetwork-%23181717?logo=github)](https://github.com/orgs/NillionNetwork/discussions) 24 | -------------------------------------------------------------------------------- /examples/matrix_multiplication/config.py: -------------------------------------------------------------------------------- 1 | """Configuration variables""" 2 | 3 | DIM = (3, 3) 4 | -------------------------------------------------------------------------------- /examples/matrix_multiplication/main.py: -------------------------------------------------------------------------------- 1 | """Matrix Multiplication example script""" 2 | 3 | import asyncio 4 | import os 5 | 6 | import numpy as np 7 | import pytest 8 | from config import DIM 9 | from dotenv import load_dotenv 10 | from nillion_client import (InputPartyBinding, Network, NilChainPayer, 11 | NilChainPrivateKey, OutputPartyBinding, 12 | Permissions, PrivateKey, SecretInteger, VmClient) 13 | 14 | import nada_numpy.client as na_client 15 | 16 | home = os.getenv("HOME") 17 | load_dotenv(f"{home}/.config/nillion/nillion-devnet.env") 18 | 19 | 20 | # 1 Party running simple addition on 1 stored secret and 1 compute time secret 21 | async def main(): 22 | # Use the devnet configuration generated by `nillion-devnet` 23 | network = Network.from_config("devnet") 24 | 25 | # Create payments config and set up Nillion wallet with a private key to pay for operations 26 | nilchain_key: str = os.getenv("NILLION_NILCHAIN_PRIVATE_KEY_0") # type: ignore 27 | payer = NilChainPayer( 28 | network, 29 | wallet_private_key=NilChainPrivateKey(bytes.fromhex(nilchain_key)), 30 | gas_limit=10000000, 31 | ) 32 | 33 | # Use a random key to identify ourselves 34 | signing_key = PrivateKey() 35 | client = await VmClient.create(signing_key, network, payer) 36 | party_names = na_client.parties(3) 37 | program_name = "matrix_multiplication" 38 | program_mir_path = f"./target/{program_name}.nada.bin" 39 | 40 | ##### STORE PROGRAM 41 | print("-----STORE PROGRAM") 42 | 43 | # Store program 44 | program_mir = open( 45 | os.path.join(os.path.dirname(os.path.abspath(__file__)), program_mir_path), "rb" 46 | ).read() 47 | program_id = await client.store_program(program_name, program_mir).invoke() 48 | 49 | # Print details about stored program 50 | print(f"Stored program_id: {program_id}") 51 | 52 | ##### STORE SECRETS 53 | print("-----STORE SECRETS Party 0") 54 | 55 | # Create a secret 56 | A = na_client.array(np.ones(DIM), "A", SecretInteger) 57 | 58 | # Create a permissions object to attach to the stored secret 59 | permissions = Permissions.defaults_for_user(client.user_id).allow_compute( 60 | client.user_id, program_id 61 | ) 62 | 63 | # Store a secret, passing in the receipt that shows proof of payment 64 | values_A = await client.store_values( 65 | A, ttl_days=5, permissions=permissions 66 | ).invoke() 67 | 68 | print("Stored values_A: ", values_A) 69 | 70 | print("-----STORE SECRETS Party 1") 71 | 72 | # Create a secret 73 | B = na_client.array(np.ones(DIM), "B", SecretInteger) 74 | 75 | # Create a permissions object to attach to the stored secret 76 | permissions = Permissions.defaults_for_user(client.user_id).allow_compute( 77 | client.user_id, program_id 78 | ) 79 | 80 | # Store a secret, passing in the receipt that shows proof of payment 81 | values_B = await client.store_values( 82 | B, ttl_days=5, permissions=permissions 83 | ).invoke() 84 | 85 | print("Stored values_B: ", values_B) 86 | 87 | ##### COMPUTE 88 | print("-----COMPUTE") 89 | 90 | # Bind the parties in the computation to the client to set input and output parties 91 | input_bindings = [ 92 | InputPartyBinding(party_names[0], client.user_id), 93 | InputPartyBinding(party_names[1], client.user_id), 94 | ] 95 | output_bindings = [OutputPartyBinding(party_names[2], [client.user_id])] 96 | 97 | # Create a computation time secret to use 98 | compute_time_values = { 99 | # "my_int2": SecretInteger(10) 100 | } 101 | 102 | # Compute, passing in the compute time values as well as the previously uploaded value. 103 | print( 104 | f"Invoking computation using program {program_id} and values id {values_A}, {values_B}" 105 | ) 106 | compute_id = await client.compute( 107 | program_id, 108 | input_bindings, 109 | output_bindings, 110 | values=compute_time_values, 111 | value_ids=[values_A, values_B], 112 | ).invoke() 113 | 114 | # Print compute result 115 | print(f"The computation was sent to the network. compute_id: {compute_id}") 116 | result = await client.retrieve_compute_results(compute_id).invoke() 117 | print(f"✅ Compute complete for compute_id {compute_id}") 118 | print(f"🖥️ The result is {result}") 119 | return result 120 | 121 | 122 | if __name__ == "__main__": 123 | asyncio.run(main()) 124 | -------------------------------------------------------------------------------- /examples/matrix_multiplication/nada-project.toml: -------------------------------------------------------------------------------- 1 | name = "matrix_multiplication" 2 | version = "0.1.0" 3 | authors = [""] 4 | 5 | [[programs]] 6 | path = "src/matrix_multiplication.py" 7 | prime_size = 128 8 | -------------------------------------------------------------------------------- /examples/matrix_multiplication/src/matrix_multiplication.py: -------------------------------------------------------------------------------- 1 | """Main Nada program""" 2 | 3 | from typing import List 4 | 5 | from config import DIM 6 | from nada_dsl import Output, SecretInteger 7 | 8 | # Step 0: Nada Numpy is imported with this line 9 | import nada_numpy as na 10 | 11 | 12 | def nada_main() -> List[Output]: 13 | """ 14 | Main dot product Nada program. 15 | 16 | Returns: 17 | List[Output]: List of program outputs. 18 | """ 19 | # Step 1: We use Nada Numpy wrapper to create "Party0", "Party1" and "Party2" 20 | parties = na.parties(3) 21 | 22 | # Step 2: Party0 creates an array of dimension (3 x 3) with name "A" 23 | a = na.array(DIM, parties[0], "A", SecretInteger) 24 | 25 | # Step 3: Party1 creates an array of dimension (3 x 3) with name "B" 26 | b = na.array(DIM, parties[1], "B", SecretInteger) 27 | 28 | # Step 4: The result is of computing the dot product between the two which is another (3 x 3) matrix 29 | result = a @ b 30 | 31 | # Step 5: We can use result.output() to produce 32 | # the output for Party2 and variable name "my_output" 33 | return result.output(parties[2], "my_output") 34 | -------------------------------------------------------------------------------- /examples/matrix_multiplication/target/.gitignore: -------------------------------------------------------------------------------- 1 | # This directory is kept purposely, so that no compilation errors arise. 2 | # Ignore everything in this directory 3 | * 4 | # Except this file 5 | !.gitignore -------------------------------------------------------------------------------- /examples/matrix_multiplication/tests/matrix_multiplication.yaml: -------------------------------------------------------------------------------- 1 | program: matrix_multiplication 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 2 5 | A_0_2: 3 6 | A_1_0: 4 7 | A_1_1: 5 8 | A_1_2: 6 9 | A_2_0: 7 10 | A_2_1: 8 11 | A_2_2: 9 12 | B_0_0: 1 13 | B_0_1: 2 14 | B_0_2: 3 15 | B_1_0: 4 16 | B_1_1: 5 17 | B_1_2: 6 18 | B_2_0: 7 19 | B_2_1: 8 20 | B_2_2: 9 21 | expected_outputs: 22 | my_output_0_0: 30 23 | my_output_0_1: 36 24 | my_output_0_2: 42 25 | my_output_1_0: 66 26 | my_output_1_1: 81 27 | my_output_1_2: 96 28 | my_output_2_0: 102 29 | my_output_2_1: 126 30 | my_output_2_2: 150 31 | -------------------------------------------------------------------------------- /examples/rational_numbers/README.md: -------------------------------------------------------------------------------- 1 | # Rational Numbers Tutorial 2 | 3 | [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/NillionNetwork/nada-numpy/blob/main/examples/rational_numbers/rational_numbers.ipynb) 4 | 5 | This tutorial shows how to use Nada Numpy Rational datatypes to work with fixed-point numbers in Nada. 6 | 7 | ## 💡 Notions 8 | 9 | In this section, we will shed some light on how rational logic works (as opposed to decimal logic) in Nada. 10 | 11 | Contrary to e.g. Python, where floats & ints co-exist peacefully and are used interchangeably, Nada makes a strong difference between the two. 12 | 13 | ### 🧮 Lore for the nerds 14 | 15 | This tutorial uses fixed point numbers as it is the only available way to use Fixed-Point numbers in Nada. The representation of a fixed point number uses integer places to represent decimal digits. Thus, every number is multiplied by a scaling factor, that we refer to as `SCALE` ($\Delta = 2^{16}$) or `LOG_SCALE` in its logarithmic notation ($log_2\Delta = 16$). In a nutshell, this means we will use 16 bits to represent decimals. 16 | 17 | If we want to input a variable `a = float(3.2)`, we need to first encode it. For that we will define a new variable `a'` which is going to be the scaled version. In this case, the scaling factor (to simplify) is going to by 3 bits so, $log_2\Delta = 3$ and $\Delta = 2^3 = 8$. With the following formula, we compute the encoded value: 18 | 19 | $$ a' = round(a * \Delta) = round(a * 2^{log_2\Delta}) = 3.2 \cdot 2^3 = 3.2 \cdot 8 = 26 $$ 20 | 21 | Thus, in order to introduce a value with 3 bits of precision, we would be inputing 26 instead of 3.2. 22 | 23 | ### 🫵 What it means for you 24 | 25 | So with all this theory you may or may not have read in mind, what does all this mean practically? 26 | 27 | Basically it means that `nada-numpy` **can** handle rational values but it does so by converting (or "quantizing") rational to decimal values. 28 | 29 | This conversion is lossy (i.e. there is some rounding error). How lossy depends on something called the `log_scale` which is set to `16` by default. This is a sensible default and should not lead to any shootings of one's feet. 30 | 31 | If you raise this value (which you can do by typing `na.set_log_scale(...)` both at the start of your Nada program and in the Python script where you provide rational values to the program), the rounding error will become smaller BUT you may risk overflowing. 32 | 33 | Therefore, feel free to play around with it but realize that you may encounter overflows if you fly a bit too close to the sun. 34 | 35 | PSA: please don't forget to run `na.set_log_scale(...)` (with the same scaling value!!) in both your Nada program AND in your Python script where you provide rational input values. 36 | 37 | ## 🚨 Limitations 38 | The choice for blind computing implies certain trade-offs in comparison to conventional computing. What you gain in privacy, you pay in extra computational overhead & capacity constraints. 39 | 40 | Therefore, you will notice that large-scale computational workloads may lead to long compilation and/or execution times or hitting network capacity guardrails. 41 | 42 | That said, the Nillion team is working around the clock to push the boundaries of this technology and bring the potential of blind computing to reality 🚀 43 | 44 | ## ➡️ Stay in touch 45 | If you want to get involved in the blind computing community and be the first to know all big updates, join our Discord 46 | 47 | [![Discord](https://img.shields.io/badge/Discord-nillionnetwork-%235865F2?logo=discord)](https://discord.gg/nillionnetwork) 48 | 49 | And if you want to contribute to the blind computing revolution, we welcome open-source contributors! 50 | 51 | [![GitHub Discussions](https://img.shields.io/badge/GitHub_Discussions-NillionNetwork-%23181717?logo=github)](https://github.com/orgs/NillionNetwork/discussions) 52 | 53 | -------------------------------------------------------------------------------- /examples/rational_numbers/main.py: -------------------------------------------------------------------------------- 1 | """Broadcasting example script""" 2 | 3 | import asyncio 4 | import os 5 | 6 | import numpy as np 7 | import pytest 8 | from dotenv import load_dotenv 9 | from nillion_client import (InputPartyBinding, Network, NilChainPayer, 10 | NilChainPrivateKey, OutputPartyBinding, 11 | Permissions, PrivateKey, SecretInteger, VmClient) 12 | 13 | import nada_numpy.client as na_client 14 | 15 | home = os.getenv("HOME") 16 | load_dotenv(f"{home}/.config/nillion/nillion-devnet.env") 17 | 18 | 19 | # 1 Party running simple addition on 1 stored secret and 1 compute time secret 20 | async def main(): 21 | # Use the devnet configuration generated by `nillion-devnet` 22 | network = Network.from_config("devnet") 23 | 24 | # Create payments config and set up Nillion wallet with a private key to pay for operations 25 | nilchain_key: str = os.getenv("NILLION_NILCHAIN_PRIVATE_KEY_0") # type: ignore 26 | payer = NilChainPayer( 27 | network, 28 | wallet_private_key=NilChainPrivateKey(bytes.fromhex(nilchain_key)), 29 | gas_limit=10000000, 30 | ) 31 | 32 | # Use a random key to identify ourselves 33 | signing_key = PrivateKey() 34 | client = await VmClient.create(signing_key, network, payer) 35 | party_names = na_client.parties(3) 36 | program_name = "rational_numbers" 37 | program_mir_path = f"./target/{program_name}.nada.bin" 38 | 39 | ##### STORE PROGRAM 40 | print("-----STORE PROGRAM") 41 | 42 | # Store program 43 | program_mir = open( 44 | os.path.join(os.path.dirname(os.path.abspath(__file__)), program_mir_path), "rb" 45 | ).read() 46 | program_id = await client.store_program(program_name, program_mir).invoke() 47 | 48 | # Print details about stored program 49 | print(f"Stored program_id: {program_id}") 50 | 51 | ##### STORE SECRETS 52 | print("-----STORE SECRETS") 53 | my_input_0 = {"my_input_0": na_client.secret_rational(3.2)} 54 | 55 | # Create a permissions object to attach to the stored secret 56 | permissions = Permissions.defaults_for_user(client.user_id).allow_compute( 57 | client.user_id, program_id 58 | ) 59 | 60 | # Store a secret, passing in the receipt that shows proof of payment 61 | values_0 = await client.store_values( 62 | my_input_0, ttl_days=5, permissions=permissions 63 | ).invoke() 64 | 65 | print("Stored values_0: ", values_0) 66 | 67 | my_input_1 = {"my_input_1": na_client.secret_rational(2.3)} 68 | # Store a secret, passing in the receipt that shows proof of payment 69 | values_1 = await client.store_values( 70 | my_input_1, ttl_days=5, permissions=permissions 71 | ).invoke() 72 | 73 | print("Stored values_1: ", values_1) 74 | 75 | ##### COMPUTE 76 | print("-----COMPUTE") 77 | 78 | # Bind the parties in the computation to the client to set input and output parties 79 | input_bindings = [ 80 | InputPartyBinding(party_names[0], client.user_id), 81 | InputPartyBinding(party_names[1], client.user_id), 82 | ] 83 | output_bindings = [OutputPartyBinding(party_names[2], [client.user_id])] 84 | 85 | # Create a computation time secret to use 86 | compute_time_values = { 87 | # "my_int2": SecretInteger(10) 88 | } 89 | 90 | # Compute, passing in the compute time values as well as the previously uploaded value. 91 | print( 92 | f"Invoking computation using program {program_id} and values id {values_0}, {values_1}" 93 | ) 94 | compute_id = await client.compute( 95 | program_id, 96 | input_bindings, 97 | output_bindings, 98 | values=compute_time_values, 99 | value_ids=[values_0, values_1], 100 | ).invoke() 101 | 102 | # Print compute result 103 | print(f"The computation was sent to the network. compute_id: {compute_id}") 104 | result = await client.retrieve_compute_results(compute_id).invoke() 105 | 106 | output = na_client.float_from_rational(result["my_output"].value) 107 | print(f"✅ Compute complete for compute_id {compute_id}") 108 | print(f"🖥️ The result is {output}") 109 | return result 110 | 111 | 112 | if __name__ == "__main__": 113 | asyncio.run(main()) 114 | -------------------------------------------------------------------------------- /examples/rational_numbers/nada-project.toml: -------------------------------------------------------------------------------- 1 | name = "rational_numbers" 2 | version = "0.1.0" 3 | authors = [""] 4 | 5 | [[programs]] 6 | path = "src/rational_numbers.py" 7 | prime_size = 128 8 | -------------------------------------------------------------------------------- /examples/rational_numbers/src/rational_numbers.py: -------------------------------------------------------------------------------- 1 | """Main Nada program""" 2 | 3 | from typing import List 4 | 5 | from nada_dsl import Output 6 | 7 | import nada_numpy as na 8 | 9 | 10 | def nada_main() -> List[Output]: 11 | """ 12 | Main dot product Nada program. 13 | 14 | Returns: 15 | List[Output]: List of program outputs. 16 | """ 17 | # We define the number of parties 18 | parties = na.parties(3) 19 | 20 | # We use na.SecretRational to create a secret rational number for party 0 21 | a = na.secret_rational("my_input_0", parties[0]) 22 | 23 | # We use na.SecretRational to create a secret rational number for party 1 24 | b = na.secret_rational("my_input_1", parties[1]) 25 | 26 | # This is a compile time rational number 27 | c = na.rational(1.2) 28 | 29 | # The formula below does operations on rational numbers and returns a rational number 30 | # It's easy to see that (a + b - c) is both on numerator and denominator, so the end result is b 31 | out = ((a + b - c) * b) / (a + b - c) 32 | 33 | return na.output(out, parties[2], "my_output") 34 | -------------------------------------------------------------------------------- /examples/rational_numbers/target/.gitignore: -------------------------------------------------------------------------------- 1 | # This directory is kept purposely, so that no compilation errors arise. 2 | # Ignore everything in this directory 3 | * 4 | # Except this file 5 | !.gitignore -------------------------------------------------------------------------------- /examples/rational_numbers/tests/rational_numbers.yaml: -------------------------------------------------------------------------------- 1 | program: rational_numbers 2 | inputs: 3 | my_input_0: 209715 4 | my_input_1: 294912 5 | expected_outputs: 6 | my_output: 294912 7 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | ignore_missing_imports = True -------------------------------------------------------------------------------- /nada_numpy/__init__.py: -------------------------------------------------------------------------------- 1 | """This is the __init__.py module""" 2 | 3 | from nada_numpy.array import NadaArray 4 | from nada_numpy.funcs import * # pylint:disable=redefined-builtin 5 | from nada_numpy.types import (PublicBoolean, Rational, SecretBoolean, 6 | SecretRational, get_log_scale, public_rational, 7 | rational, reset_log_scale, secret_rational, 8 | set_log_scale) 9 | -------------------------------------------------------------------------------- /nada_numpy/client.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides functions to work with the Python Nillion Client for handling 3 | secret and public variable integers and generating named party objects and input dictionaries. 4 | """ 5 | 6 | from typing import Dict, List, Optional, Union 7 | 8 | import numpy as np 9 | # pylint:disable=no-name-in-module 10 | from nillion_client import (Integer, SecretInteger, SecretUnsignedInteger, 11 | UnsignedInteger) 12 | 13 | from nada_numpy.types import Rational, SecretRational, get_log_scale 14 | 15 | __all__ = [ 16 | "parties", 17 | "array", 18 | "concat", 19 | "public_rational", 20 | "secret_rational", 21 | "float_from_rational", 22 | ] 23 | 24 | 25 | def parties(num: int, prefix: str = "Party") -> List: 26 | """ 27 | Creates a list of party name strings. 28 | 29 | Args: 30 | num (int): The number of parties to create. 31 | prefix (str, optional): The prefix to use for party names. Defaults to "Party". 32 | 33 | Returns: 34 | List: A list of party name strings in the format "{prefix}{i}". 35 | """ 36 | return [f"{prefix}{i}" for i in range(num)] 37 | 38 | 39 | def array( 40 | arr: np.ndarray, 41 | prefix: str, 42 | nada_type: Union[ 43 | SecretInteger, 44 | SecretUnsignedInteger, 45 | Integer, 46 | UnsignedInteger, 47 | Rational, 48 | SecretRational, 49 | ], 50 | ) -> Dict: 51 | """ 52 | Recursively generates a dictionary of Nillion input objects for each element 53 | in the given array. 54 | 55 | Args: 56 | arr (np.ndarray): The input array. 57 | prefix (str): The prefix to be added to the output names. 58 | nada_type (type): The type of the values introduced. 59 | 60 | Returns: 61 | Dict: A dictionary mapping generated names to Nillion input objects. 62 | """ 63 | # TODO: Use this version when check for zero values is removed 64 | if len(arr.shape) == 1: 65 | if nada_type == Rational: 66 | nada_type = public_rational # type: ignore 67 | elif nada_type == SecretRational: 68 | nada_type = secret_rational # type: ignore 69 | arr = np.round(arr) 70 | return { 71 | f"{prefix}_{i}": (nada_type(int(arr[i]))) for i in range(arr.shape[0]) # type: ignore 72 | } 73 | return { 74 | k: v 75 | for i in range(arr.shape[0]) 76 | for k, v in array(arr[i], f"{prefix}_{i}", nada_type).items() 77 | } 78 | 79 | 80 | def concat(list_dict: List[Dict]) -> Dict: 81 | """ 82 | Combines a list of dictionaries into a single dictionary. 83 | 84 | Note: This function will overwrite values for duplicate keys. 85 | 86 | Args: 87 | list_dict (List[Dict]): A list of dictionaries. 88 | 89 | Returns: 90 | Dict: A single merged dictionary. 91 | """ 92 | return {k: v for d in list_dict for k, v in d.items()} 93 | 94 | 95 | def __rational(value: Union[float, int]) -> int: 96 | """ 97 | Returns the integer representation of the given float value. 98 | 99 | Args: 100 | value (Union[float, int]): The input value. 101 | 102 | Returns: 103 | int: The integer representation of the input value. 104 | """ 105 | return round(value * (1 << get_log_scale())) 106 | 107 | 108 | def public_rational(value: Union[float, int]) -> Integer: 109 | """ 110 | Returns the integer representation of the given float value. 111 | 112 | Args: 113 | value (Union[float, int]): The input value. 114 | 115 | Returns: 116 | int: The integer representation of the input value. 117 | """ 118 | return Integer(__rational(value)) 119 | 120 | 121 | def secret_rational(value: Union[float, int]) -> SecretInteger: 122 | """ 123 | Returns the integer representation of the given float value. 124 | 125 | Args: 126 | value (Union[float, int]): The input value. 127 | 128 | Returns: 129 | int: The integer representation of the input value. 130 | """ 131 | return SecretInteger(__rational(value)) 132 | 133 | 134 | def float_from_rational(value: int, log_scale: Optional[int] = None) -> float: 135 | """ 136 | Returns the float representation of the given rational value. 137 | 138 | Args: 139 | value (int): The output Rational value to convert. 140 | log_scale (int, optional): The log scale to use for conversion. Defaults to None. 141 | 142 | Returns: 143 | float: The float representation of the input value. 144 | """ 145 | if log_scale is None: 146 | log_scale = get_log_scale() 147 | return value / (1 << log_scale) 148 | -------------------------------------------------------------------------------- /nada_numpy/context.py: -------------------------------------------------------------------------------- 1 | """Contains useful context managers""" 2 | 3 | from nada_numpy.types import Rational, SecretRational, _NadaRational 4 | 5 | 6 | class UnsafeArithmeticSession: 7 | """ 8 | A context manager that temporarily modifies the behavior of arithmetic operations 9 | for Rational and SecretRational types, disabling rescaling for multiplication and division. 10 | 11 | Attributes: 12 | mul_rational (function): Original __mul__ method of Rational. 13 | mul_secret_rational (function): Original __mul__ method of SecretRational. 14 | truediv_rational (function): Original __truediv__ method of Rational. 15 | truediv_secret_rational (function): Original __truediv__ method of SecretRational. 16 | """ 17 | 18 | def __init__(self): 19 | """ 20 | Initializes the UnsafeArithmeticSession by storing the original 21 | multiplication and division methods of Rational and SecretRational. 22 | """ 23 | self.mul_rational = Rational.__mul__ 24 | self.mul_secret_rational = SecretRational.__mul__ 25 | 26 | self.truediv_rational = Rational.__truediv__ 27 | self.truediv_secret_rational = SecretRational.__truediv__ 28 | 29 | def __enter__(self): 30 | """ 31 | Enters the context, temporarily replacing the multiplication and division 32 | methods of Rational and SecretRational to disable rescaling. 33 | """ 34 | 35 | def mul_no_rescale_wrapper(self: Rational, other: _NadaRational): 36 | """ 37 | Wrapper for Rational.__mul__ that disables rescaling. 38 | 39 | Args: 40 | self (Rational): The Rational instance. 41 | other (_NadaRational): The other operand. 42 | 43 | Returns: 44 | Rational: Result of the multiplication without rescaling. 45 | """ 46 | return Rational.mul_no_rescale(self, other, ignore_scale=True) 47 | 48 | def secret_mul_no_rescale_wrapper(self: SecretRational, other: _NadaRational): 49 | """ 50 | Wrapper for SecretRational.__mul__ that disables rescaling. 51 | 52 | Args: 53 | self (SecretRational): The SecretRational instance. 54 | other (_NadaRational): The other operand. 55 | 56 | Returns: 57 | SecretRational: Result of the multiplication without rescaling. 58 | """ 59 | return SecretRational.mul_no_rescale(self, other, ignore_scale=True) 60 | 61 | def divide_no_rescale_wrapper(self: Rational, other: _NadaRational): 62 | """ 63 | Wrapper for Rational.__truediv__ that disables rescaling. 64 | 65 | Args: 66 | self (Rational): The Rational instance. 67 | other (_NadaRational): The other operand. 68 | 69 | Returns: 70 | Rational: Result of the division without rescaling. 71 | """ 72 | return Rational.divide_no_rescale(self, other, ignore_scale=True) 73 | 74 | def secret_divide_no_rescale_wrapper( 75 | self: SecretRational, other: _NadaRational 76 | ): 77 | """ 78 | Wrapper for SecretRational.__truediv__ that disables rescaling. 79 | 80 | Args: 81 | self (SecretRational): The SecretRational instance. 82 | other (_NadaRational): The other operand. 83 | 84 | Returns: 85 | SecretRational: Result of the division without rescaling. 86 | """ 87 | return SecretRational.divide_no_rescale(self, other, ignore_scale=True) 88 | 89 | Rational.__mul__ = mul_no_rescale_wrapper 90 | SecretRational.__mul__ = secret_mul_no_rescale_wrapper 91 | Rational.__truediv__ = divide_no_rescale_wrapper 92 | SecretRational.__truediv__ = secret_divide_no_rescale_wrapper 93 | 94 | def __exit__(self, exc_type, exc_val, exc_tb): 95 | """ 96 | Exits the context, restoring the original multiplication and division methods 97 | of Rational and SecretRational. 98 | 99 | Args: 100 | exc_type (type): Exception type if an exception occurred, else None. 101 | exc_val (Exception): Exception instance if an exception occurred, else None. 102 | exc_tb (traceback): Traceback object if an exception occurred, else None. 103 | """ 104 | # Restore the original __mul__ method 105 | Rational.__mul__ = self.mul_rational 106 | SecretRational.__mul__ = self.mul_secret_rational 107 | 108 | # Restore the original __truediv__ method 109 | Rational.__truediv__ = self.truediv_rational 110 | SecretRational.__truediv__ = self.truediv_secret_rational 111 | -------------------------------------------------------------------------------- /nada_numpy/legacy_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides functions to work with the Python Nillion Client for handling 3 | secret and public variable integers and generating named party objects and input dictionaries. 4 | """ 5 | 6 | # pylint:disable=R0801 7 | 8 | import logging 9 | from typing import Dict, List, Optional, Union 10 | 11 | import numpy as np 12 | # pylint:disable=no-name-in-module 13 | from py_nillion_client import (Integer, SecretInteger, SecretUnsignedInteger, 14 | UnsignedInteger) 15 | 16 | from nada_numpy.types import Rational, SecretRational, get_log_scale 17 | 18 | logging.warning( 19 | "[DEPRECATED] The legacy_client module is deprecated" 20 | "and will be removed in a future release." 21 | "Use nada-numpy.client instead." 22 | ) 23 | 24 | __all__ = [ 25 | "parties", 26 | "array", 27 | "concat", 28 | "public_rational", 29 | "secret_rational", 30 | "float_from_rational", 31 | ] 32 | 33 | 34 | def parties(num: int, prefix: str = "Party") -> List: 35 | """ 36 | Creates a list of party name strings. 37 | 38 | Args: 39 | num (int): The number of parties to create. 40 | prefix (str, optional): The prefix to use for party names. Defaults to "Party". 41 | 42 | Returns: 43 | List: A list of party name strings in the format "{prefix}{i}". 44 | """ 45 | return [f"{prefix}{i}" for i in range(num)] 46 | 47 | 48 | def array( 49 | arr: np.ndarray, 50 | prefix: str, 51 | nada_type: Union[ 52 | SecretInteger, 53 | SecretUnsignedInteger, 54 | Integer, 55 | UnsignedInteger, 56 | Rational, 57 | SecretRational, 58 | ], 59 | ) -> Dict: 60 | """ 61 | Recursively generates a dictionary of Nillion input objects for each element 62 | in the given array. 63 | 64 | Args: 65 | arr (np.ndarray): The input array. 66 | prefix (str): The prefix to be added to the output names. 67 | nada_type (type): The type of the values introduced. 68 | 69 | Returns: 70 | Dict: A dictionary mapping generated names to Nillion input objects. 71 | """ 72 | # TODO: Use this version when check for zero values is removed 73 | if len(arr.shape) == 1: 74 | if nada_type == Rational: 75 | nada_type = public_rational # type: ignore 76 | elif nada_type == SecretRational: 77 | nada_type = secret_rational # type: ignore 78 | return { 79 | f"{prefix}_{i}": (nada_type(int(arr[i]))) for i in range(arr.shape[0]) # type: ignore 80 | } 81 | return { 82 | k: v 83 | for i in range(arr.shape[0]) 84 | for k, v in array(arr[i], f"{prefix}_{i}", nada_type).items() 85 | } 86 | 87 | 88 | def concat(list_dict: List[Dict]) -> Dict: 89 | """ 90 | Combines a list of dictionaries into a single dictionary. 91 | 92 | Note: This function will overwrite values for duplicate keys. 93 | 94 | Args: 95 | list_dict (List[Dict]): A list of dictionaries. 96 | 97 | Returns: 98 | Dict: A single merged dictionary. 99 | """ 100 | return {k: v for d in list_dict for k, v in d.items()} 101 | 102 | 103 | def __rational(value: Union[float, int]) -> int: 104 | """ 105 | Returns the integer representation of the given float value. 106 | 107 | Args: 108 | value (Union[float, int]): The input value. 109 | 110 | Returns: 111 | int: The integer representation of the input value. 112 | """ 113 | return round(value * (1 << get_log_scale())) 114 | 115 | 116 | def public_rational(value: Union[float, int]) -> Integer: 117 | """ 118 | Returns the integer representation of the given float value. 119 | 120 | Args: 121 | value (Union[float, int]): The input value. 122 | 123 | Returns: 124 | int: The integer representation of the input value. 125 | """ 126 | return Integer(__rational(value)) 127 | 128 | 129 | def secret_rational(value: Union[float, int]) -> SecretInteger: 130 | """ 131 | Returns the integer representation of the given float value. 132 | 133 | Args: 134 | value (Union[float, int]): The input value. 135 | 136 | Returns: 137 | int: The integer representation of the input value. 138 | """ 139 | return SecretInteger(__rational(value)) 140 | 141 | 142 | def float_from_rational(value: int, log_scale: Optional[int] = None) -> float: 143 | """ 144 | Returns the float representation of the given rational value. 145 | 146 | Args: 147 | value (int): The output Rational value to convert. 148 | log_scale (int, optional): The log scale to use for conversion. Defaults to None. 149 | 150 | Returns: 151 | float: The float representation of the input value. 152 | """ 153 | if log_scale is None: 154 | log_scale = get_log_scale() 155 | return value / (1 << log_scale) 156 | -------------------------------------------------------------------------------- /nada_numpy/nada_typing.py: -------------------------------------------------------------------------------- 1 | """Contains custom typing traits""" 2 | 3 | from typing import Union 4 | 5 | import nada_dsl as dsl 6 | 7 | from nada_numpy.types import (PublicBoolean, Rational, SecretBoolean, 8 | SecretRational) 9 | 10 | NadaRational = Union[ 11 | Rational, 12 | SecretRational, 13 | ] 14 | 15 | NadaInteger = Union[ 16 | dsl.Integer, 17 | dsl.PublicInteger, 18 | dsl.SecretInteger, 19 | ] 20 | 21 | NadaUnsignedInteger = Union[ 22 | dsl.UnsignedInteger, 23 | dsl.PublicUnsignedInteger, 24 | dsl.SecretUnsignedInteger, 25 | ] 26 | 27 | NadaBoolean = Union[ 28 | dsl.Boolean, 29 | dsl.PublicBoolean, 30 | dsl.SecretBoolean, 31 | PublicBoolean, 32 | SecretBoolean, 33 | ] 34 | 35 | NadaCleartextNumber = Union[ 36 | dsl.Integer, 37 | dsl.UnsignedInteger, 38 | Rational, 39 | ] 40 | 41 | NadaCleartextType = Union[ 42 | dsl.Integer, 43 | dsl.UnsignedInteger, 44 | dsl.Boolean, 45 | Rational, 46 | ] 47 | 48 | AnyNadaType = Union[ 49 | dsl.NadaType, 50 | Rational, 51 | SecretRational, 52 | ] 53 | -------------------------------------------------------------------------------- /nada_numpy/utils.py: -------------------------------------------------------------------------------- 1 | """General utils functions""" 2 | 3 | import re 4 | from typing import Callable 5 | 6 | 7 | def copy_metadata(source_func: Callable) -> Callable: 8 | """ 9 | Copies metadata (docstring and type annotation) from one function 10 | and adds it to the function it decorates. 11 | 12 | Args: 13 | source_func (Callable): Source function to copy metadata from. 14 | 15 | Returns: 16 | Callable: Decorated function. 17 | """ 18 | 19 | def decorator(func: Callable) -> Callable: 20 | """ 21 | Decorates function with source function's metadata. 22 | 23 | Args: 24 | func (Callable): Function without added metadata. 25 | 26 | Returns: 27 | Callable: Function with metadata. 28 | """ 29 | doc: str = "" 30 | if hasattr(source_func, "__doc__") and source_func.__doc__ is not None: 31 | doc = source_func.__doc__ 32 | # Replace NumPy references with NadaArray references 33 | doc = re.sub(r"\b(numpy)\b", "NadaArray", doc, flags=re.IGNORECASE) 34 | doc = re.sub(r"\b(np)\b", "na", doc, flags=re.IGNORECASE) 35 | 36 | func.__doc__ = doc 37 | 38 | annot = {} 39 | if hasattr(source_func, "__annotations__"): 40 | annot = source_func.__annotations__ 41 | func.__annotations__ = annot 42 | 43 | return func 44 | 45 | return decorator 46 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "nada-numpy" 3 | version = "0.6.0" 4 | description = "Nada-Numpy is a Python library designed for algebraic operations on NumPy-like array objects on top of Nada DSL and Nillion Network." 5 | authors = ["José Cabrero-Holgueras "] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.10" 10 | numpy = "^1.26.4" 11 | nada-dsl = "^0.7.1" 12 | py-nillion-client = "^0.6.0" 13 | nillion-python-helpers = "^0.3.0" 14 | black = {version="24.8.0", optional=true} 15 | isort = {version="^5.13.2", optional=true} 16 | scikit-learn = {version="^1.5.1", optional=true} 17 | nada-test = "^0.6.0" 18 | nillion-client = "^0.1.0" 19 | 20 | [tool.poetry.group.dev.dependencies] 21 | pytest = "^8.2.0" 22 | pylint = "^3.2.3" 23 | mypy = "^1.11.2" 24 | 25 | [build-system] 26 | requires = ["poetry-core"] 27 | build-backend = "poetry.core.masonry.api" 28 | 29 | [tool.poetry.extras] 30 | linter = ["black", "isort"] 31 | examples = ["scikit-learn"] -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NillionNetwork/nada-numpy/1f49a9468850e023a5947d58fda3d1a78e363174/tests/__init__.py -------------------------------------------------------------------------------- /tests/nada-tests/nada-project.toml: -------------------------------------------------------------------------------- 1 | name = "tests" 2 | version = "0.1.0" 3 | authors = [""] 4 | 5 | [[programs]] 6 | path = "src/base.py" 7 | prime_size = 128 8 | 9 | [[programs]] 10 | path = "src/broadcasting_div.py" 11 | prime_size = 128 12 | 13 | [[programs]] 14 | path = "src/broadcasting_mul.py" 15 | prime_size = 128 16 | 17 | [[programs]] 18 | path = "src/broadcasting_sub.py" 19 | prime_size = 128 20 | 21 | [[programs]] 22 | path = "src/broadcasting_sum.py" 23 | prime_size = 128 24 | 25 | [[programs]] 26 | path = "src/broadcasting_vec.py" 27 | prime_size = 128 28 | 29 | [[programs]] 30 | path = "src/dot_product.py" 31 | prime_size = 128 32 | 33 | [[programs]] 34 | path = "src/gauss_jordan.py" 35 | prime_size = 64 36 | 37 | [[programs]] 38 | path = "src/get_attr.py" 39 | prime_size = 128 40 | 41 | [[programs]] 42 | path = "src/get_item.py" 43 | prime_size = 128 44 | 45 | [[programs]] 46 | path = "src/get_vec.py" 47 | prime_size = 128 48 | 49 | [[programs]] 50 | path = "src/hstack.py" 51 | prime_size = 128 52 | 53 | [[programs]] 54 | path = "src/matrix_multiplication.py" 55 | prime_size = 128 56 | 57 | [[programs]] 58 | path = "src/reveal.py" 59 | prime_size = 128 60 | 61 | [[programs]] 62 | path = "src/set_item.py" 63 | prime_size = 128 64 | 65 | [[programs]] 66 | path = "src/shape.py" 67 | prime_size = 128 68 | 69 | [[programs]] 70 | path = "src/sum.py" 71 | prime_size = 128 72 | 73 | [[programs]] 74 | path = "src/vstack.py" 75 | prime_size = 128 76 | 77 | [[programs]] 78 | path = "src/unsigned_matrix_inverse.py" 79 | prime_size = 64 80 | 81 | [[programs]] 82 | path = "src/supported_operations.py" 83 | prime_size = 128 84 | 85 | [[programs]] 86 | path = "src/generate_array.py" 87 | prime_size = 128 88 | 89 | [[programs]] 90 | path = "src/private_inverse.py" 91 | prime_size = 64 92 | 93 | [[programs]] 94 | path = "src/rational_arithmetic.py" 95 | prime_size = 128 96 | 97 | [[programs]] 98 | path = "src/rational_comparison.py" 99 | prime_size = 128 100 | 101 | [[programs]] 102 | path = "src/secret_rational_arithmetic.py" 103 | prime_size = 128 104 | 105 | [[programs]] 106 | path = "src/secret_rational_comparison.py" 107 | prime_size = 128 108 | 109 | [[programs]] 110 | path = "src/chained_rational_operations.py" 111 | prime_size = 128 112 | 113 | [[programs]] 114 | path = "src/rational_array.py" 115 | prime_size = 128 116 | 117 | [[programs]] 118 | path = "src/rational_scaling.py" 119 | prime_size = 128 120 | 121 | [[programs]] 122 | path = "src/rational_operability.py" 123 | prime_size = 128 124 | 125 | [[programs]] 126 | path = "src/rational_if_else.py" 127 | prime_size = 128 128 | 129 | [[programs]] 130 | path = "src/random_array.py" 131 | prime_size = 128 132 | 133 | [[programs]] 134 | path = "src/rational_advanced.py" 135 | prime_size = 128 136 | 137 | [[programs]] 138 | path = "src/array_attributes.py" 139 | prime_size = 128 140 | 141 | [[programs]] 142 | path = "src/functional_operations.py" 143 | prime_size = 128 144 | 145 | [[programs]] 146 | path = "src/array_statistics.py" 147 | prime_size = 128 148 | 149 | [[programs]] 150 | path = "src/matrix_multiplication_rational.py" 151 | prime_size = 128 152 | 153 | [[programs]] 154 | path = "src/matrix_multiplication_rational_multidim.py" 155 | prime_size = 128 156 | 157 | [[programs]] 158 | path = "src/dot_product_rational.py" 159 | prime_size = 128 160 | 161 | [[programs]] 162 | path = "src/supported_operations_return_types.py" 163 | prime_size = 128 164 | 165 | [[programs]] 166 | path = "src/new_array.py" 167 | prime_size = 128 168 | 169 | [[programs]] 170 | path = "src/logistic_regression.py" 171 | prime_size = 128 172 | 173 | [[programs]] 174 | path = "src/logistic_regression_rational.py" 175 | prime_size = 128 176 | 177 | [[programs]] 178 | path = "src/broadcasting_rationals.py" 179 | prime_size = 128 180 | 181 | [[programs]] 182 | path = "src/type_guardrails.py" 183 | prime_size = 128 184 | 185 | [[programs]] 186 | path = "src/fxpmath_funcs.py" 187 | prime_size = 128 188 | 189 | [[programs]] 190 | path = "src/fxpmath_methods.py" 191 | prime_size = 128 192 | 193 | [[programs]] 194 | path = "src/fxpmath_arrays.py" 195 | prime_size = 128 196 | 197 | [[programs]] 198 | path = "src/array_comparison.py" 199 | prime_size = 128 200 | 201 | [[programs]] 202 | path = "src/shuffle.py" 203 | prime_size = 128 -------------------------------------------------------------------------------- /tests/nada-tests/src/array_attributes.py: -------------------------------------------------------------------------------- 1 | from types import NoneType 2 | 3 | import numpy as np 4 | from nada_dsl import * 5 | 6 | import nada_numpy as na 7 | from nada_numpy.nada_typing import NadaInteger, NadaRational 8 | 9 | 10 | def nada_main(): 11 | parties = na.parties(2) 12 | 13 | a = na.array([3], parties[0], "A", SecretInteger) 14 | b = na.NadaArray(np.array([])) 15 | c = na.NadaArray(np.array([na.rational(1.5)])) 16 | 17 | a += Integer(0) 18 | 19 | assert not a.empty 20 | assert b.empty 21 | assert not c.empty 22 | 23 | assert a.dtype == NadaInteger, a.dtype 24 | assert b.dtype == None, b.dtype 25 | assert c.dtype == NadaRational, c.dtype 26 | 27 | assert a.ndim == 1, a.ndim 28 | assert b.ndim == 1, b.ndim 29 | assert c.ndim == 1, c.ndim 30 | 31 | assert len(a) == 3, len(a) 32 | assert len(b) == 0, len(b) 33 | assert len(c) == 1, len(c) 34 | 35 | return a.output(parties[1], "my_output") 36 | -------------------------------------------------------------------------------- /tests/nada-tests/src/array_comparison.py: -------------------------------------------------------------------------------- 1 | from types import NoneType 2 | 3 | import numpy as np 4 | from nada_dsl import * 5 | 6 | import nada_numpy as na 7 | from nada_numpy.nada_typing import NadaInteger, NadaRational 8 | 9 | 10 | def nada_main(): 11 | parties = na.parties(2) 12 | 13 | a = na.array([1, 3], parties[0], "A", SecretInteger) 14 | b = na.array([1, 3], parties[1], "B", SecretInteger) 15 | c = Integer(1) 16 | 17 | d = a == b 18 | e = a == c 19 | f = a != b 20 | g = a != c 21 | h = a < b 22 | i = a < c 23 | j = a <= b 24 | k = a <= c 25 | l = a > b 26 | m = a > c 27 | n = a >= b 28 | o = a >= c 29 | 30 | return ( 31 | d.output(parties[1], "my_output_1") 32 | + e.output(parties[1], "my_output_2") 33 | + f.output(parties[1], "my_output_3") 34 | + g.output(parties[1], "my_output_4") 35 | + h.output(parties[1], "my_output_5") 36 | + i.output(parties[1], "my_output_6") 37 | + j.output(parties[1], "my_output_7") 38 | + k.output(parties[1], "my_output_8") 39 | + l.output(parties[1], "my_output_9") 40 | + m.output(parties[1], "my_output_10") 41 | + n.output(parties[1], "my_output_11") 42 | + o.output(parties[1], "my_output_12") 43 | ) 44 | -------------------------------------------------------------------------------- /tests/nada-tests/src/array_matrix_inverse.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(2) 8 | 9 | a = na.array([3, 3], parties[0], "A", SecretInteger) 10 | 11 | a_inv = a.inv() 12 | res = a @ a_inv 13 | 14 | return res.output(parties[1], "my_output") 15 | -------------------------------------------------------------------------------- /tests/nada-tests/src/array_statistics.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(2) 8 | 9 | a = na.array([3, 2], parties[0], "A", SecretInteger) 10 | b = na.array([3, 2], parties[0], "B", na.SecretRational) 11 | 12 | a_sum = a.sum() 13 | b_sum = b.sum() 14 | 15 | a_sum_arr = a.sum(axis=0) 16 | b_sum_arr = b.sum(axis=0) 17 | 18 | a_mean = a.mean() 19 | b_mean = b.mean() 20 | 21 | a_mean_arr = a.mean(axis=0) 22 | b_mean_arr = b.mean(axis=0) 23 | 24 | output_1 = ( 25 | na.output(a_sum, parties[1], "a_sum") 26 | + na.output(a_mean, parties[1], "a_mean") 27 | + na.output(b_sum, parties[1], "b_sum") 28 | + na.output(b_mean, parties[1], "b_mean") 29 | ) 30 | output_2 = ( 31 | a_sum_arr.output(parties[1], "a_sum_arr") 32 | + b_sum_arr.output(parties[1], "b_sum_arr") 33 | + a_mean_arr.output(parties[1], "a_mean_arr") 34 | + b_mean_arr.output(parties[1], "b_mean_arr") 35 | ) 36 | 37 | return output_1 + output_2 38 | -------------------------------------------------------------------------------- /tests/nada-tests/src/base.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | DIM1 = 10 7 | DIM2 = 4 8 | DIM3 = 7 9 | 10 | DIM4 = DIM1 + DIM2 + DIM3 11 | 12 | 13 | def bubble_sort(array: na.NadaArray): 14 | size = DIM4 15 | for i in range(size): 16 | for j in range(0, size - i - 1): 17 | condition = array[j] > array[j + 1] 18 | res1 = condition.if_else(array[j + 1], array[j]) 19 | res2 = condition.if_else(array[j], array[j + 1]) 20 | array[j] = res1 21 | array[j + 1] = res2 22 | 23 | 24 | def nada_main(): 25 | party1 = Party(name="Party1") 26 | party1 = Party(name="Party2") 27 | party1 = Party(name="Party3") 28 | party4 = Party(name="Party4") 29 | 30 | array1 = na.array([DIM1], party1, "array1", SecretInteger) 31 | array2 = na.array([DIM2], party1, "array2", SecretInteger) 32 | array3 = na.array([DIM3], party1, "array3", SecretInteger) 33 | 34 | array4 = np.concatenate((np.concatenate((array1, array2)), array3)) 35 | 36 | bubble_sort(array4) 37 | 38 | outs = [] 39 | for i in range(DIM4): 40 | outs.append(Output(array4[i], "out_array_" + str(i), party4)) 41 | 42 | return outs 43 | 44 | 45 | if __name__ == "__main__": 46 | nada_main() 47 | -------------------------------------------------------------------------------- /tests/nada-tests/src/broadcasting_div.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | b = na.array([3], parties[1], "B", SecretInteger) 11 | 12 | result1 = a / b 13 | result2 = a / Integer(2) 14 | 15 | return result1.output(parties[1], "my_output_1") + result2.output( 16 | parties[2], "my_output_2" 17 | ) 18 | -------------------------------------------------------------------------------- /tests/nada-tests/src/broadcasting_mul.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | b = na.array([3], parties[1], "B", SecretInteger) 11 | 12 | result1 = a * b 13 | result2 = a * Integer(2) 14 | 15 | return result1.output(parties[1], "my_output_1") + result2.output( 16 | parties[2], "my_output_2" 17 | ) 18 | -------------------------------------------------------------------------------- /tests/nada-tests/src/broadcasting_rationals.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | x = na.array((3,), parties[0], "X", na.SecretRational) 10 | y = na.secret_rational("y", parties[0]) 11 | 12 | out_1 = x + y 13 | out_2 = y + x 14 | out_3 = x - y 15 | out_4 = y - x 16 | out_5 = x * y 17 | out_6 = y * x 18 | out_7 = x / y 19 | out_8 = y / x 20 | 21 | return ( 22 | out_1.output(parties[2], "my_output_a") 23 | + out_2.output(parties[2], "my_output_b") 24 | + out_3.output(parties[2], "my_output_c") 25 | + out_4.output(parties[2], "my_output_d") 26 | + out_5.output(parties[2], "my_output_e") 27 | + out_6.output(parties[2], "my_output_f") 28 | + out_7.output(parties[2], "my_output_g") 29 | + out_8.output(parties[2], "my_output_h") 30 | ) 31 | -------------------------------------------------------------------------------- /tests/nada-tests/src/broadcasting_sub.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | b = na.array([3], parties[1], "B", SecretInteger) 11 | 12 | result1 = a - b 13 | result2 = a - Integer(2) 14 | 15 | return result1.output(parties[1], "my_output_1") + result2.output( 16 | parties[2], "my_output_2" 17 | ) 18 | -------------------------------------------------------------------------------- /tests/nada-tests/src/broadcasting_sum.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | b = na.array([3], parties[1], "B", SecretInteger) 11 | 12 | result1 = a + b 13 | result2 = a + Integer(2) 14 | 15 | return result1.output(parties[1], "my_output_1") + result2.output( 16 | parties[2], "my_output_2" 17 | ) 18 | -------------------------------------------------------------------------------- /tests/nada-tests/src/broadcasting_vec.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3, 3, 3], parties[0], "A", SecretInteger) 10 | b = na.ones([3, 3, 3]) 11 | c = na.zeros([3, 3, 3]) 12 | d = na.ones([3, 3, 3]) 13 | 14 | result = a + b + c - d 15 | 16 | return result.output(parties[1], "my_output") 17 | -------------------------------------------------------------------------------- /tests/nada-tests/src/chained_rational_operations.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(1) 8 | 9 | a = na.secret_rational("my_input_0", parties[0]) # 3.2 -> 209715 10 | b = na.secret_rational("my_input_1", parties[0]) # 4.5 -> 294912 11 | c = na.rational(1.2) # 1.2 -> 78643 12 | 13 | out_0 = ((a + b - c) * b) / (a + b - c) # b 14 | 15 | return [ 16 | Output(out_0.value, "my_output_0", parties[0]), 17 | ] 18 | -------------------------------------------------------------------------------- /tests/nada-tests/src/dot_product.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | b = na.array([3], parties[1], "B", SecretInteger) 11 | 12 | result = a.dot(b) 13 | 14 | return na.output(result, parties[1], "my_output") 15 | -------------------------------------------------------------------------------- /tests/nada-tests/src/dot_product_rational.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array((3,), parties[0], "A", na.SecretRational) 10 | b = na.array((3,), parties[1], "B", na.SecretRational) 11 | c = na.ones((3,), na.Rational) 12 | 13 | result = a.dot(b) 14 | 15 | result_b = a @ b 16 | 17 | result_c = a.dot(c) 18 | 19 | result_d = a @ c 20 | 21 | return ( 22 | na.output(result, parties[1], "my_output_a") 23 | + na.output(result_b, parties[1], "my_output_b") 24 | + na.output(result_c, parties[1], "my_output_c") 25 | + na.output(result_d, parties[1], "my_output_d") 26 | ) 27 | -------------------------------------------------------------------------------- /tests/nada-tests/src/functional_operations.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | 7 | def nada_main(): 8 | parties = na.parties(2) 9 | 10 | a = na.array([3], parties[0], "A", SecretInteger) 11 | b = na.array([3], parties[0], "B", na.SecretRational) 12 | 13 | a += Integer(0) 14 | b += na.rational(0) 15 | 16 | # Test all for a native NadaType 17 | _ = na.sum(a) 18 | _ = na.compress(a, [True, True, False], axis=0) 19 | _ = na.copy(a) 20 | _ = na.cumprod(a, axis=0) 21 | _ = na.cumsum(a, axis=0) 22 | _ = na.diagonal(a.reshape(1, 3)) 23 | _ = na.prod(a) 24 | _ = na.put(a, 2, Integer(20)) 25 | _ = na.ravel(a) 26 | _ = na.repeat(a, 12) 27 | _ = na.reshape(a, (1, 3)) 28 | _ = na.resize(a, (1, 3)) 29 | _ = na.squeeze(a.reshape(1, 3)) 30 | _ = na.swapaxes(a.reshape(1, 3), 1, 0) 31 | _ = na.take(a, 1) 32 | _ = na.trace(a) 33 | _ = na.transpose(a) 34 | _ = na.pad(a, 2) 35 | _ = na.pad(a, 2, mode="constant", constant_values=Integer(1)) 36 | _ = na.pad(a, 2, mode="edge") 37 | _ = na.pad(a, 2, mode="reflect") 38 | _ = na.pad(a, 2, mode="symmetric") 39 | _ = na.pad(a, 2, mode="wrap") 40 | with pytest.raises(TypeError): 41 | na.pad(a, 2, constant_values=na.rational(1)) 42 | _ = na.pad(b, 2) 43 | _ = na.pad(b, 2, mode="constant", constant_values=na.rational(1)) 44 | _ = na.pad(b, 2, mode="edge") 45 | _ = na.pad(b, 2, mode="reflect") 46 | _ = na.pad(b, 2, mode="symmetric") 47 | _ = na.pad(b, 2, mode="wrap") 48 | with pytest.raises(TypeError): 49 | na.pad(b, 2, constant_values=Integer(1)) 50 | _ = na.split(a, (1, 2)) 51 | 52 | pyfunc_out_1 = na.frompyfunc(lambda x: x + Integer(1), 1, 1)(a) 53 | assert isinstance(pyfunc_out_1, na.NadaArray), type(pyfunc_out_1).__name__ 54 | 55 | with pytest.raises(TypeError): 56 | 57 | class Counter: 58 | count = 0 59 | 60 | def mixed_types(): # generates alternating integers & rationals 61 | Counter.count += 1 62 | if Counter.count % 2 == 0: 63 | return Integer(1) 64 | return na.rational(1) 65 | 66 | na.frompyfunc(mixed_types, 1, 1)(a) 67 | 68 | with pytest.raises(TypeError): 69 | 70 | class Counter: 71 | count = 0 72 | 73 | def mixed_types(): # generates alternating integers & rationals 74 | Counter.count += 1 75 | if Counter.count % 2 == 0: 76 | return Integer(1) 77 | return na.rational(1) 78 | 79 | na.vectorize(mixed_types)(a) 80 | 81 | pyfunc_out_2, pyfunc_out_3 = na.frompyfunc( 82 | lambda x: (x + Integer(1), x + Integer(2)), 1, 2 83 | )(a) 84 | assert isinstance(pyfunc_out_2, na.NadaArray), type(pyfunc_out_2).__name__ 85 | assert isinstance(pyfunc_out_3, na.NadaArray), type(pyfunc_out_3).__name__ 86 | 87 | pyfunc_out_4 = na.frompyfunc(lambda x, y: x + y, 2, 1)(a, a) 88 | assert isinstance(pyfunc_out_4, na.NadaArray), type(pyfunc_out_4).__name__ 89 | 90 | vectorize_out_1 = na.vectorize(lambda x: x + Integer(1))(a) 91 | assert isinstance(vectorize_out_1, na.NadaArray), type(vectorize_out_1).__name__ 92 | 93 | vectorize_out_2, vectorize_out_3 = na.vectorize( 94 | lambda x: (x + Integer(1), x + Integer(2)) 95 | )(a) 96 | assert isinstance(vectorize_out_2, na.NadaArray), type(vectorize_out_2).__name__ 97 | assert isinstance(vectorize_out_3, na.NadaArray), type(vectorize_out_3).__name__ 98 | 99 | vectorize_out_4 = na.vectorize(lambda x, y: x + y)(a, a) 100 | assert isinstance(vectorize_out_4, na.NadaArray), type(vectorize_out_4).__name__ 101 | 102 | # Test all for a Rational type 103 | _ = na.sum(b) 104 | _ = na.compress(b, [True, True, False], axis=0) 105 | _ = na.copy(b) 106 | _ = na.cumprod(b, axis=0) 107 | _ = na.cumsum(b, axis=0) 108 | _ = na.diagonal(b.reshape(1, 3)) 109 | _ = na.prod(b) 110 | _ = na.put(b, 2, na.rational(20, is_scaled=True)) 111 | _ = na.ravel(b) 112 | _ = na.repeat(b, 12) 113 | _ = na.reshape(b, (1, 3)) 114 | _ = na.resize(b, (1, 3)) 115 | _ = na.squeeze(b.reshape(1, 3)) 116 | _ = na.swapaxes(b.reshape(1, 3), 1, 0) 117 | _ = na.take(b, 1) 118 | _ = na.trace(b) 119 | _ = na.transpose(b) 120 | _ = na.pad(b, 2) 121 | _ = na.pad(b, 2, mode="edge") 122 | _ = na.pad(b, 2, mode="reflect") 123 | _ = na.pad(b, 2, mode="symmetric") 124 | _ = na.pad(b, 2, mode="wrap") 125 | _ = na.split(b, (1, 2)) 126 | 127 | pyfunc_out_5 = na.frompyfunc(lambda x: x + na.rational(1), 1, 1)(b) 128 | assert isinstance(pyfunc_out_5, na.NadaArray), type(pyfunc_out_4).__name__ 129 | 130 | pyfunc_out_6, pyfunc_out_7 = na.frompyfunc( 131 | lambda x: (x + na.rational(1), x + na.rational(2)), 1, 2 132 | )(b) 133 | assert isinstance(pyfunc_out_6, na.NadaArray), type(pyfunc_out_6).__name__ 134 | assert isinstance(pyfunc_out_7, na.NadaArray), type(pyfunc_out_7).__name__ 135 | 136 | pyfunc_out_8 = na.frompyfunc(lambda x, y: x + y, 2, 1)(b, b) 137 | assert isinstance(pyfunc_out_8, na.NadaArray), type(pyfunc_out_8).__name__ 138 | 139 | vectorize_out_5 = na.vectorize(lambda x: x + na.rational(1))(b) 140 | assert isinstance(vectorize_out_5, na.NadaArray), type(pyfunc_out_4).__name__ 141 | 142 | vectorize_out_6, vectorize_out_7 = na.vectorize( 143 | lambda x: (x + na.rational(1), x + na.rational(2)) 144 | )(b) 145 | assert isinstance(vectorize_out_6, na.NadaArray), type(vectorize_out_6).__name__ 146 | assert isinstance(vectorize_out_7, na.NadaArray), type(vectorize_out_7).__name__ 147 | 148 | vectorize_out_8 = na.vectorize(lambda x, y: x + y)(b, b) 149 | assert isinstance(vectorize_out_8, na.NadaArray), type(vectorize_out_8).__name__ 150 | 151 | # Generative functions 152 | _ = na.eye(3, nada_type=na.Rational) 153 | _ = na.eye(3, nada_type=Integer) 154 | _ = na.arange(3, nada_type=na.Rational) 155 | _ = na.arange(3, nada_type=UnsignedInteger) 156 | _ = na.linspace(1, 4, 2, nada_type=na.Rational) 157 | _ = na.linspace(1, 4, 2, nada_type=Integer) 158 | 159 | return a.output(parties[1], "my_output_A") + b.output(parties[1], "my_output_B") 160 | -------------------------------------------------------------------------------- /tests/nada-tests/src/fxpmath_arrays.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | 7 | def nada_main(): 8 | 9 | parties = na.parties(2) 10 | 11 | # We use na.SecretRational to create a secret rational number for party 0 12 | a = na.secret_rational("my_input_0", parties[0]) 13 | 14 | c = na.NadaArray(np.array([a, na.rational(1.5)])) 15 | 16 | result_sign = c.sign() 17 | result_abs = c.abs() 18 | result_exp = c.exp() 19 | result_log = c.log() 20 | result_rec_NR = c.reciprocal(method="NR") 21 | result_rec_log = c.reciprocal(method="log") 22 | result_isqrt = c.inv_sqrt() 23 | result_sqrt = c.sqrt() 24 | result_sin = c.sin() 25 | result_cos = c.cos() 26 | result_tan = c.tan() 27 | result_tanh = c.tanh() 28 | result_tanh_che = c.tanh(method="chebyshev") 29 | result_tanh_motz = c.tanh(method="motzkin") 30 | result_sig = c.sigmoid() 31 | result_sig_che = c.sigmoid(method="chebyshev") 32 | result_sig_motz = c.sigmoid(method="motzkin") 33 | result_gelu = c.gelu() 34 | result_gelu_motz = c.gelu(method="motzkin") 35 | result_silu = c.silu() 36 | result_silu_che = c.silu(method_sigmoid="chebyshev") 37 | result_silu_motz = c.silu(method_sigmoid="motzkin") 38 | 39 | final_result = ( 40 | result_sign.output(parties[1], "result_sign") 41 | + result_abs.output(parties[1], "result_abs") 42 | + result_exp.output(parties[1], "result_exp") 43 | + result_log.output(parties[1], "result_log") 44 | + result_rec_NR.output(parties[1], "result_rec_NR") 45 | + result_rec_log.output(parties[1], "result_rec_log") 46 | + result_isqrt.output(parties[1], "result_isqrt") 47 | + result_sqrt.output(parties[1], "result_sqrt") 48 | + result_sin.output(parties[1], "result_sin") 49 | + result_cos.output(parties[1], "result_cos") 50 | + result_tan.output(parties[1], "result_tan") 51 | + result_tanh.output(parties[1], "result_tanh") 52 | + result_tanh_che.output(parties[1], "result_tanh_che") 53 | + result_tanh_motz.output(parties[1], "result_tanh_motz") 54 | + result_sig.output(parties[1], "result_sig") 55 | + result_sig_che.output(parties[1], "result_sig_che") 56 | + result_sig_motz.output(parties[1], "result_sig_motz") 57 | + result_gelu.output(parties[1], "result_gelu") 58 | + result_gelu_motz.output(parties[1], "result_gelu_motz") 59 | + result_silu.output(parties[1], "result_silu") 60 | + result_silu_che.output(parties[1], "result_silu_che") 61 | + result_silu_motz.output(parties[1], "result_silu_motz") 62 | ) 63 | 64 | return final_result 65 | -------------------------------------------------------------------------------- /tests/nada-tests/src/fxpmath_funcs.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | 8 | parties = na.parties(2) 9 | 10 | # We use na.SecretRational to create a secret rational number for party 0 11 | a = na.secret_rational("my_input_0", parties[0]) 12 | 13 | result_exp = na.exp(a * na.rational(2)) 14 | result_log = na.log(a * na.rational(100)) 15 | result_rec_NR = na.reciprocal(a * na.rational(2), method="NR") 16 | result_rec_log = na.reciprocal(a * na.rational(4), method="log") 17 | result_isqrt = na.inv_sqrt(a * na.rational(210)) 18 | result_sqrt = na.sqrt(a * na.rational(16)) 19 | result_sin = na.sin(a * na.rational(2.1)) 20 | result_cos = na.cos(a * na.rational(2.1)) 21 | result_tan = na.tan(a * na.rational(4.8)) 22 | result_tanh = na.tanh(a * na.rational(1.3)) 23 | result_tanh_che = na.tanh(a * na.rational(0.3), method="chebyshev") 24 | result_tanh_motz = na.tanh(a * na.rational(0.4), method="motzkin") 25 | result_sig = na.sigmoid(a * na.rational(0.1)) 26 | result_sig_che = na.sigmoid(a * na.rational(-0.1), method="chebyshev") 27 | result_sig_motz = na.sigmoid(a * na.rational(10), method="motzkin") 28 | result_gelu = na.gelu(a * na.rational(-13)) 29 | result_gelu_motz = na.gelu(a * na.rational(-13), method="motzkin") 30 | result_silu = na.silu(a * na.rational(10)) 31 | result_silu_che = na.silu(a * na.rational(-10), method_sigmoid="chebyshev") 32 | result_silu_motz = na.silu(a * na.rational(0), method_sigmoid="motzkin") 33 | 34 | return [ 35 | Output(result_exp.value, "result_exp", parties[1]), 36 | Output(result_log.value, "result_log", parties[1]), 37 | Output(result_rec_NR.value, "result_rec_NR", parties[1]), 38 | Output(result_rec_log.value, "result_rec_log", parties[1]), 39 | Output(result_isqrt.value, "result_isqrt", parties[1]), 40 | Output(result_sqrt.value, "result_sqrt", parties[1]), 41 | Output(result_sin.value, "result_sin", parties[1]), 42 | Output(result_cos.value, "result_cos", parties[1]), 43 | Output(result_tan.value, "result_tan", parties[1]), 44 | Output(result_tanh.value, "result_tanh", parties[1]), 45 | Output(result_tanh_che.value, "result_tanh_che", parties[1]), 46 | Output(result_tanh_motz.value, "result_tanh_motz", parties[1]), 47 | Output(result_sig.value, "result_sig", parties[1]), 48 | Output(result_sig_che.value, "result_sig_che", parties[1]), 49 | Output(result_sig_motz.value, "result_sig_motz", parties[1]), 50 | Output(result_gelu.value, "result_gelu", parties[1]), 51 | Output(result_gelu_motz.value, "result_gelu_motz", parties[1]), 52 | Output(result_silu.value, "result_silu", parties[1]), 53 | Output(result_silu_che.value, "result_silu_che", parties[1]), 54 | Output(result_silu_motz.value, "result_silu_motz", parties[1]), 55 | ] 56 | -------------------------------------------------------------------------------- /tests/nada-tests/src/fxpmath_methods.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | 8 | parties = na.parties(2) 9 | 10 | # We use na.SecretRational to create a secret rational number for party 0 11 | a = na.secret_rational("my_input_0", parties[0]) 12 | 13 | result_exp = (a * na.rational(2)).exp() 14 | result_log = (a * na.rational(100)).log() 15 | result_rec_NR = (a * na.rational(2)).reciprocal(method="NR") 16 | result_rec_log = (a * na.rational(4)).reciprocal(method="log") 17 | result_isqrt = (a * na.rational(210)).inv_sqrt() 18 | result_sqrt = (a * na.rational(16)).sqrt() 19 | result_sin = (a * na.rational(2.1)).sin() 20 | result_cos = (a * na.rational(2.1)).cos() 21 | result_tan = (a * na.rational(4.8)).tan() 22 | result_tanh = (a * na.rational(1.3)).tanh() 23 | result_tanh_che = (a * na.rational(0.3)).tanh(method="chebyshev") 24 | result_tanh_motz = (a * na.rational(0.4)).tanh(method="motzkin") 25 | result_sig = (a * na.rational(0.1)).sigmoid() 26 | result_sig_che = (a * na.rational(-0.1)).sigmoid(method="chebyshev") 27 | result_sig_motz = (a * na.rational(10)).sigmoid(method="motzkin") 28 | result_gelu = (a * na.rational(-13)).gelu() 29 | result_gelu_motz = (a * na.rational(-13)).gelu(method="motzkin") 30 | result_silu = (a * na.rational(10)).silu() 31 | result_silu_che = (a * na.rational(-10)).silu(method_sigmoid="chebyshev") 32 | result_silu_motz = (a * na.rational(0)).silu(method_sigmoid="motzkin") 33 | 34 | return [ 35 | Output(result_exp.value, "result_exp", parties[1]), 36 | Output(result_log.value, "result_log", parties[1]), 37 | Output(result_rec_NR.value, "result_rec_NR", parties[1]), 38 | Output(result_rec_log.value, "result_rec_log", parties[1]), 39 | Output(result_isqrt.value, "result_isqrt", parties[1]), 40 | Output(result_sqrt.value, "result_sqrt", parties[1]), 41 | Output(result_sin.value, "result_sin", parties[1]), 42 | Output(result_cos.value, "result_cos", parties[1]), 43 | Output(result_tan.value, "result_tan", parties[1]), 44 | Output(result_tanh.value, "result_tanh", parties[1]), 45 | Output(result_tanh_che.value, "result_tanh_che", parties[1]), 46 | Output(result_tanh_motz.value, "result_tanh_motz", parties[1]), 47 | Output(result_sig.value, "result_sig", parties[1]), 48 | Output(result_sig_che.value, "result_sig_che", parties[1]), 49 | Output(result_sig_motz.value, "result_sig_motz", parties[1]), 50 | Output(result_gelu.value, "result_gelu", parties[1]), 51 | Output(result_gelu_motz.value, "result_gelu_motz", parties[1]), 52 | Output(result_silu.value, "result_silu", parties[1]), 53 | Output(result_silu_che.value, "result_silu_che", parties[1]), 54 | Output(result_silu_motz.value, "result_silu_motz", parties[1]), 55 | ] 56 | -------------------------------------------------------------------------------- /tests/nada-tests/src/gauss_jordan.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | from nada_numpy.array import NadaArray 6 | 7 | # from nada_crypto import random_lu_matrix, public_modular_inverse 8 | 9 | LOG_SCALE = 16 10 | SCALE = 1 << LOG_SCALE 11 | PRIME_64 = 18446744072637906947 12 | PRIME_128 = 340282366920938463463374607429104828419 13 | PRIME_256 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98C00003 14 | PRIME = PRIME_64 15 | 16 | 17 | def public_modular_inverse( 18 | value: Integer | UnsignedInteger, modulo: int 19 | ) -> PublicUnsignedInteger | UnsignedInteger: 20 | """ 21 | Calculates the modular inverse of a value with respect to a prime modulus. 22 | 23 | Args: 24 | `value`: The value for which the modular inverse is to be calculated. 25 | `modulo`: The prime modulo with respect to which the modular inverse is to be calculated. 26 | 27 | Returns: 28 | The modular inverse of the value with respect to the modulo. 29 | 30 | Raises: 31 | Exception: If the input type is not a `PublicUnsignedInteger` or `UnsignedInteger`. 32 | """ 33 | return value ** UnsignedInteger(modulo - 2) 34 | 35 | 36 | def gauss_jordan_zn(mat: na.NadaArray, modulo: int): 37 | """ 38 | Perform Gauss-Jordan elimination on Z_n on a given matrix. 39 | 40 | Parameters: 41 | - `matrix` (numpy.ndarray): The input matrix to perform Gauss-Jordan elimination on. 42 | - `modulo` (int): The modulo representing the field `Z_n` 43 | 44 | Returns: 45 | numpy.ndarray: The reduced row echelon form of the input matrix. 46 | """ 47 | 48 | # Make a copy of the matrix to avoid modifying the original 49 | rows = mat.inner.shape[0] 50 | cols = mat.inner.shape[1] 51 | 52 | # Forward elimination 53 | for i in range(rows): 54 | # Find pivot row 55 | pivot_row = i 56 | while pivot_row < rows and (mat[pivot_row][i] == UnsignedInteger(0)) is Boolean( 57 | True 58 | ): 59 | pivot_row += 1 60 | 61 | # Swap pivot row with current row 62 | mat[[i, pivot_row]] = mat[[pivot_row, i]] 63 | 64 | # Scale pivot row to have leading 1 65 | diagonal_element = mat[i][i] 66 | pivot_inv = public_modular_inverse(diagonal_element, modulo) 67 | 68 | mat[i] = mat[i] * pivot_inv 69 | 70 | # Perform row operations to eliminate entries below pivot 71 | for j in range(i + 1, rows): 72 | factor = mat[j][i] 73 | mat[j] -= mat[i] * factor 74 | 75 | # Backward elimination 76 | for i in range(rows - 1, -1, -1): 77 | for j in range(i - 1, -1, -1): 78 | factor = mat[j][i] 79 | mat[j] -= mat[i] * factor 80 | 81 | return mat 82 | 83 | 84 | def nada_main(): 85 | parties = na.parties(3) 86 | 87 | A = na.array([3, 3], parties[0], "A", nada_type=SecretUnsignedInteger) 88 | 89 | A = A.to_public() 90 | A_inv = gauss_jordan_zn(A, PRIME) 91 | outputs = na.output(A_inv, parties[2], "my_output") 92 | 93 | return outputs 94 | -------------------------------------------------------------------------------- /tests/nada-tests/src/generate_array.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | party = Party("party_0") 8 | 9 | a = SecretInteger(Input("a", party)) 10 | 11 | ones1 = na.ones([2, 3]) 12 | ones2 = na.ones_like(ones1) 13 | 14 | zeros1 = na.zeros([2, 3]) 15 | zeros2 = na.zeros_like(zeros1) 16 | 17 | alphas1 = na.alphas([2, 3], alpha=a) 18 | alphas2 = na.alphas_like(alphas1, alpha=a) 19 | 20 | two_a = alphas1 + alphas2 21 | 22 | out = two_a + zeros1 + zeros2 + ones1 + ones2 23 | 24 | return out.output(party, "my_output") 25 | -------------------------------------------------------------------------------- /tests/nada-tests/src/get_attr.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | 11 | result = Integer(0) 12 | for i in range(a.shape[0]): # GET ATTR 13 | result += a[i] # GET ITEM 14 | 15 | return na.output(result, parties[1], "my_output") 16 | -------------------------------------------------------------------------------- /tests/nada-tests/src/get_item.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | 11 | result = a[0] + a[1] + a[2] 12 | 13 | return na.output(result, parties[1], "my_output") 14 | -------------------------------------------------------------------------------- /tests/nada-tests/src/get_vec.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3, 3], parties[0], "A", SecretInteger) 10 | 11 | result = a[:, 0] + a[:, 1] + a[:, 2] # Sum by columns 12 | 13 | return result.output(parties[1], "my_output") 14 | -------------------------------------------------------------------------------- /tests/nada-tests/src/hstack.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | b = na.array([3], parties[1], "B", SecretInteger) 11 | 12 | c = a.hstack(b) 13 | 14 | c += Integer(0) 15 | 16 | return c.output(parties[2], "my_output") 17 | -------------------------------------------------------------------------------- /tests/nada-tests/src/logistic_regression.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | weights = na.array((3,), parties[0], "A", SecretInteger) 10 | # This example does not support an array since we work with secret integers 11 | bias = SecretInteger(Input("bias", parties[0])) 12 | x = na.array((3,), parties[1], "B", SecretInteger) 13 | 14 | res = weights.dot(x) 15 | res += bias 16 | 17 | res = weights @ x 18 | res += bias 19 | 20 | return na.output(res, parties[2], "my_output") 21 | -------------------------------------------------------------------------------- /tests/nada-tests/src/logistic_regression_rational.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | weights = na.array((3,), parties[0], "A", na.SecretRational) 10 | # bias = na.secret_rational("bias", parties[0]) 11 | bias = na.array((1,), parties[0], "bias", na.SecretRational) 12 | x = na.array((3,), parties[1], "B", na.SecretRational) 13 | 14 | res = weights.dot(x) 15 | res += bias 16 | 17 | res = weights @ x 18 | res += bias 19 | 20 | return na.output(res, parties[2], "my_output") 21 | -------------------------------------------------------------------------------- /tests/nada-tests/src/matrix_multiplication.py: -------------------------------------------------------------------------------- 1 | # Step 0: Nada Numpy is imported with this line 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | 7 | def nada_main(): 8 | # Step 1: We use Nada Numpy wrapper to create "Party0", "Party1" and "Party2" 9 | parties = na.parties(3) 10 | 11 | # Step 2: Party0 creates an array of dimension (3 x 3) with name "A" 12 | a = na.array([3, 3], parties[0], "A", SecretInteger) 13 | 14 | # Step 3: Party1 creates an array of dimension (3 x 3) with name "B" 15 | b = na.array([3, 3], parties[1], "B", SecretInteger) 16 | 17 | # Step 4: The result is of computing the dot product between the two which is another (3 x 3) matrix 18 | result = a @ b 19 | 20 | # Step 5: We can use result.output() to produce the output for Party2 and variable name "my_output" 21 | return result.output(parties[1], "my_output") 22 | -------------------------------------------------------------------------------- /tests/nada-tests/src/matrix_multiplication_rational.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3, 3], parties[0], "A", na.SecretRational) 10 | 11 | b = na.array([3, 3], parties[1], "B", na.SecretRational) 12 | 13 | c = na.array((3,), parties[2], "C", na.SecretRational) 14 | 15 | d = na.ones([3, 3], na.Rational) 16 | 17 | result_a = a @ b 18 | 19 | result_b = a @ c 20 | 21 | result_c = a @ d 22 | 23 | result_d = d @ a 24 | 25 | return ( 26 | result_a.output(parties[1], "my_output") 27 | + result_b.output(parties[1], "my_output_b") 28 | + result_c.output(parties[1], "my_output_c") 29 | + result_d.output(parties[1], "my_output_d") 30 | ) 31 | -------------------------------------------------------------------------------- /tests/nada-tests/src/matrix_multiplication_rational_multidim.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([2, 1, 1, 2, 2, 2], parties[0], "A", na.SecretRational) 10 | 11 | b = na.array([2, 1, 1, 2, 2, 2], parties[1], "B", na.SecretRational) 12 | 13 | c = na.array((2,), parties[2], "C", na.SecretRational) 14 | 15 | d = na.ones([2, 1, 1, 2, 2, 2], na.Rational) 16 | 17 | result_a = a @ b 18 | 19 | result_b = a @ c 20 | 21 | result_c = a @ d 22 | 23 | result_d = d @ a 24 | 25 | return ( 26 | result_a.output(parties[1], "my_output") 27 | + result_b.output(parties[1], "my_output_b") 28 | + result_c.output(parties[1], "my_output_c") 29 | + result_d.output(parties[1], "my_output_d") 30 | ) 31 | -------------------------------------------------------------------------------- /tests/nada-tests/src/new_array.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(2) 8 | 9 | a = na.array([3, 3, 3], parties[0], "A", SecretInteger) 10 | 11 | b = a[0] 12 | c = a[0, 0] 13 | 14 | assert isinstance(a[0, 0, 0], SecretInteger), "a[0][0] should be a SecretInteger" 15 | assert isinstance(a[0], na.NadaArray), "a[0] should be a NadaArray" 16 | 17 | a += Integer(0) 18 | return a.output(parties[1], "my_output") 19 | -------------------------------------------------------------------------------- /tests/nada-tests/src/private_inverse.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | from nada_numpy.array import NadaArray 6 | 7 | LOG_SCALE = 16 8 | SCALE = 1 << LOG_SCALE 9 | PRIME_64 = 18446744072637906947 10 | PRIME_128 = 340282366920938463463374607429104828419 11 | PRIME_256 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF98C00003 12 | PRIME = PRIME_64 13 | 14 | 15 | def public_modular_inverse( 16 | value: Integer | UnsignedInteger, modulo: int 17 | ) -> PublicUnsignedInteger | UnsignedInteger: 18 | """ 19 | Calculates the modular inverse of a public value with respect to a prime modulus. 20 | 21 | Args: 22 | `value`: The value for which the modular inverse is to be calculated. 23 | `modulo`: The prime modulo with respect to which the modular inverse is to be calculated. 24 | 25 | Returns: 26 | The modular inverse of the value with respect to the modulo. 27 | 28 | Raises: 29 | Exception: If the input type is not a `PublicUnsignedInteger` or `UnsignedInteger`. 30 | """ 31 | # if not(type(value) == PublicUnsignedInteger or type(value) == UnsignedInteger): 32 | # raise Exception("Invalid input type: Expected PublicUnsignedInteger or UnsignedInteger") 33 | return value ** UnsignedInteger(modulo - 2) 34 | 35 | 36 | def private_modular_inverse( 37 | secret: SecretUnsignedInteger, modulo: int 38 | ) -> SecretUnsignedInteger: 39 | """ 40 | Calculate the modular inverse of a secret value with respect to a prime modulo. 41 | 42 | Args: 43 | secret (SecretUnsignedInteger): The secret value for which the modular inverse is to be calculated. 44 | modulo (int): The prime modulo with respect to which the modular inverse is to be calculated. 45 | 46 | Returns: 47 | SecretUnsignedInteger: The modular inverse of the secret value with respect to the modulo. 48 | """ 49 | r = SecretUnsignedInteger.random() 50 | 51 | ra = r * secret # Masking our secret 52 | ra_revealed = ra.to_public() # Revealing the masked secret 53 | 54 | ra_inv = public_modular_inverse( 55 | ra_revealed, modulo 56 | ) # Compute the inverse of the masked secret 57 | 58 | a_inv = ra_inv * r # Unmask the secret with the random shares 59 | 60 | return a_inv 61 | 62 | 63 | def nada_main(): 64 | parties = na.parties(3) 65 | 66 | a = na.array([1], parties[0], "A", nada_type=SecretUnsignedInteger) 67 | a_inv = private_modular_inverse(a[0], PRIME) 68 | 69 | result = a_inv * a[0] 70 | # A_inv = na.random([3, 3], nada_type=SecretInteger) 71 | # A_inv = [[SecretInteger.random() for i in range(3)] for j in range(3)] 72 | # outputs = na.output(A_inv, parties[2], "my_output") 73 | 74 | return na.output(result, parties[2], "my_output") 75 | -------------------------------------------------------------------------------- /tests/nada-tests/src/random_array.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | 7 | def nada_main(): 8 | parties = na.parties(2) 9 | 10 | a = na.array([3], parties[0], "A", SecretInteger) 11 | 12 | supported_types = [SecretInteger, SecretUnsignedInteger, na.SecretRational] 13 | 14 | for supported_type in supported_types: 15 | random_arr_1 = na.random((1,), supported_type) 16 | assert random_arr_1.shape == (1,), random_arr_1.shape 17 | assert isinstance(random_arr_1.item(0), supported_type) 18 | 19 | random_arr_2 = na.random((4, 2, 3), supported_type) 20 | assert random_arr_2.shape == (4, 2, 3), random_arr_2.shape 21 | assert isinstance(random_arr_2.item(0), supported_type) 22 | 23 | a += Integer(0) 24 | 25 | with pytest.raises(Exception): 26 | na.random((1,), PublicInteger) 27 | 28 | with pytest.raises(Exception): 29 | na.random((1,), na.Rational) 30 | 31 | return a.output(parties[1], "my_output") 32 | -------------------------------------------------------------------------------- /tests/nada-tests/src/rational_advanced.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | from nada_dsl import * 4 | 5 | import nada_numpy as na 6 | 7 | 8 | def nada_main(): 9 | parties = na.parties(1) 10 | 11 | a = na.secret_rational("my_input_0", parties[0]) 12 | b = na.rational(np.float16(1.2)) 13 | 14 | out_0 = a.rescale_up().divide_no_rescale(b) 15 | out_1 = a.mul_no_rescale(b) 16 | out_2 = b.rescale_up().divide_no_rescale(a) 17 | out_3 = b.mul_no_rescale(a) 18 | 19 | na.set_log_scale(10) 20 | c = na.public_rational("my_input_1", parties[0]) 21 | na.reset_log_scale() 22 | 23 | # Raise exception because different scaling 24 | with pytest.raises(Exception): 25 | a * c 26 | with pytest.raises(Exception): 27 | a.mul(c) 28 | with pytest.raises(Exception): 29 | a.mul_no_rescale(c) 30 | 31 | out_4 = a.mul(c, ignore_scale=True) 32 | out_5 = a.divide(c, ignore_scale=True) 33 | out_6 = a.mul_no_rescale(c, ignore_scale=True) 34 | out_7 = a.divide_no_rescale(c, ignore_scale=True) 35 | 36 | out_8 = b.mul(c, ignore_scale=True) 37 | out_9 = b.divide(c, ignore_scale=True) 38 | out_10 = b.mul_no_rescale(c, ignore_scale=True) 39 | out_11 = b.divide_no_rescale(c, ignore_scale=True) 40 | 41 | return [ 42 | Output(out_0.value, "out_0", parties[0]), 43 | Output(out_1.value, "out_1", parties[0]), 44 | Output(out_2.value, "out_2", parties[0]), 45 | Output(out_3.value, "out_3", parties[0]), 46 | Output(out_4.value, "out_4", parties[0]), 47 | Output(out_5.value, "out_5", parties[0]), 48 | Output(out_6.value, "out_6", parties[0]), 49 | Output(out_7.value, "out_7", parties[0]), 50 | Output(out_8.value, "out_8", parties[0]), 51 | Output(out_9.value, "out_9", parties[0]), 52 | Output(out_10.value, "out_10", parties[0]), 53 | Output(out_11.value, "out_11", parties[0]), 54 | ] 55 | -------------------------------------------------------------------------------- /tests/nada-tests/src/rational_arithmetic.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 3, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "\n", 11 | "LOG_SCALE = 16\n", 12 | "SCALE = 1 << LOG_SCALE" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 9, 18 | "metadata": {}, 19 | "outputs": [ 20 | { 21 | "name": "stdout", 22 | "output_type": "stream", 23 | "text": [ 24 | "Input values are:\n", 25 | "[3.2, 4.5, 1.2]\n", 26 | "The transformed values are: \n", 27 | "3.2 -> 209715\n", 28 | "4.5 -> 294912\n", 29 | "1.2 -> 78643\n" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "print(\"Input values are:\")\n", 35 | "values = [3.2, 4.5, 1.2]\n", 36 | "print(values)\n", 37 | "\n", 38 | "\n", 39 | "transform = lambda x: round(x * SCALE)\n", 40 | "values_ = [transform(x) for x in values]\n", 41 | "\n", 42 | "print(\"The transformed values are: \")\n", 43 | "for v, v_ in zip(values, values_):\n", 44 | " print(f\"{v} -> {v_}\")" 45 | ] 46 | } 47 | ], 48 | "metadata": { 49 | "kernelspec": { 50 | "display_name": "Python 3", 51 | "language": "python", 52 | "name": "python3" 53 | }, 54 | "language_info": { 55 | "codemirror_mode": { 56 | "name": "ipython", 57 | "version": 3 58 | }, 59 | "file_extension": ".py", 60 | "mimetype": "text/x-python", 61 | "name": "python", 62 | "nbconvert_exporter": "python", 63 | "pygments_lexer": "ipython3", 64 | "version": "3.12.3" 65 | } 66 | }, 67 | "nbformat": 4, 68 | "nbformat_minor": 2 69 | } 70 | -------------------------------------------------------------------------------- /tests/nada-tests/src/rational_arithmetic.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(1) 8 | 9 | a = na.rational(3.2) 10 | b = na.public_rational("my_input_0", parties[0]) # 4.5 11 | c = na.secret_rational("my_input_1", parties[0]) # 1.2 12 | 13 | out_0 = a + b # 7.7 -> 504627 14 | out_1 = a - b # -1.3 -> -85197 15 | out_2 = a * b # 14.4 -> 943717 16 | out_3 = a / b # 0.7111111111111111 -> 46603 17 | 18 | out_4 = a + c # 4.4 -> 288358 19 | out_5 = a - c # 2.0 -> 131072 20 | out_6 = a * c # 3.84 -> 251657 21 | out_7 = a / c # 2.6666666666666665 -> 174763 22 | 23 | out_8 = a**8 # 10.24 -> 720568320 24 | out_9 = b**0 # 1 -> 65536 25 | out_10 = a ** (-3) # 0.03051757812499999 -> 2000 26 | 27 | return [ 28 | Output(out_0.value, "my_output_0", parties[0]), 29 | Output(out_1.value, "my_output_1", parties[0]), 30 | Output(out_2.value, "my_output_2", parties[0]), 31 | Output(out_3.value, "my_output_3", parties[0]), 32 | Output(out_4.value, "my_output_4", parties[0]), 33 | Output(out_5.value, "my_output_5", parties[0]), 34 | Output(out_6.value, "my_output_6", parties[0]), 35 | Output(out_7.value, "my_output_7", parties[0]), 36 | Output(out_8.value, "my_output_8", parties[0]), 37 | Output(out_9.value, "my_output_9", parties[0]), 38 | Output(out_10.value, "my_output_10", parties[0]), 39 | ] 40 | -------------------------------------------------------------------------------- /tests/nada-tests/src/rational_array.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(2) 8 | 9 | a = na.array([3], parties[0], "A", nada_type=na.SecretRational) 10 | b = na.array([3], parties[0], "B", nada_type=na.SecretRational) 11 | c = na.ones([3], na.Rational) 12 | 13 | out_0 = a + b 14 | out_1 = a - b 15 | out_2 = a * b 16 | out_3 = a / b 17 | 18 | out_4 = a + c 19 | out_5 = a - c 20 | out_6 = a * c 21 | out_7 = a / c 22 | 23 | out_8 = -a 24 | 25 | return ( 26 | out_0.output(parties[1], "out_0") 27 | + out_1.output(parties[1], "out_1") 28 | + out_2.output(parties[1], "out_2") 29 | + out_3.output(parties[1], "out_3") 30 | + out_4.output(parties[1], "out_4") 31 | + out_5.output(parties[1], "out_5") 32 | + out_6.output(parties[1], "out_6") 33 | + out_7.output(parties[1], "out_7") 34 | + out_8.output(parties[1], "out_8") 35 | ) 36 | -------------------------------------------------------------------------------- /tests/nada-tests/src/rational_comparison.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(1) 8 | 9 | a = na.rational(3.2) 10 | b = na.rational(4.5) 11 | c = na.secret_rational("my_input_0", parties[0]) # 1.2 12 | d = na.public_rational("my_input_1", parties[0]) # 3.2 13 | 14 | out_0 = a < b # True 15 | out_1 = a <= b # True 16 | out_2 = a > b # False 17 | out_3 = a >= b # False 18 | out_4 = a == b # False 19 | 20 | out_5 = a < c # False 21 | out_6 = a <= c # False 22 | out_7 = a > c # True 23 | out_8 = a >= c # True 24 | out_9 = a == c # False 25 | 26 | out_10 = a == d # True 27 | # out_11 = a != d # False 28 | out_12 = a <= d # True 29 | out_13 = a >= d # True 30 | out_14 = a > d # False 31 | out_15 = a < d # False 32 | 33 | return [ 34 | Output(out_0, "my_output_0", parties[0]), 35 | Output(out_1, "my_output_1", parties[0]), 36 | Output(out_2, "my_output_2", parties[0]), 37 | Output(out_3, "my_output_3", parties[0]), 38 | Output(out_4, "my_output_4", parties[0]), 39 | Output(out_5, "my_output_5", parties[0]), 40 | Output(out_6, "my_output_6", parties[0]), 41 | Output(out_7, "my_output_7", parties[0]), 42 | Output(out_8, "my_output_8", parties[0]), 43 | Output(out_9, "my_output_9", parties[0]), 44 | Output(out_10, "my_output_10", parties[0]), 45 | # Output(out_11, "my_output_11", parties[0]), 46 | Output(out_12, "my_output_12", parties[0]), 47 | Output(out_13, "my_output_13", parties[0]), 48 | Output(out_14, "my_output_14", parties[0]), 49 | Output(out_15, "my_output_15", parties[0]), 50 | ] 51 | -------------------------------------------------------------------------------- /tests/nada-tests/src/rational_if_else.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | 7 | def nada_main(): 8 | parties = na.parties(3) 9 | 10 | a = na.secret_rational("A", parties[0]) 11 | b = na.secret_rational("B", parties[1]) 12 | c = na.secret_rational("C", parties[2]) 13 | d = SecretInteger(Input("D", parties[0])) 14 | e = SecretUnsignedInteger(Input("E", parties[0])) 15 | 16 | out_0 = (a > b).if_else(a, b) 17 | assert isinstance(out_0, na.SecretRational), type(out_0).__name__ 18 | out_1 = (a >= b).if_else(a, b) 19 | assert isinstance(out_1, na.SecretRational), type(out_1).__name__ 20 | out_2 = (a < b).if_else(a, b) 21 | assert isinstance(out_2, na.SecretRational), type(out_2).__name__ 22 | out_3 = (a <= b).if_else(a, b) 23 | assert isinstance(out_3, na.SecretRational), type(out_3).__name__ 24 | 25 | out_4 = (a > na.rational(1)).if_else(d, Integer(1)) 26 | assert isinstance(out_4, SecretInteger), type(out_4).__name__ 27 | out_5 = (a >= na.rational(1)).if_else(d, Integer(1)) 28 | assert isinstance(out_5, SecretInteger), type(out_5).__name__ 29 | out_6 = (a < na.rational(1)).if_else(d, Integer(1)) 30 | assert isinstance(out_6, SecretInteger), type(out_6).__name__ 31 | out_7 = (a <= na.rational(1)).if_else(d, Integer(1)) 32 | assert isinstance(out_7, SecretInteger), type(out_7).__name__ 33 | 34 | out_8 = (na.rational(0) > na.rational(1)).if_else(na.rational(1), na.rational(2)) 35 | assert isinstance(out_8, na.Rational), type(out_8).__name__ 36 | out_9 = (na.rational(0) >= na.rational(1)).if_else(na.rational(2), na.rational(1)) 37 | assert isinstance(out_9, na.Rational), type(out_9).__name__ 38 | out_10 = (na.rational(0) < na.rational(1)).if_else(Integer(1), Integer(2)) 39 | assert isinstance(out_10, PublicInteger), type(out_10).__name__ 40 | out_11 = (na.rational(0) <= na.rational(1)).if_else(Integer(1), d) 41 | assert isinstance(out_11, SecretInteger), type(out_11).__name__ 42 | out_12 = (na.rational(0) <= na.rational(1)).if_else(UnsignedInteger(1), e) 43 | assert isinstance(out_12, SecretUnsignedInteger), type(out_12).__name__ 44 | out_13 = (na.rational(0) <= na.rational(1)).if_else( 45 | UnsignedInteger(1), UnsignedInteger(0) 46 | ) 47 | assert isinstance(out_13, PublicUnsignedInteger), type(out_13).__name__ 48 | 49 | # Incompatible input types 50 | with pytest.raises(Exception): 51 | (a > Integer(1)).if_else(na.rational(0), na.rational(1)) 52 | with pytest.raises(Exception): 53 | (Integer(1) > a).if_else(na.rational(0), na.rational(1)) 54 | with pytest.raises(Exception): 55 | (a > d).if_else(na.rational(0), na.rational(1)) 56 | with pytest.raises(Exception): 57 | (d > a).if_else(na.rational(0), na.rational(1)) 58 | with pytest.raises(Exception): 59 | (na.rational(1) > Integer(1)).if_else(na.rational(0), na.rational(1)) 60 | with pytest.raises(Exception): 61 | (Integer(1) > na.rational(1)).if_else(na.rational(0), na.rational(1)) 62 | with pytest.raises(Exception): 63 | (na.rational(1) > d).if_else(na.rational(0), na.rational(1)) 64 | with pytest.raises(Exception): 65 | (d > na.rational(1)).if_else(na.rational(0), na.rational(1)) 66 | 67 | # Incompatible return types 68 | with pytest.raises(Exception): 69 | (a > b).if_else(c, Integer(1)) 70 | with pytest.raises(Exception): 71 | (a > b).if_else(Integer(1), c) 72 | with pytest.raises(Exception): 73 | (a > b).if_else(c, d) 74 | with pytest.raises(Exception): 75 | (a > b).if_else(d, c) 76 | with pytest.raises(Exception): 77 | (na.rational(0) > na.rational(1)).if_else(c, Integer(1)) 78 | with pytest.raises(Exception): 79 | (na.rational(0) > na.rational(1)).if_else(Integer(1), c) 80 | with pytest.raises(Exception): 81 | (na.rational(0) > na.rational(1)).if_else(c, d) 82 | with pytest.raises(Exception): 83 | (na.rational(0) > na.rational(1)).if_else(d, c) 84 | 85 | return ( 86 | na.output(out_0, parties[2], "out_0") 87 | + na.output(out_1, parties[2], "out_1") 88 | + na.output(out_2, parties[2], "out_2") 89 | + na.output(out_3, parties[2], "out_3") 90 | + na.output(out_4, parties[2], "out_4") 91 | + na.output(out_5, parties[2], "out_5") 92 | + na.output(out_6, parties[2], "out_6") 93 | + na.output(out_7, parties[2], "out_7") 94 | + na.output(out_8, parties[2], "out_8") 95 | + na.output(out_9, parties[2], "out_9") 96 | + na.output(out_10, parties[2], "out_10") 97 | + na.output(out_11, parties[2], "out_11") 98 | + na.output(out_12, parties[2], "out_12") 99 | + na.output(out_13, parties[2], "out_13") 100 | ) 101 | -------------------------------------------------------------------------------- /tests/nada-tests/src/rational_operability.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | 7 | def nada_main(): 8 | parties = na.parties(2) 9 | 10 | a = na.array([3], parties[0], "A", na.SecretRational) 11 | b = na.array([3], parties[0], "B", na.Rational) 12 | c = na.array([3], parties[0], "B", SecretInteger) 13 | d = na.array([3], parties[0], "B", PublicInteger) 14 | 15 | with pytest.raises(TypeError): 16 | a + c 17 | with pytest.raises(TypeError): 18 | a + d 19 | with pytest.raises(TypeError): 20 | b + c 21 | with pytest.raises(TypeError): 22 | b + d 23 | with pytest.raises(TypeError): 24 | c + a 25 | with pytest.raises(TypeError): 26 | d + a 27 | with pytest.raises(TypeError): 28 | c + b 29 | with pytest.raises(TypeError): 30 | d + b 31 | 32 | result = a + b 33 | 34 | return result.output(parties[1], "my_output") 35 | -------------------------------------------------------------------------------- /tests/nada-tests/src/rational_scaling.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | 7 | def nada_main(): 8 | parties = na.parties(2) 9 | 10 | na.set_log_scale(32) 11 | 12 | a = na.array([3], parties[0], "A", na.SecretRational) 13 | 14 | b = a + na.rational(2) # both values are on scale 32 15 | 16 | na.reset_log_scale() # resets the log scale back to the original default (16) 17 | 18 | with pytest.raises(ValueError): 19 | b + na.rational(2) # scale 32 rational + scale 16 rational 20 | 21 | with pytest.warns(): 22 | na.set_log_scale(2**16) # extremely high - most likely a mistake 23 | 24 | with pytest.warns(): 25 | na.set_log_scale(0) # extremely low - most likely a mistake 26 | 27 | return b.output(parties[1], "my_output") 28 | -------------------------------------------------------------------------------- /tests/nada-tests/src/reveal.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | from nada_numpy.nada_typing import NadaInteger, NadaRational 6 | 7 | 8 | def nada_main(): 9 | parties = na.parties(3) 10 | 11 | a = na.array([3, 3], parties[0], "A", SecretInteger) 12 | b = na.array([3, 3], parties[1], "B", na.SecretRational) 13 | 14 | c = a.to_public() 15 | d = b.to_public() 16 | 17 | assert c.dtype == NadaInteger, c.dtype 18 | assert c.shape == a.shape 19 | 20 | assert d.dtype == NadaRational, d.dtype 21 | assert d.shape == b.shape 22 | 23 | return na.output(c, parties[2], "my_output_A") + na.output( 24 | d, parties[2], "my_output_B" 25 | ) 26 | -------------------------------------------------------------------------------- /tests/nada-tests/src/secret_rational_arithmetic.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(1) 8 | 9 | a = na.secret_rational("my_input_0", parties[0]) # 3.2 -> 209715 10 | b = na.secret_rational("my_input_1", parties[0]) # 4.5 -> 294912 11 | c = na.rational(1.2) # 1.2 -> 78643 12 | 13 | out_0 = a + b # 7.7 -> 504627 14 | out_1 = a - b # -1.3 -> -85197 15 | out_2 = a * b # 14.4 -> 943717 16 | out_3 = a / b # 0.7111111111111111 -> 46603 17 | 18 | out_4 = a + c # 4.4 -> 288358 19 | out_5 = a - c # 2.0 -> 131072 20 | out_6 = a * c # 3.84 -> 251657 21 | out_7 = a / c # 2.6666666666666665 -> 174763 22 | 23 | out_8 = a**8 # 10.24 -> 720568320 24 | out_9 = -a # -3.2 -> -209716 25 | 26 | # These for now do not make much sense for users 27 | # out_10 = a << UnsignedInteger(1) 28 | # out_11 = a >> UnsignedInteger(1) 29 | 30 | out_12 = a.to_public() 31 | 32 | # These for now do not make much sense for users 33 | # out_13 = a.trunc_pr(Integer(0)) 34 | 35 | return [ 36 | Output(out_0.value, "my_output_0", parties[0]), 37 | Output(out_1.value, "my_output_1", parties[0]), 38 | Output(out_2.value, "my_output_2", parties[0]), 39 | Output(out_3.value, "my_output_3", parties[0]), 40 | Output(out_4.value, "my_output_4", parties[0]), 41 | Output(out_5.value, "my_output_5", parties[0]), 42 | Output(out_6.value, "my_output_6", parties[0]), 43 | Output(out_7.value, "my_output_7", parties[0]), 44 | Output(out_8.value, "my_output_8", parties[0]), 45 | Output(out_9.value, "my_output_9", parties[0]), 46 | # Output(out_10.value, "my_output_10", parties[0]), 47 | # Output(out_11.value, "my_output_11", parties[0]), 48 | Output(out_12.value, "my_output_12", parties[0]), 49 | # Output(out_13.value, "my_output_13", parties[0]), 50 | ] 51 | -------------------------------------------------------------------------------- /tests/nada-tests/src/secret_rational_comparison.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(1) 8 | a = na.secret_rational("my_input_0", parties[0]) # 3.2 -> 209715 9 | b = na.secret_rational("my_input_1", parties[0]) # 4.5 -> 294912 10 | c = na.rational(1.2) # 1.2 -> 78643 11 | d = na.secret_rational("my_input_2", parties[0]) # 3.2 -> 294912 12 | 13 | out_0 = a < b # True 14 | out_1 = a <= b # True 15 | out_2 = a > b # False 16 | out_3 = a >= b # False 17 | out_4 = a == b # False 18 | 19 | out_5 = a < c # False 20 | out_6 = a <= c # False 21 | out_7 = a > c # True 22 | out_8 = a >= c # True 23 | out_9 = a == c # False 24 | 25 | out_10 = a == d # True 26 | # out_11 = a != d # False 27 | out_12 = a <= d # True 28 | out_13 = a >= d # True 29 | out_14 = a > d # False 30 | out_15 = a < d # False 31 | 32 | return [ 33 | Output(out_0, "my_output_0", parties[0]), 34 | Output(out_1, "my_output_1", parties[0]), 35 | Output(out_2, "my_output_2", parties[0]), 36 | Output(out_3, "my_output_3", parties[0]), 37 | Output(out_4, "my_output_4", parties[0]), 38 | Output(out_5, "my_output_5", parties[0]), 39 | Output(out_6, "my_output_6", parties[0]), 40 | Output(out_7, "my_output_7", parties[0]), 41 | Output(out_8, "my_output_8", parties[0]), 42 | Output(out_9, "my_output_9", parties[0]), 43 | Output(out_10, "my_output_10", parties[0]), 44 | # Output(out_11, "my_output_11", parties[0]), 45 | Output(out_12, "my_output_12", parties[0]), 46 | Output(out_13, "my_output_13", parties[0]), 47 | Output(out_14, "my_output_14", parties[0]), 48 | Output(out_15, "my_output_15", parties[0]), 49 | ] 50 | -------------------------------------------------------------------------------- /tests/nada-tests/src/set_item.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3, 3], parties[0], "A", SecretInteger) 10 | 11 | result = a[0, :] + a[1, :] + a[2, :] 12 | 13 | result[0] = result[0] + Integer(1) 14 | 15 | result[1] = result[0] + Integer(1) 16 | 17 | result[2] = result[1] + Integer(1) 18 | 19 | return result.output(parties[1], "my_output") 20 | -------------------------------------------------------------------------------- /tests/nada-tests/src/shape.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3, 3], parties[0], "A", SecretInteger) 10 | 11 | result = na.zeros([3]) 12 | for i in range(a.shape[1]): 13 | result += a[:, i] # Sum by columns 14 | 15 | return result.output(parties[1], "my_output") 16 | -------------------------------------------------------------------------------- /tests/nada-tests/src/shuffle.py: -------------------------------------------------------------------------------- 1 | """Main Nada program""" 2 | 3 | from typing import List 4 | 5 | import numpy as np 6 | from nada_dsl import Integer, Output, PublicInteger, SecretInteger 7 | 8 | import nada_numpy as na 9 | from nada_numpy import shuffle 10 | 11 | 12 | def bool_to_int(bool): 13 | """Casting bool to int""" 14 | return bool.if_else(Integer(0), Integer(1)) 15 | 16 | 17 | def count(vec, element): 18 | """ 19 | Counts the number of times element is in vec. 20 | """ 21 | 22 | result = Integer(0) 23 | for e in vec: 24 | b = ~(element == e) 25 | int_b = bool_to_int(b) 26 | result += int_b 27 | 28 | return result 29 | 30 | 31 | def nada_main() -> List[Output]: 32 | 33 | n = 8 34 | 35 | parties = na.parties(2) 36 | a = na.array([n], parties[0], "A", na.Rational) 37 | b = na.array([n], parties[0], "B", na.SecretRational) 38 | c = na.array([n], parties[0], "C", PublicInteger) 39 | d = na.array([n], parties[0], "D", SecretInteger) 40 | 41 | # As a function 42 | 43 | shuffled_a = shuffle(a) 44 | shuffled_b = shuffle(b) 45 | shuffled_c = shuffle(c) 46 | shuffled_d = shuffle(d) 47 | 48 | # 1. Show shuffle works for Rational, SecretRational, PublicInteger and SecretInteger 49 | result_a = shuffled_a - shuffled_a 50 | result_b = shuffled_b - shuffled_b 51 | result_c = shuffled_c - shuffled_c 52 | result_d = shuffled_d - shuffled_d 53 | 54 | # 2. Randomness: show at least one element is in a different position 55 | # true if equal 56 | diff_position_bool = [a[i] == shuffled_a[i] for i in range(n)] 57 | # cast to int (true -> 0 and false -> 1) 58 | diff_position = np.array([bool_to_int(element) for element in diff_position_bool]) 59 | # add them 60 | sum = diff_position.sum() 61 | # if all are equal => all are 0 => sum is zero 62 | at_least_one_diff_element = sum > Integer(0) 63 | 64 | # 3. Show elements are preserved: 65 | check = Integer(0) 66 | for ai in a: 67 | nr_ai_in_shufled_a = count(shuffled_a, ai) 68 | nr_ai_in_a = count(a, ai) 69 | check += bool_to_int(nr_ai_in_shufled_a == nr_ai_in_a) 70 | elements_are_preserved = check == Integer(0) 71 | 72 | # As a method 73 | 74 | shuffled_method_a = a.shuffle() 75 | shuffled_method_b = b.shuffle() 76 | shuffled_method_c = c.shuffle() 77 | shuffled_method_d = d.shuffle() 78 | 79 | # 1. Show shuffle works for Rational, SecretRational, PublicInteger and SecretInteger 80 | result_method_a = shuffled_method_a - shuffled_method_a 81 | result_method_b = shuffled_method_b - shuffled_method_b 82 | result_method_c = shuffled_method_c - shuffled_method_c 83 | result_method_d = shuffled_method_d - shuffled_method_d 84 | 85 | # 2. Randomness: show at least one element is in a different position 86 | # true if equal 87 | diff_position_bool_method = [a[i] == shuffled_method_a[i] for i in range(n)] 88 | # cast to int (true -> 0 and false -> 1) 89 | diff_position_method = np.array( 90 | [bool_to_int(element) for element in diff_position_bool_method] 91 | ) 92 | # add them 93 | sum_method = diff_position_method.sum() 94 | # if all are equal => all are 0 => sum is zero 95 | at_least_one_diff_element_method = sum_method > Integer(0) 96 | 97 | # 3. Show elements are preserved: 98 | check = Integer(0) 99 | for ai in a: 100 | nr_ai_in_shufled_a = count(shuffled_method_a, ai) 101 | nr_ai_in_a = count(a, ai) 102 | check += bool_to_int(nr_ai_in_shufled_a == nr_ai_in_a) 103 | elements_are_preserved_method = check == Integer(0) 104 | 105 | return ( 106 | na.output(result_a, parties[1], "my_output_a") 107 | + na.output(result_b, parties[1], "my_output_b") 108 | + na.output(result_c, parties[1], "my_output_c") 109 | + na.output(result_d, parties[1], "my_output_d") 110 | + na.output(result_method_a, parties[1], "my_output_method_a") 111 | + na.output(result_method_b, parties[1], "my_output_method_b") 112 | + na.output(result_method_c, parties[1], "my_output_method_c") 113 | + na.output(result_method_d, parties[1], "my_output_method_d") 114 | + na.output(at_least_one_diff_element, parties[1], "at_least_one_diff_element") 115 | + na.output( 116 | at_least_one_diff_element_method, 117 | parties[1], 118 | "at_least_one_diff_element_method", 119 | ) 120 | + na.output(elements_are_preserved, parties[1], "elements_are_preserved") 121 | + na.output( 122 | elements_are_preserved_method, parties[1], "elements_are_preserved_method" 123 | ) 124 | ) 125 | -------------------------------------------------------------------------------- /tests/nada-tests/src/sum.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | 11 | result = a.sum() 12 | 13 | return na.output(result, parties[1], "my_output") 14 | -------------------------------------------------------------------------------- /tests/nada-tests/src/supported_operations.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from nada_dsl import * 3 | 4 | import nada_numpy as na 5 | 6 | 7 | def nada_main(): 8 | parties = na.parties(2) 9 | 10 | a = na.array([3, 3], parties[0], "A", SecretInteger) 11 | 12 | assert isinstance(a.data, memoryview) 13 | assert a.flags["WRITEABLE"] 14 | assert a.itemsize == 8 15 | assert a.nbytes == 72 16 | assert a.ndim == 2 17 | assert a.strides == (24, 8) 18 | 19 | with pytest.raises(AttributeError): 20 | a.argsort() 21 | 22 | a = a.compress([True, True, False], axis=0) 23 | a = a.copy() 24 | a = a.cumprod(axis=0) 25 | a = a.cumsum(axis=1) 26 | a = a.diagonal() 27 | a = a.item(0) 28 | 29 | b = na.array([3, 3], parties[0], "B", SecretInteger) 30 | b = b.flatten() 31 | b = b.prod() 32 | 33 | c = na.array([3, 3], parties[0], "C", SecretInteger) 34 | _ = c.put(3, Integer(20)) 35 | with pytest.raises(TypeError): 36 | c.put(3, na.rational(1)) 37 | c = c.ravel() 38 | c = c.reshape((3, 3)) 39 | c = c.sum(axis=1) 40 | c = c.take(1) 41 | 42 | d = na.array([1], parties[0], "D", SecretInteger) 43 | d = d.repeat(12) 44 | d.resize((4, 3)) 45 | twelve = Integer(d.size) 46 | d = d.squeeze() 47 | d = d.swapaxes(1, 0) 48 | d = d.T 49 | d = (d + twelve).item(0) 50 | 51 | e = na.array([3, 3], parties[0], "E", SecretInteger) 52 | five = Integer(sum(e.shape)) 53 | e = e.transpose() 54 | e = e.trace() 55 | e = e + five 56 | 57 | f = na.array([1], parties[0], "F", SecretInteger) 58 | f.fill(Integer(40)) 59 | f.itemset(0, f.item(0) + Integer(2)) 60 | with pytest.raises(Exception): 61 | f.itemset(0, na.rational(1)) 62 | f = f.tolist()[0] 63 | 64 | return ( 65 | na.output(a, parties[1], "out_0") 66 | + na.output(b, parties[1], "out_1") 67 | + na.output(c, parties[1], "out_2") 68 | + na.output(d, parties[1], "out_3") 69 | + na.output(e, parties[1], "out_4") 70 | + na.output(f, parties[1], "out_5") 71 | ) 72 | -------------------------------------------------------------------------------- /tests/nada-tests/src/supported_operations_return_types.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | from nada_dsl import * 4 | 5 | import nada_numpy as na 6 | 7 | 8 | def check_array(array): 9 | assert isinstance(array, na.NadaArray), f"{array} is not a NadaArray: {type(array)}" 10 | assert isinstance( 11 | array.inner, np.ndarray 12 | ), f"{array.inner} is not a numpy array: {type(array.inner)}" 13 | 14 | 15 | def nada_main(): 16 | parties = na.parties(2) 17 | 18 | a = na.array([3, 3], parties[0], "A", SecretInteger) 19 | 20 | assert isinstance(a.data, memoryview) 21 | assert a.flags["WRITEABLE"] 22 | check_array(a) 23 | assert a.itemsize == 8 24 | assert a.nbytes == 72 25 | assert a.ndim == 2 26 | assert a.strides == (24, 8) 27 | 28 | with pytest.raises(AttributeError): 29 | a.argsort() 30 | 31 | a = a.compress([True, True, False], axis=0) 32 | check_array(a) 33 | a = a.copy() 34 | check_array(a) 35 | a = a.cumprod(axis=0) 36 | check_array(a) 37 | a = a.cumsum(axis=1) 38 | check_array(a) 39 | a = a.diagonal() 40 | check_array(a) 41 | a = a.item(0) # Not array 42 | 43 | b = na.array([3, 3], parties[0], "B", SecretInteger) 44 | check_array(b) 45 | b = b.flatten() 46 | check_array(b) 47 | b = b.prod() # Not an array 48 | 49 | c = na.array([3, 3], parties[0], "C", SecretInteger) 50 | _ = c.put(3, Integer(20)) 51 | check_array(c) 52 | c = c.ravel() 53 | check_array(c) 54 | c = c.reshape((3, 3)) 55 | check_array(c) 56 | c = c.sum(axis=1) 57 | check_array(c) 58 | c = c.take(1) # Not an array 59 | 60 | d = na.array([1], parties[0], "D", SecretInteger) 61 | check_array(d) 62 | d = d.repeat(12) 63 | check_array(d) 64 | d.resize((4, 3)) 65 | check_array(d) 66 | twelve = Integer(d.size) 67 | d = d.squeeze() 68 | check_array(d) 69 | d = d.swapaxes(1, 0) 70 | check_array(d) 71 | d = d.T 72 | check_array(d) 73 | d = (d + twelve).item(0) # Not an array 74 | 75 | e = na.array([3, 3], parties[0], "E", SecretInteger) 76 | check_array(e) 77 | five = Integer(sum(e.shape)) 78 | e = e.transpose() 79 | check_array(e) 80 | e = e.trace() # Not an array 81 | e = e + five # Not an array either 82 | 83 | f = na.array([1], parties[0], "F", SecretInteger) 84 | check_array(f) 85 | f.fill(Integer(40)) 86 | check_array(f) 87 | f.itemset(0, f.item(0) + Integer(2)) 88 | check_array(f) 89 | assert isinstance(f.tolist(), list) 90 | f = f.tolist()[0] # Not an array 91 | 92 | return ( 93 | na.output(a, parties[1], "out_0") 94 | + na.output(b, parties[1], "out_1") 95 | + na.output(c, parties[1], "out_2") 96 | + na.output(d, parties[1], "out_3") 97 | + na.output(e, parties[1], "out_4") 98 | + na.output(f, parties[1], "out_5") 99 | ) 100 | -------------------------------------------------------------------------------- /tests/nada-tests/src/type_guardrails.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | from nada_dsl import * 4 | 5 | import nada_numpy as na 6 | 7 | 8 | def nada_main(): 9 | parties = na.parties(3) 10 | 11 | a = na.array([3], parties[0], "A", na.SecretRational) 12 | b = na.array([3], parties[0], "B", SecretInteger) 13 | c = na.array([3], parties[0], "C", PublicInteger) 14 | 15 | a += na.rational(0) 16 | b += Integer(0) 17 | c += Integer(0) 18 | 19 | # Mixed secretrational - secretinteger 20 | with pytest.raises(TypeError): 21 | na.NadaArray(np.array([a[0], b[0]])) 22 | # Mixed rational - secretinteger 23 | with pytest.raises(TypeError): 24 | na.NadaArray(np.array([na.rational(1), b[0]])) 25 | # Mixed secretrational - publicinteger 26 | with pytest.raises(TypeError): 27 | na.NadaArray(np.array([a[0], c[0]])) 28 | # Mixed rational - publicinteger 29 | with pytest.raises(TypeError): 30 | na.NadaArray(np.array([na.rational(1), c[0]])) 31 | # Mixed secretrational - publicinteger 32 | with pytest.raises(TypeError): 33 | na.NadaArray(np.array([a[0], c[0]])) 34 | 35 | # All-rational 36 | na.NadaArray(np.array([a[0], na.rational(1)])) 37 | # All-integer 38 | na.NadaArray(np.array([b[0], Integer(1)])) 39 | na.NadaArray(np.array([c[0], Integer(1)])) 40 | 41 | return a.output(parties[1], "my_output") 42 | -------------------------------------------------------------------------------- /tests/nada-tests/src/vstack.py: -------------------------------------------------------------------------------- 1 | from nada_dsl import * 2 | 3 | import nada_numpy as na 4 | 5 | 6 | def nada_main(): 7 | parties = na.parties(3) 8 | 9 | a = na.array([3], parties[0], "A", SecretInteger) 10 | b = na.array([3], parties[1], "B", SecretInteger) 11 | 12 | c = a.vstack(b) 13 | 14 | c += Integer(0) 15 | 16 | return c.output(parties[2], "my_output") 17 | -------------------------------------------------------------------------------- /tests/nada-tests/target/.gitignore: -------------------------------------------------------------------------------- 1 | # This directory is kept purposely, so that no compilation errors arise. 2 | # Ignore everything in this directory 3 | * 4 | # Except this file 5 | !.gitignore -------------------------------------------------------------------------------- /tests/nada-tests/tests/array_attributes.yaml: -------------------------------------------------------------------------------- 1 | program: array_attributes 2 | inputs: 3 | A_1: 3 4 | A_2: 3 5 | A_0: 3 6 | expected_outputs: 7 | my_output_0: 3 8 | my_output_1: 3 9 | my_output_2: 3 10 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/array_comparison.yaml: -------------------------------------------------------------------------------- 1 | program: array_comparison 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 100 5 | A_0_2: 0 6 | B_0_0: 8 7 | B_0_1: 101 8 | B_0_2: 0 9 | expected_outputs: 10 | my_output_1_0_0: false 11 | my_output_1_0_1: false 12 | my_output_1_0_2: true 13 | my_output_2_0_0: true 14 | my_output_2_0_1: false 15 | my_output_2_0_2: false 16 | my_output_3_0_0: true 17 | my_output_3_0_1: true 18 | my_output_3_0_2: false 19 | my_output_4_0_0: false 20 | my_output_4_0_1: true 21 | my_output_4_0_2: true 22 | my_output_5_0_0: true 23 | my_output_5_0_1: true 24 | my_output_5_0_2: false 25 | my_output_6_0_0: false 26 | my_output_6_0_1: false 27 | my_output_6_0_2: true 28 | my_output_7_0_0: true 29 | my_output_7_0_1: true 30 | my_output_7_0_2: true 31 | my_output_8_0_0: true 32 | my_output_8_0_1: false 33 | my_output_8_0_2: true 34 | my_output_9_0_0: false 35 | my_output_9_0_1: false 36 | my_output_9_0_2: false 37 | my_output_10_0_0: false 38 | my_output_10_0_1: true 39 | my_output_10_0_2: false 40 | my_output_11_0_0: false 41 | my_output_11_0_1: false 42 | my_output_11_0_2: true 43 | my_output_12_0_0: true 44 | my_output_12_0_1: true 45 | my_output_12_0_2: false 46 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/array_statistics.yaml: -------------------------------------------------------------------------------- 1 | program: array_statistics 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 2 5 | A_1_0: 4 6 | A_1_1: 5 7 | A_2_0: 4 8 | A_2_1: 5 9 | B_0_0: 1 10 | B_0_1: 2 11 | B_1_0: 4 12 | B_1_1: 5 13 | B_2_0: 4 14 | B_2_1: 5 15 | expected_outputs: 16 | a_mean_arr_1: 4 17 | b_mean_arr_0: 3 18 | b_sum: 21 19 | b_mean: 3 20 | a_mean_arr_0: 3 21 | b_mean_arr_1: 4 22 | b_sum_arr_0: 9 23 | a_sum: 21 24 | a_sum_arr_0: 9 25 | b_sum_arr_1: 12 26 | a_mean: 3 27 | a_sum_arr_1: 12 28 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/base.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | program: base 3 | inputs: 4 | array1_0: 5 5 | array1_1: 3 6 | array1_2: 42 7 | array1_3: -12 8 | array1_4: 3 9 | array1_5: 1 10 | array1_6: -5 11 | array1_7: 3 12 | array1_8: 1 13 | array1_9: 23 14 | 15 | array2_0: 1 16 | array2_1: 2 17 | array2_2: -52 18 | array2_3: 11 19 | 20 | array3_0: 53 21 | array3_1: 2 22 | array3_2: 15 23 | array3_3: 37 24 | array3_4: -1 25 | array3_5: 1 26 | array3_6: -2 27 | 28 | expected_outputs: 29 | out_array_0: -52 30 | out_array_1: -12 31 | out_array_2: -5 32 | out_array_3: -2 33 | out_array_4: -1 34 | out_array_5: 1 35 | out_array_6: 1 36 | out_array_7: 1 37 | out_array_8: 1 38 | out_array_9: 2 39 | out_array_10: 2 40 | out_array_11: 3 41 | out_array_12: 3 42 | out_array_13: 3 43 | out_array_14: 5 44 | out_array_15: 11 45 | out_array_16: 15 46 | out_array_17: 23 47 | out_array_18: 37 48 | out_array_19: 42 49 | out_array_20: 53 -------------------------------------------------------------------------------- /tests/nada-tests/tests/broadcasting_div.yaml: -------------------------------------------------------------------------------- 1 | program: broadcasting_div 2 | inputs: 3 | A_0: 10 4 | A_1: 8 5 | A_2: 4 6 | B_0: 10 7 | B_1: 4 8 | B_2: 2 9 | expected_outputs: 10 | my_output_1_0: 1 11 | my_output_1_1: 2 12 | my_output_1_2: 2 13 | my_output_2_0: 5 14 | my_output_2_1: 4 15 | my_output_2_2: 2 16 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/broadcasting_mul.yaml: -------------------------------------------------------------------------------- 1 | program: broadcasting_mul 2 | inputs: 3 | A_0: 10 4 | A_1: 8 5 | A_2: 4 6 | B_0: 10 7 | B_1: 4 8 | B_2: 2 9 | expected_outputs: 10 | my_output_1_0: 100 11 | my_output_1_1: 32 12 | my_output_1_2: 8 13 | my_output_2_0: 20 14 | my_output_2_1: 16 15 | my_output_2_2: 8 16 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/broadcasting_rationals.yaml: -------------------------------------------------------------------------------- 1 | program: broadcasting_rationals 2 | inputs: 3 | X_2: 196608 4 | y: 262144 5 | X_1: 131072 6 | X_0: 65536 7 | expected_outputs: 8 | my_output_a_0: 327680 9 | my_output_a_1: 393216 10 | my_output_a_2: 458752 11 | my_output_b_0: 327680 12 | my_output_b_1: 393216 13 | my_output_b_2: 458752 14 | my_output_c_0: -196608 15 | my_output_c_1: -131072 16 | my_output_c_2: -65536 17 | my_output_d_0: 196608 18 | my_output_d_1: 131072 19 | my_output_d_2: 65536 20 | my_output_e_0: 262144 21 | my_output_e_1: 524288 22 | my_output_e_2: 786432 23 | my_output_f_0: 262144 24 | my_output_f_1: 524288 25 | my_output_f_2: 786432 26 | my_output_g_0: 16384 27 | my_output_g_1: 32768 28 | my_output_g_2: 49152 29 | my_output_h_0: 262144 30 | my_output_h_1: 131072 31 | my_output_h_2: 87381 32 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/broadcasting_sub.yaml: -------------------------------------------------------------------------------- 1 | program: broadcasting_sub 2 | inputs: 3 | A_0: 10 4 | A_1: 8 5 | A_2: 4 6 | B_0: 10 7 | B_1: 4 8 | B_2: 2 9 | expected_outputs: 10 | my_output_1_0: 0 11 | my_output_1_1: 4 12 | my_output_1_2: 2 13 | my_output_2_0: 8 14 | my_output_2_1: 6 15 | my_output_2_2: 2 16 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/broadcasting_sum.yaml: -------------------------------------------------------------------------------- 1 | program: broadcasting_sum 2 | inputs: 3 | A_0: 10 4 | A_1: 8 5 | A_2: 4 6 | B_0: 10 7 | B_1: 4 8 | B_2: 2 9 | expected_outputs: 10 | my_output_1_0: 20 11 | my_output_1_1: 12 12 | my_output_1_2: 6 13 | my_output_2_0: 12 14 | my_output_2_1: 10 15 | my_output_2_2: 6 16 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/broadcasting_vec.yaml: -------------------------------------------------------------------------------- 1 | program: broadcasting_vec 2 | inputs: 3 | A_0_0_0: 3 4 | A_0_1_0: 3 5 | A_0_0_2: 3 6 | A_2_1_1: 3 7 | A_2_2_1: 3 8 | A_1_1_2: 3 9 | A_1_2_0: 3 10 | A_0_2_0: 3 11 | A_1_1_0: 3 12 | A_1_0_1: 3 13 | A_0_0_1: 3 14 | A_0_1_2: 3 15 | A_1_2_1: 3 16 | A_2_1_0: 3 17 | A_0_2_1: 3 18 | A_2_0_0: 3 19 | A_2_1_2: 3 20 | A_1_1_1: 3 21 | A_0_2_2: 3 22 | A_1_0_0: 3 23 | A_1_0_2: 3 24 | A_2_0_1: 3 25 | A_2_0_2: 3 26 | A_2_2_0: 3 27 | A_2_2_2: 3 28 | A_1_2_2: 3 29 | A_0_1_1: 3 30 | expected_outputs: 31 | my_output_1_0_2: 3 32 | my_output_0_1_2: 3 33 | my_output_0_2_2: 3 34 | my_output_1_2_0: 3 35 | my_output_2_1_1: 3 36 | my_output_0_0_0: 3 37 | my_output_1_0_1: 3 38 | my_output_0_1_1: 3 39 | my_output_1_1_0: 3 40 | my_output_1_2_2: 3 41 | my_output_2_1_2: 3 42 | my_output_2_2_2: 3 43 | my_output_1_1_2: 3 44 | my_output_0_2_0: 3 45 | my_output_1_0_0: 3 46 | my_output_0_1_0: 3 47 | my_output_1_2_1: 3 48 | my_output_2_0_1: 3 49 | my_output_1_1_1: 3 50 | my_output_2_1_0: 3 51 | my_output_2_2_1: 3 52 | my_output_0_0_1: 3 53 | my_output_2_2_0: 3 54 | my_output_0_0_2: 3 55 | my_output_2_0_2: 3 56 | my_output_2_0_0: 3 57 | my_output_0_2_1: 3 58 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/chained_rational_operations.yaml: -------------------------------------------------------------------------------- 1 | program: chained_rational_operations 2 | inputs: 3 | my_input_0: 209715 4 | my_input_1: 294912 5 | expected_outputs: 6 | my_output_0: 294912 7 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/dot_product.yaml: -------------------------------------------------------------------------------- 1 | program: dot_product 2 | inputs: 3 | A_0: 3 4 | A_1: 3 5 | A_2: 3 6 | B_0: 3 7 | B_1: 3 8 | B_2: 3 9 | expected_outputs: 10 | my_output: 27 11 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/dot_product_rational.yaml: -------------------------------------------------------------------------------- 1 | program: dot_product_rational 2 | inputs: 3 | A_0: 65536 4 | A_1: 131072 5 | A_2: 196608 6 | B_0: 65536 7 | B_1: 131072 8 | B_2: 196608 9 | expected_outputs: 10 | my_output_a: 917504 11 | my_output_b: 917504 12 | my_output_c: 393216 13 | my_output_d: 393216 14 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/functional_operations.yaml: -------------------------------------------------------------------------------- 1 | program: functional_operations 2 | inputs: 3 | A_1: 3 4 | B_0: 3 5 | B_1: 3 6 | A_0: 3 7 | expected_outputs: 8 | my_output_A_0_0: 3 9 | my_output_B_0_1: 3 10 | my_output_A_0_2: 20 11 | my_output_B_0_2: 20 12 | my_output_A_0_1: 3 13 | my_output_B_0_0: 3 14 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/fxpmath_arrays.yaml: -------------------------------------------------------------------------------- 1 | program: fxpmath_arrays 2 | inputs: 3 | my_input_0: 3 4 | expected_outputs: 5 | result_sig_0: 32711 6 | result_sqrt_1: 80265 7 | result_sig_motz_1: 53477 8 | result_silu_0: 1 9 | result_gelu_0: 1 10 | result_tan_1: 888645 11 | result_tanh_1: 59434 12 | result_tan_0: 0 13 | result_exp_0: 65536 14 | result_sig_1: 53644 15 | result_silu_motz_1: 80215 16 | result_gelu_motz_1: 91629 17 | result_sig_che_1: 53580 18 | result_log_0: -614828 19 | result_sqrt_0: 45 20 | result_silu_che_0: 1 21 | result_rec_NR_1: 43692 22 | result_tanh_che_1: 65536 23 | result_gelu_motz_0: 103 24 | result_exp_1: 292119 25 | result_isqrt_1: 53510 26 | result_cos_1: 4846 27 | result_tanh_motz_1: 59394 28 | result_cos_0: 65536 29 | result_sig_che_0: 32768 30 | result_rec_log_0: 65040192 31 | result_sig_motz_0: 32686 32 | result_log_1: 26359 33 | result_tanh_0: -114 34 | result_sin_1: 65710 35 | result_gelu_1: 91839 36 | result_silu_che_1: 80370 37 | result_tanh_motz_0: -160 38 | result_tanh_che_0: 2 39 | result_rec_NR_0: 451510139 40 | result_rec_log_1: 34575 41 | result_silu_1: 80466 42 | result_isqrt_0: 989647 43 | result_silu_motz_0: 1 44 | result_sin_0: 0 45 | result_sign_1: 65536 46 | result_sign_0: 65536 47 | result_abs_1: 98304 48 | result_abs_0: 3 49 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/fxpmath_funcs.yaml: -------------------------------------------------------------------------------- 1 | program: fxpmath_funcs 2 | inputs: 3 | my_input_0: 65536 4 | expected_outputs: 5 | result_gelu: -13 6 | result_cos: -32566 7 | result_isqrt: -2341 8 | result_gelu_motz: 0 9 | result_silu: 655340 10 | result_sin: 57328 11 | result_tanh_che: 19089 12 | result_tanh_motz: 24917 13 | result_silu_che: 0 14 | result_sig_motz: 65536 15 | result_tanh: 56624 16 | result_rec_log: 15552 17 | result_sig_che: 31130 18 | result_sig: 34401 19 | result_tan: -922765 20 | result_sqrt: 262128 21 | result_log: 298843 22 | result_exp: 480249 23 | result_rec_NR: 32768 24 | result_silu_motz: 0 -------------------------------------------------------------------------------- /tests/nada-tests/tests/fxpmath_methods.yaml: -------------------------------------------------------------------------------- 1 | program: fxpmath_methods 2 | inputs: 3 | my_input_0: 65536 4 | expected_outputs: 5 | result_gelu: -13 6 | result_cos: -32566 7 | result_isqrt: -2341 8 | result_gelu_motz: 0 9 | result_silu: 655340 10 | result_sin: 57328 11 | result_tanh_che: 19089 12 | result_tanh_motz: 24917 13 | result_silu_che: 0 14 | result_sig_motz: 65536 15 | result_tanh: 56624 16 | result_rec_log: 15552 17 | result_sig_che: 31130 18 | result_sig: 34401 19 | result_tan: -922765 20 | result_sqrt: 262128 21 | result_log: 298843 22 | result_exp: 480249 23 | result_rec_NR: 32768 24 | result_silu_motz: 0 25 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/gauss_jordan.yaml: -------------------------------------------------------------------------------- 1 | program: gauss_jordan 2 | inputs: 3 | A_0_0: 2 4 | A_0_1: 4 5 | A_0_2: 6 6 | A_1_0: 1 7 | A_1_1: 3 8 | A_1_2: 5 9 | A_2_0: 3 10 | A_2_1: 1 11 | A_2_2: 2 12 | B: 456 13 | expected_outputs: 14 | my_output_0_0: 1 15 | my_output_0_1: 0 16 | my_output_0_2: 0 17 | my_output_1_0: 0 18 | my_output_1_1: 1 19 | my_output_1_2: 0 20 | my_output_2_0: 0 21 | my_output_2_1: 0 22 | my_output_2_2: 1 23 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/generate_array.yaml: -------------------------------------------------------------------------------- 1 | program: generate_array 2 | inputs: 3 | a: 3 4 | expected_outputs: 5 | my_output_0_0: 8 6 | my_output_0_1: 8 7 | my_output_0_2: 8 8 | my_output_1_0: 8 9 | my_output_1_1: 8 10 | my_output_1_2: 8 11 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/get_attr.yaml: -------------------------------------------------------------------------------- 1 | program: get_item 2 | inputs: 3 | A_1: 3 4 | A_0: 3 5 | A_2: 3 6 | expected_outputs: 7 | my_output: 9 8 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/get_item.yaml: -------------------------------------------------------------------------------- 1 | program: get_item 2 | inputs: 3 | A_1: 3 4 | A_0: 3 5 | A_2: 3 6 | expected_outputs: 7 | my_output: 9 8 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/get_vec.yaml: -------------------------------------------------------------------------------- 1 | program: get_vec 2 | inputs: 3 | A_2_1: 3 4 | A_0_0: 3 5 | A_1_1: 3 6 | A_1_2: 3 7 | A_0_1: 3 8 | A_0_2: 3 9 | A_1_0: 3 10 | A_2_2: 3 11 | A_2_0: 3 12 | expected_outputs: 13 | my_output_0: 9 14 | my_output_1: 9 15 | my_output_2: 9 16 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/hstack.yaml: -------------------------------------------------------------------------------- 1 | program: hstack 2 | inputs: 3 | A_1: 3 4 | A_2: 3 5 | B_1: 3 6 | A_0: 3 7 | B_2: 3 8 | B_0: 3 9 | expected_outputs: 10 | my_output_4: 3 11 | my_output_3: 3 12 | my_output_5: 3 13 | my_output_1: 3 14 | my_output_0: 3 15 | my_output_2: 3 16 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/logistic_regression.yaml: -------------------------------------------------------------------------------- 1 | program: logistic_regression 2 | inputs: 3 | B_1: 65536 4 | A_1: 65536 5 | B_2: 65536 6 | bias: 65536 7 | A_0: 65536 8 | B_0: 65536 9 | A_2: 65536 10 | expected_outputs: 11 | my_output: 12884967424 12 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/logistic_regression_rational.yaml: -------------------------------------------------------------------------------- 1 | program: logistic_regression_rational 2 | inputs: 3 | B_1: 65536 4 | A_2: 65536 5 | B_2: 65536 6 | A_0: 65536 7 | B_0: 65536 8 | bias_0: 65536 9 | A_1: 65536 10 | expected_outputs: 11 | my_output_0: 262144 12 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/matrix_multiplication.yaml: -------------------------------------------------------------------------------- 1 | program: matrix_multiplication 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 2 5 | A_0_2: 3 6 | A_1_0: 4 7 | A_1_1: 5 8 | A_1_2: 6 9 | A_2_0: 7 10 | A_2_1: 8 11 | A_2_2: 9 12 | B_0_0: 1 13 | B_0_1: 2 14 | B_0_2: 3 15 | B_1_0: 4 16 | B_1_1: 5 17 | B_1_2: 6 18 | B_2_0: 7 19 | B_2_1: 8 20 | B_2_2: 9 21 | expected_outputs: 22 | my_output_0_0: 30 23 | my_output_0_1: 36 24 | my_output_0_2: 42 25 | my_output_1_0: 66 26 | my_output_1_1: 81 27 | my_output_1_2: 96 28 | my_output_2_0: 102 29 | my_output_2_1: 126 30 | my_output_2_2: 150 31 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/matrix_multiplication_rational.yaml: -------------------------------------------------------------------------------- 1 | program: matrix_multiplication_rational 2 | inputs: 3 | A_0_0: 65536 4 | A_0_1: 131072 5 | A_0_2: 196608 6 | A_1_0: 262144 7 | A_1_1: 327680 8 | A_1_2: 393216 9 | A_2_0: 458752 10 | A_2_1: 524288 11 | A_2_2: 589824 12 | B_0_0: 65536 13 | B_0_1: 131072 14 | B_0_2: 196608 15 | B_1_0: 262144 16 | B_1_1: 327680 17 | B_1_2: 393216 18 | B_2_0: 458752 19 | B_2_1: 524288 20 | B_2_2: 589824 21 | C_0: 65536 22 | C_1: 0 23 | C_2: 0 24 | expected_outputs: 25 | my_output_0_0: 1966080 26 | my_output_0_1: 2359296 27 | my_output_0_2: 2752512 28 | my_output_1_0: 4325376 29 | my_output_1_1: 5308416 30 | my_output_1_2: 6291456 31 | my_output_2_0: 6684672 32 | my_output_2_1: 8257536 33 | my_output_2_2: 9830400 34 | my_output_b_0: 65536 35 | my_output_b_1: 262144 36 | my_output_b_2: 458752 37 | my_output_c_0_0: 393216 38 | my_output_c_0_1: 393216 39 | my_output_c_0_2: 393216 40 | my_output_c_1_0: 983040 41 | my_output_c_1_1: 983040 42 | my_output_c_1_2: 983040 43 | my_output_c_2_0: 1572864 44 | my_output_c_2_1: 1572864 45 | my_output_c_2_2: 1572864 46 | my_output_d_0_0: 786432 47 | my_output_d_0_1: 983040 48 | my_output_d_0_2: 1179648 49 | my_output_d_1_0: 786432 50 | my_output_d_1_1: 983040 51 | my_output_d_1_2: 1179648 52 | my_output_d_2_0: 786432 53 | my_output_d_2_1: 983040 54 | my_output_d_2_2: 1179648 55 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/matrix_multiplication_rational_multidim.yaml: -------------------------------------------------------------------------------- 1 | program: matrix_multiplication_rational_multidim 2 | inputs: 3 | A_0_0_0_0_0_0: 65536 4 | A_0_0_0_0_0_1: 131072 5 | A_0_0_0_0_1_0: 196608 6 | A_0_0_0_0_1_1: 262144 7 | A_0_0_0_1_0_0: 65536 8 | A_0_0_0_1_0_1: 131072 9 | A_0_0_0_1_1_0: 196608 10 | A_0_0_0_1_1_1: 262144 11 | A_1_0_0_0_0_0: 65536 12 | A_1_0_0_0_0_1: 131072 13 | A_1_0_0_0_1_0: 196608 14 | A_1_0_0_0_1_1: 262144 15 | A_1_0_0_1_0_0: 65536 16 | A_1_0_0_1_0_1: 131072 17 | A_1_0_0_1_1_0: 196608 18 | A_1_0_0_1_1_1: 262144 19 | B_0_0_0_0_0_0: 65536 20 | B_0_0_0_0_0_1: 131072 21 | B_0_0_0_0_1_0: 196608 22 | B_0_0_0_0_1_1: 262144 23 | B_0_0_0_1_0_0: 65536 24 | B_0_0_0_1_0_1: 131072 25 | B_0_0_0_1_1_0: 196608 26 | B_0_0_0_1_1_1: 262144 27 | B_1_0_0_0_0_0: 65536 28 | B_1_0_0_0_0_1: 131072 29 | B_1_0_0_0_1_0: 196608 30 | B_1_0_0_0_1_1: 262144 31 | B_1_0_0_1_0_0: 65536 32 | B_1_0_0_1_0_1: 131072 33 | B_1_0_0_1_1_0: 196608 34 | B_1_0_0_1_1_1: 262144 35 | C_0: 65536 36 | C_1: 0 37 | expected_outputs: 38 | my_output_0_0_0_0_0_0: 458752 39 | my_output_0_0_0_0_0_1: 655360 40 | my_output_0_0_0_0_1_0: 983040 41 | my_output_0_0_0_0_1_1: 1441792 42 | my_output_0_0_0_1_0_0: 458752 43 | my_output_0_0_0_1_0_1: 655360 44 | my_output_0_0_0_1_1_0: 983040 45 | my_output_0_0_0_1_1_1: 1441792 46 | my_output_1_0_0_0_0_0: 458752 47 | my_output_1_0_0_0_0_1: 655360 48 | my_output_1_0_0_0_1_0: 983040 49 | my_output_1_0_0_0_1_1: 1441792 50 | my_output_1_0_0_1_0_0: 458752 51 | my_output_1_0_0_1_0_1: 655360 52 | my_output_1_0_0_1_1_0: 983040 53 | my_output_1_0_0_1_1_1: 1441792 54 | my_output_b_0_0_0_0_0: 65536 55 | my_output_b_0_0_0_0_1: 196608 56 | my_output_b_0_0_0_1_0: 65536 57 | my_output_b_0_0_0_1_1: 196608 58 | my_output_b_1_0_0_0_0: 65536 59 | my_output_b_1_0_0_0_1: 196608 60 | my_output_b_1_0_0_1_0: 65536 61 | my_output_b_1_0_0_1_1: 196608 62 | my_output_c_0_0_0_0_0_0: 196608 63 | my_output_c_0_0_0_0_0_1: 196608 64 | my_output_c_0_0_0_0_1_0: 458752 65 | my_output_c_0_0_0_0_1_1: 458752 66 | my_output_c_0_0_0_1_0_0: 196608 67 | my_output_c_0_0_0_1_0_1: 196608 68 | my_output_c_0_0_0_1_1_0: 458752 69 | my_output_c_0_0_0_1_1_1: 458752 70 | my_output_c_1_0_0_0_0_0: 196608 71 | my_output_c_1_0_0_0_0_1: 196608 72 | my_output_c_1_0_0_0_1_0: 458752 73 | my_output_c_1_0_0_0_1_1: 458752 74 | my_output_c_1_0_0_1_0_0: 196608 75 | my_output_c_1_0_0_1_0_1: 196608 76 | my_output_c_1_0_0_1_1_0: 458752 77 | my_output_c_1_0_0_1_1_1: 458752 78 | my_output_d_0_0_0_0_0_0: 262144 79 | my_output_d_0_0_0_0_0_1: 393216 80 | my_output_d_0_0_0_0_1_0: 262144 81 | my_output_d_0_0_0_0_1_1: 393216 82 | my_output_d_0_0_0_1_0_0: 262144 83 | my_output_d_0_0_0_1_0_1: 393216 84 | my_output_d_0_0_0_1_1_0: 262144 85 | my_output_d_0_0_0_1_1_1: 393216 86 | my_output_d_1_0_0_0_0_0: 262144 87 | my_output_d_1_0_0_0_0_1: 393216 88 | my_output_d_1_0_0_0_1_0: 262144 89 | my_output_d_1_0_0_0_1_1: 393216 90 | my_output_d_1_0_0_1_0_0: 262144 91 | my_output_d_1_0_0_1_0_1: 393216 92 | my_output_d_1_0_0_1_1_0: 262144 93 | my_output_d_1_0_0_1_1_1: 393216 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/new_array.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | program: new_array 3 | inputs: 4 | A_0_0_0: 3 5 | A_0_0_1: 3 6 | A_0_0_2: 3 7 | A_0_1_0: 3 8 | A_0_1_1: 3 9 | A_0_1_2: 3 10 | A_0_2_0: 3 11 | A_0_2_1: 3 12 | A_0_2_2: 3 13 | A_1_0_0: 3 14 | A_1_0_1: 3 15 | A_1_0_2: 3 16 | A_1_1_0: 3 17 | A_1_1_1: 3 18 | A_1_1_2: 3 19 | A_1_2_0: 3 20 | A_1_2_1: 3 21 | A_1_2_2: 3 22 | A_2_0_0: 3 23 | A_2_0_1: 3 24 | A_2_0_2: 3 25 | A_2_1_0: 3 26 | A_2_1_1: 3 27 | A_2_1_2: 3 28 | A_2_2_0: 3 29 | A_2_2_1: 3 30 | A_2_2_2: 3 31 | expected_outputs: 32 | my_output_0_0_0: 3 33 | my_output_0_0_1: 3 34 | my_output_0_0_2: 3 35 | my_output_0_1_0: 3 36 | my_output_0_1_1: 3 37 | my_output_0_1_2: 3 38 | my_output_0_2_0: 3 39 | my_output_0_2_1: 3 40 | my_output_0_2_2: 3 41 | my_output_1_0_0: 3 42 | my_output_1_0_1: 3 43 | my_output_1_0_2: 3 44 | my_output_1_1_0: 3 45 | my_output_1_1_1: 3 46 | my_output_1_1_2: 3 47 | my_output_1_2_0: 3 48 | my_output_1_2_1: 3 49 | my_output_1_2_2: 3 50 | my_output_2_0_0: 3 51 | my_output_2_0_1: 3 52 | my_output_2_0_2: 3 53 | my_output_2_1_0: 3 54 | my_output_2_1_1: 3 55 | my_output_2_1_2: 3 56 | my_output_2_2_0: 3 57 | my_output_2_2_1: 3 58 | my_output_2_2_2: 3 59 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/private_inverse.yaml: -------------------------------------------------------------------------------- 1 | program: private_inverse 2 | inputs: 3 | A_0: 3 4 | expected_outputs: 5 | my_output: 1 6 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/random_array.yaml: -------------------------------------------------------------------------------- 1 | program: random_array 2 | inputs: 3 | A_1: 3 4 | A_2: 3 5 | A_0: 3 6 | expected_outputs: 7 | my_output_0: 3 8 | my_output_1: 3 9 | my_output_2: 3 10 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/rational_advanced.yaml: -------------------------------------------------------------------------------- 1 | program: rational_advanced 2 | inputs: 3 | my_input_0: 209715 4 | my_input_1: 1229 5 | expected_outputs: 6 | out_3: 16495343040 7 | out_5: 11182979 8 | out_8: 1475 9 | out_0: 174734 10 | out_7: 170 11 | out_9: 4194304 12 | out_10: 96668224 13 | out_2: 24580 14 | out_1: 16495343040 15 | out_4: 3932 16 | out_6: 257739735 17 | out_11: 64 18 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/rational_arithmetic.yaml: -------------------------------------------------------------------------------- 1 | program: rational_arithmetic 2 | inputs: 3 | my_input_1: 78643 4 | my_input_0: 294912 5 | expected_outputs: 6 | my_output_0: 504627 7 | my_output_1: -85197 8 | my_output_2: 943717 9 | my_output_3: 46603 10 | my_output_4: 288358 11 | my_output_5: 131072 12 | my_output_6: 251657 13 | my_output_7: 174762 14 | my_output_8: 720568878 15 | my_output_9: 65536 16 | my_output_10: 2000 17 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/rational_array.yaml: -------------------------------------------------------------------------------- 1 | program: rational_array 2 | inputs: 3 | A_0: 0 4 | A_1: 65536 5 | A_2: 131072 6 | B_0: 65536 7 | B_1: 131072 8 | B_2: 196608 9 | expected_outputs: 10 | out_0_0: 65536 11 | out_0_1: 196608 12 | out_0_2: 327680 13 | out_1_0: -65536 14 | out_1_1: -65536 15 | out_1_2: -65536 16 | out_2_0: 0 17 | out_2_1: 131072 18 | out_2_2: 393216 19 | out_3_0: 0 20 | out_3_1: 32768 21 | out_3_2: 43690 22 | out_4_0: 65536 23 | out_4_1: 131072 24 | out_4_2: 196608 25 | out_5_0: -65536 26 | out_5_1: 0 27 | out_5_2: 65536 28 | out_6_0: 0 29 | out_6_1: 65536 30 | out_6_2: 131072 31 | out_7_0: 0 32 | out_7_1: 65536 33 | out_7_2: 131072 34 | out_8_0: 0 35 | out_8_1: -65536 36 | out_8_2: -131072 37 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/rational_comparison.yaml: -------------------------------------------------------------------------------- 1 | program: rational_comparison 2 | inputs: 3 | my_input_0: 78643 4 | my_input_1: 209715 5 | expected_outputs: 6 | my_output_0: true 7 | my_output_1: true 8 | my_output_2: false 9 | my_output_3: false 10 | my_output_4: false 11 | my_output_5: false 12 | my_output_6: false 13 | my_output_7: true 14 | my_output_8: true 15 | my_output_9: false 16 | my_output_10: true 17 | my_output_12: true 18 | my_output_13: true 19 | my_output_14: false 20 | my_output_15: false 21 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/rational_if_else.yaml: -------------------------------------------------------------------------------- 1 | program: rational_if_else 2 | inputs: 3 | A: 78643 4 | B: 294912 5 | C: 65536 6 | D: 1 7 | E: 1 8 | expected_outputs: 9 | out_0: 294912 10 | out_1: 294912 11 | out_2: 78643 12 | out_3: 78643 13 | out_4: 1 14 | out_5: 1 15 | out_6: 1 16 | out_7: 1 17 | out_8: 131072 18 | out_9: 65536 19 | out_10: 1 20 | out_11: 1 21 | out_12: 1 22 | 23 | out_13: 1 24 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/rational_operability.yaml: -------------------------------------------------------------------------------- 1 | program: rational_operability 2 | inputs: 3 | A_1: 196608 4 | A_2: 196608 5 | A_0: 196608 6 | B_1: 131072 7 | B_2: 131072 8 | B_0: 131072 9 | expected_outputs: 10 | my_output_0: 327680 11 | my_output_1: 327680 12 | my_output_2: 327680 13 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/rational_scaling.yaml: -------------------------------------------------------------------------------- 1 | program: rational_scaling 2 | inputs: 3 | A_1: 12884901888 4 | A_2: 12884901888 5 | A_0: 12884901888 6 | expected_outputs: 7 | my_output_0: 21474836480 8 | my_output_1: 21474836480 9 | my_output_2: 21474836480 10 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/reveal.yaml: -------------------------------------------------------------------------------- 1 | program: reveal 2 | inputs: 3 | A_0_0: 3 4 | A_0_2: 3 5 | A_1_2: 3 6 | B_1_2: 3 7 | A_1_0: 3 8 | B_2_2: 3 9 | B_2_1: 3 10 | B_0_1: 3 11 | A_2_1: 3 12 | A_1_1: 3 13 | A_0_1: 3 14 | B_1_1: 3 15 | A_2_2: 3 16 | B_0_0: 3 17 | A_2_0: 3 18 | B_1_0: 3 19 | B_2_0: 3 20 | B_0_2: 3 21 | expected_outputs: 22 | my_output_B_0_1: 3 23 | my_output_A_0_1: 3 24 | my_output_A_0_2: 3 25 | my_output_B_0_2: 3 26 | my_output_A_1_1: 3 27 | my_output_B_1_1: 3 28 | my_output_A_2_2: 3 29 | my_output_A_2_1: 3 30 | my_output_A_2_0: 3 31 | my_output_B_2_1: 3 32 | my_output_A_1_2: 3 33 | my_output_B_1_0: 3 34 | my_output_B_2_2: 3 35 | my_output_A_1_0: 3 36 | my_output_B_2_0: 3 37 | my_output_B_1_2: 3 38 | my_output_A_0_0: 3 39 | my_output_B_0_0: 3 40 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/secret_rational_arithmetic.yaml: -------------------------------------------------------------------------------- 1 | program: secret_rational_arithmetic 2 | inputs: 3 | my_input_0: 209715 4 | my_input_1: 294912 5 | expected_outputs: 6 | my_output_0: 504627 7 | my_output_1: -85197 8 | my_output_2: 943717 9 | my_output_3: 46603 10 | my_output_4: 288358 11 | my_output_5: 131072 12 | my_output_6: 251657 13 | my_output_7: 174762 14 | my_output_8: 720568878 15 | my_output_9: -209715 16 | my_output_12: 209715 17 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/secret_rational_comparison.yaml: -------------------------------------------------------------------------------- 1 | program: secret_rational_comparison 2 | inputs: 3 | my_input_0: 209715 4 | my_input_1: 294912 5 | my_input_2: 209715 6 | expected_outputs: 7 | my_output_0: true 8 | my_output_1: true 9 | my_output_2: false 10 | my_output_3: false 11 | my_output_4: false 12 | my_output_5: false 13 | my_output_6: false 14 | my_output_7: true 15 | my_output_8: true 16 | my_output_9: false 17 | my_output_10: true 18 | my_output_12: true 19 | my_output_13: true 20 | my_output_14: false 21 | my_output_15: false 22 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/set_item.yaml: -------------------------------------------------------------------------------- 1 | program: set_item 2 | inputs: 3 | A_2_1: 3 4 | A_1_1: 3 5 | A_2_2: 3 6 | A_0_0: 3 7 | A_1_0: 3 8 | A_1_2: 3 9 | A_2_0: 3 10 | A_0_2: 3 11 | A_0_1: 3 12 | expected_outputs: 13 | my_output_1: 11 14 | my_output_2: 12 15 | my_output_0: 10 16 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/shape.yaml: -------------------------------------------------------------------------------- 1 | program: shape 2 | inputs: 3 | A_2_1: 3 4 | A_0_0: 3 5 | A_1_1: 3 6 | A_1_2: 3 7 | A_0_1: 3 8 | A_0_2: 3 9 | A_1_0: 3 10 | A_2_2: 3 11 | A_2_0: 3 12 | expected_outputs: 13 | my_output_0: 9 14 | my_output_1: 9 15 | my_output_2: 9 16 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/shuffle.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | program: shuffle 3 | inputs: 4 | A_7: 7 5 | B_3: 3 6 | A_1: 1 7 | A_0: 0 8 | B_4: 4 9 | B_7: 7 10 | C_4: 4 11 | C_1: 1 12 | C_6: 6 13 | C_2: 2 14 | C_0: 0 15 | C_3: 3 16 | C_5: 5 17 | A_3: 3 18 | A_2: 2 19 | C_7: 7 20 | B_0: 0 21 | B_2: 2 22 | B_1: 1 23 | B_6: 6 24 | A_5: 5 25 | A_6: 6 26 | A_4: 4 27 | B_5: 5 28 | D_0: 0 29 | D_1: 1 30 | D_2: 2 31 | D_3: 3 32 | D_4: 4 33 | D_5: 5 34 | D_6: 6 35 | D_7: 7 36 | expected_outputs: 37 | my_output_a_0: 0 38 | my_output_b_2: 0 39 | my_output_c_3: 0 40 | my_output_c_6: 0 41 | my_output_a_2: 0 42 | my_output_a_6: 0 43 | my_output_a_1: 0 44 | my_output_b_7: 0 45 | my_output_a_7: 0 46 | my_output_b_3: 0 47 | my_output_c_0: 0 48 | my_output_c_1: 0 49 | my_output_b_0: 0 50 | my_output_b_4: 0 51 | my_output_c_2: 0 52 | my_output_c_4: 0 53 | my_output_c_7: 0 54 | my_output_c_5: 0 55 | my_output_b_6: 0 56 | my_output_b_5: 0 57 | my_output_a_4: 0 58 | my_output_a_3: 0 59 | my_output_b_1: 0 60 | my_output_a_5: 0 61 | my_output_d_0: 0 62 | my_output_d_1: 0 63 | my_output_d_2: 0 64 | my_output_d_3: 0 65 | my_output_d_4: 0 66 | my_output_d_5: 0 67 | my_output_d_6: 0 68 | my_output_d_7: 0 69 | my_output_method_a_0: 0 70 | my_output_method_a_1: 0 71 | my_output_method_a_2: 0 72 | my_output_method_a_3: 0 73 | my_output_method_a_4: 0 74 | my_output_method_a_5: 0 75 | my_output_method_a_6: 0 76 | my_output_method_a_7: 0 77 | my_output_method_b_0: 0 78 | my_output_method_b_1: 0 79 | my_output_method_b_2: 0 80 | my_output_method_b_3: 0 81 | my_output_method_b_4: 0 82 | my_output_method_b_5: 0 83 | my_output_method_b_6: 0 84 | my_output_method_b_7: 0 85 | my_output_method_c_0: 0 86 | my_output_method_c_1: 0 87 | my_output_method_c_2: 0 88 | my_output_method_c_3: 0 89 | my_output_method_c_4: 0 90 | my_output_method_c_5: 0 91 | my_output_method_c_6: 0 92 | my_output_method_c_7: 0 93 | my_output_method_d_0: 0 94 | my_output_method_d_1: 0 95 | my_output_method_d_2: 0 96 | my_output_method_d_3: 0 97 | my_output_method_d_4: 0 98 | my_output_method_d_5: 0 99 | my_output_method_d_6: 0 100 | my_output_method_d_7: 0 101 | at_least_one_diff_element: true 102 | at_least_one_diff_element_method: true 103 | elements_are_preserved: true 104 | elements_are_preserved_method: true 105 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/sum.yaml: -------------------------------------------------------------------------------- 1 | program: sum 2 | inputs: 3 | A_1: 3 4 | A_0: 3 5 | A_2: 3 6 | expected_outputs: 7 | my_output: 9 8 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/supported_operations.yaml: -------------------------------------------------------------------------------- 1 | program: supported_operations 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 2 5 | A_0_2: 3 6 | A_1_0: 4 7 | A_1_1: 5 8 | A_1_2: 6 9 | A_2_0: 7 10 | A_2_1: 8 11 | A_2_2: 9 12 | B_0_0: 1 13 | B_0_1: 2 14 | B_0_2: 3 15 | B_1_0: 4 16 | B_1_1: 5 17 | B_1_2: 6 18 | B_2_0: 7 19 | B_2_1: 8 20 | B_2_2: 9 21 | C_0_0: 1 22 | C_0_1: 2 23 | C_0_2: 3 24 | C_1_0: 4 25 | C_1_1: 5 26 | C_1_2: 6 27 | C_2_0: 7 28 | C_2_1: 8 29 | C_2_2: 9 30 | D_0: 1 31 | E_0_0: 1 32 | E_0_1: 2 33 | E_0_2: 3 34 | E_1_0: 4 35 | E_1_1: 5 36 | E_1_2: 6 37 | E_2_0: 7 38 | E_2_1: 8 39 | E_2_2: 9 40 | F_0: 1 41 | expected_outputs: 42 | out_0: 1 43 | out_1: 362880 44 | out_2: 31 45 | out_3: 13 46 | out_4: 21 47 | out_5: 42 48 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/supported_operations_return_types.yaml: -------------------------------------------------------------------------------- 1 | program: supported_operations_return_types 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 2 5 | A_0_2: 3 6 | A_1_0: 4 7 | A_1_1: 5 8 | A_1_2: 6 9 | A_2_0: 7 10 | A_2_1: 8 11 | A_2_2: 9 12 | B_0_0: 1 13 | B_0_1: 2 14 | B_0_2: 3 15 | B_1_0: 4 16 | B_1_1: 5 17 | B_1_2: 6 18 | B_2_0: 7 19 | B_2_1: 8 20 | B_2_2: 9 21 | C_0_0: 1 22 | C_0_1: 2 23 | C_0_2: 3 24 | C_1_0: 4 25 | C_1_1: 5 26 | C_1_2: 6 27 | C_2_0: 7 28 | C_2_1: 8 29 | C_2_2: 9 30 | D_0: 1 31 | E_0_0: 1 32 | E_0_1: 2 33 | E_0_2: 3 34 | E_1_0: 4 35 | E_1_1: 5 36 | E_1_2: 6 37 | E_2_0: 7 38 | E_2_1: 8 39 | E_2_2: 9 40 | F_0: 1 41 | expected_outputs: 42 | out_0: 1 43 | out_1: 362880 44 | out_2: 31 45 | out_3: 13 46 | out_4: 21 47 | out_5: 42 48 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/type_guardrails.yaml: -------------------------------------------------------------------------------- 1 | program: type_guardrails 2 | inputs: 3 | A_2: 3 4 | A_0: 3 5 | A_1: 3 6 | expected_outputs: 7 | my_output_1: 3 8 | my_output_0: 3 9 | my_output_2: 3 10 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/unsigned_matrix_inverse.yaml: -------------------------------------------------------------------------------- 1 | program: unsigned_matrix_inverse 2 | inputs: 3 | A_0_0: 2 4 | A_0_1: 4 5 | A_0_2: 6 6 | A_1_0: 1 7 | A_1_1: 3 8 | A_1_2: 5 9 | A_2_0: 3 10 | A_2_1: 1 11 | A_2_2: 2 12 | B: 456 13 | expected_outputs: 14 | my_output_0_0: 3074457345439651158 15 | my_output_0_1: 12297829381758604631 16 | my_output_0_2: 6148914690879302316 17 | my_output_1_0: 3074457345439651160 18 | my_output_1_1: 12297829381758604629 19 | my_output_1_2: 6148914690879302315 20 | my_output_2_0: 12297829381758604630 21 | my_output_2_1: 12297829381758604633 22 | my_output_2_2: 6148914690879302316 23 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/unsigned_matrix_inverse_2.yaml: -------------------------------------------------------------------------------- 1 | program: unsigned_matrix_inverse 2 | inputs: 3 | A_0_0: 1 4 | A_0_1: 2 5 | A_0_2: 3 6 | A_1_0: 4 7 | A_1_1: 5 8 | A_1_2: 6 9 | A_2_0: 7 10 | A_2_1: 8 11 | A_2_2: 10 12 | B: 456 13 | expected_outputs: 14 | my_output_0_0: 6148914690879302315 15 | my_output_0_1: 12297829381758604630 16 | my_output_0_2: 1 17 | my_output_1_0: 6148914690879302315 18 | my_output_1_1: 12297829381758604635 19 | my_output_1_2: 18446744072637906945 20 | my_output_2_0: 1 21 | my_output_2_1: 18446744072637906945 22 | my_output_2_2: 1 23 | -------------------------------------------------------------------------------- /tests/nada-tests/tests/vstack.yaml: -------------------------------------------------------------------------------- 1 | program: vstack 2 | inputs: 3 | A_2: 3 4 | B_1: 3 5 | B_0: 3 6 | B_2: 3 7 | A_0: 3 8 | A_1: 3 9 | expected_outputs: 10 | my_output_1_2: 3 11 | my_output_0_1: 3 12 | my_output_0_0: 3 13 | my_output_1_0: 3 14 | my_output_0_2: 3 15 | my_output_1_1: 3 16 | -------------------------------------------------------------------------------- /tests/test_all.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | 4 | import pytest 5 | 6 | TESTS = [ 7 | "base", 8 | "new_array", 9 | "dot_product", 10 | "sum", 11 | "broadcasting_sum", 12 | "broadcasting_sub", 13 | "broadcasting_mul", 14 | "broadcasting_div", 15 | "broadcasting_vec", 16 | "hstack", 17 | "vstack", 18 | "reveal", 19 | "matrix_multiplication", 20 | "generate_array", 21 | "supported_operations", 22 | "get_item", 23 | "get_attr", 24 | "set_item", 25 | "gauss_jordan", 26 | "generate_array", 27 | "rational_arithmetic", 28 | "rational_comparison", 29 | "secret_rational_arithmetic", 30 | "secret_rational_comparison", 31 | "chained_rational_operations", 32 | "rational_array", 33 | "rational_scaling", 34 | "rational_operability", 35 | "rational_if_else", 36 | "random_array", 37 | "rational_advanced", 38 | "array_attributes", 39 | "functional_operations", 40 | "array_statistics", 41 | "matrix_multiplication_rational", 42 | "matrix_multiplication_rational_multidim", 43 | "dot_product_rational", 44 | "supported_operations_return_types", 45 | "logistic_regression", 46 | "logistic_regression_rational", 47 | "type_guardrails", 48 | "shape", 49 | "get_vec", 50 | "unsigned_matrix_inverse", 51 | "private_inverse", 52 | "fxpmath_funcs", 53 | "fxpmath_arrays", 54 | "fxpmath_methods", 55 | "shuffle", 56 | "array_comparison", 57 | "private_inverse", 58 | ] 59 | 60 | EXAMPLES = [ 61 | # FORMAT: (EXAMPLES_DIR (DIR), EXAMPLE_NAME (PY), TEST_NAME (YAML)) 62 | ("dot_product", "dot_product", "dot_product"), 63 | ("matrix_multiplication", "matrix_multiplication", "matrix_multiplication"), 64 | ("broadcasting", "broadcasting", "broadcasting"), 65 | ("rational_numbers", "rational_numbers", "rational_numbers"), 66 | ("linear_regression", "determinant", "determinant_1"), 67 | ("linear_regression", "determinant", "determinant_2"), 68 | ("linear_regression", "determinant", "determinant_3"), 69 | ("linear_regression", "gauss_jordan", "gauss_jordan"), 70 | ("linear_regression", "matrix_inverse", "nada-test::matrix_inverse.py:my_test"), 71 | ("linear_regression", "linear_regression", "linear_regression"), 72 | ("linear_regression", "linear_regression", "linear_regression_1"), 73 | ("linear_regression", "linear_regression", "linear_regression_2"), 74 | ("linear_regression", "linear_regression", "linear_regression_256_1"), 75 | ("linear_regression", "linear_regression", "linear_regression_256_2"), 76 | ("linear_regression", "modular_inverse", "modular_inverse"), 77 | ] 78 | 79 | TESTS = [("tests/nada-tests/", test, test) for test in TESTS] + [ 80 | (os.path.join("examples/", test[0]), test[1], test[2]) for test in EXAMPLES 81 | ] 82 | 83 | 84 | @pytest.fixture(params=TESTS) 85 | def testname(request): 86 | return request.param 87 | 88 | 89 | def build_nada(test_dir): 90 | print(test_dir) 91 | result = subprocess.run( 92 | ["nada", "build", test_dir[1]], cwd=test_dir[0], capture_output=True, text=True 93 | ) 94 | err = result.stderr.lower() + result.stdout.lower() 95 | if result.returncode != 0 or "error" in err or "fail" in err: 96 | pytest.fail(f"Build {test_dir}:\n{result.stdout + result.stderr}") 97 | 98 | 99 | def run_nada(test_dir): 100 | result = subprocess.run( 101 | ["nada", "test", test_dir[2]], cwd=test_dir[0], capture_output=True, text=True 102 | ) 103 | 104 | # if "shape" in test_dir[1]: 105 | # pytest.fail(f"Run {test_dir}:\n{result.stdout + result.stderr}") 106 | 107 | err = result.stderr.lower() + result.stdout.lower() 108 | if result.returncode != 0 or "error" in err or "fail" in err: 109 | pytest.fail(f"Run {test_dir}:\n{result.stdout + result.stderr}") 110 | 111 | 112 | class TestSuite: 113 | 114 | def test_build(self, testname): 115 | # Build Nada Program 116 | build_nada(testname) 117 | 118 | def test_run(self, testname): 119 | # Build Nada Program 120 | run_nada(testname) 121 | 122 | 123 | def test_client(): 124 | import nillion_client as nillion 125 | import numpy as np 126 | 127 | import nada_numpy.client as na_client # For use with Python Client 128 | 129 | parties = na_client.parties(3) 130 | 131 | assert parties is not None 132 | 133 | secrets = na_client.concat( 134 | [ 135 | na_client.array(np.ones((3, 3)), "A", nillion.SecretInteger), 136 | na_client.array(np.ones((3, 3)), "B", nillion.SecretUnsignedInteger), 137 | ] 138 | ) 139 | 140 | assert secrets is not None 141 | 142 | public_variables = na_client.concat( 143 | [ 144 | na_client.array(np.zeros((4, 4)), "C", nillion.Integer), 145 | na_client.array(np.zeros((3, 3)), "D", nillion.UnsignedInteger), 146 | ] 147 | ) 148 | 149 | assert public_variables is not None 150 | 151 | 152 | def test_rational_client(): 153 | import nillion_client as nillion 154 | 155 | import nada_numpy.client as na_client # For use with Python Client 156 | 157 | secret_rational = na_client.secret_rational(3.2) 158 | 159 | assert isinstance(secret_rational, nillion.SecretInteger) 160 | 161 | rational = na_client.public_rational(1.7) 162 | 163 | assert isinstance(rational, nillion.Integer) 164 | --------------------------------------------------------------------------------