From ad558906f345744ece337a9c4225f20fb90bc979 Mon Sep 17 00:00:00 2001 From: Ashrit Verma Date: Sat, 3 Feb 2024 20:15:16 -0500 Subject: [PATCH 01/20] testing from ashrit --- moodys_challenge.ipynb | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 2f33633..549a2d7 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -36,6 +36,24 @@ "**5.** Use of simulated annealing and other quantum computing techniques to find the solution of the problem. " ] }, + { + "cell_type": "code", + "execution_count": 1, + "id": "15770539", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "moodys_challenge.ipynb\tREADME.md returns_data.txt\n" + ] + } + ], + "source": [ + "!ls" + ] + }, { "cell_type": "markdown", "id": "85d339f8-0a2d-4c84-88d2-ae2f5d7e7597", @@ -377,23 +395,17 @@ ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "id": "ec015dad-7449-433c-bd44-593e5fecb4ec", "metadata": {}, + "outputs": [], "source": [ "
NOTE:\n", "L has the following form: $L=\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}-\\sum_{k=1}^{n}\\mu_{k}x_{k}+\\textit{penalty terms}$, with the penalty terms to be found in this section (and include all the equalities and inequalities of the problem). This will need to be mapped to a quantum Hamiltonian in the following section. Each penalty term will correspond to one lagrange multiplier that will need to be fine-tuned in the next sections, so that the constraints are imposed.\n", "
" ] }, - { - "cell_type": "markdown", - "id": "23645c2f-0916-477e-9cf8-2ecb6ed114d1", - "metadata": {}, - "source": [ - "**Answer:**" - ] - }, { "cell_type": "markdown", "id": "bb2982bd-fbb9-49de-8dbc-370e9390173b", @@ -835,7 +847,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3 [Default]", "language": "python", "name": "python3" }, @@ -849,7 +861,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.11.7" } }, "nbformat": 4, From aa88a63b665ef55ef6a4936a3ac6f5afb16eb4a5 Mon Sep 17 00:00:00 2001 From: Ashrit Verma Date: Sat, 3 Feb 2024 20:23:23 -0500 Subject: [PATCH 02/20] new file for returns_data --- fromAshrit.ipynb | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 fromAshrit.ipynb diff --git a/fromAshrit.ipynb b/fromAshrit.ipynb new file mode 100644 index 0000000..e69de29 From af4f3cae5f5a4782828f729a1c3143942e834248 Mon Sep 17 00:00:00 2001 From: Ashrit Date: Sun, 4 Feb 2024 02:15:56 +0000 Subject: [PATCH 03/20] qbraid help --- .../moodys_challenge-checkpoint.ipynb | 869 ++++++++++++++++++ moodys_challenge.ipynb | 37 +- 2 files changed, 898 insertions(+), 8 deletions(-) create mode 100644 .ipynb_checkpoints/moodys_challenge-checkpoint.ipynb diff --git a/.ipynb_checkpoints/moodys_challenge-checkpoint.ipynb b/.ipynb_checkpoints/moodys_challenge-checkpoint.ipynb new file mode 100644 index 0000000..549a2d7 --- /dev/null +++ b/.ipynb_checkpoints/moodys_challenge-checkpoint.ipynb @@ -0,0 +1,869 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "de7fe4be-dd2d-4d8c-9358-5ab6a636496e", + "metadata": {}, + "source": [ + "# The path to Quantum in production in the financial industry" + ] + }, + { + "cell_type": "markdown", + "id": "266aceea-015c-4c8b-a75e-7e9e74c47f33", + "metadata": {}, + "source": [ + "## How could quantum avoid the drawbacks of classical optimization algorithms?\n" + ] + }, + { + "cell_type": "markdown", + "id": "6c89cba0-4047-4b23-b949-baa20f67fae5", + "metadata": {}, + "source": [ + "## Learning objectives of the challenge" + ] + }, + { + "cell_type": "markdown", + "id": "7fb51a43-6f42-4077-a894-38c874c61721", + "metadata": {}, + "source": [ + "**1.** Leverage quantum computing to try to avoid the drawbacks of classical optimization algorithms for portfolio optimization in the financial industry. What are the most promising problems and the corresponding techniques to solve them?
\n", + "**2.** What are the main bottlenecks/steps to solve financial optimization problems with quantum? What are the proposals in the literature to overcome them? Inclusion of equality and inequality constraints in optimization problems.
\n", + "**3.** Mapping a classical portfolio optimization problem to a quantum one.
\n", + "**4.** Think about resource estimation, can we do something useful with near term devices? How far are we from quantum advantage? How do we translate hardware roadmaps into utility timelines?
\n", + "**5.** Use of simulated annealing and other quantum computing techniques to find the solution of the problem. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "15770539", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "moodys_challenge.ipynb\tREADME.md returns_data.txt\n" + ] + } + ], + "source": [ + "!ls" + ] + }, + { + "cell_type": "markdown", + "id": "85d339f8-0a2d-4c84-88d2-ae2f5d7e7597", + "metadata": {}, + "source": [ + "## The challenge" + ] + }, + { + "cell_type": "markdown", + "id": "ddf18fd5", + "metadata": {}, + "source": [ + "### Portfolio optimization in the financial industry\n", + "\n", + "\n", + "Portfolio optimization is a formal mathematical approach to making investment decisions across a collection of financial instruments or assets. In 1952, Harry Markowitz introduced Modern Portfolio Theory (MPT). MPT introduced the notion that the diversification of a portfolio can inherently decrease the risk of a portfolio. Simply put, this meant that investors could increase their returns while also reducing their risk. Markowitz’s work on MPT was groundbreaking in the world of asset allocation, eventually earning him a Nobel prize for his work in 1990.\n", + "\n", + "\n", + "The behaviour of a portfolio can be quite different from the behaviour of\n", + "individual components of the portfolio. The risk of a properly constructed\n", + "portfolio from equities in leading markets could be half the sum of the risks of\n", + "individual assets in the portfolio. This is due to complex correlation patterns\n", + "between individual assets or equities. A good optimizer can exploit the\n", + "correlations, the expected returns, the risk (variance) and user constraints\n", + "to obtain an optimized portfolio.\n", + "\n", + "Portfolio optimization is often called **mean-variance (MV)** optimization.\n", + "The term mean refers to the *mean or the expected return* of the investment\n", + "and the *variance* is the measure of the risk associated with the portfolio.\n", + "\n", + "![image](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAh8AAAEYCAYAAAD8sJT0AAAAAXNSR0IArs4c6QAAIABJREFUeF7sfQd4VFX6/juTSW+EEnqX3qRJFQRRqiKCCFhX165rd11/rrq6u/beVte1ISgqCCoqiA0pIggiIr2D9PSezMz//57kjpM6d2bulJv5vufJk5A55TvvueG892vH4nQ6nRARBAQBQUAQEAQEAUEgSAhYhHwECWmZRhAQBAQBQUAQEAQUAkI+5EEQBAQBQUAQEAQEgaAiIOQjqHDLZIKAICAICAKCgCAg5EOeAUFAEBAEBAFBQBAIKgJCPoIKt0wmCAgCgoAgIAgIAkI+5BkQBAQBQUAQEAQEgaAiIOQjqHDLZIKAICAICAKCgCAg5EOeAUFAEBAEBAFBQBAIKgJCPoIKt0wmCAgCgoAgIAgIAkI+5BkQBAQBQUAQEAQEgaAiIOQjqHDLZIKAICAICAKCgCAg5EOeAUHADQFHfj7yPvkEZXv3IqpZMyRNnIioJk2qYWQ/cQJ5CxfCfvIkotu1Q+LEibAmJUU8loJLxD8CAoAgoAsBIR+6YJJGEYGA04mCb79F4cqVruVaoqORdM45iOne3fU7Z1kZ8j7+GCW//fZHu4QEpFx4IWwtW0YEVDUtUnCJ2K2XhQsCXiMg5MNryKRDfUXAUViI3Hffhf34cSSMGYOo5GTkff45aA2hZSOuTx+1dHtGBnLeeQc8bGkZ4ecFX36pPkueMQPRbduaAyKnEyW7dsFZWIjYnj0Bi8UvvcMeF4PX6xdY0lkQiHAEhHxE+AMQEct3OpV7pPiXX1CycyfoGrBERcHWqpWyalhTUhQMJBE5b78Na3IykqdPhyUmRhGN3A8+UP3ZNrZXL9iPHkX2nDmI7doViWPHAlFRKNu/H7kffQRnSUm5BaRNm7CHlrrmLlgA+8GDSJ41C7YWLfzSOdxxMXq9foElnQWBCEdAyEeEPwD1cfnOggIUrV8PfncUF6N09244cnNrXGrckCFIPPPMcotGBamg5SL5vPMUqVCkJCcHue+/j7JjxxSxcALKQuLeV+uf8957yiIS26kTin/7DQnjx7ssJnqwLjtwADnvv4/o9u2RdO65sNhserr51EY7jBnfkkLy4SdhohWlTlzsdqRedBGimjb1SV9/O+lZbzDx93c90l8QMDMCQj7MvHuie40IaIeg9mFUaipievRAbO/eiGrYELBalYUif8kSRLdsidh+/eokH/yw7MgR5Mydq1wqHCd33rxq5IPtSrdvR+78+YDNBmdxMRgz4o0rhv1zPvwQcDgQP2wYEs44w293SG2PiXYYl+7cibj+/ZUliASMc9P6E9u/v7L+6JXayIcLlwULEDtwoIvs6R3XqHZ61ksiFiz8jVqXjCMImBEBIR9m3DUAx44dw4kTJ9DdLRDSpEsxXO2yw4eRO2cOnHa7OsDjBg1SJMCT1Gb5YD91cM2fj6jGjVV2S23kg1aSHFpF+vVTbQtXr4atadNyElFhSfGoR0YGin78sdzVM2kSrKmpnrro/pyupdK9e2E/fFgRqrJDh+AsLa3c32pVJC1x/Hiv4lfqIh8uXPr3R9yAAbr19behL+ulqy1Q+Pu7HukvCNQXBIR8mHQn7733XsyePRsPP/wwZs2aZdJVBEZtRRQ+/FDFdqRcfHG5taMuYUzIiRPqsM9bvFiRC3e3i6ur0wkSG5IUtqvqdtG9Gs537BjsOTmISklBVHp6wKwb7jqRZOQtWICSHTuqqco1x/TsiejWrRHVoIFuoqQG8hYXpxPFGzeicMUK2LOyYElIgK1ZMyQyyJdYGCQBW68H/eh2K1q9GoVr1yrXH61ItJgljB7tii8yaIkyjCBgWgSEfJh062bMmIHVq1cr7fft22fSVQRGbc28zjiPlBkz1H/+PCDtmZkqPZZuBma2KLFaEXfaaeVvusePq1/xMIwfMgQxHTqovpb4eEUO+DmzXPg2rbo2aID4oUPVwcI+1rg4jySCb9V5ixYpi4MmjO9InjoVFvb3INS7aNWq8piW0lJVgyTp7LNhq8iw4eckR7bmzWGJjVUBtnkffaTWkDJzpop/KfzuO8R07YroU05B0YYNKN21S8WX0J3ki3iLS9G6dchfulS5dyqJ1aosRMSeVqviDRtQtGZNOUGJjlZpzLRixZxyigvnYK/XE/5cU/5XXym9qwr3I3HCBMTSWulnZpEv+yR9BIFwQkDIRzjthhe6zJw5E6tWrYLVasWePXu86Fn/m6qslXfeUZYMpssy/sKRl6diMDRRpCImRgWTMtaBX0U//FDdBVHRgdaA6O7d4czPR/GmTdUPzgoiQytL/ODBKium9MABRXpILig8pJVFhnqlpiKqVStFBpjqGj98eLlrhgGu2dnKNcI4FfeAU5IKBqPycx5kdOPwzVrFlVx0EaJbtULxr7+q4mccL7ZbNzAAVgu2pU7M2CHh0oRxL0Vr1/pFPkj2CpYtQ9HPP3vEJaZjR5UpRBJG4kY9mXlETPK/+AKl+/cjbvBgRRBpHaIoYhcdDTuDhh0OlZWTPG2asiIEYr3+4K+57lBaisSzz0bsqaeqNXBdTMfmOrW9qv9/ibJCQaB2BIR8mPTpoKtl5cqVsNls2LVrl0lXERi1q76JqwMsNlYdxpprwRV/4XCot2we4Iq0zJ0LZ3a2ihVBfDxK9+xRqajq4ANUsGR0hw6qHQ9x1c5uVySCMRQ86NUb7rhxKN68Wf079eKLFVFgWisP1dgePVSKLg9VZZn48ENliVDEICpKkQEe5ioDpcKioWpozJ2ryBLJDecleSpYvhyF33/vcgFpcRfUkXOrCqxt2ihLi7J+VHFDcS4SEH8sH9ouKtw94BI/cCAKf/hBVY9NvuACWBMTKz0EtE7lvvee0ltZpfr3R/zIkcqqxP0pWrkShevWKTcNrVqljO959121J0at1x/8mc7NAnSsm0IrR9WAXRI1RSjdCGBg/gpkVEEgvBEQ8hHe+1OrdhdddBFWrFiBmJgY7KjBh2/SZRmituvt024vd40MGKBIh6egU1cdCKbU1hErotUDIclInjkTVrplqogWb1B29Kgai9YNHsxRaWmqhohWW4Rv8qUHD6o4C+13RT/9hPzPP/+DENCUv3gxijZuVG4gWgxiOndWb9P5y5apsbUiaFpfpU4FOUoYNkzFqNDl5E5o2MRI8qEHF40cxXTrVmNcjXtGisr2GTmy8kHNKrTLlqFwzRoVEEshVkau1y/8K8icz/FAhvwFyCCCQPgjIOQj/PeoRg0vueQSLF++HPHx8di6datJVxEYtemy4EEfP2iQCvLT6193kY8KwhDVqFGNCmqHLN05tGqomJAaJO+zz1C8fr0iKBS+odd26Lp31w5opgAnTZigglyZvYOYmHIXRWbmH81pHejdGwlnn63esjUywQaM4aAFxv33VS0cPMTpDiBOJDUUBt8yJoO/o4VBr+jBRdPPE/motfaI06ksPbT40GVDywktN0au1x/8tT0X8qH3qZF2kYqAkA+T7vyll16K7777DomJifjN7Y4Rky7HULVLtm5VsRVM6Yw//XTlFiljMKnDoeIunHl5aj5nVJTK7lABo4yhcDjUpXKM6SBhYHxCTcLYkZx589QhXVfRLBV4uHq1K/6CsQ7OoqJyKwWDO2sJOmS1VJInHvysEUKLBwkCXTV0Q7AQVvHOncriQh3ds2W0GA4SJ7o1mO5LcRGyoUNdsSX8veYmcD8sNULirStGDy4u8kE303nnVS+ixj0gafv5Z5VmrMVMqEU4HMqVlf/ZZ8pKxPXRSsExjVyvP/hr5IOuMZboFxEEBIGaERDyYdIn47LLLsO3336L5ORk/PrrryZdRWDUdn/79zgDMyyGD0f8iBGqqdbX3RJQbQynU71tM2uDgY+M16hJKo01eLBKLS1YsUIdogxMZVxATJcusKalVYoN0NxGtvR05aIp+OYbpRddEIpM1SFKr59+QuJZZ5XX06ggONq9KyQqyeef75rPPUaEa2FgLtNxacVgdoxXqa86cNEwIVmgO0plIlWR0n37VNwHY3EYXBrdogXsrE+yc6cKGqZOJEYkZ4FYrz/4a+RDZTBNm1ZOakUEAUGgGgJCPkz6UGjkIzU1Fb/88otJVxEYtWm5oAVDS+VUwaa9epVXOG3cuM6qneoul/nzlcumrtRTvh0zjoJBhbVdJMdsDWansH6FIii82GzbtvKsh+zsSounjqqk+qRJKsYhd+FCVQOEJKJkzx4VlKoCRmfNUum1tYmjqAglmzapW3grBXPSXbFqlcrAYQ0TLa2XgZ0qkNVdH42QDR/udWCkJ1wUJnQ/deyIBLqEaiof73SCBIS3C5cdPKgykBivo2qRdO+u4l20Qz0Q61XuNx/xZ9oyrW6MV1HByJJSG5g/chnV9AgI+TDpFl5++eX45ptv0LBhQ2zYsMGkqwiQ2k6nOrCcDgcsVqtXJcIDpFHlYZ1OlbXCA5ZfZfv2qVoWinxMngxrUlLl9m4Bp8yQSRo/XllMVJl4ko3t25UVhtk2ylXCG2r1itOp+uZ/+aWKJ2HWCC0mPOgDdnDa7eXa6az4qncputr5sl4v8efeEEvJaNG1I9IoQhEQ8mHSjb/iiivw1VdfoVGjRli/fr1JVyFq60VA1dJYuhRFtHJVLc5VMQhdGbSc2Fq31jusqx3Hp3hzl4vXk4RRB2/XG2j8wwgaUUUQCAoCQj6CArPxk1x55ZVYtmwZ0tPTsXbtWuMnkBHDD4GKsuwMumSsBsrKVL0MBs3GdOpUnqorZv7A7ZvgHzhsZeSIQ0DIh0m3/M9//jO+/PJLNGvWDGtqKOVs0mWJ2oKAICAICAIRgICQD5Nu8lVXXYWlS5eiRYsWrjteTLqUoKudM3u2Sr9NOv98uegr6OjLhIKAICAIAEI+TPoUXHPNNfjiiy/QqlUrVWZdRD8COW++qaqKqttrZ81SQakigoAgIAgIAsFDQMhH8LA2dCaNfLRt21ZVOhXRjwDvOcmdNw+OggKVgssMExFBQBAQBASB4CEg5CN4WBs607XXXovPP/8c7du3V8XGRLxDQKuCyl68wj3hzDO9G0BaCwKCgCAgCPiMgJAPn6ELbcfrrrsOn332GTp27Iivv/46tMqYdHb3S9gSzjpLFRYTEQQEAUFAEAg8AkI+Ao9xQGa44YYb8Omnn6JTp04q5VbENwQKvvtOXVRGYdlxVtAUEQQEAUFAEAgsAkI+AotvwEa/8cYb8cknn6Br165YUnGrZ8Amq+cD86KyovXrValvdeV8mzb1fMWyPEFAEBAEQouAkI/Q4u/z7DfddBM+/vhjdOvWTWW9iPiHAANQS3bsQFSDBoqAWBs29G9A6S0ICAKCgCBQKwJCPkz6cPzlL3/BokWL0LNnTyxevNikqwgftXmLKwlI2e+/w9aqlSIgkVJqPHx2QTTxhABv9XWWlak6Nbz1l1Vute/8naO0tPx32veKn1Uf/lxSUn7vET/XvttsiO3RQ2V+iQQWgfxiO77fkYm+rZPRNDWybzwW8hHYZy1go2vko3fv3sr9IuI/AmVHj5an4ObkqFtoeSW6iCAQCAQcWVmw5+TAmZ8PEl+mfTsLCuDIy1OXBaovkgOSBX4VFwdCjUpjyjMfcIhxLLcEH647il4tk3F65waBnzCMZxDyEcabU5dqt9xyCz766CP07dsXCxcuNOkqwk9tdSX6+++rt8n4gQORMHZs+CkpGoUtAiQNjtzcckKRmwtnXp4iGfbs7PKfs7IUyQhXYdXfWAm6dm3PybxSfLbpBDLyS9G2UTzGdG+IpNgon7dPIx9tGsZjXK9GsFktPo9l9o5CPky6g7feeisWLFiAfv36KRIiYhwCxRs3Iq/CmpQwahTihw0zbnAZydQIkFTYMzPhyMx0fXdkZ5dbMWi1oHvDxCJF9/7YvDKHE0t+PYmth/Ndv4yPsWJK33Q0b+Cby0TIxx/4Cvkw6X8Ut912G+bPn4+BAwfiww8/NOkqwlftwpUrUfDNN0rBpHPOQWyfPuGrrGhmGAK0VtBKQdcbSYX7d/vJk+WxEvVYAkU+Sssc+GzTSRzOLsbobg3RsUk8okL01u90OrH3RBEKSx3o1jwBllpugs7ML8WHPx1Dqd2Bs3s0QkGJHd9szVS7f36/dLRuGOf1kyDkQ8iH1w9NuHW4/fbbFekYNGgQ3n///XBTr17ok790KYp+/FGtJWXmTER37Fgv1hWJi1DuEFoo6BIhqaCVgkSD3/PyXG6SSMTGfc2J55yDuAAQ7bxiO95fexQ80DVJTbDhlPQE9GqZhIaJ0aiFAxi+JSRCn2w8gd+zijFtQDqa1RL4qRGFTumJGNUtTblIDmYWY/HG4yh1OHFe33S0SvPOAqKN2bNlEk7v1KBW4mP4osNwQLF8hOGm6FHpzjvvVKRjyJAheO+99/R0kTY+IJC7YAFKfvsNUUlJSJo5E7amTescpfCHH1C6YwcSx49HVOPGPswoXbxGoKwMtEpo7g8SC0UwSDT4RZdIPbdYeI1ZDR0CGXBKa8OmQ/n4ZksG6M6oKvExUTi1dRIGtEtBjC2wFz1q5ONARhGmDmhaK4HYe6IQ8386htPap1YKDj2aU4KF64/B7nRi2oCmSE+O0Q0/ycv8dUfRr22KGvNQZjEW/XwcbRvFYWzP8IkBIUlcuOE4YmwWnN+vKehuMlqEfBiNaJDG08jHsGHDMHfu3CDNGnnTMNMg9733ULp/P2zNmiF5xgxYk5JqBSJn9myU7tuHhGHDED9qVOQBFqAVMzuEJEJzidgzMspjLjIylBXDzGKJi1Np3ZboaPWFmBhYY2KA6GhYY2PVd7bx2zRQkWrLFFuHe8otgLhTTw14qq321h9lsWBKv3QkxkZh/8kibDyYh8NZxXA4nYiNtmJ8z8bo0CQuYFYBjXzsOVGIU9sko0VqrMpCISdKjLXi1NbJigDVRj74rO08VoDFG0+4SITe56/qmBzn040nYHc4MahDKoadkhqwdevVke2OZhdj/vpjKCxxoGvzxIAQIyEf3uxIGLW96667MG/ePIwYMQKzZ88OI83qnyo86EhA+D3mlFMUAalNSn79FbkLFyKqYUM0uP76+gdGgFekLBUZGSg9cgT2I0fA9Gf7sWMBnjUww1vi42FNTlZfUamp6rslJcX1c1SjRoGZOExHLScfx9AsJQbn9GmMaDcLBw9fHsRfbclAcalTxVj0aJlo2EpYX+NAZhGOZpfgWE4Jfs8uRpm9sgXGarEgLdGGM7s1VPEcdZGP3CI7Fvx0DH3aJCmyole0MU/vnIbT2qeoblkFZfhpbw4yC0rVulPibXqHC2g7Yvbz/lzsPVmEs7o3RHqKfguPHsWEfOhBKQzb/PWvf1XuljPOOANvvfVWGGpYv1Qq27+/vAZIcTFiTz0VSZMm1brAjEceUVkPcldMdYhUfYsTJ8rdJAzqZH2LwsLyWAwGdJogW4RWCo1UaN8tSUmIIrkg0SDhSE2tX38ABqxm9/FCfLT+GAbzDb9T9RoXJCC/HMzD11syVAwI4zGS47w7iElwvt6SiSPZxcpQ1DAhGqe2ScLOY0XYfbx6ijPTZ7s2T0CLBrHq0Gdch9MJHM0pxvHcUizdfLKa28UfKDTyMa4nyVXtFlR/5jBLXyEfZtmpKnrefffdePfddzF69Gi88cYbJl2FudRm7AdjQCgJp5+O+JEja1xA/hdfoGjdOlUvgXUTIlkchYUqBqZ0506U7NypCmaFu0SlpSnyQGuFpeI7/+0iGnSJiHiNAN/uv92Wid6tkpVbhcKMExKFw1klyCgoVdYIWiAGdUhRbghmxJSUOfDjnhxsOpinMk7ommnTME7FhzRPjXG5KY7nlihrBINb3YXxJhyTZKR7iyS0bxKvxiIRqIkEnMgrt9DwzZ+SGm/DaR1S0CotDgkxUYi1cazK9TkYI/HNtkwwjoRrYDBtp/QE9G+XUqkuiF7yQZ1/zyrB9zuyFD4kRLTKUH/GxgQ6Lqa2zeVeLPstAw2TojGofUo1HHKLyrD5UL6yYtF6xLgWYl+TCPnw+k8oPDrcc889mDNnDsaMGYP//e9/4aFUBGjB7BdmwVASx41D3IAB1VZdduQIsl97DbBakXb99bA2iKxKhixRz2JtJBtlhw6FzVPBWB2NRJBgWBITYeUXLRUVP1sSEsJG3/qkCA/TZb9l4peDuTUuiwdUUlyUCv7s3jwRbRrFA3Bi1/FCddhpRECzhPCQo3RumqDiEaKjLMri8fOBXDXG+F6NlSWjqNSOtXtysX5/jsqsIdkgofl6ayY27MupkXwwJuS77Vn49VCeisWoKpp7pn/bFDU/idFP+3JUW45NHQtL7Sgudah/n9E1DX1aJamDmuRj0YbjGNerMbo0K3/WcgrLsD+jSMVW0PJCgkWStu1wgYqDsUVZFIEhqSKxIfma0ItxMcRIn1A3BvrGRFlAb9OW3/PVvIx5YewNrVJf/HoS3VskYninBkqPolIHth/NV2uMiy4vrKYRM8aCuLvGqNuaXdnYVIFZs9QYDO3YAO0a1x67I+RD396FXav/+7//wzvvvIOxY8fi1VdfDTv96rNCBV9/jcJVq9QSWYKdWQJVJft//0PZ4cNIGD0a8UOH1mc4lDWjdPdulGzbVm7dKCwM6nqVGyQxEXR9uNwhKSnlsRYpKeWuEHGDBHVPqk5Wandi0YZjOJBRjCbJ0apIFw9pZnmkJdhqfJN3t0DwwGXWSb+2yepgZJzEih1Z2HYkXx3aI7ukqUOdFohz+zZRlhF34eHLL81isGF/rnLv1OX+OJFXqjJTSBpoiXE4nNiXUaxiRkh+qFPbhnHYfrTcnUNX0ehuaRVzW7DvZKGqC0JdtYOaMRTfbcuslGVDXdTv+pfXDtGIEcdsmRaHM7uloUlyjCIPO44U4JttGSCe3tQaoS4frT+Oc/o0QUKsFfPXHUNxmUO5wE5pmqCyd0ggYm1WV/qxZqWh7r1albuIiOHy7VlYvy9HkSIGDmvWEBJE6jukQ4oij55Sp4V8hPRP0vfJ7733XhVoOm7cOLzyyiu+DyQ9fUIg/5NPULRxIyyxsaoGCC+jcxe6Xeh+sbVogdQrrvBpjnDuZD9+XBENWjhK9+4NqKrWlBTY0tPB78pyoX0n2UhKkgsAA4q+MYPzYGKdD77BT+7bRJfbQOvDN/RR3RqiV8vESmZ+rXAZ39rH9WqIH3blqAN6Wv90pCVG16m4HvKhzU/CQmIQF1093VQ7oBskROPcUxsrkuAuGoFh3wsGNsW2IwVYXoV8kJAw0FYjQppujEM559Qm1cq57ztZpMhC28bxmNS7sSIBnkTTc0jHVFWrhO4hCq0aJEfUM8oKRWo0skHCsuCn48q95X4PjTsB0eZlKu7wTmnKcqK3ZLyQD0+7Fqafa+Rj4sSJeOmll8JUy/qtVu7cuSjZvRvMWmAGDE35mvDtP+PJJ9U/Uy6+GNHt2pkeDN5LUvzLLyjZtEmluQZC6PaIbtlSkTmmNpO8MWtExNwI+FLZUzv8ayMUPATpKth+pEBljmw8mIvoKKsu8qHFn7hnnfAel2+2ZmBE5zSV2aHNT5cOyQdrkVQV7VDv0iyxxrtasgvK8L6yngDT+jcF03urkg9tjD6tkzCmeyNo5KNqfRFt7kOZRViw/jgaJUbj/P5NXC6Rup4QbQ6tDQmCVm+FbiTGtJA4ffLzcVeArbZnzVJiq2UnMQ6FOhSW2BWRZPZSu8be/Z0K+TDp3/R9992nslzOOeccvPDCCyZdhbnVZn2JnLlzVSooyUXy9OmV3sLzFi1C8aZNiOvfXxUdM6Mw9bX4t99QsmULyg4eNHQJJG0kGFZ+T09HVNOmlQicoZPJYCFFwBfyQXM+XSmsRDqlf3olVwpjHxhnsWpntorxGNghBZ/9cgKsIUILA10gdcnmQ3mKuLgf8Boh0SwQjNlYuOEYTuaX1lpMjAXHPlh7VAWYkqAwIFUTumaWbs5QtUzothncsQE2Hih390zs3Vi5iyha4TEe/iQTdE2x9kfH9HhMoGWjogw942ZO5pepCqtZhWUqJZiVUvWIO/kgXqzqum5vjurKlOYzuzZU1g+Wk+/UNF5ZP0ic+G8Gu04fmA5adyhsR5LCy/Y06ZiegAm9GumyaGl9hHzo2bkwbHP//ffjzTffxOTJk/Hcc8+FoYaRoRJrUeS8+64qzx3bsyeSzjvPtXDGQZCcWBMSkHbLLSoA1QzCGI6SzZtRtGkTmGJspDA+JqZLF0XWGJMhEhkIaIcfScH0gU1VkKMeYeYE011pOWAcB90pWQWlKkCTBIQxIzwoi8oc6qp61gg599Qm6uCuSzR92H9y33Rl5eCBymDP8/unKysAD3stiJVjdmpaPRjZ/fI5WkZ4Zw3dICyaxlRd6k2rzGkdUhWJ0Kwa7hYXjZg1SYpRLim7A1iw/iiOZJeooNn2jcvjV+huoYuEsSYjOjdAr5bJHuMqNAy09TKmg64cEgeSIBKRiX3KXTvE4IN1R5X1iESKliW6yrgGXqaXGBeFTQdysXJXtvqM5eHbN45X+0MCRb0GtkvBwPap0HNtj5APPX8BYdjmgQceUCm2U6ZMwTPPPBOGGppfpdI9e5AzZw54s23caaeVV5+sQZhGmlNR4j5u0CAknnWWq1XWiy8qF0XixImI69s3rEFhlkrRTz+hePNmwMB6GyQZvLCMFiBv619kF9rx84E8HM4uhRNONG8Qg76tkpAar+/w8gbwYM7ljV71oa12wDZOilGxEVr2hKe1lRcfK8S6vdnqMOe/ecjxkGe8AuMeeKhr8R88VBkE2SCh7vogbMd0Wi1rhnpoKb60UGiHJw9VHq4sslXbRXLMCqHVZMuRfGUtoHB+6teleSKaJPHemvK4DM1SQnJzXt8mai2a7iQAI7uW3yHDOJe1e3JUMCsJEXVjoC6DQ5kN5G0hMm29jMlg3AeDTX/7PR+npMe7LBokW4w/Wb8/T5EPzrF8eyZO5JYoCwx5zuj9AAAgAElEQVSDZ3nDL8nVqK6MwWEGD9RYdFdt+Z3ptbGK/OlJBRby4enpD9PP//GPf+D111/HtGnT8GRFbEGYqmpatQq//x4F332n9KdbgASEBcZqkuKff0bep5+qjxLHjEHc4MHqZ+123JgOHZA8a1bYYcFy5YzhKP71V1X8yyhhhVdl4eBXlWBcvXPkFZVhyZZMxCfEo0OTcvPyrmN5KCoqwtiuDZDkZQGquuYN5lx611+f2vFg48GbGGtDcpzxxJFY0T1ASwRjNDyJdtB+uy1LGSTbNYpX9TP0ZGl4GruuzzUcqGfz1NiQ3ezr7RqoN2uOrN+Xqyw5Wg0Wb8dxby/kwx/0Qtj3wQcfVPU9pk+fjscffzyEmtTvqVmnomjtWnU4U6I7dED8aach+pRTqi3cnawkTZ6s3vZZyTPr5ZdVW5Zb56EcalGFv7ZsUcGjpQbGcTBIlIQjpnNnFYTrrzCtcOPxEpzaunKdlE0HMtGjSTQ6N9Pn79ajRzDn0qOPtAkOArQ6qL/rAF9mF5zVBHYWWp2YDUPXjac0Wj2aCPnQg1IYtvnnP/+J//73v5gxYwYeffTRMNSwfqlUsmOHIiGM46AoN8Jpp8HWvHmlheZ//rlyXViiopB84YWKrOS+/z5Ktm9HwvDhiD/jjNAAU1aGYtbh+PVXlOzaBTjK/9P1V0imiEVsnz4qBdZI2XGsABuPVicfGw5koXcjG7q0MJB8BHEuIzGSsQQBsyIg5MOkO6eRj1mzZuHhhx826SrMp3bRzz+jeO1adeGZxWpVBIRf7gdv3gcfqIOe8Q0pF16osmFCddkc5y788UcVQGrUtfIs6hXbuzdievb02aWiZ+eVK2RrFuLi4tAxvZxo7D6eh8KCQoztlma82yVIc+lZu7QRBOo7AkI+TLrD//rXv1Rl04v/fw0J/iwSPAR4+RkJCEut80I0BlTSFcNgUzqQmS2SO2cOSg8dgq1lS3W/S87rr6uMGMZ9MP4j0KKyVWipMdCtEt25s8ro4Z01wRIGgW5QV66XwAILmqdGK998oAJOgzVXsPCTeQSBcEVAyEe47owHvf7973+ryqaXXnopHnroIZOuwtxqM1hTkZC1a+G025ULRgWl9uqlroVnpgzbqBiItDQUrllTLR3XaASoC0u/sz6Hv6IKfrVti5iOHRHTrZuq5ioiCAgCgoARCAj5MALFEIzxyCOP4OWXX8bll18OZr6IhA4B1vqga4MBnJSYU05RJIRWENYAgd2OmHbtUMIy5FYrGv71ryomxEihFaZg5Uo48/N9HpbkIrp9e2WZsbVpg6jGjX0eSzoKAoKAIFAXAkI+TPp8MMiUZdWvvPJKsNqpSOgR4D0ntDzwzhNKXJ8+sKSkgFkwSlg7w2ZD0sSJiDWg5ody/2zYgIIVK3wmHbTWMCg2umNHRLdpUw1EqX0R+udKNBAE6iMCQj5MuquPPfYYXnzxRVx11VXgPS8i4YMAS6rTEsFbbS02G6xpaWABL0dWFpijxlohqZdf7rvCDgcY+Fq4fDlY4t0rsVrLXSmdOytXCi9mq02k9oVXyEpjQUAQ8AIBIR9egBVOTVnbg3e6XHPNNbjnnnvCSTXRhQiQIDD+4scf4cjOVsTDWVCAsop7YBrdcw+sPtT8KNm6FQXffut1QTBe0Bbbr58KFmW2ih6R2hd6UJI2goAg4AsCQj58QS0M+jzxxBN4/vnncd111+Huu+8OA41EhZoQoGWCJISWENYKsR89qqwfKZddhuSpU3WDRitK/pIlXl3uxhgOZqfEDRzoU/xGMOts6AZCGgoCgkC9QEDIh0m3kSXVeaHcjTfeiDvvvNOkq4gctVlvo2DNGuTOno2yI0dga9gQLRYu9AgAyQpLvLNImV5hzZH4oUNV4a/a7qPRM1Yw62zo0UfaCAKCQP1BQMiHSffyqaeewrPPPoubbroJd9xxh0lXEXlqs0x75v33w2mzofm8ebUCwCJmhd9+q6wleoUlzeOHD1epvkZJMOtsGKWzjCMICALhj4CQj/Dfoxo1fPrpp9VttrfccgtuvfVWk65C1K6KgLO4GAXffIOidet0gxPVpAkSRoxQAaRmkr3HC3A0uxSwAE1TotGuSfUry71Zj2TmeIOWtBUEQouAkI/Q4u/z7JrbRciHzxCGV0enE0Xr1ytrBy9+0yO0dCSMHFlOOoy46UnPpAa1WbY5A6sOFCA5PhpOK5CXV4ahbeIxpodvF+9JZo5BGyPDCAJBQkDIR5CANnoaLdVWyIfRyAZ/PN58m7dwoUrN1SO8M4aWDuVe4X3gJpNtR/Pw0cZsjO7TspLm32z8HRecmooO6Yler6g+ZubUV0sOr5PfeCAXvVslITrKt+f3SHYxth4pwJbD+WiYEI0LT2vq9TMjHUKLgJCP0OLv8+xaeXUhHz5DGPqOTqcqhV6wfLmqgupJ1B0yw4cjjgXKTEg6tPWt2p6F7/cXY1SvygfGN5uPYUjraIzolOYJimqf17fMnPpqycnIL8VXWzKx70Qh2jeJx9T+6br3OrOgDFt+z8fWw/ngOJo0S43FxUOa6R5HGoYHAkI+wmMfvNZCu9VWyIfX0IVFB2+sHdbERJW9EjdgAGBwWfZQgLFdWT5yMKpPi0rTf73xEKb0SUWXprUXPqtN3/qWmVMfLTl7Txbhq99OIjO/DAkxUbh+dCuPj19+sV2RjS2HC0BrhybJ8TZ0aBKPjk3i1XcR8yEg5MN8e6Y0fuCBB/DGG29IwKkJ969wxQpVKEyP0NIRP2RIvbvUTcV87C9EUpINFgeQW1iKoa0TfI75IJb1KTOnvllyfjmQhy9/y4DT6UTLtFjMHFS7paLU7lSEg24VWkg0ibZZ0alpgot0REdZ9PwJSZswRUDIR5hujCe1WFJ99uzZQj48ARVGn7O+R97HH4M1P+oUiwUx3bsj8cwzwZod9VVUtktOKeAEmqb6n+1Sn3CqT5aclTuysHpXttqeni0TMa5XzRcWbjtSAH7tOlYAu8Pp2k6NcNDCkRhr7IWM9emZMdtahHyYbccq9GVJ9Tlz5gj5CPP949v4xn3ZKFq+HKm//YSEaAsaJ0YjxlbzWxvTZpMmTYKtZeVgzDBfpqgXAATMbskpKXPiy99OqjgNyoguaTitfWUyvedEIXYcLcTOYwUoKP4j7ql1ozh0Si+3cjRIsAUAXRky1AgI+Qj1Dvg4/1133YV58+YJ+fARv2B049vrt9/+imZrvkZaWYGaMqewFHa7Ha0axFSK9LfExSHhjDMQ17+/6dJmg4GlzGEuBI7nluDLzRn4Pas8TmNy3ybKZULh70g4aOFwDxxtnByNrs2T0KFJHNKT9d0/ZC5URFt3BIR8mPR5uP322/Hhhx8K+QjT/WOxsN0LPkfGug3K0uEuJ/OK0TDOitSE6PJbbvv0QQJdLPHhFTgXiFTPQIwZpo9AxKpFUrF0cwYYLJqaYMOUfumsIwfGsew6VojDFYSEACXF2dC9RaIKHGUsiEjkICDkw6R7ffPNN2PhwoVCPsJw/0p27kT+p58i83gWThY60Dip8lvcibwSNIyzoFGb5kicNAnRrTxH/etdplGHeyBSPQMxpl5cpF1wEPhpXw6+2ZKpJmuSHIMeLRKw92Qx9roFjtqirOjWPAFdmiWiXeO44Cgms4QdAkI+wm5L9CnEO10+/vhjIR/64ApKK2dBAfKWLEHJ5s1qvlK7AwezShAVFYWU+HLrB90upU4nOo0fhbTRI2ut1+ELiTDycA9EqmcgxgzKxsokuhD4eksG1u7JUa4UWjSS46JQZq8cONqtRaKK47BZJVNFF6j1uJGQD5Nu7nXXXYfPPvtMyEeY7B8vjMtfuhQkIO7CoLsT+aUoKHEo03NMmzZoO3MK0lo0qVVzX0lEbYf7Lwcy0bNJNDo3018/IxCpnoEYM0y2v1Y1fCGR4b6mqvoVlNjx9qoj+OVAriIeLPrVqsKF0rphHLo2T0DnZomIj/atmqnZ8BB99SEg5EMfTmHX6uqrr8aSJUuEfIR4Zxx5eSp9tnT37jo1scTEIGH06PJCYR7EVxJh5OEeiFTPQIzpCctQfu4riQylzr7Mfd7zG1UQqdViQcNEG9JTYhQB4feEGKv6ffkXDX0WdQ2R698WCyxwwmKxoHfrJHRr7n1pfV90lj6hR0DIR+j3wCcNrrzySixbtkzIh0/oGdOpeP165C9bBmdJSZ0D2lq1QvKUKeCdLHrEVxLhz+Fe0xt6h0bx2H2SAYIl4BHRPDUap7ZOQmq877UWzJ4+qmf/tDaR4maa/NxGZBeWoWN6PNo0jPO5Fseorg3Rv12yNxBLWxMjIOTDpJt32WWX4dtvvxXyEYL9s2dmKmtH2YEDdVs7bDYkjBqFuEGDvNLSXxKx4WCeV4QhUt7QvdoEAxr7SiINmNrrIfx1DzmdgMPpBGuDsYopv//x7+q/42eqj+OPts1TY2GTqqVe751ZOwj5MOnOXXzxxfj++++FfAR5/wp//BEFS5d6nDW6fXskTpyIqAYNPLatqUEwLQSR8obu00b40ckfEunHtF53FfLpNWTSwQAEhHwYAGIohpg5cyZWrVol5CNI4Dtyc5G3aBFK9+6tc0ZeApcwZkz5dfcmETO9oZsEUpeawSSRvmIj5NNX5KSfPwgI+fAHvRD2nT59OtasWSPkIwh7ULxpEwqWLIGjqKjO2dR9LOPHh12xME8Q+fOG7q+53pNu8nngERDyGXiMZYbqCAj5MOlTcf755+Onn34S8hHA/XPa7apYGMlHXUJrB10sMZ07B1CbwA7tyxt6sMz1QnACu/f+kM/Aaiaj12cEhHyYdHfPO+88bNiwQchHgPaPKbS58+ah7PDhuq0dPXogcdw401k7jIAtGOb6YBEcI/Aw8xi+kE8zr1d0Dz0CQj5Cvwc+aTBp0iRs2rRJyIdP6NXdiXEduQsWVCsY5t7LEhuLpHPOQUzXrgHQwBxDBsNcHwyCYw60RUtBoH4hIOTDpPs5btw4bNmyRciHkfvncKBg+XIUrlzJ3MBaR7a1aYOUqVNhSdRfECncXQe+6BcMc30wCI6Rj5CMJQgIAvoQEPKhD6ewa3XWWWdh+/btQj4M2hlHVpaydpT9/nvtI1osiD/9dCScfrpX196Hu+vAH/0Cba43muD4QrIMesRkGEFAEHBDQMiHSR+H0aNHY9euXUI+/Nw/HkZrl2/C/l92wOlwoKk9Dz1KjyElyl5pZEt8PJKnTkV0u3Zez+hruXSvJ/KxQ7i7NowiOP6QLB+hlW6CgCBQCwJCPkz6aIwcORJ79+4V8uHH/uXmFOCTBSsR6wDaxZSqkfaW2FBw5BhGlOxFkq18cFuzZkiePh3WlBSfZgt310G46+cT6DV0CneSZdQ6ZRxBwAwICPkwwy7VoOOwYcNw8OBBIR8+7h/dLD+/sQC/RTdBr4aV7yrZlFGGzie2o6OtCDEdOyLpggtgsVUwER/mM9p14IMKdXYJd/2MWm+kkCyj8JJxBIFAIiDkI5DoBnDsIUOG4Pfffxfy4QPG9pMnkf3WW9iVa8G2xh3RK60ysfglw47OJ7ahR99OKqMFVv+vAtfrOghVTIJe/XyAO2y6RArJChvARRFBoA4EhHyY9PEYOHAgjh07JuTDy/2zHz2KnNmzVbXSvDJgeUw7JDRLR7uYMjXS3pJo5B89grM7p6DZ1Mleju5fc4lJ8A8/Pb0jgWTpwUHaCAKhRkDIR6h3wMf5+/bti4yMDCEfXuBXtm8fcubNg7OkxNUrxx6FzdHpOBqVBAuAdHs++nZNR8vzJ3kxsjFN/Q1MDZXVpLbVH88tw7Ktmdh9orwsfcfG8TizawM0SfbdhWUM0jKKICAIhBoBIR+h3gEf5+/Tpw+ysrKEfOjEr2TrVpVKC4ejzh6xPXogafJkQ1wtOlWDRho27s9HZhkwsEMaWqfFIj663N2z4UAWejeyoUuLpFqHDDerSVZRGV5fcQwJSQno2bY8UPfXfTkoyMvHFcObokGcEBC9z4e0EwTqIwJCPky6qz169EBeXp6QDx37V7xhA/IWL/bYMrZ3bySde67HdkY2cCcNzZPjsHp3FsqsNjROtKFHswQcyipAYUEhxnZLQ1IdB7a/VhMj18Sx1u7Nxcq9xRjarXGloVdvPY5BrWMxuINvmUNG6ynjCQKCQGgQEPIRGtz9nrVr164oLCwU8uEByYKvv0bhqlUe8VbFw0aO9NjO6AZVSUNeoR3bjuXhl/1ZaJYYhd6tknFq6ySkxlfOyKmqR7hlctRGPlb8dhzD2sZhUIdko6GU8SIEgXBzL0YI7IYvU8iH4ZAGZ8BTTjkFpaWlQj5qgdtZVqbcLKXbt9e5IZaoKCSddx5iunULzsZVmcUo0hBumRzlbpejSEhKrOJ2KcAVw9PF7RKSp838k4abe9H8iIZuBUI+Qoe9XzN36NABdrtdyEcNKDKTJffdd1F26FCdGFvj4pB84YWwtW7t117409lI0hBumRwMOP1qaxZ2nShUEHVoHIcxXdN0BZzK260/T1X97SuF4urP3gr5MOFeOp1OtKso833LLbfg1ltvNeEqAqOyIy9PpdKylkddYk1NReoll8DaoEFgFPFi1HAiDeFw6MvbrRcPT4Q1NcpSGGGwheVyhXyE5bbUrVRZWRk6duyoGgn5+AMrR3a2Kh7myMmpE0Bb8+ZImTkTloQEE+5+4FQOl0Nf3m4Dt8dmH9lIS6HZsTC7/kI+TLiDxcXF6Ny5s5APt70j4ch+/XXQ8lGXRHfogJTp0wE/yqW7jx8OlgKjHuFwOfQj8e22Pj1HRj2PtY0TTpbCQK+1Po8v5MOEu5ufn4/u3bsL+ajYO+VqefNN2LOy6txNo2t4hIulwKhHOFwO/Uh7u61vz5FRz6OMU78REPJhwv3NyclBr169hHwAcOTnI+fttz3GeMT264ekCRPq3G1v3z7DrbaGv49yOB36kfR2Gy4WJ3+fH+kvCHiDgJAPb9AyuC1TZZ955hls27YNDz/8MJo0aaJrhszMTJx66qkRTz5YJp0xHryvpS6JHzQICWedVWcbX94+w8VSoOuh0dkokg59nZAEvFl9fI4CDppMYHoEhHyEcAtJIm6++WZs2bIFr7/+usua4UmlEydOoH///pFNPhwO5Lz7Lkr37KmbeOgsHuaLFSOcLAWenhn5PHwRkOcofPdGNAscAkI+qmDL+1KWLl2KkSNHomnTpoFDHsDu3btx/fXX4+TJk16RjyNHjmDQoEERTT7yPv4Yxb/8Uuf+JI4fj7gKkuZpI319+wxXS4G3LiRP+Jjlc7OuO1yfI7Psu+hpPgR8Jh8OhwMHDx7El19+iZUrV+Knn35CYmIihg8fjrvuuguNG1e+08Es0Kxbtw5Tp07FjTfeiNtuuw1RUXWXtfZnXZzr0ksvBQNIX3vtNZzlwTWgzfX7779jyJAhEUs+ClevRsFXX9UOvdWK5ClTvKpaGq5vn74cpr64kPx5jsOlb6SuO1zwFz0EAW8Q0E0+aBFYvHgxGOzIw3LFihXYsGFDjXP9/e9/x5///Gdv9Aibthr5oP533303oqOjA6bbwoULlduF4g352L9/P04//fSIJB+M78h67TXA6axxXyzR0UieMQPRbdt6vW/h9vbp62HqiwvJa7DCsIMEbobhpohKgkAtCOgmH9qhrI1z2mmnYdy4cRgzZgxatmwJm82GgoICvPTSS+ClZ5MmTTIl6No6ub7rrrsOx48fR0ZGhloLrTpalokRi3vuuefw5JNPIj093Su3C901o0aNijzy4XAg67//hf348ZqJR1QUUmbNgs0H4mHEfho9hq8kwlcXktH6B3u8SF13sHGW+QQBIxDQTT62b9+uDmPepEqXxPnnn4+4uDgjdAjpGHQfMdvkl19+wYEDB7B27Vr88MMP1XTiXSpc81VXXWXYujXywUviSNq6dOmiC4sdO3Yo0keJpAqnhStWoODbb2vGiK6WCy5ATKdOrs99cVno2oAgNfL1MA1XF1KgYYvUdQcaVxlfEAgEArrJB0nHAw88AB58zz//vLJ2mEl4H8rhw4dBl0Vqaio6deqkrDV0HV100UXKleQu3bp1w7Rp01RWCYlHSkoKLBaLoUvWyMfYsWNVqm2jRo10jb9161awTySRD3tGBrJeeqlWfJImT0ZsRe0TNvLVZaFrA4LUyJ/DNNxcSEGCDJG67mDhK/MIAkYhoJt8FBUV4cEHH8SxY8fw6KOPqoOSVgMe6N9++62yFjAuhMJD/YorrnDFJdBN8PTTT+Oaa65Bz549K+nOMUhoPvnkE2zcuFFZVVj/4tlnn8XmzZuVa4fWBrpyajr8Ofarr76Kzz//XAW8Tpw4Eddee22lg5x6cf4PPvjARTLotvj3v/+NmJgY3HPPPUonzmW1WlXtDZKPxx57DMnJyXVinZubi/feew9z587F0aNHlWvmhhtuQJ8+fVz9uMbffvsNq1atUjfRDh06FL1791Ykjm4XEo+ZvGtEJ7khLhMqCmZFiuUj75NPULxxY417ETdgABLHjav0ma8ui9o221srirft65p3w8E8HM4qgQUWNE+Nxqmtk5AaH7hAaKP+c5FxBAFBQBCoDQHd5CM7O1uZ+NesWYMePXoo1wOzXXj4a9K3b19FAPbs2aOIx3333af+TWJBUjFgwABFKlq1agVaIvgG//LLL2PRokVo0KCBskAw/oGHflVLBFNSeXsryQKF/Tkug1tJLjg3idGhQ4dwzjnn4JFHHkFSUpJKY/3nP/+JBQsWKALDFFpaO3788UdFhpiZQ7Kkyd69e5WurVu39kg+uPY777wTjBOhdSQ+Pl4RJlqFGEDKEui8BO5///ufWre2JmLy4osv4uuvv8bbb7/tVbAp9SRJO/fcc5XKkUA+nAUFyHjmGcDhqPYcWxMT0eCGG2CpeC60Br66LGr6Q/HWiuJte3/+e/KH5PjT11edQzGnr7pKP0FAEAgcArrJh3Yob9q0yaUND1xaGhh/QEKiZYbwwOUbfmxsrGrLolh/+9vfVP0Mtr///vsVceCbP4VWkgsuuEARiptuukml7VJIOBi4+sQTT6iDmv3+9Kc/KQvBsmXLcPvtt6Nt27aKQNCaQCsEycjq1atVACcP///85z+KREyePFllr7Ro0cJVX4OxFhpJ0RalFf4iQajL8kGSw1RcEijqPGvWLEXIZs+erUiXlvGzfPlyZYnhXHfccYfCiaSNRITjkwiRmJx33nm6d5l9tPaRQD4Y58F4j5okedo0xHTtWu0jf1wWVQfz1oribXvdG1+loT8kx5++odDX1zmlnyAgCIQnArrJh1YQizeqMl6Ch/mZZ56pO/jSnYBoUJCI8ODWXCru8Rdnn322ckewXsjOnTvVwU1S88ILL6g5eeiSZEyZMkVZTGhN+fjjj1XgJg96HugkQ3/5y19AnUlg+HsKSQ4DaGmpaNOmTaWd0UM+qAfdONSFFhcSosGDByurB1021JffR4wYoVxVdEs99dRTqg2F/WnxoU4Ub9N6GRTLeBRKvScfZWXIfPZZOP5/zFFVsbVqhdTLL6/1L8so/7+3VhRv2/v6X4M/JCcUaamhmNNXbKWfICAIBBYB3eRDIwZ0ffCw1BufoKnPA//DDz9UJILCmAW6Q9yDLN3rXrgfyLSiMDaCrgrGVtByQGsHCQoPenfXD60xtHDwM7p/aD1p3769RxeKpifdNCQsnJOWGd63wp8ZL0L3DuNPdu3apTJ/GLhKNxBJkCa0mNCSw3lJeljHg2Tp8ccfV+2JA9trVhC24ReDT/VWVGX/GTNmqCm5HyQg9VVKtmxB7vz5NS6vapBpoDDw1oribXtf9faH5PjTtzZ9PblUAjGnr9hJP0FAEAgtArrJx/fff4+LL75YHZr8zgOZrhi+xe/bt89VC4PWBgaVMqAyISFBrY4H7pIlS5QVgO4KCg9pWjYYu6ARmTlz5riCP2mloGWBgZ9asCs/p2WDrgzGPfBnxogwfoPpsuzTr18/F6FhfQ4ezrQUUO/LL79cBZTWJXTdkNgweFTL6tEICTNe6Cqh+4guF1pX6BaixYNzMDh14MCBaNeunZpHs6IQK5KLtLQ0ZQW59957VfYMrSdM8eV8XCtjVfQIC7zR2kOhHlqhMj19zdYmb/FiFNdQzI4xHg1vvx0IYAVad6y8taJ4296XffGH5PjTtyZd9bhxjJ7TF8ykjyAgCIQHArrJh7tVwpPqJBaM8bjkkktUwOd///tfFVjJtFUetEzbZVwEL1QbNmwY/vrXv6rsEB7QjJlgrAYPaVoxaCGhBYPVVenGoFWD7gr+jq4LkpO6hKTnH//4hyI9dJEwo4VzkiAwILWqBced6Lz//vtKZwbEMoaDhzwtMlocCbNlNBJQkw7uY3EttHzQgkSdafXh2NSL4zLA9V//+pciZZ7ku+++U2XZI4F8ZD73HBw5OdUgie3ZE0lexMl4wtSsn/tDcvzpWxUvvS4VI+c0656J3oKAIADoJh8M+GQWiJaxoRXdonuDQZ81FRzjHSS0PDANl8SBdUI6duyocKfrgoctP+Pvp0+f7iIfPNy1z+nqYCYMC3xdeeWVaN68uevwv+yyy5SlpK5iZ7S60DJBYkMi4i7MSuEdKQxcZSAqhe3feOMNRVjchfpTX8aIMOOHWLA/rRd069QmdAnR0sH7b0gsGKNCAuPeh+PNmzdPkTA9rhcG2xILCnWnm6g+Sl21PRInTEBcv371cdmmXJO4VEy5baK0IBAyBHSTDx7KfJNnzQqKnjd0pucyFZW3sPIwp+WhLtEsH9r18nTpMB6C7ht3CwXH0wJOGfhKlwotBxTWHfniiy8U0WB/uk5ILKg/iQyzdejqoJuEXwwCpVXDnQy4B8fSSkLXyvjx45XbhOIecEorBgkQs22YspuXl6diOj766CNFOEhORo8ebegGc21XX321GpNrZ9BufZTS3blSTHgAACAASURBVLuRM3dujUtrcN11iPJQlM1TDEJ9xCxUaxKXSqiQl3kFAXMioJt8+Lo8EhbGPOghK1XJR11z0u3y0EMP4atabjflfCzcRQuFRhq8WQNJFq08HKemOBHtHhsSpao1SbR5WFOEbqaqhdW80aOmtnRBMaC1vpOP4k2bkLdoUTUIWNsj7dZb64RRTwyCv/sQiP5mJkyBcqmYGZNAPCMypiBQHxAIOPnwBiRvyAfHLSkpwfr161UAKi0aTJ1loCuLmbGehqfqpN7oVlNbWlNIgr755hvlimEsCy0hJBuDBg1SmTKeAlx90YExKJqrpT5bPgp/+AEFy5ZVg8jWtClSr7qqTuj8SUP1ZU+M6GNWwmTE2msbQzAJJLrejy1E0HvMpEfNCIQV+dCCWplOS3eHSM0IzJ8/X2W51HfLR8HXX6Nw1arq5MNDfQ92MGMMghkJU6D/RgWTQCOsf3whgvqxkpaeEQgr8qGl89JVwftRRGpGgFk4jKWh8DtdS/VRCpcvR8Hy5dWWFnPKKUiuqHNS5xvz1iwVjNwxPUk12308D4UFhRjbLQ1JcX+U1K86Rqje7sxImI7nlmHZ1kzsPlGkYOzYOB5ndm2AJsm14+vNs2pGTLxZn5naChE0026Fv65hRT60Eu5aDQw9mR/hD7HxGrrXQ6nPRK22mA9b8+ZIrcj2qQtdX2IQQvl2Z7agzayiMry+4hgSkhLQs22K2opf9+WgIC8fVwxvigZ1EDy9fxVmw0TvuszYToigGXctfHUOK/LBGA4W8WJMA8uTi+ul5geHNVOYoUOpz+Sj7NAhZL/xRnUQrFY0uvtuwEPBOF/+7EL9ducLYfJlnUb0Wbs3Fyv3FmNot8aVhlu99TgGtY7F4A7lhMRfMRMm/q41nPsLEQzn3TGfbmFFPggfK4yyxgfve6mrfof5oDZOY2bYaHVIWBtEy3wxbobwGMlZUoKMxx6rUZmUyy9HdKtWhitq9re7YLqMaiMfK347jmFt4zCoQ7Lh+yMDhhYBIYKhxb8+zR525KM+gRuotbz66quq4BmFFV95z0x9FXWpXG5uteXFDxuGhFGjDF+2md/ugu0yKne7HEVCUmIVt0sBrhiebojbxfAN9mPAYBI7P9SUroKAKRAQ8mGKbaqsJO+0efTRRyOCfOR/9hmK1q+vtkvWlBSksbiaxWL4Dpr17c4fl5GvBysDTr/amoVdJ8pvHe7QOA5juqYZFnBq+Ob6OGCwiZ2Pako3QcA0CHhFPljXghU86Q7hBXIioUGAVVt5vw2Fd+hce+21oVEkCLOW7NqF3HffrXGm1Esuga1t2yBoYY4pfHUZycHqeX/9IXaeR5cWgkDkIeAV+dDud2Epc17QxntRWFSL2SlVL2jzB0qWQefNsawQKhkv1ZF86qmn1I26kUA+4HAg44knwPiPqlKfLpfz1fLgjomvLiM5WD3/b+UrsfM8srQQBCITAa/IBy8/Y3ZFVeElcxMnTlSXw5GY+EtE1q1bh6lTp6r6FSymFRWka9PN8gg8/vjjeOGFF5S6vFfmmmuuMYvqPumZ+9FHKNm8uXpfqxVpN98Mlls3sxhpefDFZSQHq+enx1di53lkaSEIRCYCuskH72h58803sXXrVnXJWuPGjVFaWoqff/5ZXaLG31N4CRtdAa38yETQyAdvf2VApbh4Kj+cDz/8sLrZl/J///d/rkvm6usjXNcFcwkjRiD+/9+YrFd4OC/dkoHvd+Qhu7AUsTYrujdPxPl9G6FNwxi9wxjaLtSWBzlY9W2nL8RO38jSShCIPAR0k49PPvlEWSImTJiganG435vCS9h27NiBV155BSz9TUvII488ou438UU08nHaaaepTI7jx48jIyNDDTV8+HD06tXLl2HrTR9eqPfaa69FDPngQrP+8x/YT5yotofqkrmbb9ZV84OH7LwNh7Fydwk6t2qI7q3SkFtUhm0Hc1BSmI1bzmiOZg3iDHtO9LpSwsHyIAerYdsuAwkCgoAOBHSTD1o4rrrqKuVWYcBjy5Ytqw1PErJgwQLcfvvtKh6E7Xi5midhv23btqmr7g8cOKCuuv/hhx+qdSOpOf/885UekVwD5P7771dWKMq9996r8KjvUrxhA/IWL65xmYnjxyOuf3+PENDC8N76DBTChpHdm7naZxWUYNOeExjZJhpn9fL8vHqcCIA3rhSxPOhBVNoIAoJAfUJAN/nYvXu3KmaVmpqKf/7zn+o22RYtWihLhBbj4U4+Bg8ejKefflq18SQbNmzARRddVO1qegazTps2Df3791fWFH8DW1m8jIGa3333HRITE9G9e3fMmDEDo0eP9ur22bKyMuzcuROffvqpus128+bNKuaFpCApqfwekUAKCcfs2bMjinzAbkfmM8/AUVie0ukuFlo/broJFlvd94nQwjD3pwyUIhqnd2vqGiKzoAQbd5/AGa1iMPbUytU6fd1Hb10pYnnwFWnpJwgIAmZEwGvy0b59e1xwwQX405/+pNZL18jpp5+uXCO81p5EgkSBsRpnn312peBTEhgWyPr888/V4c8Dm2miTOFl4CSFWTS8hp7l1Uk+qrp4NJDZZ/v27SguLlbVUGNi6vbX79mzB7feeqvSr6rMnDlTXVF/9OhRdOnSBQkJCYpUcA3p6elgdgktPQUFBaD76cMPP8SPP/5YaRiSLa1doB8EVjV97733Iot8/P9S8rxkjpfN1SQJZ56J+CFD6oReuV1+PoIVu4rRpVUjdG/VQFkotv2eieL8fNxyRjPD3C7h4EoJ9HMY7PH1urGCrVeg54vUdQcaVxk/tAj4RD5IGPiWX/UgJ6GgpeLqq6+uFHBKosBDm/eRMI22b9++OHbsGA4dOoRzzjlHxYe4Wwy0C+bo4qmNfLDvTTfdpNw0LDdeNQ6Eqbr8PQlB8+bNVXYIa2PQbUOiQ3cQa5bwEGe73r17K1L04IMPqjtl7rjjDtf6aPHhv9944w0w3oIyduxYXHzxxWot7vEvwdhO6vLBBx+oqYgpA3MjQZhum/X88zVbP2JjVeyHxQMJ5X/kS7Zm4PvtecgtKFMBp92aJ+D8fsYGnIorxdgn0hs3lrEzh3a0SF13aFGX2YOBgG7yQavGFVdcgQEDBihCcOLECdx3331Yvnw5GjVqpNwZjPOw1WD6XrZsmYoDadu2rUrVHTp0qLrDhQfn6tWrq5GHzMxM3Hzzzco6Uhv5oAuFpICWCVb7dHfvMAuHhObdd98Fq4HSbcN5aSkhASFhcBe6URhjQtcP3TAkRnQr0XKzcuVKNGvWTMWv0DJC4rJlyxZlbaElhW1oqQmm3HLLLSrDKNLIB9dbtG4d8r/4oka44wYOROLYscHcijrnEleKcVvhrRvLuJlDO1Kkrju0qMvswUBAN/nQMlD4xs8DnPEX+fn5yqLAA762DJcjR46AhyVJxpQpU9QBzzTcjz/+WPU75ZRTFHFxT83VQz7qSsc9ePCgIi90n5CYxMfHq3/T0lFbsKyWzUPQSXro2qA7hu4fWkdo9ejTpw9OnjypyNI777yjrDgkISRBxCVYQbC0+BA/CgnglTqulw/GwxSUORwOZL30EuxZWdWns1jQ4M9/RlTTP+I5gqKTiScx2qRv9HgatJHqxorUdZv4T0pU14mAbvKhBYXysHU/wGk1WLhwoXJH0OLAGhTnnnuuK9ZDK0xGCwGDNBn3oUltsSE84BmDYbfbXRkz/JmuBh74zO6gPixERjLDA1g7+KkPA11JirS0YMajkCCQjNRGPrgGEhQKU4pvuOEG1V77PVNbzzrrLJfuJDIkVAz8ZABrgwYNcOmll6oYFpKXQArnoIuIQvcXLVKRJKW7diGnlpLrUY0aIfWqqzwGn0YSXrWt1WiTvtHjuesdqW6sSF23/H3WfwR0kw8t2yUnJ0fVmGCmiCaM6aCbgte8b9y4UblTeCAyGPSBBx5Qv6OVgy4SBmoyrZYWj379+imXTVWhS4ZuEro5NLKgERJaXOiKoR60ALC4GQtujRgxAiyERgsF+9Aqo6X7cjySDxIUfsZYkqry4osvqnE5Dm+MbdOmjWqikS5aQi677DLQKkM3C7N+KFqNE2KyePFiFSRb1ZJj9GNE8sWYlkglH1x33qJFKN60qUZoY7p2RfLUqQG5dM7ovQzleEab9I0eryo2kerGitR1h/JvQ+YOPAK6yQeJAK0RJCE1BXhSVVo2tEDNuXPnomfPnsrlwkyTl19+WR3MeoQkgoGfc+bMwfvvv69iNhYtWqRIDa0TDLAkAaDrgdVUSTRokSHJYCDq5MmTERsbq/rSAtKjRw9FPvbv3690Z2ZKTeSDBKSq5UYLbOX4tLAwW+fJJ5/E5ZdfrtaWlpamhqLFhWumjoEuC89MI96zQyG50zKP9GBbX9o4CwuR+dJL4PeaJLZvXyRNnBjU5RrpcjByrNpAMNqkb/R4Qd08mUwQEASCioBu8kE3A1NP+aZPYsDy6jUJgzpJCHg40+1BqwQtCrQaMFhTT1wELSm0YNCS4i5VrRJsR9cH3SystcGaIyzvPmbMGGU1IZFgFgx14X0oJEFVg1O18UlgGBjLOTRCwc84B2M+GHBL/VnJla4OWnNqE7YjQQrUnTSXXHKJCvSlECOuLxKlZOtW5H74Ya1Lj+vXD4kTJngNjS8Hv5EuByPHqmvxRpv0jR7P642TDoKAIGAaBHSTD19X5B5wSosELSOa2+Pw4cP44osvsGTJEmU5oEtEy1phNg1JDN0LTH0lqeCdMu7EwBudSJroBmLwqb/Ceh9ffvmlqubKaqwkLrSu0BXFwFPexuup7og/OjAQdtWqVRFPPghAwVdfoXD16lrhjG7VCskzZsASp69suq8Hv5EuByPH8vScGW3SN3o8T/rL54KAIGBOBAJOPggLLQ4MSP3qq69qRIkBmjxQ6a5wJxckDDzY+Xmw01nDeTt5ezCLoFFohaJVKZIld/58lGzZUisEUWlpioAwGNWT+HrwG+lyMHIsT+uVzwUBQUAQCAUCQSEfXFhJSYkKSqW7gC4MWiBY2It1Q2g1CHahrlCAbdScTFkmlkI+KhAtK0P2W2+h7PDhWiFm6XXefquqoFostbbz9eA30uVg5FhGPXMyjiAgCAgCRiIQNPJhpNKRPhZTmbWYE1qUmOIb6eIsLkbOe++h7MCBOqGwNW+OpHPPRVQtFx76c/Ab6XIwcqxIfzZk/YKAIBB+CAj5CL898agRA3kZYEvhJX8MQBVRec/I++STWlNwXRhZrYgbNAgJI0bAEh1dDTo5+OVpEgQEAUEgsAgI+QgsvgEZnQXbWCuFIpaP6hAXrlqFgopU5Lo2wJqcjMSzz0aMzhTwgGymDCoICAKCQAQiIOTDhJs+evRo8G4bsXzUvnll+/cj96OP4MjN9bjD0W3bqpRcPQGpHgeTBoKAICAICAIeERDy4RGi8GvAVF7e/EthNVberitSHQHegpv/+eee3TAVXWP79UPCqFGwGpCOHar98KVGSah0lXkFAUEgchEQ8mHCveetwKy8KuRD3+aV7NihYkGcBQUeO1hiYhA/eDDiBg8GfzaT+FqjxExrFF0FAUGgfiAg5MOE+zho0CCweBvl3//+t7pcT6RuBByFhSj86isU/fyzLqhYlCx+6FDEDRjgFwkJpiXC1xolugAJYKNgYhTAZcjQgoAg4AUCQj68ACtcmvKuG1aAFfLh/Y6UHjyI/E8/hb0CP08jWOLj/yAhNWTG1NU/2JYIX2uUeMIgkJ8HG6NArkXGFgQEAf0ICPnQj1XYtOzTpw+ysrKUPry/ZtasWWGjmykUcThQ9NNPKPjuOziLinSpbE1MVK4YZQnRSUKCbYnwp0aJLhAC0CjYGAVgCfVqSLFC1avtDOvFCPkI6+2pWTlWhOVFf5RHHnlElaYX8R4B5Yr55hsUbdjAGwR1DcBg1NhTT0XsgAGISk2ts08oLBGBrlFi9OEUCox0bbSPjYzGx0c1fOomViifYJNOPiIg5MNH4ELZrWvXriisuEpeyIf/O0EXDC+oY2CqNxLTsaMiITGdOtXYzYyWiLrWH4jDqT5hFAh8vHke/W0rVih/EZT+3iAg5MMbtMKkbceOHdUtwJRHH30UM2bMCBPNzK0G74bJX7rUY4n2qqtksbLYXr2URSSqYcNKHwfaEhFMxAN1ONUXjAKFT7D2uL5ZoYKFm8zjGwJCPnzDLaS92rVrB2eFm0DIh/FbUbpvH4pWr0bJzp1eD25r1QqxvXsjtmdPv7JkvJ44CB3kcKobZLPjU5+sUEH4c5Ap/ERAyIefAAa7u91uR4cOHVzTPvbYY7jwwguDrUZEzGc/ehQF33+PEpay1xkT4gLGZkNst26KiES3a1fnTbpmAVMOp7p3qj7gU1+sUGb5m4pkPYV8mGz3i4uL0blzZ5fWjz/+OKZPn26yVZhLXXtGBorWrEHxxo1wVri7vFkB3TIxXbogumNHRUT0Zst4M0ew2srhVDfSgk+wnkSZx+wICPkw2Q7m5+eje/fuYvkIwb6xQmrhunUoWrdOV7XU2lSMbt++nIi0bw9b06YhWIlMKQgIAoJAaBEQ8hFa/L2ePScnB7169RLLh9fIGduheNMmdWdM6e7dfg3M+iHMlrF16ABmz1hiY/0aTzoLAoKAIGAGBIR8mGGX3HQ8efIk+vXr5/rNE088gQsuuMBkq6g/6tIaQhJS9MsvYIyIv2Jr2bLcItKiBWzNm4MuGxFBQBAQBOobAkI+TLajx44dw8CBA4V8hOG+2bOzUbptG4q3bStP13U4/NaSlpGo5s0VEYlu3hxRzZrBmpLi97gygCAgCAgCoURAyEco0fdh7t9//x1Dhgxx9XzyyScxbdo0H0aSLoFEwFlcjJKtW1G6Z4/6cuTnGzYdL72LatIEUY0bw5aerr7z39akJMPmkIEEAUFAEAgkAkI+AoluAMY+cOAAhg8fLuQjANgGcsiyI0dQtmcPSnbtQunevQGZSpGSCiKivjdqhKi0NPVdRBAQBASBcEJAyEc47YYOXXbv3o1Ro0a5Wj711FOYOnWqjp7SJGwQKCtDyb59KNu7VwWslhkQK+Jpbay8am3YEFENGsCalgZbWhqsDRqUV2S12Tx1l88FAUFAEDAUASEfhsIZ+MF27NiBMWPGuCZ6+umncf755wd+YpkhYAg4CwsVCWFl1dL9+8G7ZoIpzLCxxMcjKilJfWeciSUxUblxGPBqTU1Vl+hZEhKCqZbMJQgIAvUYASEfJtvcLVu2YNy4cUI+TLZv3qjrLC1VmTO8a4bfSyu+ezNGoNrSjWNJSQFv97UmJCiyQnePIi8kJ3Fxrt+TxIgIAoKAIFATAkI+TPZcbNq0CZMmTXJp/cwzz2DKlCkmW4Wo6wsC7kREkZOjR8HA1nAWZVXRCAlJCv8dE1P+nb+PiQH474rfWVnnxK2N+re4hcJ5i0U3QcAnBIR8+ARb6Dpt2LAB5513nkuBZ599ttK/Q6eZzBwKBOyZmeVWEga0Hjmifnbk5oZClYDOqchJdDQsJCI2W/nPJC7azxX/Vp+zbVSUauu0WtXP4Pfo6PLv/Dc/r/jOn638TPs9x9R+DmLRN2d+PhxFRXAUFsJit7vwtOfmwpmbi+iuXVUAsYggUB8QEPJhsl1ct25dpQBTIR8m28AgqEtrCONGFDHJyIAjM1N98d9GpvwGYSmmmkIRH5Iaq7Uy6dHIjxu5oWuN+0SiwUJ1eiVh9GjEDx2qt7m0EwTCFgEhH2G7NTUrtnr1asyYMcP14XPPPYfJkyebbBWibqgQUPEkJCTZ2eWkJDdXERIegigqgnrL5oFYUhIqFWVeDwg0uO46SZ+Wp8T0CAj5MNkWrlixAhdddJGQD5Ptm9nU5e29iqDk5ICVW538npOjzP/2vDxFVNRbu5CUoG9t4tixiHOrchx0BWRCQcAABIR8GABiMIf47rvvcOmllwr5CCboMledCKhYBRKRii/+7CgogJMEhd/5+6Ii5WZQrgYGyZaUgFYYEe8REPLhPWbSI/wQEPIRfntSp0bLli3DlVde6Wrz/PPP49xzzzXZKkRdQaAcAQZYgqSEZITfy8rKSQvJCq0q/B3jI0hU7HbXz+p3ZWUAv1cQGdWG/eu5NabBDTdI4Kn8AZkeASEfJtvCJUuW4OqrrxbyYbJ9E3XDFwGN2JDcOEhoSGD4nRkn/HI44NCIjdtnrjb8nbfLcw84JcGqsAx5CkBNOOMMxLtdr+DttNJeEAgXBIR8hMtO6NRj8eLFuP76612tX3jhBZxzzjk6e0szQUAQMAMCzIApy8iAMztbxdyQBMX06FFeDl9EEKgHCAj5MNkmLlq0CH/5y19cWr/44ouVio6ZbDmiriAgCAgCgkAEIiDkw2SbPn/+fNx2221CPky2b6KuICAICAKCwB8ICPkw2dPw/vvv484773Rp/dJLL2HixIkmW4WoKwgIAoKAIBDJCAj5MNnuz5kzB/fcc4+QD5Ptm6grCAgCgoAgIJYP0z4Db7/9Nv7+97+79H/55ZcxYcIE065HFBcEBAFBQBCIPATE8mGyPX/99dfxj3/8w6X1f/7zH4wfP95kqxB1BQFBQBAQBCIZASEfJtv9V199Ff/617+EfJhs30RdQUAQEAQEAXG7mPYZYIDpo48+6tL/lVdewbhx40y7HlFcEBAEBAFBIPIQEMuHyfac5dSfeOIJl9a0hIwdO9ZkqxB1BQFBQBAQBCIZASEfJtv9p556Cs8++6yQD5Ptm6grCAgCgoAgIG4X0z4Djz/+OFhSXROxfJh2K0VxQUAQEAQiFgGxfJhs6x9++GEww0XIh8k2TtQVBAQBQUAQcCEg5MNkD8NDDz2E1157zaX1f//7X5x99tkmW4WoKwgIAoKAIBDJCAj5MNnu33///XjzzTddWpOInHXWWSZbhagrCAgCgoAgEMkICPkw2e7fe++9mD17tpAPk+2bqCsICAKCgCDwBwJCPkz2NPz1r3/Fe++959L6f//7H8aMGWOyVYi6goAgIAgIApGMgJAPk+3+HXfcgQ8++MClNcutn3nmmSZbhagrCAgCgoAgEMkICPkw2e7fcsst+Oijj4R8mGzfRF1BQBAQBAQBcbuY9hm46aab8PHHH7v0f+ONNzB69GjTrkcUFwQEAUFAEIg8BMTyYbI9v/baa/H5558L+TDZvom6goAgIAgIAmL5MO0zcNVVV2Hp0qUu/Zl2O2rUKNOuRxQXBAQBQUAQiDwExPJhsj3/05/+hK+//tql9VtvvYUzzjjDZKsQdQUBQUAQEAQiGQEhHybb/UsuuQTLly8X8mGyfRN1BQFBQBAQBMTtYtpnYObMmVi1apWQD9PuoCguCAgCgoAgIJYPkz0D06dPx5o1a4R8mGzfRF1BQBAQBAQBsXyY9hmYMmUK1q9f79L/7bffxsiRI027HlFcEBAEBAFBIPIQEMuHyfb83HPPxcaNG11a856XESNGmGwVoq4gIAgIAoJAJCMg5MNkuz9hwgRs3rxZyIfJ9k3UFQQEAUFAEBC3i2mfgbPPPhvbtm1z6f/OO+/g9NNPN+16RHFBQBAQBASByENALB8m23OWUt+1a5dL6zlz5mD48OEmW4WoKwgIAoKAIBDJCAj5MNnuM7h07969Qj5Mtm+iriAgCAgCgoC4XUz7DAwdOhSHDh1y6T937lwMGzbMtOsRxQUBQUAQEAQiDwGxfJhszwcNGoQjR44I+TDZvom6goAgEB4I2O12OBwO9VXXz/zM6XSqNr78rGcOvW00PVu0aIHx48cjKSkpPMD0QwshH36AF4qu/fv3x4kTJ1xTv/vuu6A1REQQEATMgUBdB47eg7G2MXw5JN0PWL2HoZ52wdSlJtxqw9IcT0ntWr733nsYMmSI2ZcBIR8m28I+ffogKyvLpXV9eRBNtg0Roa72xqf9J15WVuZ6U+TP2puj+8/ufWr6mX18HUfrW/Xg8/fAdh8vGAdmRDw8skhDEWjVqhWmTZsGfhfLh6HQymB6EejRowfy8vKEfOgFrKKddsD4e/D5exDrPXw9Heh6x/GHDHgJsTQXBASBACEwePBgzJs3L0Cjh2ZYsXyEBnefZ+3atSsKCwvRrl07lfXy1ltvYcCAAeqN1KgDy6hx9L75uh/oet+m9R6+2lpoWhYRBAQB3xGIioqC1WpVX3X9zM8sFotq4+/Peuarq41enWsbw1/93XHwZy0cp76JkA+T7OjJkyexfft28FZbOUhNsmmipqEIaP95a4dfuBwMng5jPYeOGdZi6GbKYBGPgJAPkzwCV155JZYtW2YSbcNfTe0/e/eDQ3tL4e/cf3Y/9LT2/Lymnz31rXqAuo9TtW8odKhLn6o41PXvqvj6Oq77OPXx7S/8/1JEQ0EgMAgI+QgMroaP+vTTT+OHH37A7t271cFI90tdB1m4HWqeDta6DlqjDmz3cQzfIBlQEBAEBAFBQDcCQj50QyUNBQFBQBAQBAQBQcAIBIR8GIGijCEICAKCgCAgCAgCuhEQ8qEbKmkoCAgCgoAgIAgIAkYgIOTDCBRlDEFAEBAEBAFBQBDQjYCQD91QSUNBQBAQBAQBQUAQMAIBIR9GoChjCAKCgCAgCAgCgoBuBIR86IZKGgoCgoAgIAgIAoKAEQgI+TACRRlDEBAEBAFBQBAQBHQjIORDN1TSUBAQBAQBQUAQEASMQEDIhxEoyhiCgCAgCAgCgoAgoBsBIR+6oZKGgoAgIAgIAoKAIGAEAkI+jEBRxhAEBAFBQBAQBAQB3QgI+dANlTQUBAQBQUAQEAQEASMQEPJhBIoyhiAgCAgCgoAgIAjoRkDIh26opKEgIAgIAoKAICAIGIGAkA8jUJQxBAFBQBAQBAQBQUA3AkI+dEMlDQUBQUAQEAQEAUHACASEfBiBoowhCAgCpkEgMzMTr732Gs477zx06tQpZHrb7XbMnz8fsbGxOPfcc2GxWEKmfjEHqgAADeJJREFUi0wsCAQbASEfwUZc5hMEBIE6Edi7dy/uueceNG3aFA899BCSkpIMRWz37t24/vrrMW3aNPz5z382dGxvBisqKsKDDz4IkqHHHnsMycnJ3nSXtoKAqREQ8mHq7RPlBYH6h8DChQtx8803o3///nj++efRsmVLQxepkY9hw4bh7rvvRnR0tKHj6x1MIx9bt24NyDr16iHtBIFQICDkIxSoy5yCQBAQcDqdyMvLQ1xcXMgOWG+XmZ2djTvvvBNLlizB1KlTlWUgUJYPIR/e7o60FwSMQ0DIh3FYykiCQFgh8PXXX+PGG29E69atMW7cOAwZMgTdunVDSkpK2MYXLF++HNdeey3Gjh2Lu+66C82bNzccU6MtH1lZWVi6dClGjhypXEV6xRvLB4nk2rVrQXI2ZsyYsN0/vWuXdoKAkA95BgSBeorAvHnz1AFeVTp06ICJEydi+vTpipgYFej44osv4ueff8YTTzyB1NRUr1Gllea+++7Dxo0b1Rh9+/b1egw9HTTyQcsKYz78Xf+6deuUlYZE77bbbkNUVJQeNaCRjxMnTuDxxx+vEzOt7Zo1a/DSSy+hS5cuuuaQRoJAuCIg5CNcd0b0EgT8QICH1ZtvvgnGE4wfPx6NGzdGaWmpIgcfffSR+j1l0qRJ+Nvf/oZWrVrh0KFD6me6OR5++GGvCcRzzz2H2bNn4/XXX0evXr281p7WA8ZgMN5j1qxZSh+6jJo1a1bjWCQRdNHYbDY8/fTTaNGiha45N23ahCuuuALXXHONIQGnGvkgkakphqSkpATPPvssFi9eDGLUu3dvpWdubq6LHDLgNCcnp1b8NfLx5Zdf+oyvLnCkkSAQJASEfAQJaJlGEAgmAp988ol6E58wYUK1TAqHw4EdO3bglVdeUametIQ88sgjaNiwIW666SZs2bIFV111lToYY2JidKutBYq+8cYbGD16tO5+bHj8+HE1H4kEiU9GRgauu+46FBYWqrTY7t27VxqPRIo68zOu84YbbkBCQoKuOTWy8Pe//91Q8nHaaacpnbkW6k8ZPnw4OnbsqGJX3n33XQwYMEAREZI9ZrmQaCUmJqo9OnLkSK34a+Rjzpw5uP3229GzZ0/s3LkTTNflvv2/9s7vxaYvDOMrfwGJy4mSlAtS4oZI3EmUxuT3jUtRyo1CoVyQC0XmTtMkJkXiSpKfKZSiuJEfEeWSUi6+fd5vz2nZ9p7ZY86xZ855Vunra/Zae61nn+l99vM+73vWr19f+/y1QPJFRqDDCJh8dBhgL28EmkAAhQMCQVqlqmIEEnL16tUIZvhBuI7gf/369fT48eP493nz5tXevsgHgbS/v7/2PPwMEJZz585FaS3+FKVGIEJl63348CEC9axZs4KsoOzUHSIf492n1ge3169fpxcvXiT2gRcDvIoDUrdp06Z4DtOmTUsPHz4Msgcx27hxY4t8cAbICSQEQiL8mYca8ubNm7jfo0ePQg3KB3OWLl0az0qKSl0cfJ0RaBIBk48m0fe9jUCHEFDwxntx7Nix9OzZs0hL8CYuj0NOPpYvXz5q6uLXr1/xpn3jxo2E7+Dly5fhGzl8+HCrGkXko6go4OV48uRJ4n6rVq0KgpMP7ZVUjdaDkAwNDaVDhw5FekSqiObhZzl69Ghcj3dlPL4NkQ9Uk7Vr1477CTx//jxt3bo1ff/+/be5mHnpHUKJMMRjLGOvlA8anRXTNT9//ozndvHixT/2x3kxnaIGkZLKS4XBmD9FjMd9SE8wAh1GwOSjwwB7eSPQBAIK6HPnzk2bN29Ou3fvjm2QGlixYkWkBvA+EEgJlAS/devW/RHEf/z4kUjhjIyMBIHIB4Tl9OnTrT4cOfnAU0Fq5/Lly+nWrVvxxj5//vxQVxYsWNBaBlKDXwMfCmuxpoa8GQR1UhUzZsyIH3379i0dPHgwEaBPnjzZ8np8/PgxYXq9e/du3I+1CNKoDzNnzmytOxr5YG0I1oMHD6JyhZQJKY18PgZRmqAxUGlQNc6cOROVRONpFjYa+YB8oYDgEcGzs2zZsvCMkLqpIk2kojDqyneTYwkeEDWqZU6dOpX6+vqa+Fj6nkaghYDJhz8MRqALEcjJB6WrBB6IRj6Q7HlT37NnT3gQGJgg7927l1auXBmKBoGOVAiD8tdt27ZFFUpZN06RD4Ieb98iKxs2bEjbt29Pixcv/qPfCKkL9kcagjQKBlMNfBP79+9Pr169+s1kqftAADB5EnRRSSA2lL1Onz49iMCXL18ifcP/cwZIBAoJ5ANvBoGa8lgN1CHWJNUDIQMTyBeKEQpMVRktHVlV0lxFPiBZpGYoHYbQMEQ+wJP5KBhF/PPnBbFi/SryAbHYt29f+GS4LicYwgxcIDQQQQ8j0CQCJh9Nou97G4EOISDVAIMjgYi3dcpY6aPBWzxKAj6PojwvVQAzKm/1eUBGsYAMoJDwtl8cCnD6d1QWFJA1a9aUGlfZE9U1BNwTJ05EwM8HZkoUBYIlPyfVIdXj7du36ezZs0EyZK5lLmSCPUJ0UA+ePn0a8/kva7F3rsdjkVflkEpC/Zk9e3akeCAEEBXm8GfHjh1BQPgeluIoGkfLiJmu4YwQHIy8Ii18x4zavBfxz+8lfKvIB+oGBA7vh+7BfKptwA9fDWfES+K0TId+8bxsbQRMPmpD5QuNwNRBQEEMtYI3fPwHeBQI2PSJUIULcn4+5GegX4UCIgGfQI26gLIACeG7UVg7VyooA82/K4UgThVK2VC6hYCIYbTqi9VQYVBbVLVz+/btqBDZuXNnBFjur7NCpspIDEQFQkLaBiJx586ddPz48Rb5UFfV9+/fx/wlS5bElvNuq6hEVJqU9R6pQz6kSrBfqSNl3zFThr/wG4t8VDVPw7CK0oOxtaiITJ1PtHfabQiYfHTbE/V5jEBKkWJBKYAo5NUuBH2CGGkI0hXFwK8ARgBGKcnJBcZRKi7wFOCrIJ2BIkDahOAsEkCA/vr1a6gI3JuKm+Kg8gMSgbqC+lFVJkvqZO/eveFPgURBACATuT9krD4bnz9/jjVQa/CXkA7KyQdpFoIz3hjOQpMwVBNwIo2hAZ5FTPgZ5Iz1UWo4L0Gev1+5ciXIGkoDWKO2oCSJ/JWRj9Hwp1IGUphX6UCYSMeoogZSmLeNJ23EM2bu35hz/ctkBDqFgMlHp5D1ukagQQQUxCjVLPbJILASBKkWoZso1SmkRwjOku5JORBI8TvwZs/P1LVUfUJYFxMkqQ9MjKQzIAiQCe5BHw4UC9QEyIVSFurpAREiIGOKrRp56kXXQHhywqI3e7wUxU6hkCCICuZN5tC8jH1CfNSPROQDHwTzIR+XLl1qmWOptsHoqSZoxbSFmoVBlET0REhQnCALkDj1JdF99Yzw16iapwx/nVvKUl5NJELCs1i0aFEoPJAz7gkJvHDhQqg9eX+RBj+WvrURaCFg8uEPgxHoQgSkGBDgqjqOUjp74MCBUEmGh4fjjVkpBMgHwRKDJJ4JyMWuXbtCCVDVCeSBeQRDDKWsQ+Aj2LEWb9yoJAylLSinpZ8HwRwVgdTNWGWy6lkiNYUKl7yJGZ4Ggu3g4GAoPZTPEuwxs96/fz+MnPTB2LJlS3gtiv1I8gZe+UcBUgBhoaQ1xwocIAuciZHPp7qHUttr164FLpAcUlEQGnWAldFV5GPhwoW/9flgTo6/9iSFh3McOXIknhVqBmQORWfOnDlBmvB1FMffNI3rwl8LH2kSIWDyMYkehrdiBNqFACkSghAqBepCVRMuVAMCLAGVahCUBt6iCYyQAwIspIIgh0pSNSAxtGwn6GPMhKDgMSHFwJs+6+A3YX2uRU0gjVHH+Mg6rElDtKp+JKgP/PzmzZuthl8QIMyuq1evDnVFJEdKCUSI9At7+/TpU5ADyoLpGEoKhkZpeYmtcHj37l0QCyqFGGqShpJUJC+sr6oTyAMqEKQOlYWUCBjz7xAyFKQy/LUmpcQQE67XYO9SdCA4EKG88kfET2bbdn2+vI4RmCgCJh8TRdDzjUAPIECgRPYnwNPRE0LAGzuqAOoFVSZlrdgJzARr0j8EYYIxhEgEpQ50rEGzLVVr1FFLRluX9SAgKCYE/DoESOuh9jCv6FFR5Q6pGUgP35lDfw6pRFX7YR5Ej32UVcnk89T7A7KB3waFhzJmKnvKqo+kyNAdVS3d6+Dta4zAv0DA5ONfoOx7GAEjEAgQEHlDz7tydgs0kCpIGWpEGRlo1znBkJGbgcvWVgt6utpiiB3P9/S0a69exwhUIWDy4c+GETACRqALEaAFPf6SokemC4/qI01BBEw+puBD85aNgBEwAqMhID8JZlQZh42YEZhMCJh8TKan4b0YASNgBNqAgCqEqLRRtU0blvUSRqBtCJh8tA1KL2QEjIARaB4BjKlUFlHOfP78+Wi37mEEJhsCJh+T7Yl4P0bACBiBCSBAPxSajfEFc3l32wks6alGoO0ImHy0HVIvaASMgBFoDgG+BZhUCx1Pq75lt7nd+c5G4H8ETD78STACRsAIdBECfIkdbeJpZDYwMNCVZc1d9Lh69igmHz376H1wI2AEjIARMALNIGDy0QzuvqsRMAJGwAgYgZ5FwOSjZx+9D24EjIARMAJGoBkETD6awd13NQJGwAgYASPQswiYfPTso/fBjYARMAJGwAg0g4DJRzO4+65GwAgYASNgBHoWAZOPnn30PrgRMAJGwAgYgWYQMPloBnff1QgYASNgBIxAzyLwH3pxkLUQNwHIAAAAAElFTkSuQmCC)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "In this challenge, we consider an alternative Markowitz’s mean–variance model in which the variance is replaced with an industry standard risk measure, Value-at-Risk (VaR), in order to better assess market risk exposure associated with financial and commodity asset price fluctuations.\n", + "\n", + "\n", + " VaR is defined as the maximum dollar amount expected to be lost over a given time horizon, at a pre-defined confidence level. For example, if the 95% one-month VAR is 1 million dollars, there is 95% confidence that over the next month the portfolio will not lose more than 1 million dollars. Realistic portfolio optimization in the mean-VaR framework is a challenging problem since it leads to a *non-convex NP-hard problem* which is computationally intractable. In fact, minimizing a nonparametric VaR measure is a complex task due to the non-smooth objective function landscape with many local minima. When more dimensions and trading constraints are added to the problem, the complexity of the problem increases. Hence, a good candidate to becnhmark quantum optimization heuristics.\n", + "\n", + "In the MV model, risk is defined by a dispersion parameter and it is assumed that returns are normally or elliptically distributed. However, the distributions of returns are asymmetric and usually have excess kurtosis in practice. Variance as a risk measure has thus been widely criticized by practitioners due to its symmetrical measure which equally weights desirable positive returns and undesirable negative ones. In fact, Markowitz recognized the inefficiencies embedded in the mean–variance approach. As a result, several extensions\n", + "and modifications of the basic Markowitz model reflecting real-world constraints have been developed.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "c898b22a-1ee3-4cf5-af66-0c9ff99ecaa8", + "metadata": {}, + "source": [ + "### Problem Statement -- The Mean-Variance-VaR model" + ] + }, + { + "cell_type": "markdown", + "id": "cfe38b05-e39a-4d01-a1d6-b6fafcec3db3", + "metadata": {}, + "source": [ + "Note that here we closely follow reference\n", + "[Mean-Variance-VaR portfolios: MIQP formulation and performance analysis](https://arxiv.org/abs/2111.09773). One could read this reference for an overview on the topic." + ] + }, + { + "cell_type": "markdown", + "id": "98d58de7-4005-4836-9b30-55a6ce04df2f", + "metadata": {}, + "source": [ + "Consider n assets with the following portfolio:" + ] + }, + { + "cell_type": "markdown", + "id": "5bf58d94-2e32-4a34-9289-8be19473ec46", + "metadata": {}, + "source": [ + "$$x \\epsilon \\Delta = \\{ x \\epsilon \\mathbb{R}^{n} : \\sum_{k=1}^{n} x_{k}=1, x_{k} \\ge 0, k=1, \\dots, n \\}$$" + ] + }, + { + "cell_type": "markdown", + "id": "be7a6796-0b53-499d-8579-c710fc40975e", + "metadata": {}, + "source": [ + "with $x_{k}$ the percentage of the total portfolio invested in asset $k$ (let's call this $\\textit{Asset}_{k}$) and $n$ the total number of assets. The equality $\\sum_{k=1}^{n} x_{k}=1$ that 100% of the total portfolio is invested and $x_{k}\\ge 0$ indicates that all the percentages are positive (no-short sellings constraint).\n", + "\n", + "By having in mind that the investment decision at a particular point in time is made considering **T** equally likely scenarios, the portfolio return is given by:" + ] + }, + { + "cell_type": "markdown", + "id": "55deece6-9c98-45ff-9b73-1bceb783f2d0", + "metadata": {}, + "source": [ + "$$R_{Pt}(x)=\\sum_{k=1}^{n}x_{k}r_{kt}$$" + ] + }, + { + "cell_type": "markdown", + "id": "74a4485c-36d8-4257-a19b-e6b330d07a7b", + "metadata": {}, + "source": [ + "with the variables $r_{kt}$ corresponding to the return of each asset $k$ for the scenario $t \\epsilon \\{ 1, \\dots, T \\}$." + ] + }, + { + "cell_type": "markdown", + "id": "42f4e7c4-404c-4f06-bc29-448c3360127f", + "metadata": {}, + "source": [ + "The classical **Mean-Variance** portfolio optimization problem focuses on minimizing the variance (risk), while at the same time making sure that the expected return is above a specific value $\\eta$. It has the following convex form:" + ] + }, + { + "cell_type": "markdown", + "id": "81769ffb-ef50-4d4f-ba67-aea1d2a2a64d", + "metadata": {}, + "source": [ + "$$\\min \\sum_{k=1}^{n}\\sum_{j=1}^{n} x_{k}x_{j}\\sigma_{kj}$$" + ] + }, + { + "cell_type": "markdown", + "id": "7953dade-e77f-4ca0-a6b7-15e9d7704489", + "metadata": {}, + "source": [ + "$$\\text{s.t.}$$" + ] + }, + { + "cell_type": "markdown", + "id": "227a6a97-f5af-4b37-aec9-14c6abdb8297", + "metadata": {}, + "source": [ + "$$\\sum_{k=1}^{n} \\mu_{k}x_{k} \\ge \\eta$$ \n", + "\n", + "\n", + "$$\\sum_{k=1}^{n} x_{k}=1$$ \n", + "\n", + "\n", + "$$x_{k}\\ge 0 \\ \\text{for}\\ k=1,\\dots,n $$" + ] + }, + { + "cell_type": "markdown", + "id": "78d223bd-40f9-4191-821b-1a782b1b157e", + "metadata": {}, + "source": [ + "with $\\sigma^{2}_{P}(x)=\\sum_{k=1}^{n}\\sum_{j=1}^{n} x_{k}x_{j}\\sigma_{kj}$ the portfolio variance ($\\sigma_{kj}$ the covariance between the assets $k$ and $j$), $\\mu_{P}(x)=\\sum_{k=1}^{n} \\mu_{k}x_{k}$ the expected total return ($\\mu_{k}=\\frac{1}{T}\\sum_{t=1}^{T}r_{kt}$ is the expected return of asset k).\n", + "\\\n", + "\\\n", + "The inequality $\\sum_{k=1}^{n} \\mu_{k}x_{k} \\ge \\eta$ indicates that the expected total returns should be above or equal a specific target value ($\\eta$). \n", + "\\\n", + "\\\n", + "By solving the optimization problem above, one gets the **best distribution of assets**, or differently the optimal set of percentages $\\{x_{k}\\}$ of the total portfolio that should be invested in each one of the assets $k$. For example consider $\\textit{Asset}_1$, with the corresponding optimal percentage $x_{1}$. If the solution of the problem is $x_{1}=0.1$ that means that 10% of the total portfolio should be invested in $\\textit{Asset}_1$." + ] + }, + { + "cell_type": "markdown", + "id": "6843ae65-192a-4d62-aa96-79f615af7614", + "metadata": {}, + "source": [ + "The above optimization problem does not include the Value-at-Risk (VaR), which is an additional risk measure. $VaR_{\\epsilon}$ is defined as the **maximum loss** for scenarios $\\{1, \\dots, T\\}$ with $(1-\\epsilon)$ the given confidence level. $\\epsilon$ typically takes the values $\\epsilon=0.01, 0.05, 0.10$.\n", + "\n", + "To further understand what VaR is, first consider that for a given portfolio $x$, the portfolio loss is given by $L_{P}(x)=-\\sum_{k=1}^{n}x_{k}R_{k}$, with $R_{k}$ the return of asset $k$. $VaR_{\\epsilon}(x)$ is the value such that the portfolio loss $L_{P}(x)$ is above $VaR_{\\epsilon}(x)$ with probability $\\epsilon \\times 100 \\%$. If we include $VaR_{\\epsilon}$ in the **Mean-Variance** portfolio optimization problem we have the **Mean-Variance-VaR** approach. In this approach, a portfolio return $R_{P}(x)$ is preferred to $R_{P}(y)$ iff $\\mu_{P}(x)\\ge \\mu_{P}(y)$, $\\sigma^{2}_{P}(x)\\le \\sigma^{2}_{P}(y)$ and $VaR_{\\epsilon}(x)\\le VaR_{\\epsilon}(y)$." + ] + }, + { + "cell_type": "markdown", + "id": "eee858f6-43ec-4872-a014-21ef86e4ee0b", + "metadata": {}, + "source": [ + "By following [Mean-Variance-VaR portfolios: MIQP formulation and performance analysis](https://arxiv.org/abs/2111.09773) the **Mean-Variance-VaR** problem is written as a **Mixed-Integer Quadratic Programming (MIQP)** problem:" + ] + }, + { + "cell_type": "markdown", + "id": "cb3d5b16-9150-49af-84dc-5f89bcd08bf3", + "metadata": {}, + "source": [ + "$$min_{(x, r_{\\epsilon}, y)}\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}$$" + ] + }, + { + "cell_type": "markdown", + "id": "431b0e46-0516-46f6-9c52-258f0704697b", + "metadata": {}, + "source": [ + "$$\\text s.t.$$ \n", + "$$\\sum_{k=1}^{n}\\mu_{k}x_{k}\\ge \\eta$$\n", + "$$-r_{\\epsilon} \\le z$$\n", + "$$r_{\\epsilon}\\le \\sum_{k=1}^{n}r_{kt}x_{k}+M(1-y_{t})\\ \\text{for}\\ t=1, \\dots, T$$\n", + "$$\\sum_{t=1}^{T} y_{t} \\ge (1-\\epsilon)T$$ \n", + "$$\\sum_{k=1}^{n}x_{k}=1$$\n", + "$$x_{k} \\ge 0\\ \\text{for}\\ k=1, \\dots, n$$\n", + "$$y_{t}\\epsilon \\{ 0,1 \\} \\ \\text{for}\\ t=1, \\dots, T$$" + ] + }, + { + "cell_type": "markdown", + "id": "87424257-51ce-4280-ab5a-c5c12d8ef307", + "metadata": {}, + "source": [ + "with $VaR_{\\epsilon}(x)=-r_{\\epsilon}$, $r_{\\epsilon}$ a real and positive variable. Therefore $-r_{\\epsilon}$ represents the VaR of the portfolio and is bounded above by $z$ (which is real and positive, $z\\gt 0$), which is actually the target VaR. This is expressed as $-r_{\\epsilon} \\le z$ above.\n", + "\\\n", + "\\\n", + "$M$ is a large positive number that has to be fine-tuned in the optimization procedure. Find the optimal $M$ value here: [Mean-Variance-VaR portfolios: MIQP formulation and performance analysis](https://link.springer.com/article/10.1007/s00291-023-00719-x) and use it in the exercises below. $y_{t}$ (for $t=1,\\dots,T$) are boolean variables.\n", + "\\\n", + "\\\n", + "The inequalities $r_{\\epsilon}\\le \\sum_{k=1}^{n}r_{kt}x_{k}+M(1-y_{t})$ (one for each event $t$) indicate the following: if the **portfolio loss** $-\\sum_{k=1}^{n}r_{kt}x_{k}$ is above the VaR $-r_{\\epsilon}$ that means that $y_{t}$ must be 0 (for sufficiently big $M$). That practically means that if the portfolio loss is above the VaR, the event $t$ should **not** happen ($y_{t}=0$).\n", + "\\\n", + "\\\n", + "For the challenge, we simplify the problem as follows:\n", + "\n", + "$$ \\textbf{Mean-Variance-VaR Simplified Version}$$" + ] + }, + { + "cell_type": "markdown", + "id": "ef016e31-eaad-4c39-a47f-dbaa522d4d1e", + "metadata": {}, + "source": [ + "$$min_{(x, r_{\\epsilon}, y)}\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}-\\sum_{k=1}^{n}\\mu_{k}x_{k}$$" + ] + }, + { + "cell_type": "markdown", + "id": "408985e0-8c1d-4c12-9640-c117a68a0cc5", + "metadata": {}, + "source": [ + "$$\\text s.t.$$\n", + "$$-r_{\\epsilon} \\le z$$\n", + "$$r_{\\epsilon}\\le \\sum_{k=1}^{n}r_{kt}x_{k}+M(1-y_{t})\\ \\text{for}\\ t=1, \\dots, T$$\n", + "$$\\sum_{t=1}^{T} y_{t} \\ge (1-\\epsilon)T$$ \n", + "$$\\sum_{k=1}^{n}x_{k}=1$$\n", + "$$x_{k} \\ge 0\\ \\text{for}\\ k=1, \\dots, n$$\n", + "$$y_{t}\\epsilon \\{ 0,1 \\} \\ \\text{for}\\ t=1, \\dots, T$$" + ] + }, + { + "cell_type": "markdown", + "id": "6d1e1ae4-fb34-43fe-a9a7-7fcf9497efa6", + "metadata": {}, + "source": [ + "Specifically, we include the expected total returns, $\\sum_{k=1}^{n}\\mu_{k}x_{k}$, in the objective fuction $f(x)=\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}-\\sum_{k=1}^{n}\\mu_{k}x_{k}$ that is minimized. Now, by minimizing the objective function we select the **best distribution of assets** that maximize the return and minimize the variance (risk), while making sure that the equality/inequality constraints written above are also satisfied." + ] + }, + { + "cell_type": "markdown", + "id": "e8cc901b", + "metadata": {}, + "source": [ + "In the next sections you will learn how one can solve the **Mean-Variance-VaR Simplified Version** problem with quantum or quantum-inspired techniques.\n" + ] + }, + { + "cell_type": "markdown", + "id": "effe5813-0118-46ce-bd2a-560cb74e6a70", + "metadata": {}, + "source": [ + "
\n", + " \n", + "**STEPS:**\n", + " \n", + "1. Learn how to incorporate the equality/inequality constraints of the **Mean-Variance-VaR Simplified Version** problem in the objective function that is minimized.\n", + "2. Cast the problem into the QUBO formulation, by choosing an **encoding** for all the variables of step 1. The QUBO formulation can be used for finding the solution of the problem **classically** or with **quantum** or **quantum-inspired** methods.\n", + "3. Use Simulated Annealing to solve the problem. This is a specific algorithm used for **optimization problems**.\n", + "4. Evaluate the solutions of step 3.\n", + "5. Move to **quantum** optimization techniques such as Quantum Annealing or QAOA to solve the problem. \n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d76f8c15", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "19a0a45f", + "metadata": {}, + "source": [ + "### Reading in financial data\n", + "\n", + "\n", + "To examine the practical applicability of the mean-VaR model with quantum techniques, we will use a small dataset that compromises the weekly linear returns for Eurostoxx50 Market Index from 01-22-2007 to 05-06-2013 and contains up to 32 assets. You may find the associated file: *returns_data.txt*" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a78f8aea", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# You may choose to select different parameters/values\n", + "number_assets = 3\n", + "T = 10\n", + "# Read returns\n", + "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "\n", + "Rraw = df.values.T\n", + "\n", + "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", + "R = Rraw[:number_assets,:T]\n", + "\n", + "# Expected return of each asset\n", + "expected_returns = np.mean(R, axis = 1)\n", + "\n", + "# Covariance matrix of asset returns\n", + "covariance_matrix = np.cov(R)" + ] + }, + { + "cell_type": "markdown", + "id": "736c3eea-e219-4d6c-8457-3a4f21e9f6fc", + "metadata": {}, + "source": [ + "### Section 1: Incorporating equality/inequality constraints" + ] + }, + { + "cell_type": "markdown", + "id": "8a6ce336-2cfc-4a5a-80a4-6616b280adaf", + "metadata": {}, + "source": [ + "- Convert the inequalities of the **Mean-Variance-VaR Simplified Version** problem into equalities, with the use of slack variables and the penalty method approach. For reference one could read [A real world test of Portfolio Optimization with Quantum Annealing](https://arxiv.org/abs/2303.12601) (page 10).\n", + "Specifically, re-write the objective function above as a new objective function called L that includes the inequality constraints. To do that usually one introduces new variables, called **slack variables**, that transform an inequality constraint into an equality constraint. If you do that for each inequality of the optimization problem, what are the total number of variables to be optimized including the slack variables?\n", + "- In a similar way and by following the same reference, try to incorporate the one equality constraint, $\\sum_{k=1}^{n}x_{k}=1$ as a penalty term in the objective function L. Notice that now slack variables are not needed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec015dad-7449-433c-bd44-593e5fecb4ec", + "metadata": {}, + "outputs": [], + "source": [ + "
NOTE:\n", + "L has the following form: $L=\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}-\\sum_{k=1}^{n}\\mu_{k}x_{k}+\\textit{penalty terms}$, with the penalty terms to be found in this section (and include all the equalities and inequalities of the problem). This will need to be mapped to a quantum Hamiltonian in the following section. Each penalty term will correspond to one lagrange multiplier that will need to be fine-tuned in the next sections, so that the constraints are imposed.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "bb2982bd-fbb9-49de-8dbc-370e9390173b", + "metadata": {}, + "source": [ + "### Section 2: Encoding/Casting the problem into a QUBO formulation" + ] + }, + { + "cell_type": "markdown", + "id": "1282b39d-ec53-46b8-a149-b353c624c533", + "metadata": {}, + "source": [ + "Many discrete optimization problems that are NP hard can be mapped to quadratically unconstrained binary optimization (QUBO) problems. A QUBO can be expressed in the following form:" + ] + }, + { + "cell_type": "markdown", + "id": "557fd82b-e680-4819-8c41-d8381a5a3fe9", + "metadata": {}, + "source": [ + " $$\\text{min} \\quad z^{T}Qz$$" + ] + }, + { + "cell_type": "markdown", + "id": "4c50b9c4-bf08-4f63-8e9a-d9ad5e55e0d4", + "metadata": {}, + "source": [ + "with $z \\epsilon \\{ 0,1\\}^{N}$ the bit vector and $Q \\epsilon \\mathbb{R}^{N \\times N}$ the corresponding QUBO matrix. The aim of this exercise is to write the objective function L you found in **Section 1** in the QUBO form, with $z$ a vector that includes all the variables $\\{x_{i}\\}$ ($i \\in \\{1, \\dots n\\}$), $r_{\\epsilon}$, $\\{ y_{t} \\}$ ($t \\in \\{1, \\dots, T \\}$) and the slack variables introduced in **Section 1**." + ] + }, + { + "cell_type": "markdown", + "id": "f0f0847a-f9a6-4a03-9921-ea8b5bf0acb8", + "metadata": {}, + "source": [ + "Map the classical objective function of **Section 1** to a quantum Hamiltonian. Follow the steps:\n", + "- Map all the variables $\\{x_{i}\\}$, $r_{\\epsilon}$, $\\{ y_{t} \\}$ and the slack variables of **Section 1** into different binary variables. Reference [Approaching Collateral Optimization for NISQ and Quantum-Inspired Computing](https://arxiv.org/pdf/2305.16395.pdf) indicates how to map the slack variables into binary ones, with the use of the \"log-encoding\" in pages 4-5. However, reference [Solving the Optimal Trading Trajectory Problem Using a Quantum Annealer](https://arxiv.org/abs/1508.06182) presents different encodings.\n", + "\n", + "The encodings can be writen as a linear function of binary variables. For example, for the variable $x_{i}$ we have:\n", + "$$x_{i}=\\sum_{d=1}^{D}f(d)z_{di}$$\n", + "with $z_{di} \\epsilon \\{ 0,1\\}$ and $f(d)$ the function you are looking for (particular for each encoding). Notice that you will need to introduce $D$ new binary variables, for each variable $x_{i}$.\n", + "\n", + "- Which encoding did you use for each of the variables $\\{x_{i}\\}$, $r_{\\epsilon}$, $\\{y_{t}\\}$, which for the slack variables and why?\n", + "- As you can see, the total number of variables increase after the encoding. What is the total number of binary variables that you have after the mapping?\n", + "- One could cast the problem into a QUBO form, by finding $Q$. Then one can map it to a Hamiltonian by promoting all the binary variables (for example $z_{di}$) to binary operators ($z_{di} \\rightarrow \\hat{z}_{di}$). The Hamiltonian will then have the following form $\\hat{H}=\\hat{z}^{T}Q\\hat{z}$ with $Q \\epsilon \\mathbb{R}^{N \\times N}$ the corresponding QUBO matrix. You do **not** need to find the explicit QUBO form of the Hamiltonian $\\hat{H}$. Instead, substitute the encoding you found in this section to the Langrange formulation of **Section 1** (let's call it $L_{fin}$). Then, use $L_{fin}$ and with the help of [pyqubo](https://pyqubo.readthedocs.io/en/latest/getting_started.html#) find the QUBO formulation of the problem, by finding the matrix $Q$. One could then promote all the variables to operators, to find the quantum version of $L_{fin}$ (let's call it $\\hat{H}$). $L_{fin}$ and $\\hat{H}$ will be used in the following sections." + ] + }, + { + "cell_type": "markdown", + "id": "f1aefa04-e56e-4826-9be5-3eaf66ebb17e", + "metadata": {}, + "source": [ + "
NOTE 1:\n", + " \n", + "Notice that the above optimization problem can be translated to an Ising Hamiltonian with the following change of variables $x_{i}=\\frac{1-\\sigma_{i}}{2}$ as:\n", + " $$H(s)=-\\sum_{j}h_{j}\\sigma_{j}-\\sum_{j" + ] + }, + { + "cell_type": "markdown", + "id": "f7f83e50-386f-4a6b-a137-fd3cbbb82a47", + "metadata": {}, + "source": [ + "
NOTE 2 (Hint):\n", + " \n", + "Encoding requires finding the minimum and maximum values for each one of the variables of the problem. You will need to find them. \n", + "Notice that $-z \\le r_{\\epsilon} \\le 0$, with $z$ a positive, real number. Are the variables needed to be normalized and why?\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "9cabd342-4061-488b-9b3d-5d93d96344ec", + "metadata": {}, + "source": [ + "
NOTE 3 (Hint):\n", + "\n", + "Ignore how the choice of encoding might affect the noise level of a quantum device and only consider how it affects the granularity of the problem as well as the number of binary variables needed for the encoding of each variable of the problem. One could also read \n", + "[A real world test of Portfolio Optimization with Quantum Annealing](https://arxiv.org/abs/2303.12601). \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "897a8e15-2da5-4ae2-9108-31aa005d6057", + "metadata": {}, + "source": [ + "**Answer:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "553d8b31", + "metadata": {}, + "outputs": [], + "source": [ + "# write your code here" + ] + }, + { + "cell_type": "markdown", + "id": "ed9d5b11-b2f3-4c79-bdf9-000cd7685f85", + "metadata": {}, + "source": [ + "
\n", + " \n", + "BONUS EXERCISE: \n", + "As you notice the number of binary operators one has to use scales with the number of variables. Introducing the slack variables is therefore very costly. Try to suggest a new way of implementing the inequalities, without a lot of details. You can use the help of reference [Unbalanced penalization: A new approach to encode inequality constraints of combinatorial problems for quantum optimization algorithms](https://arxiv.org/abs/2211.13914).\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "51466cca-e371-4f55-bdee-7b2eea6159d8", + "metadata": {}, + "source": [ + "**Answer:**" + ] + }, + { + "cell_type": "markdown", + "id": "9344d008-0e1a-45fd-9aa3-1399e56b859c", + "metadata": {}, + "source": [ + "### Section 3: Simulated Annealing" + ] + }, + { + "cell_type": "markdown", + "id": "1a9abb2c-9062-451e-9b4b-8009bd455e3c", + "metadata": {}, + "source": [ + "Optimization problems, like the one above, can be solved with the use of simulated annealing. It is one of the most preferred heuristic methods to solve optimization problems, since it avoids local minima." + ] + }, + { + "cell_type": "markdown", + "id": "49533860-07ce-4bea-a2b8-c1a8cd3d2670", + "metadata": {}, + "source": [ + "- Using simulated annealing and the **data** calculate the ground state and find the best solution of the QUBO problem, $L_{fin}$, found in **Section 2**. Note that the lagrange multipliers in front of the penalty terms/constraints will need to be fine-tuned so that the equalities/inequalities of the problem are all satisfied. The output of the algorithm will give you the optimal values of the binary variables of the encoding of $\\{x_{i}\\}$, $r_{\\epsilon}$, $\\{y_{t}\\}$ and the slack variables so that the return is maximized, the risk is minimized while at the same time the inequality/equality constraints of the problem are all satisfied. In the next section you will learn how to evaluate your solutions." + ] + }, + { + "cell_type": "markdown", + "id": "9a897de7-1efd-414e-bf16-a3f2353c9f6b", + "metadata": {}, + "source": [ + "
NOTE 1 (Hint):\n", + " \n", + "\n", + "Feel free to first read any references provided online for Simulated Annealing like [Optimization Simulated Annealing](https://www.science.org/doi/10.1126/science.220.4598.671). You do not need to write your own code for this Section. Instead try [pyqubo](https://pyqubo.readthedocs.io/en/latest/getting_started.html#) and [D-Wave neal](https://docs.ocean.dwavesys.com/projects/neal/en/latest/).\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "54c2a1b7-feda-4461-bd63-b5e183e0b8f7", + "metadata": {}, + "source": [ + "
NOTE 2 (Hint):\n", + "\n", + " [pyqubo](https://pyqubo.readthedocs.io/en/latest/getting_started.html#) offers an easy way to evaluate whether the constraints are satisfied or not. Use it so that you are able to fine-tune the lagrange multipliers and get your solution to be as close as possible in satisfying the constraints. \n", + " \n", + " The lagrange multipliers act as relative weights between the different terms of the QUBO problem. Fine-tuning them so that the constraints of the problem are satisfied is **not** trivial. The fine-tuning process goes as following:\n", + " \n", + " Consider the following constraint optimization problem:\n", + " $$\\text{min} f(\\textbf{x})$$\n", + " subject to the constraint:\n", + " $$c(\\textbf{x})=0.$$\n", + "We can find a solution to this problem with the use of the **penalty method** approach, where we define a new objective function that includes the constraint as an added penalty term in the objective fuction. The $k^{th}$ step of the now unconstraint optimization problem is the following:\n", + "$$\\text{min}\\Psi_{k}(\\textbf{x})=f(\\textbf{x})+\\mu_{k}c(\\textbf{x})^{2}$$\n", + " with $\\mu_{k}$ the lagrange multiplier of this step (or else the penalty term coefficient). In order to fine-tune the langrange multiplier you start with a large-enough value of $\\mu_{1}$ (in comparison to the coefficients of the non-penalty terms of the QUBO problem). In the next steps of the iteration you increase the value of the coefficient until the constraints are satisfied.\n", + " \n", + " Specifically take a look at [pyqubo--validation of the constraints](https://pyqubo.readthedocs.io/en/latest/getting_started.html#validation-of-constraints). One could check whether the constraints are satisfied by making sure the energy contribution of each penalty term is 0. [pyqubo--validation of the constraints](https://pyqubo.readthedocs.io/en/latest/getting_started.html#validation-of-constraints) outputs the energy contribution of each penalty term. If you do not manage to get the energy contribution of the penalty terms to exactly 0, make sure they are as close as possible to that. \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "ce724e8f-cc21-49fb-89b6-7e2b1e93296f", + "metadata": {}, + "source": [ + "
NOTE 3 (Hint):\n", + "The binary variables used for the encoding of $\\{x_{i}\\}$, $r_{\\epsilon}$, $\\{y_{t}\\}$ and all the slack variables affect the granularity of the problem. Choose the number of binary variables you use for the encoding of each one of the variables $\\{x_{i}\\}$, $r_{\\epsilon}$ and the slack variables. Why do you think that's a reasonable number of binary variables? How many binary variables do you need to use for the variables $\\{y_{t}\\}$?\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a359a291-bbb0-4f4f-97fd-acd5f90d8eb6", + "metadata": {}, + "outputs": [], + "source": [ + "# write your code here" + ] + }, + { + "cell_type": "markdown", + "id": "5e4786ea-1db6-46a6-b9f8-d1bfcdb88b20", + "metadata": {}, + "source": [ + "### Section 4: Simulated Annealing--Verifying your solutions" + ] + }, + { + "cell_type": "markdown", + "id": "37e4d030-3484-4fe2-b2de-7c2d374e58af", + "metadata": {}, + "source": [ + "In the previous section you solved the **Mean-Variance-VaR** problem with Simulated Annealing, making sure that the penalty terms are imposed properly, so that the equalities/inequalities of the problem are satisfied. In this section, you will further test your solutions.\n", + "- From the output of **Section 3** you get the optimal values of the binary variables of the encoding. For example, for the variable $x_{i}$ we have:\n", + "$$x_{i}=\\sum_{d=1}^{D}f(d)z_{di}$$\n", + "with $\\{ z_{di} \\}$ the output of **Section 3** and $f(d)$ the function you have chosen for the encoding. Use this output in order to calculate the optimal percentages of the total portfolio invested in each one of the assets, $\\{x_{i}\\}$.\n", + "- After obtaining $\\{ x_{i} \\}$ use them to calculate the **expected total returns**: $$\\mu_{P}(x):=\\sum_{k=1}^{n}\\mu_{k}x_{k}$$\n", + " and **variance**: $$V(x):=\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}$$\n", + "- In a similar way, from the output of **Section 3** calculate the $VaR_{\\epsilon}=-r_{\\epsilon}$.\n", + "- Run the code of **Section 3** multiple times. Each time calculate the expected returns, the variance and $VaR_{\\epsilon}$. Peak the best result and justify your answer.\n", + "- Calculate the historical VaR with the use of the following function:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "95761549", + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_historical_VaR(weights, mu_R, confidence_level=0.95):\n", + " \n", + " portfolio_returns = np.dot(mu_R, np.transpose(np.array(weights)))\n", + "\n", + " VaR = np.percentile(portfolio_returns, 100 * (1 - confidence_level))\n", + " \n", + " return -VaR" + ] + }, + { + "cell_type": "markdown", + "id": "09d0c42b", + "metadata": {}, + "source": [ + "with $\\textit{weights}$ being a list that includes the optimal values (your solution) for the variables $\\{x_{i}\\}$ and $\\textit{mu_R}$ the expected returns of the assets (given by the data). Compare the return value got from this function to $VaR_{\\epsilon}$ calculated above." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c6d5972", + "metadata": {}, + "outputs": [], + "source": [ + "# write your code here" + ] + }, + { + "cell_type": "markdown", + "id": "e32da760-2d7c-44cc-b541-0fd576eaedf2", + "metadata": {}, + "source": [ + "**Answer:**" + ] + }, + { + "cell_type": "markdown", + "id": "9acc19d6-5b85-4ef4-aef4-7cdfe709b566", + "metadata": {}, + "source": [ + "### Section 5: Quantum optimization (Quantum Annealing and/or QAOA)" + ] + }, + { + "cell_type": "markdown", + "id": "ac6a4eab-e29a-470d-ba29-a3995c8eac10", + "metadata": {}, + "source": [ + "\n", + "**Note**: For the exercises below use the $\\hat{H}$ you found in **Section 2**, with the same encoding you used in that exercise and with the use of the **data** provided in the beginning of the challenge.\n", + "\n", + "**STEP 1:** Study the **Mean-Variance-VaR Simplified Version** problem with QAOA and Quantum Annealing. \n", + "\n", + "The Quantum Approximate Optimization Algorithm (QAOA) was first introduced in [A Quantum Approximate Optimization Algorithm](https://arxiv.org/abs/1411.4028). QAOA is a popular method to solve combinatorial optimization problems in noisy intermediate-scale quantum (NISQ) devices.\n", + "\n", + "Useful tutorials on implementing QAOA can be found here: \n", + "- [Pulser tutorial QAOA](https://pulser.readthedocs.io/en/latest/tutorials/qubo.html)\n", + "- [Qiskit QAOA](https://docs.quantum.ibm.com/api/qiskit/qiskit.algorithms.minimum_eigensolvers.QAOA), [Qiskit tutorial QAOA](https://qiskit.org/documentation/stable/0.40/tutorials/algorithms/05_qaoa.html)\n", + "- [pyQAOA tutorial QAOA](https://grove-docs.readthedocs.io/en/latest/qaoa.html)\n", + "- [PennyLane tutorial QAOA](https://pennylane.ai/qml/demos/tutorial_qaoa_intro/)\n", + "- [OpenQAOA-EntropicaLabs](https://openqaoa.entropicalabs.com/)\n", + "\n", + "For Quantum Annealing, you may read this reference [D-Wave, Quantum Annealing](https://docs.dwavesys.com/docs/latest/c_gs_2.html#getting-started-qa) and follow [D-Wave Ocean Software documentation](https://docs.ocean.dwavesys.com/en/stable/index.html) for the implementation. \n", + "In [Solving the Optimal Trading Trajectory Problem Using a Quantum Annealer](https://arxiv.org/abs/1508.06182), the authors explain how the choice of encoding might differ when considering solving an optimization problem with quantum annealing instead of simulated annealing.\n", + " \n", + "Do not implement anything at the moment. This step is for introducing you to different quantum or quantum-inspired algorithms one could utilize for this problem.\n", + "\n", + "**STEP 2:** In **Section 3** you used [D-Wave neal](https://docs.ocean.dwavesys.com/projects/neal/en/latest/) to solve the problem with Simulated Annealing. As also seen in the previous step, [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) gives you immediate, secure access to D-Wave quantum and hybrid solvers. Study the capabilities of [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) and answer the following questions:\n", + "\n", + "- For reasonable granularity (i.e. number of qubits per encoded variable) how many assets $n$ and events $T$ you can study with a quantum device and D-Wave? For that you will need to find the total number of qubits that you can explore with [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) for the algorithm of your choice. Notice that the resources available to the public are much less than the capabilities of these devices.\n", + "\n", + "**STEP 3:** After exploring the capabilities of [D-Wave Ocean Software documentation](https://docs.ocean.dwavesys.com/en/stable/index.html), you are encouraged to use a **quantum simulator/backend** to study the **Mean-Variance-VaR Simplified Version** problem with the **data** provided in the beginning of the challenge (feel free to change $T$ and $n$ if needed) and any method of your choice. For this, you can use your qBraid account. Explore the options below:\n", + "\n", + "- [Qiskit](https://docs.quantum.ibm.com/api/qiskit)\n", + "- [Amazon Braket](https://docs.aws.amazon.com/braket/latest/developerguide/what-is-braket.html)\n", + "\n", + "Or even more, you may also opt to explore the usage of GPUs for simulating quantum algorithms. For example, see a recent [work](https://www.jpmorgan.com/technology/technology-blog/quantum-optimization-research) that studies QAOA scaling performance on a fast GPU-specific QAOA simulator.\n", + "\n", + "No matter what algorithm you choose, **you are encouraged to run small-scale simulations of the problems on quantum backends and simulators, always keeping in mind the resources that you are utilizing and their cost**. What problems/limitations do you encounter in comparison with simulated annealing?" + ] + }, + { + "cell_type": "markdown", + "id": "dbc17d65-627d-4805-ac9f-3a3803bfc70d", + "metadata": {}, + "source": [ + "
Note 1 (Hint):\n", + " \n", + " One could read the following references: \n", + " \n", + "- [Quantum Optimization: Potential, Challenges, and the Path Forward](https://arxiv.org/abs/2312.02279) and references within.\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "8b4a5f08-14de-4d26-8031-8c1b2cd4f003", + "metadata": {}, + "source": [ + "
Note 2 (Hint):\n", + " \n", + "One can take a look at the [IBM Quantum Development Roadmap to 2033](https://newsroom.ibm.com/2023-12-04-IBM-Debuts-Next-Generation-Quantum-Processor-IBM-Quantum-System-Two,-Extends-Roadmap-to-Advance-Era-of-Quantum-Utility) and [QuEra's Quantum Computing Roadmap](https://www.quera.com/press-releases/quera-computing-releases-a-groundbreaking-roadmap-for-advanced-error-corrected-quantum-computers-pioneering-the-next-frontier-in-quantum-innovation) for an idea about the current and predicted quantum capabilities.\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "990a82c7-6ccc-4552-8b86-97a95fece3cd", + "metadata": {}, + "source": [ + "**Answer:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa60be68", + "metadata": {}, + "outputs": [], + "source": [ + "# write your code here" + ] + }, + { + "cell_type": "markdown", + "id": "95e10967", + "metadata": {}, + "source": [ + "### Section 6 (BONUS):\n", + "\n", + "Above you followed specific steps and used simulated annealing as well as quantum or quantum-inspired methods to solve the problem of interest. To do this, you were instructed to incorporate the constraints of the problem (equalities/inequalities) in the objective function and then to find the QUBO formulation of the problem in question. In this exercise you are asked to think of another way to solve the **Mean-Variance-VaR Simplified Version** problem with quantum, quantum-inspired or hybrid methods, without following any of the steps mentioned in this challenge." + ] + }, + { + "cell_type": "markdown", + "id": "d57153d6", + "metadata": {}, + "source": [ + "**Answer:**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "067cea0c", + "metadata": {}, + "outputs": [], + "source": [ + "# write your code here" + ] + }, + { + "cell_type": "markdown", + "id": "71ae8d5b-93a8-4305-95a1-0cebf1f29cf6", + "metadata": {}, + "source": [ + "### Section 7: Pitch your quantum strategy to a client" + ] + }, + { + "cell_type": "markdown", + "id": "ecb9a766-b6df-4b91-b0ff-13ced8fc5b52", + "metadata": {}, + "source": [ + "Imagine that you are part of the Quantum team at Moody's. You are meeting the board of stakeholders of an important company in the US. Given what you now know about the current quantum hardware capabilities, in which quantum algorithms should the stakeholders invest and why? Prepare your pitch focusing only on portfolio optimization problems. How do you think quantum could potentially avoid the drawbacks of classical optimization algorithms and why should a company invest in quantum today?" + ] + }, + { + "cell_type": "markdown", + "id": "a440d8cd-2ed2-45e9-8a89-895847db823c", + "metadata": {}, + "source": [ + "
Hint:\n", + " \n", + " One could read the following references: \n", + " \n", + "- [Quantum Optimization: Potential, Challenges, and the Path Forward](https://arxiv.org/abs/2312.02279) \n", + "- [QAOA with N⋅p≥200](https://arxiv.org/abs/2303.02064) \n", + "- [Evidence of Scaling Advantage for the Quantum Approximate Optimization Algorithm on a Classically Intractable Problem](https://arxiv.org/pdf/2308.02342.pdf)\n", + "- [Towards optimization under uncertainty for fundamental models in energy markets using quantum computers](https://arxiv.org/abs/2301.01108)\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "550f2bc1-7726-417e-88b5-8253ac12baeb", + "metadata": {}, + "source": [ + "**Answer:**" + ] + }, + { + "cell_type": "markdown", + "id": "c098599c-7ab0-48b1-9e33-51d22502310d", + "metadata": {}, + "source": [ + "# This is the end of the challenge. Good luck!" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 [Default]", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 549a2d7..d269cad 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -40,13 +40,15 @@ "cell_type": "code", "execution_count": 1, "id": "15770539", - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "moodys_challenge.ipynb\tREADME.md returns_data.txt\n" + "fromAshrit.ipynb moodys_challenge.ipynb README.md returns_data.txt\n" ] } ], @@ -350,10 +352,29 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "id": "a78f8aea", - "metadata": {}, - "outputs": [], + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_493/969284350.py:2: DeprecationWarning: \n", + "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", + "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", + "but was not found to be installed on your system.\n", + "If this would cause problems for you,\n", + "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", + " \n", + " import pandas as pd\n", + "/tmp/ipykernel_493/969284350.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + ] + } + ], "source": [ "import numpy as np\n", "import pandas as pd\n", @@ -847,9 +868,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 [Default]", + "display_name": "Python 3 [Moody's]", "language": "python", - "name": "python3" + "name": "python3_moodys_xbto4j" }, "language_info": { "codemirror_mode": { @@ -861,7 +882,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.18" } }, "nbformat": 4, From 35af5f11e9df074d9303d3728b0176bff081eaa5 Mon Sep 17 00:00:00 2001 From: Ashrit Date: Sun, 4 Feb 2024 02:41:08 +0000 Subject: [PATCH 04/20] Adding changes --- .../moodys_challenge-checkpoint.ipynb | 55 ++++++---------- moodys_challenge.ipynb | 62 ++++--------------- 2 files changed, 31 insertions(+), 86 deletions(-) diff --git a/.ipynb_checkpoints/moodys_challenge-checkpoint.ipynb b/.ipynb_checkpoints/moodys_challenge-checkpoint.ipynb index 549a2d7..b3a968e 100644 --- a/.ipynb_checkpoints/moodys_challenge-checkpoint.ipynb +++ b/.ipynb_checkpoints/moodys_challenge-checkpoint.ipynb @@ -38,15 +38,17 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 5, "id": "15770539", - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "moodys_challenge.ipynb\tREADME.md returns_data.txt\n" + "fromAshrit.ipynb moodys_challenge.ipynb README.md returns_data.txt\n" ] } ], @@ -348,34 +350,6 @@ "To examine the practical applicability of the mean-VaR model with quantum techniques, we will use a small dataset that compromises the weekly linear returns for Eurostoxx50 Market Index from 01-22-2007 to 05-06-2013 and contains up to 32 assets. You may find the associated file: *returns_data.txt*" ] }, - { - "cell_type": "code", - "execution_count": 1, - "id": "a78f8aea", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "\n", - "# You may choose to select different parameters/values\n", - "number_assets = 3\n", - "T = 10\n", - "# Read returns\n", - "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", - "\n", - "Rraw = df.values.T\n", - "\n", - "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", - "R = Rraw[:number_assets,:T]\n", - "\n", - "# Expected return of each asset\n", - "expected_returns = np.mean(R, axis = 1)\n", - "\n", - "# Covariance matrix of asset returns\n", - "covariance_matrix = np.cov(R)" - ] - }, { "cell_type": "markdown", "id": "736c3eea-e219-4d6c-8457-3a4f21e9f6fc", @@ -396,10 +370,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "ec015dad-7449-433c-bd44-593e5fecb4ec", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (4199961082.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Cell \u001b[0;32mIn[6], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m
NOTE:\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], "source": [ "
NOTE:\n", "L has the following form: $L=\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}-\\sum_{k=1}^{n}\\mu_{k}x_{k}+\\textit{penalty terms}$, with the penalty terms to be found in this section (and include all the equalities and inequalities of the problem). This will need to be mapped to a quantum Hamiltonian in the following section. Each penalty term will correspond to one lagrange multiplier that will need to be fine-tuned in the next sections, so that the constraints are imposed.\n", @@ -847,9 +830,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 [Default]", + "display_name": "Python 3 [Moody's]", "language": "python", - "name": "python3" + "name": "python3_moodys_xbto4j" }, "language_info": { "codemirror_mode": { @@ -861,7 +844,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.9.18" } }, "nbformat": 4, diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index d269cad..b3a968e 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 5, "id": "15770539", "metadata": { "tags": [] @@ -350,53 +350,6 @@ "To examine the practical applicability of the mean-VaR model with quantum techniques, we will use a small dataset that compromises the weekly linear returns for Eurostoxx50 Market Index from 01-22-2007 to 05-06-2013 and contains up to 32 assets. You may find the associated file: *returns_data.txt*" ] }, - { - "cell_type": "code", - "execution_count": 2, - "id": "a78f8aea", - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_493/969284350.py:2: DeprecationWarning: \n", - "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", - "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", - "but was not found to be installed on your system.\n", - "If this would cause problems for you,\n", - "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", - " \n", - " import pandas as pd\n", - "/tmp/ipykernel_493/969284350.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", - " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" - ] - } - ], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "\n", - "# You may choose to select different parameters/values\n", - "number_assets = 3\n", - "T = 10\n", - "# Read returns\n", - "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", - "\n", - "Rraw = df.values.T\n", - "\n", - "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", - "R = Rraw[:number_assets,:T]\n", - "\n", - "# Expected return of each asset\n", - "expected_returns = np.mean(R, axis = 1)\n", - "\n", - "# Covariance matrix of asset returns\n", - "covariance_matrix = np.cov(R)" - ] - }, { "cell_type": "markdown", "id": "736c3eea-e219-4d6c-8457-3a4f21e9f6fc", @@ -417,10 +370,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "ec015dad-7449-433c-bd44-593e5fecb4ec", "metadata": {}, - "outputs": [], + "outputs": [ + { + "ename": "SyntaxError", + "evalue": "invalid syntax (4199961082.py, line 1)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m Cell \u001b[0;32mIn[6], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m
NOTE:\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" + ] + } + ], "source": [ "
NOTE:\n", "L has the following form: $L=\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}-\\sum_{k=1}^{n}\\mu_{k}x_{k}+\\textit{penalty terms}$, with the penalty terms to be found in this section (and include all the equalities and inequalities of the problem). This will need to be mapped to a quantum Hamiltonian in the following section. Each penalty term will correspond to one lagrange multiplier that will need to be fine-tuned in the next sections, so that the constraints are imposed.\n", From 31d549a11e696587d9e72c11fb6b3368d359de81 Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 02:34:03 -0500 Subject: [PATCH 05/20] Implement steps 1-3 --- moodys_challenge.ipynb | 320 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 301 insertions(+), 19 deletions(-) diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 2f33633..94d9c75 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -332,30 +332,52 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 12, "id": "a78f8aea", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ 0.0157004 -0.00608947 -0.01741893]\n", + "[[0.00040521 0.00016108 0.00047332]\n", + " [0.00016108 0.00072066 0.00064889]\n", + " [0.00047332 0.00064889 0.00164269]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_11496\\3824432534.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + ] + } + ], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "# You may choose to select different parameters/values\n", - "number_assets = 3\n", - "T = 10\n", + "N = 3\n", + "T = 5\n", "# Read returns\n", "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", "\n", "Rraw = df.values.T\n", "\n", "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", - "R = Rraw[:number_assets,:T]\n", + "reward = Rraw[:N,:T]\n", "\n", "# Expected return of each asset\n", - "expected_returns = np.mean(R, axis = 1)\n", + "expected_returns = np.mean(reward, axis = 1)\n", "\n", "# Covariance matrix of asset returns\n", - "covariance_matrix = np.cov(R)" + "covariance_matrix = np.cov(reward)\n", + "\n", + "print(expected_returns)\n", + "print(covariance_matrix)" ] }, { @@ -394,6 +416,14 @@ "**Answer:**" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "f0d54474", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", "id": "bb2982bd-fbb9-49de-8dbc-370e9390173b", @@ -469,6 +499,36 @@ "
" ] }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Array([Binary('x_full[0][0]'), Binary('x_full[1][0]'), Binary('x_full[2][0]'), Binary('x_full[3][0]'), Binary('x_full[4][0]')])\n", + "\n", + "\n" + ] + } + ], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Add\n", + "D_X = 5\n", + "D_A = 5\n", + "x_full = Array.create(\"x_full\", shape=(D_X, N), vartype=\"BINARY\")\n", + "print(x_full[:,0])\n", + "def binary2integer(x):\n", + " return sum([2**i*x[i] for i in range(len(x))])\n", + "print(type(binary2integer(x_full[:,0])))\n", + "\n", + "A = Array.create(\"A\", shape=(D_A, 1), vartype=\"BINARY\")\n", + "print(type(binary2integer(A[:,0])))" + ] + }, { "cell_type": "markdown", "id": "9cabd342-4061-488b-9b3d-5d93d96344ec", @@ -491,12 +551,123 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "553d8b31", "metadata": {}, - "outputs": [], - "source": [ - "# write your code here" + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "BinaryQuadraticModel({'x_full[1][2]': -34834.58508411406, 'x_full[1][0]': 31401.908438851064, 'x_full[2][1]': -20352.919804753736, 'B_full[0][4]': 1000500.0, 'x_full[2][2]': -65662.68673217949, 'B_full[3][4]': 8032000.0, 'x_full[4][1]': 14647.365549565578, 'r[2][0]': -3988000.0, 'x_full[3][1]': -24695.998814744024, 'r[0][0]': -998800.0, 'x_full[1][1]': -12177.690001722289, 'y[2][0]': -500001700.0, 'x_full[2][0]': 66806.10279699802, 'x_full[4][0]': 363279.2732510936, 'y[3][0]': -500001700.0, 'r[1][0]': -1996400.0, 'x_full[0][0]': 15200.668479513544, 'r[4][0]': -15836800.0, 'y[4][0]': -500001700.0, 'x_full[3][0]': 149621.34927117964, 'x_full[0][2]': -17918.102971563105, 'x_full[3][2]': -115299.43972016452, 'x_full[4][2]': -166495.14446355117, 'C[2][0]': 10800.0, 'C[0][0]': 2100.0, 'y[0][0]': -500001700.0, 'B_full[4][4]': 16128000.0, 'r[3][0]': -7956800.0, 'y[1][0]': -500001700.0, 'B_full[2][4]': 4008000.0, 'A[3][0]': 1600.0, 'A[1][0]': -800.0, 'B_full[1][4]': 2002000.0, 'x_full[0][1]': -6589.152525697503, 'A[2][0]': -800.0, 'A[0][0]': -500.0, 'C[3][0]': 28000.0, 'C[1][0]': 4600.0, 'C[4][0]': 81600.0, 'A[4][0]': 16000.0}, {('x_full[1][0]', 'x_full[1][2]'): 4000.4244618825674, ('x_full[2][1]', 'x_full[1][2]'): 8005.011832959769, ('x_full[2][1]', 'x_full[1][0]'): 8000.268662619726, ('B_full[0][4]', 'x_full[1][2]'): -34.83786164, ('B_full[0][4]', 'x_full[1][0]'): 31.400796879999994, ('B_full[0][4]', 'x_full[2][1]'): -24.35786456, ('x_full[2][2]', 'x_full[1][2]'): 8012.966872097232, ('x_full[2][2]', 'x_full[1][0]'): 8000.848923765135, ('x_full[2][2]', 'x_full[2][1]'): 16010.023665919538, ('x_full[2][2]', 'B_full[0][4]'): -69.67572328, ('B_full[3][4]', 'x_full[1][2]'): -278.70289312, ('B_full[3][4]', 'x_full[1][0]'): 251.20637503999995, ('B_full[3][4]', 'x_full[2][1]'): -194.86291648, ('B_full[3][4]', 'B_full[0][4]'): 8000.0, ('B_full[3][4]', 'x_full[2][2]'): -557.40578624, ('x_full[4][1]', 'x_full[1][2]'): 32020.047331839076, ('x_full[4][1]', 'x_full[1][0]'): 32001.074650478902, ('x_full[4][1]', 'x_full[2][1]'): 64039.363179053675, ('x_full[4][1]', 'B_full[0][4]'): -97.43145824, ('x_full[4][1]', 'x_full[2][2]'): 64040.09466367815, ('x_full[4][1]', 'B_full[3][4]'): -779.45166592, ('r[2][0]', 'x_full[1][2]'): 139.35144656, ('r[2][0]', 'x_full[1][0]'): -125.60318751999998, ('r[2][0]', 'x_full[2][1]'): 97.43145824, ('r[2][0]', 'B_full[0][4]'): -4000.0, ('r[2][0]', 'x_full[2][2]'): 278.70289312, ('r[2][0]', 'B_full[3][4]'): -32000.0, ('r[2][0]', 'x_full[4][1]'): 389.72583296, ('x_full[3][1]', 'x_full[1][2]'): 16010.023665919538, ('x_full[3][1]', 'x_full[1][0]'): 16000.537325239451, ('x_full[3][1]', 'x_full[2][1]'): 32019.681589526837, ('x_full[3][1]', 'B_full[0][4]'): -48.71572912, ('x_full[3][1]', 'x_full[2][2]'): 32020.047331839076, ('x_full[3][1]', 'B_full[3][4]'): -389.72583296, ('x_full[3][1]', 'x_full[4][1]'): 128078.72635810735, ('x_full[3][1]', 'r[2][0]'): 194.86291648, ('r[0][0]', 'x_full[1][2]'): 34.83786164, ('r[0][0]', 'x_full[1][0]'): -31.400796879999994, ('r[0][0]', 'x_full[2][1]'): 24.35786456, ('r[0][0]', 'B_full[0][4]'): -1000.0, ('r[0][0]', 'x_full[2][2]'): 69.67572328, ('r[0][0]', 'B_full[3][4]'): -8000.0, ('r[0][0]', 'x_full[4][1]'): 97.43145824, ('r[0][0]', 'r[2][0]'): 4800.0, ('r[0][0]', 'x_full[3][1]'): 48.71572912, ('x_full[1][1]', 'x_full[1][2]'): 4002.5059164798845, ('x_full[1][1]', 'x_full[1][0]'): 4000.134331309863, ('x_full[1][1]', 'x_full[2][1]'): 8004.920397381709, ('x_full[1][1]', 'B_full[0][4]'): -12.17893228, ('x_full[1][1]', 'x_full[2][2]'): 8005.011832959769, ('x_full[1][1]', 'B_full[3][4]'): -97.43145824, ('x_full[1][1]', 'x_full[4][1]'): 32019.681589526837, ('x_full[1][1]', 'r[2][0]'): 48.71572912, ('x_full[1][1]', 'x_full[3][1]'): 16009.840794763419, ('x_full[1][1]', 'r[0][0]'): 12.17893228, ('y[2][0]', 'x_full[1][2]'): 34837.86164, ('y[2][0]', 'x_full[1][0]'): -31400.79688, ('y[2][0]', 'x_full[2][1]'): 24357.864559999998, ('y[2][0]', 'B_full[0][4]'): -1000000.0, ('y[2][0]', 'x_full[2][2]'): 69675.72328, ('y[2][0]', 'B_full[3][4]'): -8000000.0, ('y[2][0]', 'x_full[4][1]'): 97431.45823999999, ('y[2][0]', 'r[2][0]'): 4000000.0, ('y[2][0]', 'x_full[3][1]'): 48715.729119999996, ('y[2][0]', 'r[0][0]'): 1000000.0, ('y[2][0]', 'x_full[1][1]'): 12178.932279999999, ('x_full[2][0]', 'x_full[1][2]'): 8000.848923765135, ('x_full[2][0]', 'x_full[1][0]'): 8004.571838591786, ('x_full[2][0]', 'x_full[2][1]'): 16000.537325239451, ('x_full[2][0]', 'B_full[0][4]'): 62.80159375999999, ('x_full[2][0]', 'x_full[2][2]'): 16001.69784753027, ('x_full[2][0]', 'B_full[3][4]'): 502.4127500799999, ('x_full[2][0]', 'x_full[4][1]'): 64002.149300957804, ('x_full[2][0]', 'r[2][0]'): -251.20637503999995, ('x_full[2][0]', 'x_full[3][1]'): 32001.074650478902, ('x_full[2][0]', 'r[0][0]'): -62.80159375999999, ('x_full[2][0]', 'x_full[1][1]'): 8000.268662619726, ('x_full[2][0]', 'y[2][0]'): -62801.59376, ('x_full[4][0]', 'x_full[1][2]'): 32003.39569506054, ('x_full[4][0]', 'x_full[1][0]'): 32018.287354367145, ('x_full[4][0]', 'x_full[2][1]'): 64002.149300957804, ('x_full[4][0]', 'B_full[0][4]'): 251.20637503999995, ('x_full[4][0]', 'x_full[2][2]'): 64006.79139012108, ('x_full[4][0]', 'B_full[3][4]'): 2009.6510003199996, ('x_full[4][0]', 'x_full[4][1]'): 256008.59720383122, ('x_full[4][0]', 'r[2][0]'): -1004.8255001599998, ('x_full[4][0]', 'x_full[3][1]'): 128004.29860191561, ('x_full[4][0]', 'r[0][0]'): -251.20637503999995, ('x_full[4][0]', 'x_full[1][1]'): 32001.074650478902, ('x_full[4][0]', 'y[2][0]'): -251206.37504, ('x_full[4][0]', 'x_full[2][0]'): 64036.57470873429, ('y[3][0]', 'x_full[1][2]'): 34837.86164, ('y[3][0]', 'x_full[1][0]'): -31400.79688, ('y[3][0]', 'x_full[2][1]'): 24357.864559999998, ('y[3][0]', 'B_full[0][4]'): -1000000.0, ('y[3][0]', 'x_full[2][2]'): 69675.72328, ('y[3][0]', 'B_full[3][4]'): -8000000.0, ('y[3][0]', 'x_full[4][1]'): 97431.45823999999, ('y[3][0]', 'r[2][0]'): 4000000.0, ('y[3][0]', 'x_full[3][1]'): 48715.729119999996, ('y[3][0]', 'r[0][0]'): 1000000.0, ('y[3][0]', 'x_full[1][1]'): 12178.932279999999, ('y[3][0]', 'y[2][0]'): 1000000400.0, ('y[3][0]', 'x_full[2][0]'): -62801.59376, ('y[3][0]', 'x_full[4][0]'): -251206.37504, ('r[1][0]', 'x_full[1][2]'): 69.67572328, ('r[1][0]', 'x_full[1][0]'): -62.80159375999999, ('r[1][0]', 'x_full[2][1]'): 48.71572912, ('r[1][0]', 'B_full[0][4]'): -2000.0, ('r[1][0]', 'x_full[2][2]'): 139.35144656, ('r[1][0]', 'B_full[3][4]'): -16000.0, ('r[1][0]', 'x_full[4][1]'): 194.86291648, ('r[1][0]', 'r[2][0]'): 9600.0, ('r[1][0]', 'x_full[3][1]'): 97.43145824, ('r[1][0]', 'r[0][0]'): 2400.0, ('r[1][0]', 'x_full[1][1]'): 24.35786456, ('r[1][0]', 'y[2][0]'): 2000000.0, ('r[1][0]', 'x_full[2][0]'): -125.60318751999998, ('r[1][0]', 'x_full[4][0]'): -502.4127500799999, ('r[1][0]', 'y[3][0]'): 2000000.0, ('x_full[0][0]', 'x_full[1][2]'): 2000.2122309412837, ('x_full[0][0]', 'x_full[1][0]'): 2001.1429596479466, ('x_full[0][0]', 'x_full[2][1]'): 4000.134331309863, ('x_full[0][0]', 'B_full[0][4]'): 15.700398439999997, ('x_full[0][0]', 'x_full[2][2]'): 4000.4244618825674, ('x_full[0][0]', 'B_full[3][4]'): 125.60318751999998, ('x_full[0][0]', 'x_full[4][1]'): 16000.537325239451, ('x_full[0][0]', 'r[2][0]'): -62.80159375999999, ('x_full[0][0]', 'x_full[3][1]'): 8000.268662619726, ('x_full[0][0]', 'r[0][0]'): -15.700398439999997, ('x_full[0][0]', 'x_full[1][1]'): 2000.0671656549314, ('x_full[0][0]', 'y[2][0]'): -15700.39844, ('x_full[0][0]', 'x_full[2][0]'): 4002.285919295893, ('x_full[0][0]', 'x_full[4][0]'): 16009.143677183572, ('x_full[0][0]', 'y[3][0]'): -15700.39844, ('x_full[0][0]', 'r[1][0]'): -31.400796879999994, ('r[4][0]', 'x_full[1][2]'): 557.40578624, ('r[4][0]', 'x_full[1][0]'): -502.4127500799999, ('r[4][0]', 'x_full[2][1]'): 389.72583296, ('r[4][0]', 'B_full[0][4]'): -16000.0, ('r[4][0]', 'x_full[2][2]'): 1114.81157248, ('r[4][0]', 'B_full[3][4]'): -128000.0, ('r[4][0]', 'x_full[4][1]'): 1558.90333184, ('r[4][0]', 'r[2][0]'): 76800.0, ('r[4][0]', 'x_full[3][1]'): 779.45166592, ('r[4][0]', 'r[0][0]'): 19200.0, ('r[4][0]', 'x_full[1][1]'): 194.86291648, ('r[4][0]', 'y[2][0]'): 16000000.0, ('r[4][0]', 'x_full[2][0]'): -1004.8255001599998, ('r[4][0]', 'x_full[4][0]'): -4019.3020006399993, ('r[4][0]', 'y[3][0]'): 16000000.0, ('r[4][0]', 'r[1][0]'): 38400.0, ('r[4][0]', 'x_full[0][0]'): -251.20637503999995, ('y[4][0]', 'x_full[1][2]'): 34837.86164, ('y[4][0]', 'x_full[1][0]'): -31400.79688, ('y[4][0]', 'x_full[2][1]'): 24357.864559999998, ('y[4][0]', 'B_full[0][4]'): -1000000.0, ('y[4][0]', 'x_full[2][2]'): 69675.72328, ('y[4][0]', 'B_full[3][4]'): -8000000.0, ('y[4][0]', 'x_full[4][1]'): 97431.45823999999, ('y[4][0]', 'r[2][0]'): 4000000.0, ('y[4][0]', 'x_full[3][1]'): 48715.729119999996, ('y[4][0]', 'r[0][0]'): 1000000.0, ('y[4][0]', 'x_full[1][1]'): 12178.932279999999, ('y[4][0]', 'y[2][0]'): 1000000400.0, ('y[4][0]', 'x_full[2][0]'): -62801.59376, ('y[4][0]', 'x_full[4][0]'): -251206.37504, ('y[4][0]', 'y[3][0]'): 1000000400.0, ('y[4][0]', 'r[1][0]'): 2000000.0, ('y[4][0]', 'x_full[0][0]'): -15700.39844, ('y[4][0]', 'r[4][0]'): 16000000.0, ('x_full[3][0]', 'x_full[1][2]'): 16001.69784753027, ('x_full[3][0]', 'x_full[1][0]'): 16009.143677183572, ('x_full[3][0]', 'x_full[2][1]'): 32001.074650478902, ('x_full[3][0]', 'B_full[0][4]'): 125.60318751999998, ('x_full[3][0]', 'x_full[2][2]'): 32003.39569506054, ('x_full[3][0]', 'B_full[3][4]'): 1004.8255001599998, ('x_full[3][0]', 'x_full[4][1]'): 128004.29860191561, ('x_full[3][0]', 'r[2][0]'): -502.4127500799999, ('x_full[3][0]', 'x_full[3][1]'): 64002.149300957804, ('x_full[3][0]', 'r[0][0]'): -125.60318751999998, ('x_full[3][0]', 'x_full[1][1]'): 16000.537325239451, ('x_full[3][0]', 'y[2][0]'): -125603.18752, ('x_full[3][0]', 'x_full[2][0]'): 32018.287354367145, ('x_full[3][0]', 'x_full[4][0]'): 128073.14941746858, ('x_full[3][0]', 'y[3][0]'): -125603.18752, ('x_full[3][0]', 'r[1][0]'): -251.20637503999995, ('x_full[3][0]', 'x_full[0][0]'): 8004.571838591786, ('x_full[3][0]', 'r[4][0]'): -2009.6510003199996, ('x_full[3][0]', 'y[4][0]'): -125603.18752, ('x_full[0][2]', 'x_full[1][2]'): 2003.241718024308, ('x_full[0][2]', 'x_full[1][0]'): 2000.2122309412837, ('x_full[0][2]', 'x_full[2][1]'): 4002.5059164798845, ('x_full[0][2]', 'B_full[0][4]'): -17.41893082, ('x_full[0][2]', 'x_full[2][2]'): 4006.483436048616, ('x_full[0][2]', 'B_full[3][4]'): -139.35144656, ('x_full[0][2]', 'x_full[4][1]'): 16010.023665919538, ('x_full[0][2]', 'r[2][0]'): 69.67572328, ('x_full[0][2]', 'x_full[3][1]'): 8005.011832959769, ('x_full[0][2]', 'r[0][0]'): 17.41893082, ('x_full[0][2]', 'x_full[1][1]'): 2001.2529582399422, ('x_full[0][2]', 'y[2][0]'): 17418.93082, ('x_full[0][2]', 'x_full[2][0]'): 4000.4244618825674, ('x_full[0][2]', 'x_full[4][0]'): 16001.69784753027, ('x_full[0][2]', 'y[3][0]'): 17418.93082, ('x_full[0][2]', 'r[1][0]'): 34.83786164, ('x_full[0][2]', 'x_full[0][0]'): 1000.1061154706418, ('x_full[0][2]', 'r[4][0]'): 278.70289312, ('x_full[0][2]', 'y[4][0]'): 17418.93082, ('x_full[0][2]', 'x_full[3][0]'): 8000.848923765135, ('x_full[3][2]', 'x_full[1][2]'): 16025.933744194464, ('x_full[3][2]', 'x_full[1][0]'): 16001.69784753027, ('x_full[3][2]', 'x_full[2][1]'): 32020.047331839076, ('x_full[3][2]', 'B_full[0][4]'): -139.35144656, ('x_full[3][2]', 'x_full[2][2]'): 32051.86748838893, ('x_full[3][2]', 'B_full[3][4]'): -1114.81157248, ('x_full[3][2]', 'x_full[4][1]'): 128080.1893273563, ('x_full[3][2]', 'r[2][0]'): 557.40578624, ('x_full[3][2]', 'x_full[3][1]'): 64040.09466367815, ('x_full[3][2]', 'r[0][0]'): 139.35144656, ('x_full[3][2]', 'x_full[1][1]'): 16010.023665919538, ('x_full[3][2]', 'y[2][0]'): 139351.44656, ('x_full[3][2]', 'x_full[2][0]'): 32003.39569506054, ('x_full[3][2]', 'x_full[4][0]'): 128013.58278024216, ('x_full[3][2]', 'y[3][0]'): 139351.44656, ('x_full[3][2]', 'r[1][0]'): 278.70289312, ('x_full[3][2]', 'x_full[0][0]'): 8000.848923765135, ('x_full[3][2]', 'r[4][0]'): 2229.62314496, ('x_full[3][2]', 'y[4][0]'): 139351.44656, ('x_full[3][2]', 'x_full[3][0]'): 64006.79139012108, ('x_full[3][2]', 'x_full[0][2]'): 8012.966872097232, ('x_full[4][2]', 'x_full[1][2]'): 32051.86748838893, ('x_full[4][2]', 'x_full[1][0]'): 32003.39569506054, ('x_full[4][2]', 'x_full[2][1]'): 64040.09466367815, ('x_full[4][2]', 'B_full[0][4]'): -278.70289312, ('x_full[4][2]', 'x_full[2][2]'): 64103.73497677786, ('x_full[4][2]', 'B_full[3][4]'): -2229.62314496, ('x_full[4][2]', 'x_full[4][1]'): 256160.3786547126, ('x_full[4][2]', 'r[2][0]'): 1114.81157248, ('x_full[4][2]', 'x_full[3][1]'): 128080.1893273563, ('x_full[4][2]', 'r[0][0]'): 278.70289312, ('x_full[4][2]', 'x_full[1][1]'): 32020.047331839076, ('x_full[4][2]', 'y[2][0]'): 278702.89312, ('x_full[4][2]', 'x_full[2][0]'): 64006.79139012108, ('x_full[4][2]', 'x_full[4][0]'): 256027.1655604843, ('x_full[4][2]', 'y[3][0]'): 278702.89312, ('x_full[4][2]', 'r[1][0]'): 557.40578624, ('x_full[4][2]', 'x_full[0][0]'): 16001.69784753027, ('x_full[4][2]', 'r[4][0]'): 4459.24628992, ('x_full[4][2]', 'y[4][0]'): 278702.89312, ('x_full[4][2]', 'x_full[3][0]'): 128013.58278024216, ('x_full[4][2]', 'x_full[0][2]'): 16025.933744194464, ('x_full[4][2]', 'x_full[3][2]'): 128207.46995355572, ('C[2][0]', 'y[2][0]'): -1600.0, ('C[2][0]', 'y[3][0]'): -1600.0, ('C[2][0]', 'y[4][0]'): -1600.0, ('C[0][0]', 'y[2][0]'): -400.0, ('C[0][0]', 'y[3][0]'): -400.0, ('C[0][0]', 'y[4][0]'): -400.0, ('C[0][0]', 'C[2][0]'): 1600.0, ('y[0][0]', 'x_full[1][2]'): 34837.86164, ('y[0][0]', 'x_full[1][0]'): -31400.79688, ('y[0][0]', 'x_full[2][1]'): 24357.864559999998, ('y[0][0]', 'B_full[0][4]'): -1000000.0, ('y[0][0]', 'x_full[2][2]'): 69675.72328, ('y[0][0]', 'B_full[3][4]'): -8000000.0, ('y[0][0]', 'x_full[4][1]'): 97431.45823999999, ('y[0][0]', 'r[2][0]'): 4000000.0, ('y[0][0]', 'x_full[3][1]'): 48715.729119999996, ('y[0][0]', 'r[0][0]'): 1000000.0, ('y[0][0]', 'x_full[1][1]'): 12178.932279999999, ('y[0][0]', 'y[2][0]'): 1000000400.0, ('y[0][0]', 'x_full[2][0]'): -62801.59376, ('y[0][0]', 'x_full[4][0]'): -251206.37504, ('y[0][0]', 'y[3][0]'): 1000000400.0, ('y[0][0]', 'r[1][0]'): 2000000.0, ('y[0][0]', 'x_full[0][0]'): -15700.39844, ('y[0][0]', 'r[4][0]'): 16000000.0, ('y[0][0]', 'y[4][0]'): 1000000400.0, ('y[0][0]', 'x_full[3][0]'): -125603.18752, ('y[0][0]', 'x_full[0][2]'): 17418.93082, ('y[0][0]', 'x_full[3][2]'): 139351.44656, ('y[0][0]', 'x_full[4][2]'): 278702.89312, ('y[0][0]', 'C[2][0]'): -1600.0, ('y[0][0]', 'C[0][0]'): -400.0, ('B_full[4][4]', 'x_full[1][2]'): -557.40578624, ('B_full[4][4]', 'x_full[1][0]'): 502.4127500799999, ('B_full[4][4]', 'x_full[2][1]'): -389.72583296, ('B_full[4][4]', 'B_full[0][4]'): 16000.0, ('B_full[4][4]', 'x_full[2][2]'): -1114.81157248, ('B_full[4][4]', 'B_full[3][4]'): 128000.0, ('B_full[4][4]', 'x_full[4][1]'): -1558.90333184, ('B_full[4][4]', 'r[2][0]'): -64000.0, ('B_full[4][4]', 'x_full[3][1]'): -779.45166592, ('B_full[4][4]', 'r[0][0]'): -16000.0, ('B_full[4][4]', 'x_full[1][1]'): -194.86291648, ('B_full[4][4]', 'y[2][0]'): -16000000.0, ('B_full[4][4]', 'x_full[2][0]'): 1004.8255001599998, ('B_full[4][4]', 'x_full[4][0]'): 4019.3020006399993, ('B_full[4][4]', 'y[3][0]'): -16000000.0, ('B_full[4][4]', 'r[1][0]'): -32000.0, ('B_full[4][4]', 'x_full[0][0]'): 251.20637503999995, ('B_full[4][4]', 'r[4][0]'): -256000.0, ('B_full[4][4]', 'y[4][0]'): -16000000.0, ('B_full[4][4]', 'x_full[3][0]'): 2009.6510003199996, ('B_full[4][4]', 'x_full[0][2]'): -278.70289312, ('B_full[4][4]', 'x_full[3][2]'): -2229.62314496, ('B_full[4][4]', 'x_full[4][2]'): -4459.24628992, ('B_full[4][4]', 'y[0][0]'): -16000000.0, ('r[3][0]', 'x_full[1][2]'): 278.70289312, ('r[3][0]', 'x_full[1][0]'): -251.20637503999995, ('r[3][0]', 'x_full[2][1]'): 194.86291648, ('r[3][0]', 'B_full[0][4]'): -8000.0, ('r[3][0]', 'x_full[2][2]'): 557.40578624, ('r[3][0]', 'B_full[3][4]'): -64000.0, ('r[3][0]', 'x_full[4][1]'): 779.45166592, ('r[3][0]', 'r[2][0]'): 38400.0, ('r[3][0]', 'x_full[3][1]'): 389.72583296, ('r[3][0]', 'r[0][0]'): 9600.0, ('r[3][0]', 'x_full[1][1]'): 97.43145824, ('r[3][0]', 'y[2][0]'): 8000000.0, ('r[3][0]', 'x_full[2][0]'): -502.4127500799999, ('r[3][0]', 'x_full[4][0]'): -2009.6510003199996, ('r[3][0]', 'y[3][0]'): 8000000.0, ('r[3][0]', 'r[1][0]'): 19200.0, ('r[3][0]', 'x_full[0][0]'): -125.60318751999998, ('r[3][0]', 'r[4][0]'): 153600.0, ('r[3][0]', 'y[4][0]'): 8000000.0, ('r[3][0]', 'x_full[3][0]'): -1004.8255001599998, ('r[3][0]', 'x_full[0][2]'): 139.35144656, ('r[3][0]', 'x_full[3][2]'): 1114.81157248, ('r[3][0]', 'x_full[4][2]'): 2229.62314496, ('r[3][0]', 'y[0][0]'): 8000000.0, ('r[3][0]', 'B_full[4][4]'): -128000.0, ('y[1][0]', 'x_full[1][2]'): 34837.86164, ('y[1][0]', 'x_full[1][0]'): -31400.79688, ('y[1][0]', 'x_full[2][1]'): 24357.864559999998, ('y[1][0]', 'B_full[0][4]'): -1000000.0, ('y[1][0]', 'x_full[2][2]'): 69675.72328, ('y[1][0]', 'B_full[3][4]'): -8000000.0, ('y[1][0]', 'x_full[4][1]'): 97431.45823999999, ('y[1][0]', 'r[2][0]'): 4000000.0, ('y[1][0]', 'x_full[3][1]'): 48715.729119999996, ('y[1][0]', 'r[0][0]'): 1000000.0, ('y[1][0]', 'x_full[1][1]'): 12178.932279999999, ('y[1][0]', 'y[2][0]'): 1000000400.0, ('y[1][0]', 'x_full[2][0]'): -62801.59376, ('y[1][0]', 'x_full[4][0]'): -251206.37504, ('y[1][0]', 'y[3][0]'): 1000000400.0, ('y[1][0]', 'r[1][0]'): 2000000.0, ('y[1][0]', 'x_full[0][0]'): -15700.39844, ('y[1][0]', 'r[4][0]'): 16000000.0, ('y[1][0]', 'y[4][0]'): 1000000400.0, ('y[1][0]', 'x_full[3][0]'): -125603.18752, ('y[1][0]', 'x_full[0][2]'): 17418.93082, ('y[1][0]', 'x_full[3][2]'): 139351.44656, ('y[1][0]', 'x_full[4][2]'): 278702.89312, ('y[1][0]', 'C[2][0]'): -1600.0, ('y[1][0]', 'C[0][0]'): -400.0, ('y[1][0]', 'y[0][0]'): 1000000400.0, ('y[1][0]', 'B_full[4][4]'): -16000000.0, ('y[1][0]', 'r[3][0]'): 8000000.0, ('B_full[2][4]', 'x_full[1][2]'): -139.35144656, ('B_full[2][4]', 'x_full[1][0]'): 125.60318751999998, ('B_full[2][4]', 'x_full[2][1]'): -97.43145824, ('B_full[2][4]', 'B_full[0][4]'): 4000.0, ('B_full[2][4]', 'x_full[2][2]'): -278.70289312, ('B_full[2][4]', 'B_full[3][4]'): 32000.0, ('B_full[2][4]', 'x_full[4][1]'): -389.72583296, ('B_full[2][4]', 'r[2][0]'): -16000.0, ('B_full[2][4]', 'x_full[3][1]'): -194.86291648, ('B_full[2][4]', 'r[0][0]'): -4000.0, ('B_full[2][4]', 'x_full[1][1]'): -48.71572912, ('B_full[2][4]', 'y[2][0]'): -4000000.0, ('B_full[2][4]', 'x_full[2][0]'): 251.20637503999995, ('B_full[2][4]', 'x_full[4][0]'): 1004.8255001599998, ('B_full[2][4]', 'y[3][0]'): -4000000.0, ('B_full[2][4]', 'r[1][0]'): -8000.0, ('B_full[2][4]', 'x_full[0][0]'): 62.80159375999999, ('B_full[2][4]', 'r[4][0]'): -64000.0, ('B_full[2][4]', 'y[4][0]'): -4000000.0, ('B_full[2][4]', 'x_full[3][0]'): 502.4127500799999, ('B_full[2][4]', 'x_full[0][2]'): -69.67572328, ('B_full[2][4]', 'x_full[3][2]'): -557.40578624, ('B_full[2][4]', 'x_full[4][2]'): -1114.81157248, ('B_full[2][4]', 'y[0][0]'): -4000000.0, ('B_full[2][4]', 'B_full[4][4]'): 64000.0, ('B_full[2][4]', 'r[3][0]'): -32000.0, ('B_full[2][4]', 'y[1][0]'): -4000000.0, ('A[3][0]', 'r[2][0]'): -6400.0, ('A[3][0]', 'r[0][0]'): -1600.0, ('A[3][0]', 'r[1][0]'): -3200.0, ('A[3][0]', 'r[4][0]'): -25600.0, ('A[3][0]', 'r[3][0]'): -12800.0, ('A[1][0]', 'r[2][0]'): -1600.0, ('A[1][0]', 'r[0][0]'): -400.0, ('A[1][0]', 'r[1][0]'): -800.0, ('A[1][0]', 'r[4][0]'): -6400.0, ('A[1][0]', 'r[3][0]'): -3200.0, ('A[1][0]', 'A[3][0]'): 3200.0, ('B_full[1][4]', 'x_full[1][2]'): -69.67572328, ('B_full[1][4]', 'x_full[1][0]'): 62.80159375999999, ('B_full[1][4]', 'x_full[2][1]'): -48.71572912, ('B_full[1][4]', 'B_full[0][4]'): 2000.0, ('B_full[1][4]', 'x_full[2][2]'): -139.35144656, ('B_full[1][4]', 'B_full[3][4]'): 16000.0, ('B_full[1][4]', 'x_full[4][1]'): -194.86291648, ('B_full[1][4]', 'r[2][0]'): -8000.0, ('B_full[1][4]', 'x_full[3][1]'): -97.43145824, ('B_full[1][4]', 'r[0][0]'): -2000.0, ('B_full[1][4]', 'x_full[1][1]'): -24.35786456, ('B_full[1][4]', 'y[2][0]'): -2000000.0, ('B_full[1][4]', 'x_full[2][0]'): 125.60318751999998, ('B_full[1][4]', 'x_full[4][0]'): 502.4127500799999, ('B_full[1][4]', 'y[3][0]'): -2000000.0, ('B_full[1][4]', 'r[1][0]'): -4000.0, ('B_full[1][4]', 'x_full[0][0]'): 31.400796879999994, ('B_full[1][4]', 'r[4][0]'): -32000.0, ('B_full[1][4]', 'y[4][0]'): -2000000.0, ('B_full[1][4]', 'x_full[3][0]'): 251.20637503999995, ('B_full[1][4]', 'x_full[0][2]'): -34.83786164, ('B_full[1][4]', 'x_full[3][2]'): -278.70289312, ('B_full[1][4]', 'x_full[4][2]'): -557.40578624, ('B_full[1][4]', 'y[0][0]'): -2000000.0, ('B_full[1][4]', 'B_full[4][4]'): 32000.0, ('B_full[1][4]', 'r[3][0]'): -16000.0, ('B_full[1][4]', 'y[1][0]'): -2000000.0, ('B_full[1][4]', 'B_full[2][4]'): 8000.0, ('x_full[0][1]', 'x_full[1][2]'): 2001.2529582399422, ('x_full[0][1]', 'x_full[1][0]'): 2000.0671656549314, ('x_full[0][1]', 'x_full[2][1]'): 4002.4601986908547, ('x_full[0][1]', 'B_full[0][4]'): -6.08946614, ('x_full[0][1]', 'x_full[2][2]'): 4002.5059164798845, ('x_full[0][1]', 'B_full[3][4]'): -48.71572912, ('x_full[0][1]', 'x_full[4][1]'): 16009.840794763419, ('x_full[0][1]', 'r[2][0]'): 24.35786456, ('x_full[0][1]', 'x_full[3][1]'): 8004.920397381709, ('x_full[0][1]', 'r[0][0]'): 6.08946614, ('x_full[0][1]', 'x_full[1][1]'): 2001.2300993454273, ('x_full[0][1]', 'y[2][0]'): 6089.4661399999995, ('x_full[0][1]', 'x_full[2][0]'): 4000.134331309863, ('x_full[0][1]', 'x_full[4][0]'): 16000.537325239451, ('x_full[0][1]', 'y[3][0]'): 6089.4661399999995, ('x_full[0][1]', 'r[1][0]'): 12.17893228, ('x_full[0][1]', 'x_full[0][0]'): 1000.0335828274657, ('x_full[0][1]', 'r[4][0]'): 97.43145824, ('x_full[0][1]', 'y[4][0]'): 6089.4661399999995, ('x_full[0][1]', 'x_full[3][0]'): 8000.268662619726, ('x_full[0][1]', 'x_full[0][2]'): 1000.6264791199711, ('x_full[0][1]', 'x_full[3][2]'): 8005.011832959769, ('x_full[0][1]', 'x_full[4][2]'): 16010.023665919538, ('x_full[0][1]', 'y[0][0]'): 6089.4661399999995, ('x_full[0][1]', 'B_full[4][4]'): -97.43145824, ('x_full[0][1]', 'r[3][0]'): 48.71572912, ('x_full[0][1]', 'y[1][0]'): 6089.4661399999995, ('x_full[0][1]', 'B_full[2][4]'): -24.35786456, ('x_full[0][1]', 'B_full[1][4]'): -12.17893228, ('A[2][0]', 'r[2][0]'): -3200.0, ('A[2][0]', 'r[0][0]'): -800.0, ('A[2][0]', 'r[1][0]'): -1600.0, ('A[2][0]', 'r[4][0]'): -12800.0, ('A[2][0]', 'r[3][0]'): -6400.0, ('A[2][0]', 'A[3][0]'): 6400.0, ('A[2][0]', 'A[1][0]'): 1600.0, ('A[0][0]', 'r[2][0]'): -800.0, ('A[0][0]', 'r[0][0]'): -200.0, ('A[0][0]', 'r[1][0]'): -400.0, ('A[0][0]', 'r[4][0]'): -3200.0, ('A[0][0]', 'r[3][0]'): -1600.0, ('A[0][0]', 'A[3][0]'): 1600.0, ('A[0][0]', 'A[1][0]'): 400.0, ('A[0][0]', 'A[2][0]'): 800.0, ('C[3][0]', 'y[2][0]'): -3200.0, ('C[3][0]', 'y[3][0]'): -3200.0, ('C[3][0]', 'y[4][0]'): -3200.0, ('C[3][0]', 'C[2][0]'): 12800.0, ('C[3][0]', 'C[0][0]'): 3200.0, ('C[3][0]', 'y[0][0]'): -3200.0, ('C[3][0]', 'y[1][0]'): -3200.0, ('C[1][0]', 'y[2][0]'): -800.0, ('C[1][0]', 'y[3][0]'): -800.0, ('C[1][0]', 'y[4][0]'): -800.0, ('C[1][0]', 'C[2][0]'): 3200.0, ('C[1][0]', 'C[0][0]'): 800.0, ('C[1][0]', 'y[0][0]'): -800.0, ('C[1][0]', 'y[1][0]'): -800.0, ('C[1][0]', 'C[3][0]'): 6400.0, ('C[4][0]', 'y[2][0]'): -6400.0, ('C[4][0]', 'y[3][0]'): -6400.0, ('C[4][0]', 'y[4][0]'): -6400.0, ('C[4][0]', 'C[2][0]'): 25600.0, ('C[4][0]', 'C[0][0]'): 6400.0, ('C[4][0]', 'y[0][0]'): -6400.0, ('C[4][0]', 'y[1][0]'): -6400.0, ('C[4][0]', 'C[3][0]'): 51200.0, ('C[4][0]', 'C[1][0]'): 12800.0, ('A[4][0]', 'r[2][0]'): -12800.0, ('A[4][0]', 'r[0][0]'): -3200.0, ('A[4][0]', 'r[1][0]'): -6400.0, ('A[4][0]', 'r[4][0]'): -51200.0, ('A[4][0]', 'r[3][0]'): -25600.0, ('A[4][0]', 'A[3][0]'): 25600.0, ('A[4][0]', 'A[1][0]'): 6400.0, ('A[4][0]', 'A[2][0]'): 12800.0, ('A[4][0]', 'A[0][0]'): 3200.0}, 500005912.5, 'BINARY')\n" + ] + } + ], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Add\n", + "import numpy as np\n", + "\n", + "z = 3\n", + "epsilon = 0.05\n", + "M = 1000\n", + "lambda_1 = 100\n", + "lambda_2 = 100\n", + "lambda_3 = 200\n", + "lambda_4 = 500\n", + "\n", + "D_X = 5\n", + "D_A = 5\n", + "D_B = 5\n", + "D_C = 5\n", + "D_R = 5\n", + "\n", + "\n", + "\n", + "x_full = Array.create(\"x_full\", shape=(D_X, N), vartype=\"BINARY\")\n", + "A = Array.create(\"A\", shape=(D_A, 1), vartype=\"BINARY\")\n", + "C = Array.create(\"C\", shape=(D_B, 1), vartype=\"BINARY\")\n", + "B = Array.create(\"B_full\", shape=(D_C, T), vartype=\"BINARY\")\n", + "r = Array.create(\"r\", shape=(D_R, 1), vartype=\"BINARY\")\n", + "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + "def binary2integer(x):\n", + " return sum([2**i*x[i] for i in range(len(x))])\n", + "\n", + "# Need new binary 2 integer\n", + "# Normalize the x and r_epsilon\n", + "\n", + "def create_hamiltonian(x_full, A, B, C, r, z, lambda_1, lambda_2, lambda_3, lambda_4, N, T, M, epsilon):\n", + " # Variance\n", + " variance_term = 0\n", + " for i in range(N):\n", + " for ii in range(N):\n", + " # first_term = binary2integer(x_full[:,i])\n", + " # second_term = first_term * (binary2integer(x_full[:,ii]))\n", + " # variance_term += second_term * (covariance_matrix[i][ii])\n", + " variance_term += (binary2integer(x_full[:,i])) * (binary2integer(x_full[:,ii])) * (covariance_matrix[i][ii])\n", + " # Mean\n", + " the_mean = 0\n", + " for i in range(N):\n", + " the_mean -= expected_returns[i]*binary2integer(x_full[:,i])\n", + " ### H_1\n", + " m = 0\n", + " # m += (binary2integer(r).mul(-1) + binary2integer(A).mul(1) - z)\n", + " # print(type(binary2integer(r[:,0]) * -1))\n", + " # print(type(binary2integer(A[:,0]) * 1))\n", + " m += (binary2integer(r[:,0]) * -1 + binary2integer(A[:,0]) * 1 - float(z))\n", + " # print(\"Type m before is \" + str(type(m)))\n", + " m = m**2\n", + " # print(\"Type m after is \" + str(type(m)))\n", + " H_1 = lambda_1*m\n", + " ### H2_t\n", + " H_2 = 0\n", + " for i in range(T):\n", + " the_sum = binary2integer(r[:,0])\n", + " for ii in range(N):\n", + " the_sum -= reward[ii][i]*binary2integer(x_full[:,ii])\n", + " y_terms = 0\n", + " for i in range(len(y)):\n", + " y_terms += y[i][0]\n", + " #print(type(y_terms))\n", + " #print(type(binary2integer(B[:,i])))\n", + " # print(type(((1-y_terms)*M)+1-1))\n", + " # The \"+1-1\" is to change it into an Add instance.\n", + " # For some reason Add does not have a constructor.\n", + " # And addition is not supported between an Add and a Base.\n", + " # So this silly workaround is needed to keep this working.\n", + " the_sum -= M*(1-y_terms)+1-1 + binary2integer(B[:,i])\n", + " the_sum = the_sum * the_sum\n", + " the_sum = the_sum*lambda_2\n", + " H_2 += the_sum\n", + " ### H3\n", + " y_terms = 0\n", + " for i in range(len(y)):\n", + " y_terms += y[i][0]\n", + " H_3 = (1-epsilon)*T+1-1 - y_terms + binary2integer(C[:,0])\n", + " H_3 = H_3 * H_3\n", + " H_3 = H_3 * lambda_3\n", + " ### H4\n", + " m = 0\n", + " for i in range(x_full.shape[1]):\n", + " m+= binary2integer(x_full[:,i])\n", + " m = (m - 1)**2\n", + " H_4 = lambda_4*m\n", + " ### Total\n", + " first = variance_term + the_mean\n", + " second = first + H_1\n", + " # print(type(H_2))\n", + " # print(type(second))\n", + " third = second + H_2\n", + " fourth = third + H_3\n", + " fifth = fourth + H_4\n", + " return variance_term + the_mean + H_1 + H_2 + H_3 + H_4\n", + "\n", + "H = create_hamiltonian(x_full,A,B,C,r,z,lambda_1,lambda_2,lambda_3,lambda_4,N,T,M,epsilon)\n", + "model = H.compile()\n", + "#Q = model.to_qubo()\n", + "Q = model.to_bqm()\n", + "print(Q)\n" ] }, { @@ -591,12 +762,123 @@ }, { "cell_type": "code", - "execution_count": 2, - "id": "a359a291-bbb0-4f4f-97fd-acd5f90d8eb6", + "execution_count": 15, + "id": "fbc5f36d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"\\nfrom pyqubo import Array\\nnumbers = [1,1]\\ns = Array.create('s', shape=len(numbers), vartype='BINARY')\\nH = sum(n * s for s, n in zip(s, numbers))**2\\nmodel = H.compile()\\nqubo = model.to_bqm()\\nprint(qubo) # doctest: +SKIP\\n\"" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'''\n", + "from pyqubo import Array\n", + "numbers = [1,1]\n", + "s = Array.create('s', shape=len(numbers), vartype='BINARY')\n", + "H = sum(n * s for s, n in zip(s, numbers))**2\n", + "model = H.compile()\n", + "qubo = model.to_bqm()\n", + "print(qubo) # doctest: +SKIP\n", + "'''" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e012568c", "metadata": {}, "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "f7e34fa0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "36" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# write your code here" + "-160+196" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "1b15890f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"\\n# write your code here\\nimport pyqubo\\nfrom pyqubo import Array\\nfrom dwave.samplers import SimulatedAnnealingSampler\\nsampler = SimulatedAnnealingSampler()\\n\\nimport dimod\\nbqm = dimod.generators.gnp_random_bqm(100, .5, 'BINARY')\\nsampleset = sampler.sample(bqm)\\nprint(sampleset.lowest())\\nprint(sampleset.to_pandas_dataframe())\\n\"" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'''\n", + "# write your code here\n", + "import pyqubo\n", + "from pyqubo import Array\n", + "from dwave.samplers import SimulatedAnnealingSampler\n", + "sampler = SimulatedAnnealingSampler()\n", + "\n", + "import dimod\n", + "bqm = dimod.generators.gnp_random_bqm(100, .5, 'BINARY')\n", + "sampleset = sampler.sample(bqm)\n", + "print(sampleset.lowest())\n", + "print(sampleset.to_pandas_dataframe())\n", + "'''\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "a359a291-bbb0-4f4f-97fd-acd5f90d8eb6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'A[0][0]': 1, 'A[1][0]': 1, 'A[2][0]': 0, 'A[3][0]': 0, 'B_full[1][4]': 1, 'x_full[4][1]': 0, 'A[4][0]': 1, 'B_full[0][4]': 1, 'x_full[2][2]': 0, 'B_full[2][4]': 1, 'B_full[3][4]': 1, 'y[1][0]': 1, 'x_full[4][2]': 0, 'B_full[4][4]': 0, 'r[1][0]': 0, 'C[0][0]': 0, 'r[0][0]': 0, 'C[1][0]': 0, 'r[3][0]': 0, 'C[2][0]': 0, 'x_full[0][2]': 1, 'r[2][0]': 0, 'C[3][0]': 0, 'x_full[3][2]': 0, 'C[4][0]': 0, 'r[4][0]': 1, 'x_full[0][0]': 0, 'x_full[0][1]': 0, 'x_full[2][1]': 0, 'x_full[1][0]': 0, 'x_full[2][0]': 0, 'x_full[1][1]': 0, 'x_full[1][2]': 0, 'x_full[3][0]': 0, 'x_full[3][1]': 0, 'x_full[4][0]': 0, 'y[0][0]': 0, 'y[2][0]': 0, 'y[3][0]': 0, 'y[4][0]': 0}\n", + "\n" + ] + } + ], + "source": [ + "import pyqubo\n", + "from pyqubo import Array\n", + "from dwave.samplers import SimulatedAnnealingSampler\n", + "sampler = SimulatedAnnealingSampler()\n", + "\n", + "# sampleset = sampler.sample_qubo(Q)\n", + "sampleset = sampler.sample(Q)\n", + "samples = model.decode_sampleset(sampleset)\n", + "best_sample = min(samples, key=lambda s: s.energy)\n", + "print(best_sample.sample)\n", + "\n", + "print(type(best_sample.sample))\n" ] }, { @@ -625,7 +907,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 19, "id": "95761549", "metadata": {}, "outputs": [], @@ -649,7 +931,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "id": "7c6d5972", "metadata": {}, "outputs": [], @@ -747,7 +1029,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "id": "aa60be68", "metadata": {}, "outputs": [], @@ -775,7 +1057,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "id": "067cea0c", "metadata": {}, "outputs": [], @@ -849,7 +1131,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.11.7" } }, "nbformat": 4, From a552bb95d292aee50d559dda56d21698274e7cb5 Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 02:51:25 -0500 Subject: [PATCH 06/20] Debug issues --- moodys_challenge.ipynb | 48 +++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index d6055c3..63c1f3c 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -36,24 +36,6 @@ "**5.** Use of simulated annealing and other quantum computing techniques to find the solution of the problem. " ] }, - { - "cell_type": "code", - "execution_count": 1, - "id": "15770539", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "moodys_challenge.ipynb\tREADME.md returns_data.txt\n" - ] - } - ], - "source": [ - "!ls" - ] - }, { "cell_type": "markdown", "id": "85d339f8-0a2d-4c84-88d2-ae2f5d7e7597", @@ -417,11 +399,9 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "id": "ec015dad-7449-433c-bd44-593e5fecb4ec", "metadata": {}, - "outputs": [], "source": [ "
NOTE:\n", "L has the following form: $L=\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}-\\sum_{k=1}^{n}\\mu_{k}x_{k}+\\textit{penalty terms}$, with the penalty terms to be found in this section (and include all the equalities and inequalities of the problem). This will need to be mapped to a quantum Hamiltonian in the following section. Each penalty term will correspond to one lagrange multiplier that will need to be fine-tuned in the next sections, so that the constraints are imposed.\n", @@ -430,7 +410,6 @@ }, { "cell_type": "markdown", -<<<<<<< HEAD "id": "23645c2f-0916-477e-9cf8-2ecb6ed114d1", "metadata": {}, "source": [ @@ -447,8 +426,6 @@ }, { "cell_type": "markdown", -======= ->>>>>>> aa88a63b665ef55ef6a4936a3ac6f5afb16eb4a5 "id": "bb2982bd-fbb9-49de-8dbc-370e9390173b", "metadata": {}, "source": [ @@ -876,7 +853,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 39, "id": "a359a291-bbb0-4f4f-97fd-acd5f90d8eb6", "metadata": {}, "outputs": [ @@ -884,8 +861,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'A[0][0]': 1, 'A[1][0]': 1, 'A[2][0]': 0, 'A[3][0]': 0, 'B_full[1][4]': 1, 'x_full[4][1]': 0, 'A[4][0]': 1, 'B_full[0][4]': 1, 'x_full[2][2]': 0, 'B_full[2][4]': 1, 'B_full[3][4]': 1, 'y[1][0]': 1, 'x_full[4][2]': 0, 'B_full[4][4]': 0, 'r[1][0]': 0, 'C[0][0]': 0, 'r[0][0]': 0, 'C[1][0]': 0, 'r[3][0]': 0, 'C[2][0]': 0, 'x_full[0][2]': 1, 'r[2][0]': 0, 'C[3][0]': 0, 'x_full[3][2]': 0, 'C[4][0]': 0, 'r[4][0]': 1, 'x_full[0][0]': 0, 'x_full[0][1]': 0, 'x_full[2][1]': 0, 'x_full[1][0]': 0, 'x_full[2][0]': 0, 'x_full[1][1]': 0, 'x_full[1][2]': 0, 'x_full[3][0]': 0, 'x_full[3][1]': 0, 'x_full[4][0]': 0, 'y[0][0]': 0, 'y[2][0]': 0, 'y[3][0]': 0, 'y[4][0]': 0}\n", - "\n" + "{'A[0][0]': 0, 'A[1][0]': 1, 'A[2][0]': 0, 'A[3][0]': 0, 'B_full[1][4]': 0, 'x_full[4][1]': 0, 'A[4][0]': 1, 'B_full[0][4]': 0, 'x_full[2][2]': 0, 'B_full[2][4]': 0, 'B_full[3][4]': 0, 'y[1][0]': 0, 'x_full[4][2]': 0, 'B_full[4][4]': 1, 'r[1][0]': 1, 'C[0][0]': 0, 'r[0][0]': 1, 'C[1][0]': 0, 'r[3][0]': 1, 'C[2][0]': 0, 'x_full[0][2]': 0, 'r[2][0]': 1, 'C[3][0]': 0, 'x_full[3][2]': 0, 'C[4][0]': 0, 'r[4][0]': 0, 'x_full[0][0]': 0, 'x_full[0][1]': 1, 'x_full[2][1]': 0, 'x_full[1][0]': 0, 'x_full[2][0]': 0, 'x_full[1][1]': 0, 'x_full[1][2]': 0, 'x_full[3][0]': 0, 'x_full[3][1]': 0, 'x_full[4][0]': 0, 'y[0][0]': 0, 'y[2][0]': 0, 'y[3][0]': 1, 'y[4][0]': 0}\n", + "\n", + "['x_full[0][0]', 'x_full[0][1]', 'x_full[0][2]', 'x_full[1][0]', 'x_full[1][1]', 'x_full[1][2]', 'x_full[2][0]', 'x_full[2][1]', 'x_full[2][2]', 'x_full[3][0]', 'x_full[3][1]', 'x_full[3][2]', 'x_full[4][0]', 'x_full[4][1]', 'x_full[4][2]']\n" ] } ], @@ -901,7 +879,19 @@ "best_sample = min(samples, key=lambda s: s.energy)\n", "print(best_sample.sample)\n", "\n", - "print(type(best_sample.sample))\n" + "print(type(best_sample.sample))\n", + "x_full_stuff = []\n", + "a_stuff = []\n", + "b_stuff = []\n", + "c_stuff = []\n", + "for key, value in best_sample.sample.items():\n", + " if key[0] == \"x\":\n", + " x_full_stuff.append(key)\n", + "x_full_stuff.sort()\n", + "print(x_full_stuff)\n", + "\n", + "\n", + "def reassemble_x_weights():\n" ] }, { @@ -1140,7 +1130,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 [Default]", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, From 0f50ede50a2fc04f5f084b7b2094685e62043d1d Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 03:09:54 -0500 Subject: [PATCH 07/20] Debugging Stuff --- moodys_challenge.ipynb | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 63c1f3c..860ff54 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -332,7 +332,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 1, "id": "a78f8aea", "metadata": {}, "outputs": [ @@ -350,7 +350,15 @@ "name": "stderr", "output_type": "stream", "text": [ - "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_11496\\3824432534.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_28652\\3824432534.py:2: DeprecationWarning: \n", + "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", + "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", + "but was not found to be installed on your system.\n", + "If this would cause problems for you,\n", + "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", + " \n", + " import pandas as pd\n", + "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_28652\\3824432534.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" ] } @@ -501,7 +509,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -551,7 +559,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 3, "id": "553d8b31", "metadata": {}, "outputs": [ @@ -762,7 +770,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 4, "id": "fbc5f36d", "metadata": {}, "outputs": [ @@ -772,7 +780,7 @@ "\"\\nfrom pyqubo import Array\\nnumbers = [1,1]\\ns = Array.create('s', shape=len(numbers), vartype='BINARY')\\nH = sum(n * s for s, n in zip(s, numbers))**2\\nmodel = H.compile()\\nqubo = model.to_bqm()\\nprint(qubo) # doctest: +SKIP\\n\"" ] }, - "execution_count": 15, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -799,7 +807,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 5, "id": "f7e34fa0", "metadata": {}, "outputs": [ @@ -809,7 +817,7 @@ "36" ] }, - "execution_count": 16, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -820,7 +828,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 6, "id": "1b15890f", "metadata": {}, "outputs": [ @@ -830,7 +838,7 @@ "\"\\n# write your code here\\nimport pyqubo\\nfrom pyqubo import Array\\nfrom dwave.samplers import SimulatedAnnealingSampler\\nsampler = SimulatedAnnealingSampler()\\n\\nimport dimod\\nbqm = dimod.generators.gnp_random_bqm(100, .5, 'BINARY')\\nsampleset = sampler.sample(bqm)\\nprint(sampleset.lowest())\\nprint(sampleset.to_pandas_dataframe())\\n\"" ] }, - "execution_count": 17, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -853,7 +861,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 7, "id": "a359a291-bbb0-4f4f-97fd-acd5f90d8eb6", "metadata": {}, "outputs": [ @@ -861,7 +869,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'A[0][0]': 0, 'A[1][0]': 1, 'A[2][0]': 0, 'A[3][0]': 0, 'B_full[1][4]': 0, 'x_full[4][1]': 0, 'A[4][0]': 1, 'B_full[0][4]': 0, 'x_full[2][2]': 0, 'B_full[2][4]': 0, 'B_full[3][4]': 0, 'y[1][0]': 0, 'x_full[4][2]': 0, 'B_full[4][4]': 1, 'r[1][0]': 1, 'C[0][0]': 0, 'r[0][0]': 1, 'C[1][0]': 0, 'r[3][0]': 1, 'C[2][0]': 0, 'x_full[0][2]': 0, 'r[2][0]': 1, 'C[3][0]': 0, 'x_full[3][2]': 0, 'C[4][0]': 0, 'r[4][0]': 0, 'x_full[0][0]': 0, 'x_full[0][1]': 1, 'x_full[2][1]': 0, 'x_full[1][0]': 0, 'x_full[2][0]': 0, 'x_full[1][1]': 0, 'x_full[1][2]': 0, 'x_full[3][0]': 0, 'x_full[3][1]': 0, 'x_full[4][0]': 0, 'y[0][0]': 0, 'y[2][0]': 0, 'y[3][0]': 1, 'y[4][0]': 0}\n", + "{'A[0][0]': 0, 'A[1][0]': 1, 'A[2][0]': 1, 'A[3][0]': 1, 'B_full[1][4]': 1, 'x_full[4][1]': 0, 'A[4][0]': 0, 'B_full[0][4]': 1, 'x_full[2][2]': 0, 'B_full[2][4]': 0, 'B_full[3][4]': 1, 'y[1][0]': 0, 'x_full[4][2]': 0, 'B_full[4][4]': 0, 'r[1][0]': 1, 'C[0][0]': 0, 'r[0][0]': 1, 'C[1][0]': 0, 'r[3][0]': 1, 'C[2][0]': 0, 'x_full[0][2]': 0, 'r[2][0]': 0, 'C[3][0]': 0, 'x_full[3][2]': 0, 'C[4][0]': 0, 'r[4][0]': 0, 'x_full[0][0]': 1, 'x_full[0][1]': 0, 'x_full[2][1]': 0, 'x_full[1][0]': 0, 'x_full[2][0]': 0, 'x_full[1][1]': 0, 'x_full[1][2]': 0, 'x_full[3][0]': 0, 'x_full[3][1]': 0, 'x_full[4][0]': 0, 'y[0][0]': 1, 'y[2][0]': 0, 'y[3][0]': 0, 'y[4][0]': 0}\n", "\n", "['x_full[0][0]', 'x_full[0][1]', 'x_full[0][2]', 'x_full[1][0]', 'x_full[1][1]', 'x_full[1][2]', 'x_full[2][0]', 'x_full[2][1]', 'x_full[2][2]', 'x_full[3][0]', 'x_full[3][1]', 'x_full[3][2]', 'x_full[4][0]', 'x_full[4][1]', 'x_full[4][2]']\n" ] @@ -891,7 +899,7 @@ "print(x_full_stuff)\n", "\n", "\n", - "def reassemble_x_weights():\n" + "# def reassemble_x_weights():\n" ] }, { @@ -920,7 +928,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 8, "id": "95761549", "metadata": {}, "outputs": [], @@ -944,7 +952,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 9, "id": "7c6d5972", "metadata": {}, "outputs": [], @@ -1042,7 +1050,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 10, "id": "aa60be68", "metadata": {}, "outputs": [], @@ -1070,7 +1078,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 11, "id": "067cea0c", "metadata": {}, "outputs": [], From b0d55fa1ae3379c3198ab853c16039f5d6937ad1 Mon Sep 17 00:00:00 2001 From: Ashrit Date: Sun, 4 Feb 2024 08:10:20 +0000 Subject: [PATCH 08/20] random weights lol --- .../random_weights-checkpoint.ipynb | 108 ++++++++++++++++++ random_weights.ipynb | 108 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 .ipynb_checkpoints/random_weights-checkpoint.ipynb create mode 100644 random_weights.ipynb diff --git a/.ipynb_checkpoints/random_weights-checkpoint.ipynb b/.ipynb_checkpoints/random_weights-checkpoint.ipynb new file mode 100644 index 0000000..c2965d7 --- /dev/null +++ b/.ipynb_checkpoints/random_weights-checkpoint.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6605f21a-f3db-4919-a3fd-abf4649a9fbd", + "metadata": {}, + "source": [ + "In order to find the constant z for our constraint equations, we need to derive several random datasets to get an idea of what the value-at-risk typically is for most portfolios. To do this, we need 2 things:\n", + "To create a function that can generate random weights for the 32 assets, such that the sum of all the weights are equal to 1 and that all weights are positive:\n", + "To take the results of this function and get the return by following the procedure in step 4:\n", + "To then analyze several runs of this function and come up with a rough idea of what z should be." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b72fcc66-3685-4bc4-9f9c-6f39866ad7e4", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "54b02392-a341-4b3b-850a-59510a7fdfdd", + "metadata": {}, + "source": [ + "Function w random weights for 32 assets. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "28cddbc4-eca5-4f77-9319-2ca5a320a9a6", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([0.02145949, 0.00456517, 0.00902196, 0.01646961, 0.02766029,\n", + " 0.02103205, 0.0039472 , 0.01579856, 0.02687757, 0.06383004,\n", + " 0.0754687 , 0.0664405 , 0.05007134, 0.01548443, 0.07385879,\n", + " 0.01271008, 0.00995356, 0.06317959, 0.05776948, 0.01503632,\n", + " 0.01587735, 0.0581431 , 0.0447636 , 0.00933955, 0.00324077,\n", + " 0.03831576, 0.01376532, 0.05882531, 0.02058008, 0.01440161,\n", + " 0.06448786, 0.00762494]),\n", + " 0.9999999999999999)" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "def generate_random_weights(n_assets):\n", + " # Generate random numbers\n", + " random_numbers = np.random.random(n_assets)\n", + " # Normalize so that the sum is 1\n", + " weights = random_numbers / sum(random_numbers)\n", + " return weights\n", + "\n", + "# Test the function with 32 assets\n", + "n_assets = 32\n", + "random_weights = generate_random_weights(n_assets)\n", + "random_weights, sum(random_weights)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24f56d7d-d157-43b1-bc22-f142be689f8f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "98e184d1-2edf-41da-adf2-04376b1c7a93", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 [Moody's]", + "language": "python", + "name": "python3_moodys_xbto4j" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/random_weights.ipynb b/random_weights.ipynb new file mode 100644 index 0000000..c2965d7 --- /dev/null +++ b/random_weights.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6605f21a-f3db-4919-a3fd-abf4649a9fbd", + "metadata": {}, + "source": [ + "In order to find the constant z for our constraint equations, we need to derive several random datasets to get an idea of what the value-at-risk typically is for most portfolios. To do this, we need 2 things:\n", + "To create a function that can generate random weights for the 32 assets, such that the sum of all the weights are equal to 1 and that all weights are positive:\n", + "To take the results of this function and get the return by following the procedure in step 4:\n", + "To then analyze several runs of this function and come up with a rough idea of what z should be." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b72fcc66-3685-4bc4-9f9c-6f39866ad7e4", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "54b02392-a341-4b3b-850a-59510a7fdfdd", + "metadata": {}, + "source": [ + "Function w random weights for 32 assets. " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "28cddbc4-eca5-4f77-9319-2ca5a320a9a6", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([0.02145949, 0.00456517, 0.00902196, 0.01646961, 0.02766029,\n", + " 0.02103205, 0.0039472 , 0.01579856, 0.02687757, 0.06383004,\n", + " 0.0754687 , 0.0664405 , 0.05007134, 0.01548443, 0.07385879,\n", + " 0.01271008, 0.00995356, 0.06317959, 0.05776948, 0.01503632,\n", + " 0.01587735, 0.0581431 , 0.0447636 , 0.00933955, 0.00324077,\n", + " 0.03831576, 0.01376532, 0.05882531, 0.02058008, 0.01440161,\n", + " 0.06448786, 0.00762494]),\n", + " 0.9999999999999999)" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "def generate_random_weights(n_assets):\n", + " # Generate random numbers\n", + " random_numbers = np.random.random(n_assets)\n", + " # Normalize so that the sum is 1\n", + " weights = random_numbers / sum(random_numbers)\n", + " return weights\n", + "\n", + "# Test the function with 32 assets\n", + "n_assets = 32\n", + "random_weights = generate_random_weights(n_assets)\n", + "random_weights, sum(random_weights)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24f56d7d-d157-43b1-bc22-f142be689f8f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "98e184d1-2edf-41da-adf2-04376b1c7a93", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 [Moody's]", + "language": "python", + "name": "python3_moodys_xbto4j" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From be51fe11bd22c19b4ba3c993fc8b3f33f9416a52 Mon Sep 17 00:00:00 2001 From: Ashrit Date: Sun, 4 Feb 2024 08:33:33 +0000 Subject: [PATCH 09/20] before shit crashes again --- .ipynb_checkpoints/deriveZ-checkpoint.ipynb | 287 ++++++++++++++++++++ deriveZ.ipynb | 287 ++++++++++++++++++++ moodys_challenge.ipynb | 64 ++++- 3 files changed, 625 insertions(+), 13 deletions(-) create mode 100644 .ipynb_checkpoints/deriveZ-checkpoint.ipynb create mode 100644 deriveZ.ipynb diff --git a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb new file mode 100644 index 0000000..0572d53 --- /dev/null +++ b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb @@ -0,0 +1,287 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "39502968-c6bb-4b52-8a6e-bc58fb5d8cc3", + "metadata": {}, + "source": [ + "Deriving z." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_512/3483040339.py:4: DeprecationWarning: \n", + "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", + "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", + "but was not found to be installed on your system.\n", + "If this would cause problems for you,\n", + "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", + " \n", + " import pandas as pd\n", + "/tmp/ipykernel_512/3483040339.py:10: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + ] + } + ], + "source": [ + "# modifying \"reading in financial data\" to 32 assets and right T value\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# You may choose to select different parameters/values\n", + "number_assets = 32\n", + "T = 392\n", + "# Read returns\n", + "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "\n", + "Rraw = df.values.T\n", + "\n", + "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", + "R = Rraw[:number_assets,:T]\n", + "\n", + "# Expected return of each asset\n", + "expected_returns = np.mean(R, axis = 1)\n", + "\n", + "# Covariance matrix of asset returns\n", + "covariance_matrix = np.cov(R)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "1454b6b3-e891-4511-8c78-f8396fedce42", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([5.09966343e-02, 4.53727733e-03, 1.00000515e-02, 2.29285930e-02,\n", + " 3.09233780e-02, 1.15785783e-03, 4.23591284e-02, 5.60462282e-02,\n", + " 4.28687344e-02, 2.88422553e-06, 9.16611045e-03, 2.08512285e-02,\n", + " 1.74773246e-02, 4.98874449e-02, 3.34892557e-02, 2.18674602e-02,\n", + " 1.30252699e-02, 2.88221387e-02, 3.96564751e-02, 5.59792135e-02,\n", + " 4.05992617e-02, 1.04650265e-02, 5.25108242e-02, 3.42191975e-02,\n", + " 9.83692217e-03, 5.60762032e-02, 2.96385619e-02, 5.40760816e-02,\n", + " 4.28708190e-02, 3.60760968e-02, 2.97263551e-02, 5.18619619e-02]),\n", + " 0.9999999999999999)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# import random weight function from random_weights.ipynb\n", + "\n", + "def generate_random_weights(number_of_assets):\n", + " weights = np.random.rand(number_of_assets) # Generate random numbers\n", + " weights /= np.sum(weights) # Normalize so that the sum is 1\n", + " return weights\n", + "\n", + "# Test the function with 32 assets\n", + "n_assets = 32\n", + "random_weights = generate_random_weights(n_assets)\n", + "random_weights, sum(random_weights)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Redefining the calculate_historical_VaR function\n", + "def calculate_historical_VaR(weights, mu_R, confidence_level=0.95):\n", + " portfolio_returns = np.dot(mu_R, np.transpose(np.array(weights)))\n", + " VaR = np.percentile(portfolio_returns, 100 * (1 - confidence_level))\n", + " return -VaR\n", + "\n", + "# Define a function to simulate the random generation of weights and calculation of VaR\n", + "def simulate_VaR(num_simulations, num_assets, mu_R, confidence_level=0.95):\n", + " VaR_results = []\n", + " weights_list = []\n", + "\n", + " for _ in range(num_simulations):\n", + " weights = generate_random_weights(num_assets)\n", + " VaR = calculate_historical_VaR(weights, mu_R, confidence_level)\n", + " VaR_results.append(VaR)\n", + " weights_list.append(weights)\n", + " \n", + " return VaR_results, weights_list" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "([0.33478069678017486,\n", + " 0.31380399284909316,\n", + " 0.33764572699909956,\n", + " 0.3552637321435413,\n", + " 0.3078384247834268],\n", + " [array([0.03563346, 0.04352192, 0.02860185, 0.05366071, 0.00273621,\n", + " 0.01050274, 0.03983288, 0.00543957, 0.037661 , 0.00371156,\n", + " 0.05034161, 0.00014823, 0.00145112, 0.03447267, 0.04733192,\n", + " 0.01874436, 0.01923075, 0.04976332, 0.03784558, 0.05275445,\n", + " 0.05605801, 0.05505757, 0.03344434, 0.03201722, 0.02835223,\n", + " 0.019846 , 0.0409556 , 0.05237392, 0.03053973, 0.04146229,\n", + " 0.01626037, 0.02024677]),\n", + " array([0.04687872, 0.032786 , 0.0125963 , 0.03430151, 0.0162518 ,\n", + " 0.0082743 , 0.02358875, 0.04415627, 0.0217607 , 0.04591394,\n", + " 0.04507806, 0.04419739, 0.04813156, 0.00641797, 0.01725279,\n", + " 0.00813625, 0.03020361, 0.04028722, 0.05129885, 0.04795799,\n", + " 0.02350275, 0.01127891, 0.00113751, 0.04436431, 0.04858859,\n", + " 0.02265057, 0.02337002, 0.0431164 , 0.0352238 , 0.04258584,\n", + " 0.04689514, 0.03181619]),\n", + " array([0.0211461 , 0.0529119 , 0.07285264, 0.00050029, 0.03326014,\n", + " 0.02911207, 0.00682674, 0.05442366, 0.04658641, 0.0189938 ,\n", + " 0.07284515, 0.03484071, 0.05311161, 0.01557952, 0.01951858,\n", + " 0.01420304, 0.02244713, 0.06186267, 0.00335635, 0.03571577,\n", + " 0.02570347, 0.03125191, 0.03170835, 0.00690012, 0.01399259,\n", + " 0.02294648, 0.00574843, 0.05621745, 0.0628539 , 0.06235272,\n", + " 0.00371388, 0.00651645]),\n", + " array([0.01424482, 0.06156012, 0.00174449, 0.00731834, 0.05656582,\n", + " 0.01016888, 0.0442818 , 0.0469028 , 0.01712657, 0.0605412 ,\n", + " 0.0334373 , 0.00687328, 0.02064063, 0.02578439, 0.06548179,\n", + " 0.03952896, 0.0617938 , 0.03850067, 0.02258279, 0.00804128,\n", + " 0.00663337, 0.0614951 , 0.0117445 , 0.06548832, 0.01967127,\n", + " 0.01175255, 0.05695005, 0.00917514, 0.02767445, 0.00419278,\n", + " 0.04953954, 0.03256321]),\n", + " array([0.0132437 , 0.02751263, 0.03705897, 0.04919671, 0.03761503,\n", + " 0.04895974, 0.0034073 , 0.02950381, 0.04254487, 0.0370143 ,\n", + " 0.04606661, 0.00758007, 0.03064939, 0.04778214, 0.03431231,\n", + " 0.04050375, 0.04726696, 0.00553647, 0.00418306, 0.0428012 ,\n", + " 0.04052225, 0.03519681, 0.02109712, 0.01979697, 0.04047861,\n", + " 0.0453971 , 0.03042294, 0.02994999, 0.03816592, 0.03182084,\n", + " 0.03069817, 0.00371424])])" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Example usage\n", + "num_simulations = 1000 # The number of times we want to run the simulation\n", + "num_assets = 32 # Number of assets in the portfolio\n", + "\n", + "# We need the mu_R matrix to run the simulations which should be the historical returns matrix\n", + "# For the sake of the example, let's create a dummy mu_R with random values\n", + "# Normally, you would use actual historical returns data for your assets here\n", + "mu_R = np.random.randn(1000, num_assets) # 1000 scenarios, 32 assets\n", + "\n", + "# Run the simulation\n", + "VaR_results, weights_list = simulate_VaR(num_simulations, num_assets, mu_R)\n", + "\n", + "# Display the first few results to ensure it's working\n", + "VaR_results[:5], weights_list[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "generate_random_weights() takes 1 positional argument but 2 were given", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[12], line 14\u001b[0m\n\u001b[1;32m 11\u001b[0m mu_R \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mrandom\u001b[38;5;241m.\u001b[39mrandn(\u001b[38;5;241m1000\u001b[39m, number_of_assets) \u001b[38;5;66;03m# This should be the historical returns data for your assets\u001b[39;00m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;66;03m# Generate the random weights for the portfolios\u001b[39;00m\n\u001b[0;32m---> 14\u001b[0m random_weights \u001b[38;5;241m=\u001b[39m \u001b[43mgenerate_random_weights\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumber_of_assets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnumber_of_portfolios\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;66;03m# Calculate the VaR for each portfolio\u001b[39;00m\n\u001b[1;32m 17\u001b[0m VaRs \u001b[38;5;241m=\u001b[39m []\n", + "\u001b[0;31mTypeError\u001b[0m: generate_random_weights() takes 1 positional argument but 2 were given" + ] + } + ], + "source": [ + "# let's now actually calculate z\n", + "\n", + "\n", + "# Parameters\n", + "number_of_assets = 32\n", + "number_of_portfolios = 1000 # The number of random portfolios you want to generate\n", + "confidence_level = 0.95\n", + "\n", + "# Assuming mu_R is matrix of historical returns for the assets\n", + "# Replace this with our actual historical returns data\n", + "mu_R = np.random.randn(1000, number_of_assets) # This should be the historical returns data for your assets\n", + "\n", + "# Generate the random weights for the portfolios\n", + "random_weights = generate_random_weights(number_of_assets, number_of_portfolios)\n", + "\n", + "# Calculate the VaR for each portfolio\n", + "VaRs = []\n", + "for weights in random_weights:\n", + " VaR = calculate_historical_VaR(weights, mu_R, confidence_level)\n", + " VaRs.append(VaR)\n", + "\n", + "# Find the z value for your constraint equations\n", + "# This could be the mean VaR, median VaR, or some other statistic that you determine is appropriate for your needs\n", + "mean_VaR = np.mean(VaRs)\n", + "median_VaR = np.median(VaRs)\n", + "z = mean_VaR # or median_VaR, depending on your specific requirements\n", + "\n", + "print(f\"The mean VaR at a {confidence_level*100}% confidence level is: {mean_VaR}\")\n", + "print(f\"The median VaR at a {confidence_level*100}% confidence level is: {median_VaR}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96b1afa0-a9b9-4f7c-b891-6dbc0eedf3df", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 [Moody's]", + "language": "python", + "name": "python3_moodys_xbto4j" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/deriveZ.ipynb b/deriveZ.ipynb new file mode 100644 index 0000000..0572d53 --- /dev/null +++ b/deriveZ.ipynb @@ -0,0 +1,287 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "39502968-c6bb-4b52-8a6e-bc58fb5d8cc3", + "metadata": {}, + "source": [ + "Deriving z." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_512/3483040339.py:4: DeprecationWarning: \n", + "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", + "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", + "but was not found to be installed on your system.\n", + "If this would cause problems for you,\n", + "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", + " \n", + " import pandas as pd\n", + "/tmp/ipykernel_512/3483040339.py:10: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + ] + } + ], + "source": [ + "# modifying \"reading in financial data\" to 32 assets and right T value\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# You may choose to select different parameters/values\n", + "number_assets = 32\n", + "T = 392\n", + "# Read returns\n", + "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "\n", + "Rraw = df.values.T\n", + "\n", + "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", + "R = Rraw[:number_assets,:T]\n", + "\n", + "# Expected return of each asset\n", + "expected_returns = np.mean(R, axis = 1)\n", + "\n", + "# Covariance matrix of asset returns\n", + "covariance_matrix = np.cov(R)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "1454b6b3-e891-4511-8c78-f8396fedce42", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([5.09966343e-02, 4.53727733e-03, 1.00000515e-02, 2.29285930e-02,\n", + " 3.09233780e-02, 1.15785783e-03, 4.23591284e-02, 5.60462282e-02,\n", + " 4.28687344e-02, 2.88422553e-06, 9.16611045e-03, 2.08512285e-02,\n", + " 1.74773246e-02, 4.98874449e-02, 3.34892557e-02, 2.18674602e-02,\n", + " 1.30252699e-02, 2.88221387e-02, 3.96564751e-02, 5.59792135e-02,\n", + " 4.05992617e-02, 1.04650265e-02, 5.25108242e-02, 3.42191975e-02,\n", + " 9.83692217e-03, 5.60762032e-02, 2.96385619e-02, 5.40760816e-02,\n", + " 4.28708190e-02, 3.60760968e-02, 2.97263551e-02, 5.18619619e-02]),\n", + " 0.9999999999999999)" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# import random weight function from random_weights.ipynb\n", + "\n", + "def generate_random_weights(number_of_assets):\n", + " weights = np.random.rand(number_of_assets) # Generate random numbers\n", + " weights /= np.sum(weights) # Normalize so that the sum is 1\n", + " return weights\n", + "\n", + "# Test the function with 32 assets\n", + "n_assets = 32\n", + "random_weights = generate_random_weights(n_assets)\n", + "random_weights, sum(random_weights)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Redefining the calculate_historical_VaR function\n", + "def calculate_historical_VaR(weights, mu_R, confidence_level=0.95):\n", + " portfolio_returns = np.dot(mu_R, np.transpose(np.array(weights)))\n", + " VaR = np.percentile(portfolio_returns, 100 * (1 - confidence_level))\n", + " return -VaR\n", + "\n", + "# Define a function to simulate the random generation of weights and calculation of VaR\n", + "def simulate_VaR(num_simulations, num_assets, mu_R, confidence_level=0.95):\n", + " VaR_results = []\n", + " weights_list = []\n", + "\n", + " for _ in range(num_simulations):\n", + " weights = generate_random_weights(num_assets)\n", + " VaR = calculate_historical_VaR(weights, mu_R, confidence_level)\n", + " VaR_results.append(VaR)\n", + " weights_list.append(weights)\n", + " \n", + " return VaR_results, weights_list" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "([0.33478069678017486,\n", + " 0.31380399284909316,\n", + " 0.33764572699909956,\n", + " 0.3552637321435413,\n", + " 0.3078384247834268],\n", + " [array([0.03563346, 0.04352192, 0.02860185, 0.05366071, 0.00273621,\n", + " 0.01050274, 0.03983288, 0.00543957, 0.037661 , 0.00371156,\n", + " 0.05034161, 0.00014823, 0.00145112, 0.03447267, 0.04733192,\n", + " 0.01874436, 0.01923075, 0.04976332, 0.03784558, 0.05275445,\n", + " 0.05605801, 0.05505757, 0.03344434, 0.03201722, 0.02835223,\n", + " 0.019846 , 0.0409556 , 0.05237392, 0.03053973, 0.04146229,\n", + " 0.01626037, 0.02024677]),\n", + " array([0.04687872, 0.032786 , 0.0125963 , 0.03430151, 0.0162518 ,\n", + " 0.0082743 , 0.02358875, 0.04415627, 0.0217607 , 0.04591394,\n", + " 0.04507806, 0.04419739, 0.04813156, 0.00641797, 0.01725279,\n", + " 0.00813625, 0.03020361, 0.04028722, 0.05129885, 0.04795799,\n", + " 0.02350275, 0.01127891, 0.00113751, 0.04436431, 0.04858859,\n", + " 0.02265057, 0.02337002, 0.0431164 , 0.0352238 , 0.04258584,\n", + " 0.04689514, 0.03181619]),\n", + " array([0.0211461 , 0.0529119 , 0.07285264, 0.00050029, 0.03326014,\n", + " 0.02911207, 0.00682674, 0.05442366, 0.04658641, 0.0189938 ,\n", + " 0.07284515, 0.03484071, 0.05311161, 0.01557952, 0.01951858,\n", + " 0.01420304, 0.02244713, 0.06186267, 0.00335635, 0.03571577,\n", + " 0.02570347, 0.03125191, 0.03170835, 0.00690012, 0.01399259,\n", + " 0.02294648, 0.00574843, 0.05621745, 0.0628539 , 0.06235272,\n", + " 0.00371388, 0.00651645]),\n", + " array([0.01424482, 0.06156012, 0.00174449, 0.00731834, 0.05656582,\n", + " 0.01016888, 0.0442818 , 0.0469028 , 0.01712657, 0.0605412 ,\n", + " 0.0334373 , 0.00687328, 0.02064063, 0.02578439, 0.06548179,\n", + " 0.03952896, 0.0617938 , 0.03850067, 0.02258279, 0.00804128,\n", + " 0.00663337, 0.0614951 , 0.0117445 , 0.06548832, 0.01967127,\n", + " 0.01175255, 0.05695005, 0.00917514, 0.02767445, 0.00419278,\n", + " 0.04953954, 0.03256321]),\n", + " array([0.0132437 , 0.02751263, 0.03705897, 0.04919671, 0.03761503,\n", + " 0.04895974, 0.0034073 , 0.02950381, 0.04254487, 0.0370143 ,\n", + " 0.04606661, 0.00758007, 0.03064939, 0.04778214, 0.03431231,\n", + " 0.04050375, 0.04726696, 0.00553647, 0.00418306, 0.0428012 ,\n", + " 0.04052225, 0.03519681, 0.02109712, 0.01979697, 0.04047861,\n", + " 0.0453971 , 0.03042294, 0.02994999, 0.03816592, 0.03182084,\n", + " 0.03069817, 0.00371424])])" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Example usage\n", + "num_simulations = 1000 # The number of times we want to run the simulation\n", + "num_assets = 32 # Number of assets in the portfolio\n", + "\n", + "# We need the mu_R matrix to run the simulations which should be the historical returns matrix\n", + "# For the sake of the example, let's create a dummy mu_R with random values\n", + "# Normally, you would use actual historical returns data for your assets here\n", + "mu_R = np.random.randn(1000, num_assets) # 1000 scenarios, 32 assets\n", + "\n", + "# Run the simulation\n", + "VaR_results, weights_list = simulate_VaR(num_simulations, num_assets, mu_R)\n", + "\n", + "# Display the first few results to ensure it's working\n", + "VaR_results[:5], weights_list[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "ename": "TypeError", + "evalue": "generate_random_weights() takes 1 positional argument but 2 were given", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[12], line 14\u001b[0m\n\u001b[1;32m 11\u001b[0m mu_R \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mrandom\u001b[38;5;241m.\u001b[39mrandn(\u001b[38;5;241m1000\u001b[39m, number_of_assets) \u001b[38;5;66;03m# This should be the historical returns data for your assets\u001b[39;00m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;66;03m# Generate the random weights for the portfolios\u001b[39;00m\n\u001b[0;32m---> 14\u001b[0m random_weights \u001b[38;5;241m=\u001b[39m \u001b[43mgenerate_random_weights\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumber_of_assets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnumber_of_portfolios\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;66;03m# Calculate the VaR for each portfolio\u001b[39;00m\n\u001b[1;32m 17\u001b[0m VaRs \u001b[38;5;241m=\u001b[39m []\n", + "\u001b[0;31mTypeError\u001b[0m: generate_random_weights() takes 1 positional argument but 2 were given" + ] + } + ], + "source": [ + "# let's now actually calculate z\n", + "\n", + "\n", + "# Parameters\n", + "number_of_assets = 32\n", + "number_of_portfolios = 1000 # The number of random portfolios you want to generate\n", + "confidence_level = 0.95\n", + "\n", + "# Assuming mu_R is matrix of historical returns for the assets\n", + "# Replace this with our actual historical returns data\n", + "mu_R = np.random.randn(1000, number_of_assets) # This should be the historical returns data for your assets\n", + "\n", + "# Generate the random weights for the portfolios\n", + "random_weights = generate_random_weights(number_of_assets, number_of_portfolios)\n", + "\n", + "# Calculate the VaR for each portfolio\n", + "VaRs = []\n", + "for weights in random_weights:\n", + " VaR = calculate_historical_VaR(weights, mu_R, confidence_level)\n", + " VaRs.append(VaR)\n", + "\n", + "# Find the z value for your constraint equations\n", + "# This could be the mean VaR, median VaR, or some other statistic that you determine is appropriate for your needs\n", + "mean_VaR = np.mean(VaRs)\n", + "median_VaR = np.median(VaRs)\n", + "z = mean_VaR # or median_VaR, depending on your specific requirements\n", + "\n", + "print(f\"The mean VaR at a {confidence_level*100}% confidence level is: {mean_VaR}\")\n", + "print(f\"The median VaR at a {confidence_level*100}% confidence level is: {median_VaR}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96b1afa0-a9b9-4f7c-b891-6dbc0eedf3df", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 [Moody's]", + "language": "python", + "name": "python3_moodys_xbto4j" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index b3a968e..c738946 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 1, "id": "15770539", "metadata": { "tags": [] @@ -350,6 +350,53 @@ "To examine the practical applicability of the mean-VaR model with quantum techniques, we will use a small dataset that compromises the weekly linear returns for Eurostoxx50 Market Index from 01-22-2007 to 05-06-2013 and contains up to 32 assets. You may find the associated file: *returns_data.txt*" ] }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a78f8aea", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_493/969284350.py:2: DeprecationWarning: \n", + "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", + "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", + "but was not found to be installed on your system.\n", + "If this would cause problems for you,\n", + "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", + " \n", + " import pandas as pd\n", + "/tmp/ipykernel_493/969284350.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# You may choose to select different parameters/values\n", + "number_assets = 3\n", + "T = 10\n", + "# Read returns\n", + "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "\n", + "Rraw = df.values.T\n", + "\n", + "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", + "R = Rraw[:number_assets,:T]\n", + "\n", + "# Expected return of each asset\n", + "expected_returns = np.mean(R, axis = 1)\n", + "\n", + "# Covariance matrix of asset returns\n", + "covariance_matrix = np.cov(R)" + ] + }, { "cell_type": "markdown", "id": "736c3eea-e219-4d6c-8457-3a4f21e9f6fc", @@ -370,19 +417,10 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "ec015dad-7449-433c-bd44-593e5fecb4ec", "metadata": {}, - "outputs": [ - { - "ename": "SyntaxError", - "evalue": "invalid syntax (4199961082.py, line 1)", - "output_type": "error", - "traceback": [ - "\u001b[0;36m Cell \u001b[0;32mIn[6], line 1\u001b[0;36m\u001b[0m\n\u001b[0;31m
NOTE:\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n" - ] - } - ], + "outputs": [], "source": [ "
NOTE:\n", "L has the following form: $L=\\sum_{k=1}^{n}\\sum_{j=1}^{n}x_{k}x_{j}\\sigma_{kj}-\\sum_{k=1}^{n}\\mu_{k}x_{k}+\\textit{penalty terms}$, with the penalty terms to be found in this section (and include all the equalities and inequalities of the problem). This will need to be mapped to a quantum Hamiltonian in the following section. Each penalty term will correspond to one lagrange multiplier that will need to be fine-tuned in the next sections, so that the constraints are imposed.\n", @@ -620,7 +658,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "id": "95761549", "metadata": {}, "outputs": [], From 008dc4a77292c6b8ac04541a53143c1678e20aa6 Mon Sep 17 00:00:00 2001 From: Ashrit Date: Sun, 4 Feb 2024 08:44:37 +0000 Subject: [PATCH 10/20] before debugging --- .ipynb_checkpoints/deriveZ-checkpoint.ipynb | 165 +++++--------------- deriveZ.ipynb | 165 +++++--------------- 2 files changed, 86 insertions(+), 244 deletions(-) diff --git a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb index 0572d53..6808b9c 100644 --- a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb +++ b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb @@ -3,36 +3,24 @@ { "cell_type": "markdown", "id": "39502968-c6bb-4b52-8a6e-bc58fb5d8cc3", - "metadata": {}, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [] + }, "source": [ "Deriving z." ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_512/3483040339.py:4: DeprecationWarning: \n", - "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", - "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", - "but was not found to be installed on your system.\n", - "If this would cause problems for you,\n", - "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", - " \n", - " import pandas as pd\n", - "/tmp/ipykernel_512/3483040339.py:10: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", - " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" - ] - } - ], + "outputs": [], "source": [ "# modifying \"reading in financial data\" to 32 assets and right T value\n", "\n", @@ -59,31 +47,12 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "1454b6b3-e891-4511-8c78-f8396fedce42", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "(array([5.09966343e-02, 4.53727733e-03, 1.00000515e-02, 2.29285930e-02,\n", - " 3.09233780e-02, 1.15785783e-03, 4.23591284e-02, 5.60462282e-02,\n", - " 4.28687344e-02, 2.88422553e-06, 9.16611045e-03, 2.08512285e-02,\n", - " 1.74773246e-02, 4.98874449e-02, 3.34892557e-02, 2.18674602e-02,\n", - " 1.30252699e-02, 2.88221387e-02, 3.96564751e-02, 5.59792135e-02,\n", - " 4.05992617e-02, 1.04650265e-02, 5.25108242e-02, 3.42191975e-02,\n", - " 9.83692217e-03, 5.60762032e-02, 2.96385619e-02, 5.40760816e-02,\n", - " 4.28708190e-02, 3.60760968e-02, 2.97263551e-02, 5.18619619e-02]),\n", - " 0.9999999999999999)" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# import random weight function from random_weights.ipynb\n", "\n", @@ -100,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", "metadata": { "tags": [] @@ -109,10 +78,20 @@ "source": [ "# Redefining the calculate_historical_VaR function\n", "def calculate_historical_VaR(weights, mu_R, confidence_level=0.95):\n", + " # Calculate portfolio returns for the given weights\n", " portfolio_returns = np.dot(mu_R, np.transpose(np.array(weights)))\n", + " # Calculate the VaR at the specified confidence level\n", " VaR = np.percentile(portfolio_returns, 100 * (1 - confidence_level))\n", - " return -VaR\n", - "\n", + " return -VaR" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9e7df0d-6d54-41d0-8396-3419e202c41f", + "metadata": {}, + "outputs": [], + "source": [ "# Define a function to simulate the random generation of weights and calculation of VaR\n", "def simulate_VaR(num_simulations, num_assets, mu_R, confidence_level=0.95):\n", " VaR_results = []\n", @@ -129,62 +108,12 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "([0.33478069678017486,\n", - " 0.31380399284909316,\n", - " 0.33764572699909956,\n", - " 0.3552637321435413,\n", - " 0.3078384247834268],\n", - " [array([0.03563346, 0.04352192, 0.02860185, 0.05366071, 0.00273621,\n", - " 0.01050274, 0.03983288, 0.00543957, 0.037661 , 0.00371156,\n", - " 0.05034161, 0.00014823, 0.00145112, 0.03447267, 0.04733192,\n", - " 0.01874436, 0.01923075, 0.04976332, 0.03784558, 0.05275445,\n", - " 0.05605801, 0.05505757, 0.03344434, 0.03201722, 0.02835223,\n", - " 0.019846 , 0.0409556 , 0.05237392, 0.03053973, 0.04146229,\n", - " 0.01626037, 0.02024677]),\n", - " array([0.04687872, 0.032786 , 0.0125963 , 0.03430151, 0.0162518 ,\n", - " 0.0082743 , 0.02358875, 0.04415627, 0.0217607 , 0.04591394,\n", - " 0.04507806, 0.04419739, 0.04813156, 0.00641797, 0.01725279,\n", - " 0.00813625, 0.03020361, 0.04028722, 0.05129885, 0.04795799,\n", - " 0.02350275, 0.01127891, 0.00113751, 0.04436431, 0.04858859,\n", - " 0.02265057, 0.02337002, 0.0431164 , 0.0352238 , 0.04258584,\n", - " 0.04689514, 0.03181619]),\n", - " array([0.0211461 , 0.0529119 , 0.07285264, 0.00050029, 0.03326014,\n", - " 0.02911207, 0.00682674, 0.05442366, 0.04658641, 0.0189938 ,\n", - " 0.07284515, 0.03484071, 0.05311161, 0.01557952, 0.01951858,\n", - " 0.01420304, 0.02244713, 0.06186267, 0.00335635, 0.03571577,\n", - " 0.02570347, 0.03125191, 0.03170835, 0.00690012, 0.01399259,\n", - " 0.02294648, 0.00574843, 0.05621745, 0.0628539 , 0.06235272,\n", - " 0.00371388, 0.00651645]),\n", - " array([0.01424482, 0.06156012, 0.00174449, 0.00731834, 0.05656582,\n", - " 0.01016888, 0.0442818 , 0.0469028 , 0.01712657, 0.0605412 ,\n", - " 0.0334373 , 0.00687328, 0.02064063, 0.02578439, 0.06548179,\n", - " 0.03952896, 0.0617938 , 0.03850067, 0.02258279, 0.00804128,\n", - " 0.00663337, 0.0614951 , 0.0117445 , 0.06548832, 0.01967127,\n", - " 0.01175255, 0.05695005, 0.00917514, 0.02767445, 0.00419278,\n", - " 0.04953954, 0.03256321]),\n", - " array([0.0132437 , 0.02751263, 0.03705897, 0.04919671, 0.03761503,\n", - " 0.04895974, 0.0034073 , 0.02950381, 0.04254487, 0.0370143 ,\n", - " 0.04606661, 0.00758007, 0.03064939, 0.04778214, 0.03431231,\n", - " 0.04050375, 0.04726696, 0.00553647, 0.00418306, 0.0428012 ,\n", - " 0.04052225, 0.03519681, 0.02109712, 0.01979697, 0.04047861,\n", - " 0.0453971 , 0.03042294, 0.02994999, 0.03816592, 0.03182084,\n", - " 0.03069817, 0.00371424])])" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Example usage\n", "num_simulations = 1000 # The number of times we want to run the simulation\n", @@ -204,54 +133,38 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", "metadata": { "tags": [] }, - "outputs": [ - { - "ename": "TypeError", - "evalue": "generate_random_weights() takes 1 positional argument but 2 were given", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[12], line 14\u001b[0m\n\u001b[1;32m 11\u001b[0m mu_R \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mrandom\u001b[38;5;241m.\u001b[39mrandn(\u001b[38;5;241m1000\u001b[39m, number_of_assets) \u001b[38;5;66;03m# This should be the historical returns data for your assets\u001b[39;00m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;66;03m# Generate the random weights for the portfolios\u001b[39;00m\n\u001b[0;32m---> 14\u001b[0m random_weights \u001b[38;5;241m=\u001b[39m \u001b[43mgenerate_random_weights\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumber_of_assets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnumber_of_portfolios\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;66;03m# Calculate the VaR for each portfolio\u001b[39;00m\n\u001b[1;32m 17\u001b[0m VaRs \u001b[38;5;241m=\u001b[39m []\n", - "\u001b[0;31mTypeError\u001b[0m: generate_random_weights() takes 1 positional argument but 2 were given" - ] - } - ], + "outputs": [], "source": [ "# let's now actually calculate z\n", "\n", - "\n", "# Parameters\n", "number_of_assets = 32\n", "number_of_portfolios = 1000 # The number of random portfolios you want to generate\n", "confidence_level = 0.95\n", "\n", - "# Assuming mu_R is matrix of historical returns for the assets\n", - "# Replace this with our actual historical returns data\n", - "mu_R = np.random.randn(1000, number_of_assets) # This should be the historical returns data for your assets\n", - "\n", - "# Generate the random weights for the portfolios\n", - "random_weights = generate_random_weights(number_of_assets, number_of_portfolios)\n", + "# Assuming mu_R is the matrix of historical returns for the assets\n", + "# Replace this with your actual historical returns data\n", + "mu_R = expected_returns # This should be the historical returns data for your assets\n", "\n", - "# Calculate the VaR for each portfolio\n", + "# Generate the random weights for the portfolios and calculate the VaR for each\n", "VaRs = []\n", - "for weights in random_weights:\n", + "for _ in range(number_of_portfolios):\n", + " weights = generate_random_weights(number_of_assets)\n", " VaR = calculate_historical_VaR(weights, mu_R, confidence_level)\n", " VaRs.append(VaR)\n", "\n", - "# Find the z value for your constraint equations\n", - "# This could be the mean VaR, median VaR, or some other statistic that you determine is appropriate for your needs\n", + "# Calculate z\n", "mean_VaR = np.mean(VaRs)\n", "median_VaR = np.median(VaRs)\n", "z = mean_VaR # or median_VaR, depending on your specific requirements\n", "\n", "print(f\"The mean VaR at a {confidence_level*100}% confidence level is: {mean_VaR}\")\n", - "print(f\"The median VaR at a {confidence_level*100}% confidence level is: {median_VaR}\")" + "print(f\"The median VaR at a {confidence_level*100}% confidence level is: {median_VaR}\")\n" ] }, { @@ -261,6 +174,14 @@ "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "297bcd02-43bb-4a1c-a4c6-1ef6a08bbfed", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/deriveZ.ipynb b/deriveZ.ipynb index 0572d53..6808b9c 100644 --- a/deriveZ.ipynb +++ b/deriveZ.ipynb @@ -3,36 +3,24 @@ { "cell_type": "markdown", "id": "39502968-c6bb-4b52-8a6e-bc58fb5d8cc3", - "metadata": {}, + "metadata": { + "jupyter": { + "source_hidden": true + }, + "tags": [] + }, "source": [ "Deriving z." ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", "metadata": { "tags": [] }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_512/3483040339.py:4: DeprecationWarning: \n", - "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", - "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", - "but was not found to be installed on your system.\n", - "If this would cause problems for you,\n", - "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", - " \n", - " import pandas as pd\n", - "/tmp/ipykernel_512/3483040339.py:10: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", - " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" - ] - } - ], + "outputs": [], "source": [ "# modifying \"reading in financial data\" to 32 assets and right T value\n", "\n", @@ -59,31 +47,12 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "id": "1454b6b3-e891-4511-8c78-f8396fedce42", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "(array([5.09966343e-02, 4.53727733e-03, 1.00000515e-02, 2.29285930e-02,\n", - " 3.09233780e-02, 1.15785783e-03, 4.23591284e-02, 5.60462282e-02,\n", - " 4.28687344e-02, 2.88422553e-06, 9.16611045e-03, 2.08512285e-02,\n", - " 1.74773246e-02, 4.98874449e-02, 3.34892557e-02, 2.18674602e-02,\n", - " 1.30252699e-02, 2.88221387e-02, 3.96564751e-02, 5.59792135e-02,\n", - " 4.05992617e-02, 1.04650265e-02, 5.25108242e-02, 3.42191975e-02,\n", - " 9.83692217e-03, 5.60762032e-02, 2.96385619e-02, 5.40760816e-02,\n", - " 4.28708190e-02, 3.60760968e-02, 2.97263551e-02, 5.18619619e-02]),\n", - " 0.9999999999999999)" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# import random weight function from random_weights.ipynb\n", "\n", @@ -100,7 +69,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", "metadata": { "tags": [] @@ -109,10 +78,20 @@ "source": [ "# Redefining the calculate_historical_VaR function\n", "def calculate_historical_VaR(weights, mu_R, confidence_level=0.95):\n", + " # Calculate portfolio returns for the given weights\n", " portfolio_returns = np.dot(mu_R, np.transpose(np.array(weights)))\n", + " # Calculate the VaR at the specified confidence level\n", " VaR = np.percentile(portfolio_returns, 100 * (1 - confidence_level))\n", - " return -VaR\n", - "\n", + " return -VaR" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9e7df0d-6d54-41d0-8396-3419e202c41f", + "metadata": {}, + "outputs": [], + "source": [ "# Define a function to simulate the random generation of weights and calculation of VaR\n", "def simulate_VaR(num_simulations, num_assets, mu_R, confidence_level=0.95):\n", " VaR_results = []\n", @@ -129,62 +108,12 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", "metadata": { "tags": [] }, - "outputs": [ - { - "data": { - "text/plain": [ - "([0.33478069678017486,\n", - " 0.31380399284909316,\n", - " 0.33764572699909956,\n", - " 0.3552637321435413,\n", - " 0.3078384247834268],\n", - " [array([0.03563346, 0.04352192, 0.02860185, 0.05366071, 0.00273621,\n", - " 0.01050274, 0.03983288, 0.00543957, 0.037661 , 0.00371156,\n", - " 0.05034161, 0.00014823, 0.00145112, 0.03447267, 0.04733192,\n", - " 0.01874436, 0.01923075, 0.04976332, 0.03784558, 0.05275445,\n", - " 0.05605801, 0.05505757, 0.03344434, 0.03201722, 0.02835223,\n", - " 0.019846 , 0.0409556 , 0.05237392, 0.03053973, 0.04146229,\n", - " 0.01626037, 0.02024677]),\n", - " array([0.04687872, 0.032786 , 0.0125963 , 0.03430151, 0.0162518 ,\n", - " 0.0082743 , 0.02358875, 0.04415627, 0.0217607 , 0.04591394,\n", - " 0.04507806, 0.04419739, 0.04813156, 0.00641797, 0.01725279,\n", - " 0.00813625, 0.03020361, 0.04028722, 0.05129885, 0.04795799,\n", - " 0.02350275, 0.01127891, 0.00113751, 0.04436431, 0.04858859,\n", - " 0.02265057, 0.02337002, 0.0431164 , 0.0352238 , 0.04258584,\n", - " 0.04689514, 0.03181619]),\n", - " array([0.0211461 , 0.0529119 , 0.07285264, 0.00050029, 0.03326014,\n", - " 0.02911207, 0.00682674, 0.05442366, 0.04658641, 0.0189938 ,\n", - " 0.07284515, 0.03484071, 0.05311161, 0.01557952, 0.01951858,\n", - " 0.01420304, 0.02244713, 0.06186267, 0.00335635, 0.03571577,\n", - " 0.02570347, 0.03125191, 0.03170835, 0.00690012, 0.01399259,\n", - " 0.02294648, 0.00574843, 0.05621745, 0.0628539 , 0.06235272,\n", - " 0.00371388, 0.00651645]),\n", - " array([0.01424482, 0.06156012, 0.00174449, 0.00731834, 0.05656582,\n", - " 0.01016888, 0.0442818 , 0.0469028 , 0.01712657, 0.0605412 ,\n", - " 0.0334373 , 0.00687328, 0.02064063, 0.02578439, 0.06548179,\n", - " 0.03952896, 0.0617938 , 0.03850067, 0.02258279, 0.00804128,\n", - " 0.00663337, 0.0614951 , 0.0117445 , 0.06548832, 0.01967127,\n", - " 0.01175255, 0.05695005, 0.00917514, 0.02767445, 0.00419278,\n", - " 0.04953954, 0.03256321]),\n", - " array([0.0132437 , 0.02751263, 0.03705897, 0.04919671, 0.03761503,\n", - " 0.04895974, 0.0034073 , 0.02950381, 0.04254487, 0.0370143 ,\n", - " 0.04606661, 0.00758007, 0.03064939, 0.04778214, 0.03431231,\n", - " 0.04050375, 0.04726696, 0.00553647, 0.00418306, 0.0428012 ,\n", - " 0.04052225, 0.03519681, 0.02109712, 0.01979697, 0.04047861,\n", - " 0.0453971 , 0.03042294, 0.02994999, 0.03816592, 0.03182084,\n", - " 0.03069817, 0.00371424])])" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# Example usage\n", "num_simulations = 1000 # The number of times we want to run the simulation\n", @@ -204,54 +133,38 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", "metadata": { "tags": [] }, - "outputs": [ - { - "ename": "TypeError", - "evalue": "generate_random_weights() takes 1 positional argument but 2 were given", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[12], line 14\u001b[0m\n\u001b[1;32m 11\u001b[0m mu_R \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mrandom\u001b[38;5;241m.\u001b[39mrandn(\u001b[38;5;241m1000\u001b[39m, number_of_assets) \u001b[38;5;66;03m# This should be the historical returns data for your assets\u001b[39;00m\n\u001b[1;32m 13\u001b[0m \u001b[38;5;66;03m# Generate the random weights for the portfolios\u001b[39;00m\n\u001b[0;32m---> 14\u001b[0m random_weights \u001b[38;5;241m=\u001b[39m \u001b[43mgenerate_random_weights\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnumber_of_assets\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnumber_of_portfolios\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 16\u001b[0m \u001b[38;5;66;03m# Calculate the VaR for each portfolio\u001b[39;00m\n\u001b[1;32m 17\u001b[0m VaRs \u001b[38;5;241m=\u001b[39m []\n", - "\u001b[0;31mTypeError\u001b[0m: generate_random_weights() takes 1 positional argument but 2 were given" - ] - } - ], + "outputs": [], "source": [ "# let's now actually calculate z\n", "\n", - "\n", "# Parameters\n", "number_of_assets = 32\n", "number_of_portfolios = 1000 # The number of random portfolios you want to generate\n", "confidence_level = 0.95\n", "\n", - "# Assuming mu_R is matrix of historical returns for the assets\n", - "# Replace this with our actual historical returns data\n", - "mu_R = np.random.randn(1000, number_of_assets) # This should be the historical returns data for your assets\n", - "\n", - "# Generate the random weights for the portfolios\n", - "random_weights = generate_random_weights(number_of_assets, number_of_portfolios)\n", + "# Assuming mu_R is the matrix of historical returns for the assets\n", + "# Replace this with your actual historical returns data\n", + "mu_R = expected_returns # This should be the historical returns data for your assets\n", "\n", - "# Calculate the VaR for each portfolio\n", + "# Generate the random weights for the portfolios and calculate the VaR for each\n", "VaRs = []\n", - "for weights in random_weights:\n", + "for _ in range(number_of_portfolios):\n", + " weights = generate_random_weights(number_of_assets)\n", " VaR = calculate_historical_VaR(weights, mu_R, confidence_level)\n", " VaRs.append(VaR)\n", "\n", - "# Find the z value for your constraint equations\n", - "# This could be the mean VaR, median VaR, or some other statistic that you determine is appropriate for your needs\n", + "# Calculate z\n", "mean_VaR = np.mean(VaRs)\n", "median_VaR = np.median(VaRs)\n", "z = mean_VaR # or median_VaR, depending on your specific requirements\n", "\n", "print(f\"The mean VaR at a {confidence_level*100}% confidence level is: {mean_VaR}\")\n", - "print(f\"The median VaR at a {confidence_level*100}% confidence level is: {median_VaR}\")" + "print(f\"The median VaR at a {confidence_level*100}% confidence level is: {median_VaR}\")\n" ] }, { @@ -261,6 +174,14 @@ "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "297bcd02-43bb-4a1c-a4c6-1ef6a08bbfed", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From cb19862f15d4504d84c120fdcb339955b9e44931 Mon Sep 17 00:00:00 2001 From: Ashrit Date: Sun, 4 Feb 2024 09:14:31 +0000 Subject: [PATCH 11/20] need help w z --- .ipynb_checkpoints/deriveZ-checkpoint.ipynb | 116 +++++++++++++++++--- deriveZ.ipynb | 114 ++++++++++++++++--- 2 files changed, 201 insertions(+), 29 deletions(-) diff --git a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb index 6808b9c..4dc84dd 100644 --- a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb +++ b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb @@ -4,9 +4,6 @@ "cell_type": "markdown", "id": "39502968-c6bb-4b52-8a6e-bc58fb5d8cc3", "metadata": { - "jupyter": { - "source_hidden": true - }, "tags": [] }, "source": [ @@ -15,12 +12,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_512/3483040339.py:10: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + ] + } + ], "source": [ "# modifying \"reading in financial data\" to 32 assets and right T value\n", "\n", @@ -47,12 +53,31 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "id": "1454b6b3-e891-4511-8c78-f8396fedce42", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(array([4.51054074e-03, 8.73512634e-05, 7.90540398e-03, 1.40783575e-02,\n", + " 2.07775903e-02, 1.64123539e-03, 1.91034896e-02, 1.28609808e-02,\n", + " 2.80859844e-04, 6.61409780e-02, 6.51414292e-02, 3.98803992e-02,\n", + " 7.00376103e-02, 3.46769279e-02, 4.44522258e-02, 1.80953143e-02,\n", + " 5.18566058e-02, 1.49361414e-02, 2.43586883e-02, 4.35945518e-02,\n", + " 7.52722277e-02, 7.10196380e-02, 1.96061804e-03, 3.71767195e-03,\n", + " 8.65341180e-03, 2.16549540e-02, 4.95875647e-02, 7.07621919e-02,\n", + " 6.89387137e-02, 5.14832364e-03, 5.86121947e-02, 1.02558085e-02]),\n", + " 1.0000000000000002)" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# import random weight function from random_weights.ipynb\n", "\n", @@ -69,7 +94,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", "metadata": { "tags": [] @@ -87,9 +112,11 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "id": "d9e7df0d-6d54-41d0-8396-3419e202c41f", - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "# Define a function to simulate the random generation of weights and calculation of VaR\n", @@ -108,12 +135,62 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "([0.355336682932509,\n", + " 0.3486524751161616,\n", + " 0.3153310798265738,\n", + " 0.3458236857416237,\n", + " 0.3284936295255029],\n", + " [array([0.04355964, 0.00331318, 0.03962351, 0.00446816, 0.02004242,\n", + " 0.0073511 , 0.05799071, 0.04472408, 0.06603708, 0.00088515,\n", + " 0.00764356, 0.0047062 , 0.05034145, 0.00329546, 0.01488312,\n", + " 0.05515591, 0.04475396, 0.01491287, 0.0683395 , 0.07056326,\n", + " 0.03745494, 0.02609458, 0.02203422, 0.01296996, 0.050467 ,\n", + " 0.01384013, 0.04922779, 0.03142448, 0.01422148, 0.00488373,\n", + " 0.05422915, 0.06056223]),\n", + " array([0.04874561, 0.03993086, 0.04785653, 0.05465976, 0.00019219,\n", + " 0.04019522, 0.00810607, 0.06345375, 0.02573358, 0.02708537,\n", + " 0.04455019, 0.04537882, 0.02676014, 0.0433288 , 0.06012773,\n", + " 0.02735331, 0.05145284, 0.01656183, 0.01235625, 0.00276128,\n", + " 0.01715967, 0.04823428, 0.00194451, 0.04691764, 0.05292875,\n", + " 0.02993959, 0.00154663, 0.04849301, 0.01671295, 0.02641807,\n", + " 0.01500313, 0.00811164]),\n", + " array([0.05407912, 0.00771579, 0.03898179, 0.0360573 , 0.04710808,\n", + " 0.02554926, 0.03036403, 0.04929535, 0.03950859, 0.03908034,\n", + " 0.05741809, 0.03047458, 0.05604574, 0.00530165, 0.02561892,\n", + " 0.01067558, 0.02881062, 0.03662797, 0.03708003, 0.01491708,\n", + " 0.03633884, 0.0174767 , 0.03415313, 0.01768372, 0.00391247,\n", + " 0.04466098, 0.06156805, 0.00268126, 0.01123347, 0.03619209,\n", + " 0.03278803, 0.03060133]),\n", + " array([0.01324834, 0.02055701, 0.03873027, 0.05622685, 0.04835239,\n", + " 0.03054007, 0.02763519, 0.05789158, 0.0541646 , 0.00560576,\n", + " 0.00030345, 0.03324706, 0.03112665, 0.02911636, 0.02694478,\n", + " 0.06162621, 0.06285638, 0.00590672, 0.04596359, 0.00188474,\n", + " 0.00836228, 0.04720747, 0.0179548 , 0.03406875, 0.03500403,\n", + " 0.02352799, 0.02899271, 0.0351087 , 0.04108958, 0.00511435,\n", + " 0.02003537, 0.05160599]),\n", + " array([0.03928864, 0.06604121, 0.04455049, 0.05844166, 0.03450061,\n", + " 0.00578215, 0.00745817, 0.04684066, 0.02250656, 0.05992387,\n", + " 0.0013096 , 0.03850481, 0.05485348, 0.00092822, 0.02524379,\n", + " 0.06463862, 0.03410129, 0.01284057, 0.04778934, 0.01594721,\n", + " 0.0531338 , 0.00809094, 0.01694831, 0.03596166, 0.01669566,\n", + " 0.009567 , 0.0003432 , 0.03155908, 0.03194489, 0.06121041,\n", + " 0.02109639, 0.03195769])])" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Example usage\n", "num_simulations = 1000 # The number of times we want to run the simulation\n", @@ -133,12 +210,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The mean VaR at a 95.0% confidence level is: -0.001003955534921795\n", + "The median VaR at a 95.0% confidence level is: -0.001012017761058015\n" + ] + } + ], "source": [ "# let's now actually calculate z\n", "\n", @@ -149,7 +235,7 @@ "\n", "# Assuming mu_R is the matrix of historical returns for the assets\n", "# Replace this with your actual historical returns data\n", - "mu_R = expected_returns # This should be the historical returns data for your assets\n", + "mu_R = R.T # This should be the historical returns data for your assets\n", "\n", "# Generate the random weights for the portfolios and calculate the VaR for each\n", "VaRs = []\n", diff --git a/deriveZ.ipynb b/deriveZ.ipynb index 6808b9c..7aab876 100644 --- a/deriveZ.ipynb +++ b/deriveZ.ipynb @@ -4,9 +4,6 @@ "cell_type": "markdown", "id": "39502968-c6bb-4b52-8a6e-bc58fb5d8cc3", "metadata": { - "jupyter": { - "source_hidden": true - }, "tags": [] }, "source": [ @@ -15,12 +12,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_512/3483040339.py:10: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + ] + } + ], "source": [ "# modifying \"reading in financial data\" to 32 assets and right T value\n", "\n", @@ -47,12 +53,30 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "id": "1454b6b3-e891-4511-8c78-f8396fedce42", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(array([0.04530294, 0.03248244, 0.01591696, 0.05819326, 0.05515743,\n", + " 0.0018886 , 0.04133333, 0.04105249, 0.00532421, 0.02746209,\n", + " 0.06062974, 0.02659319, 0.04392856, 0.00207729, 0.05706425,\n", + " 0.00622429, 0.00509702, 0.0474765 , 0.02714145, 0.06078049,\n", + " 0.05412684, 0.00992554, 0.00093757, 0.00173598, 0.01410071,\n", + " 0.02860928, 0.05080505, 0.00879419, 0.01877773, 0.03859788,\n", + " 0.05160082, 0.06086186]),\n", + " 1.0)" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# import random weight function from random_weights.ipynb\n", "\n", @@ -69,7 +93,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", "metadata": { "tags": [] @@ -87,9 +111,11 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "id": "d9e7df0d-6d54-41d0-8396-3419e202c41f", - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "# Define a function to simulate the random generation of weights and calculation of VaR\n", @@ -108,12 +134,63 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "([0.3201754590963047,\n", + " 0.3040817367921548,\n", + " 0.33570642951204405,\n", + " 0.324621105208764,\n", + " 0.35256804169434885],\n", + " [array([0.03519249, 0.03290085, 0.01317292, 0.0536117 , 0.05335179,\n", + " 0.0282839 , 0.04913777, 0.03639204, 0.00584395, 0.00473127,\n", + " 0.01556526, 0.05040084, 0.00158258, 0.05193638, 0.01152281,\n", + " 0.0274181 , 0.04280595, 0.02104865, 0.04392498, 0.01501355,\n", + " 0.00104685, 0.03588135, 0.04501165, 0.01426848, 0.03105206,\n", + " 0.04519589, 0.02398323, 0.03468755, 0.03416542, 0.04537202,\n", + " 0.04764116, 0.04785654]),\n", + " array([0.04150249, 0.04927715, 0.04050631, 0.01704145, 0.02052018,\n", + " 0.02598609, 0.03970675, 0.04701215, 0.02573006, 0.03526343,\n", + " 0.0118426 , 0.0231728 , 0.03486327, 0.00175815, 0.02021195,\n", + " 0.04307619, 0.01233438, 0.04928072, 0.0216664 , 0.01882121,\n", + " 0.05057916, 0.02193385, 0.02624362, 0.01480431, 0.04967795,\n", + " 0.01678528, 0.04148598, 0.03736406, 0.03326363, 0.03940288,\n", + " 0.03435846, 0.05452709]),\n", + " array([0.01604598, 0.04529005, 0.01110671, 0.05985637, 0.04223921,\n", + " 0.01186149, 0.04146411, 0.0174574 , 0.04475275, 0.0483324 ,\n", + " 0.01738737, 0.03744703, 0.01976326, 0.06031816, 0.01705841,\n", + " 0.01359626, 0.05254128, 0.03369028, 0.06079489, 0.00420929,\n", + " 0.00269271, 0.02072749, 0.02317871, 0.06133557, 0.05006869,\n", + " 0.03762329, 0.0628452 , 0.00436101, 0.03140263, 0.00272598,\n", + " 0.00502145, 0.04280456]),\n", + " array([3.75143474e-02, 3.56680465e-02, 6.07580111e-02, 1.78872938e-02,\n", + " 4.47482137e-02, 4.36094397e-02, 1.02396118e-02, 3.50628726e-02,\n", + " 5.74763685e-03, 5.88533879e-02, 1.77641933e-02, 2.20699045e-02,\n", + " 2.89828714e-02, 1.46328359e-02, 3.59180566e-02, 1.63276401e-02,\n", + " 3.79104899e-02, 4.88496753e-02, 1.12161826e-03, 3.36144250e-02,\n", + " 5.87756224e-02, 7.50474417e-03, 4.26659419e-02, 3.54226972e-02,\n", + " 5.63592516e-02, 3.53101020e-02, 2.35589519e-02, 1.05982766e-02,\n", + " 5.60329215e-02, 5.09637794e-03, 7.95060325e-05, 6.13150354e-02]),\n", + " array([0.03894738, 0.03544109, 0.00490616, 0.05484427, 0.01377497,\n", + " 0.0112676 , 0.06334437, 0.05774461, 0.04677672, 0.02128168,\n", + " 0.0643672 , 0.01938623, 0.02496569, 0.03924972, 0.05728707,\n", + " 0.04692048, 0.01114821, 0.0088831 , 0.00936529, 0.06442574,\n", + " 0.04214907, 0.0072486 , 0.0600262 , 0.02152251, 0.00609748,\n", + " 0.02466099, 0.00043209, 0.00359317, 0.0311648 , 0.05129704,\n", + " 0.05541976, 0.00206072])])" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Example usage\n", "num_simulations = 1000 # The number of times we want to run the simulation\n", @@ -133,12 +210,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", "metadata": { "tags": [] }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The mean VaR at a 95.0% confidence level is: -0.0010112892735360864\n", + "The median VaR at a 95.0% confidence level is: -0.0010098220472010626\n" + ] + } + ], "source": [ "# let's now actually calculate z\n", "\n", From f0d4864b345bde2fda3f929095c690c7b9d50638 Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 04:32:55 -0500 Subject: [PATCH 12/20] Complete stage 4 --- moodys_challenge.ipynb | 125 +++++++++++++++++++++++++++++++++-------- 1 file changed, 103 insertions(+), 22 deletions(-) diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 860ff54..3e5a43b 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -350,7 +350,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_28652\\3824432534.py:2: DeprecationWarning: \n", + "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_22900\\3824432534.py:2: DeprecationWarning: \n", "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", "but was not found to be installed on your system.\n", @@ -358,7 +358,7 @@ "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", " \n", " import pandas as pd\n", - "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_28652\\3824432534.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_22900\\3824432534.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" ] } @@ -567,7 +567,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "BinaryQuadraticModel({'x_full[1][2]': -34834.58508411406, 'x_full[1][0]': 31401.908438851064, 'x_full[2][1]': -20352.919804753736, 'B_full[0][4]': 1000500.0, 'x_full[2][2]': -65662.68673217949, 'B_full[3][4]': 8032000.0, 'x_full[4][1]': 14647.365549565578, 'r[2][0]': -3988000.0, 'x_full[3][1]': -24695.998814744024, 'r[0][0]': -998800.0, 'x_full[1][1]': -12177.690001722289, 'y[2][0]': -500001700.0, 'x_full[2][0]': 66806.10279699802, 'x_full[4][0]': 363279.2732510936, 'y[3][0]': -500001700.0, 'r[1][0]': -1996400.0, 'x_full[0][0]': 15200.668479513544, 'r[4][0]': -15836800.0, 'y[4][0]': -500001700.0, 'x_full[3][0]': 149621.34927117964, 'x_full[0][2]': -17918.102971563105, 'x_full[3][2]': -115299.43972016452, 'x_full[4][2]': -166495.14446355117, 'C[2][0]': 10800.0, 'C[0][0]': 2100.0, 'y[0][0]': -500001700.0, 'B_full[4][4]': 16128000.0, 'r[3][0]': -7956800.0, 'y[1][0]': -500001700.0, 'B_full[2][4]': 4008000.0, 'A[3][0]': 1600.0, 'A[1][0]': -800.0, 'B_full[1][4]': 2002000.0, 'x_full[0][1]': -6589.152525697503, 'A[2][0]': -800.0, 'A[0][0]': -500.0, 'C[3][0]': 28000.0, 'C[1][0]': 4600.0, 'C[4][0]': 81600.0, 'A[4][0]': 16000.0}, {('x_full[1][0]', 'x_full[1][2]'): 4000.4244618825674, ('x_full[2][1]', 'x_full[1][2]'): 8005.011832959769, ('x_full[2][1]', 'x_full[1][0]'): 8000.268662619726, ('B_full[0][4]', 'x_full[1][2]'): -34.83786164, ('B_full[0][4]', 'x_full[1][0]'): 31.400796879999994, ('B_full[0][4]', 'x_full[2][1]'): -24.35786456, ('x_full[2][2]', 'x_full[1][2]'): 8012.966872097232, ('x_full[2][2]', 'x_full[1][0]'): 8000.848923765135, ('x_full[2][2]', 'x_full[2][1]'): 16010.023665919538, ('x_full[2][2]', 'B_full[0][4]'): -69.67572328, ('B_full[3][4]', 'x_full[1][2]'): -278.70289312, ('B_full[3][4]', 'x_full[1][0]'): 251.20637503999995, ('B_full[3][4]', 'x_full[2][1]'): -194.86291648, ('B_full[3][4]', 'B_full[0][4]'): 8000.0, ('B_full[3][4]', 'x_full[2][2]'): -557.40578624, ('x_full[4][1]', 'x_full[1][2]'): 32020.047331839076, ('x_full[4][1]', 'x_full[1][0]'): 32001.074650478902, ('x_full[4][1]', 'x_full[2][1]'): 64039.363179053675, ('x_full[4][1]', 'B_full[0][4]'): -97.43145824, ('x_full[4][1]', 'x_full[2][2]'): 64040.09466367815, ('x_full[4][1]', 'B_full[3][4]'): -779.45166592, ('r[2][0]', 'x_full[1][2]'): 139.35144656, ('r[2][0]', 'x_full[1][0]'): -125.60318751999998, ('r[2][0]', 'x_full[2][1]'): 97.43145824, ('r[2][0]', 'B_full[0][4]'): -4000.0, ('r[2][0]', 'x_full[2][2]'): 278.70289312, ('r[2][0]', 'B_full[3][4]'): -32000.0, ('r[2][0]', 'x_full[4][1]'): 389.72583296, ('x_full[3][1]', 'x_full[1][2]'): 16010.023665919538, ('x_full[3][1]', 'x_full[1][0]'): 16000.537325239451, ('x_full[3][1]', 'x_full[2][1]'): 32019.681589526837, ('x_full[3][1]', 'B_full[0][4]'): -48.71572912, ('x_full[3][1]', 'x_full[2][2]'): 32020.047331839076, ('x_full[3][1]', 'B_full[3][4]'): -389.72583296, ('x_full[3][1]', 'x_full[4][1]'): 128078.72635810735, ('x_full[3][1]', 'r[2][0]'): 194.86291648, ('r[0][0]', 'x_full[1][2]'): 34.83786164, ('r[0][0]', 'x_full[1][0]'): -31.400796879999994, ('r[0][0]', 'x_full[2][1]'): 24.35786456, ('r[0][0]', 'B_full[0][4]'): -1000.0, ('r[0][0]', 'x_full[2][2]'): 69.67572328, ('r[0][0]', 'B_full[3][4]'): -8000.0, ('r[0][0]', 'x_full[4][1]'): 97.43145824, ('r[0][0]', 'r[2][0]'): 4800.0, ('r[0][0]', 'x_full[3][1]'): 48.71572912, ('x_full[1][1]', 'x_full[1][2]'): 4002.5059164798845, ('x_full[1][1]', 'x_full[1][0]'): 4000.134331309863, ('x_full[1][1]', 'x_full[2][1]'): 8004.920397381709, ('x_full[1][1]', 'B_full[0][4]'): -12.17893228, ('x_full[1][1]', 'x_full[2][2]'): 8005.011832959769, ('x_full[1][1]', 'B_full[3][4]'): -97.43145824, ('x_full[1][1]', 'x_full[4][1]'): 32019.681589526837, ('x_full[1][1]', 'r[2][0]'): 48.71572912, ('x_full[1][1]', 'x_full[3][1]'): 16009.840794763419, ('x_full[1][1]', 'r[0][0]'): 12.17893228, ('y[2][0]', 'x_full[1][2]'): 34837.86164, ('y[2][0]', 'x_full[1][0]'): -31400.79688, ('y[2][0]', 'x_full[2][1]'): 24357.864559999998, ('y[2][0]', 'B_full[0][4]'): -1000000.0, ('y[2][0]', 'x_full[2][2]'): 69675.72328, ('y[2][0]', 'B_full[3][4]'): -8000000.0, ('y[2][0]', 'x_full[4][1]'): 97431.45823999999, ('y[2][0]', 'r[2][0]'): 4000000.0, ('y[2][0]', 'x_full[3][1]'): 48715.729119999996, ('y[2][0]', 'r[0][0]'): 1000000.0, ('y[2][0]', 'x_full[1][1]'): 12178.932279999999, ('x_full[2][0]', 'x_full[1][2]'): 8000.848923765135, ('x_full[2][0]', 'x_full[1][0]'): 8004.571838591786, ('x_full[2][0]', 'x_full[2][1]'): 16000.537325239451, ('x_full[2][0]', 'B_full[0][4]'): 62.80159375999999, ('x_full[2][0]', 'x_full[2][2]'): 16001.69784753027, ('x_full[2][0]', 'B_full[3][4]'): 502.4127500799999, ('x_full[2][0]', 'x_full[4][1]'): 64002.149300957804, ('x_full[2][0]', 'r[2][0]'): -251.20637503999995, ('x_full[2][0]', 'x_full[3][1]'): 32001.074650478902, ('x_full[2][0]', 'r[0][0]'): -62.80159375999999, ('x_full[2][0]', 'x_full[1][1]'): 8000.268662619726, ('x_full[2][0]', 'y[2][0]'): -62801.59376, ('x_full[4][0]', 'x_full[1][2]'): 32003.39569506054, ('x_full[4][0]', 'x_full[1][0]'): 32018.287354367145, ('x_full[4][0]', 'x_full[2][1]'): 64002.149300957804, ('x_full[4][0]', 'B_full[0][4]'): 251.20637503999995, ('x_full[4][0]', 'x_full[2][2]'): 64006.79139012108, ('x_full[4][0]', 'B_full[3][4]'): 2009.6510003199996, ('x_full[4][0]', 'x_full[4][1]'): 256008.59720383122, ('x_full[4][0]', 'r[2][0]'): -1004.8255001599998, ('x_full[4][0]', 'x_full[3][1]'): 128004.29860191561, ('x_full[4][0]', 'r[0][0]'): -251.20637503999995, ('x_full[4][0]', 'x_full[1][1]'): 32001.074650478902, ('x_full[4][0]', 'y[2][0]'): -251206.37504, ('x_full[4][0]', 'x_full[2][0]'): 64036.57470873429, ('y[3][0]', 'x_full[1][2]'): 34837.86164, ('y[3][0]', 'x_full[1][0]'): -31400.79688, ('y[3][0]', 'x_full[2][1]'): 24357.864559999998, ('y[3][0]', 'B_full[0][4]'): -1000000.0, ('y[3][0]', 'x_full[2][2]'): 69675.72328, ('y[3][0]', 'B_full[3][4]'): -8000000.0, ('y[3][0]', 'x_full[4][1]'): 97431.45823999999, ('y[3][0]', 'r[2][0]'): 4000000.0, ('y[3][0]', 'x_full[3][1]'): 48715.729119999996, ('y[3][0]', 'r[0][0]'): 1000000.0, ('y[3][0]', 'x_full[1][1]'): 12178.932279999999, ('y[3][0]', 'y[2][0]'): 1000000400.0, ('y[3][0]', 'x_full[2][0]'): -62801.59376, ('y[3][0]', 'x_full[4][0]'): -251206.37504, ('r[1][0]', 'x_full[1][2]'): 69.67572328, ('r[1][0]', 'x_full[1][0]'): -62.80159375999999, ('r[1][0]', 'x_full[2][1]'): 48.71572912, ('r[1][0]', 'B_full[0][4]'): -2000.0, ('r[1][0]', 'x_full[2][2]'): 139.35144656, ('r[1][0]', 'B_full[3][4]'): -16000.0, ('r[1][0]', 'x_full[4][1]'): 194.86291648, ('r[1][0]', 'r[2][0]'): 9600.0, ('r[1][0]', 'x_full[3][1]'): 97.43145824, ('r[1][0]', 'r[0][0]'): 2400.0, ('r[1][0]', 'x_full[1][1]'): 24.35786456, ('r[1][0]', 'y[2][0]'): 2000000.0, ('r[1][0]', 'x_full[2][0]'): -125.60318751999998, ('r[1][0]', 'x_full[4][0]'): -502.4127500799999, ('r[1][0]', 'y[3][0]'): 2000000.0, ('x_full[0][0]', 'x_full[1][2]'): 2000.2122309412837, ('x_full[0][0]', 'x_full[1][0]'): 2001.1429596479466, ('x_full[0][0]', 'x_full[2][1]'): 4000.134331309863, ('x_full[0][0]', 'B_full[0][4]'): 15.700398439999997, ('x_full[0][0]', 'x_full[2][2]'): 4000.4244618825674, ('x_full[0][0]', 'B_full[3][4]'): 125.60318751999998, ('x_full[0][0]', 'x_full[4][1]'): 16000.537325239451, ('x_full[0][0]', 'r[2][0]'): -62.80159375999999, ('x_full[0][0]', 'x_full[3][1]'): 8000.268662619726, ('x_full[0][0]', 'r[0][0]'): -15.700398439999997, ('x_full[0][0]', 'x_full[1][1]'): 2000.0671656549314, ('x_full[0][0]', 'y[2][0]'): -15700.39844, ('x_full[0][0]', 'x_full[2][0]'): 4002.285919295893, ('x_full[0][0]', 'x_full[4][0]'): 16009.143677183572, ('x_full[0][0]', 'y[3][0]'): -15700.39844, ('x_full[0][0]', 'r[1][0]'): -31.400796879999994, ('r[4][0]', 'x_full[1][2]'): 557.40578624, ('r[4][0]', 'x_full[1][0]'): -502.4127500799999, ('r[4][0]', 'x_full[2][1]'): 389.72583296, ('r[4][0]', 'B_full[0][4]'): -16000.0, ('r[4][0]', 'x_full[2][2]'): 1114.81157248, ('r[4][0]', 'B_full[3][4]'): -128000.0, ('r[4][0]', 'x_full[4][1]'): 1558.90333184, ('r[4][0]', 'r[2][0]'): 76800.0, ('r[4][0]', 'x_full[3][1]'): 779.45166592, ('r[4][0]', 'r[0][0]'): 19200.0, ('r[4][0]', 'x_full[1][1]'): 194.86291648, ('r[4][0]', 'y[2][0]'): 16000000.0, ('r[4][0]', 'x_full[2][0]'): -1004.8255001599998, ('r[4][0]', 'x_full[4][0]'): -4019.3020006399993, ('r[4][0]', 'y[3][0]'): 16000000.0, ('r[4][0]', 'r[1][0]'): 38400.0, ('r[4][0]', 'x_full[0][0]'): -251.20637503999995, ('y[4][0]', 'x_full[1][2]'): 34837.86164, ('y[4][0]', 'x_full[1][0]'): -31400.79688, ('y[4][0]', 'x_full[2][1]'): 24357.864559999998, ('y[4][0]', 'B_full[0][4]'): -1000000.0, ('y[4][0]', 'x_full[2][2]'): 69675.72328, ('y[4][0]', 'B_full[3][4]'): -8000000.0, ('y[4][0]', 'x_full[4][1]'): 97431.45823999999, ('y[4][0]', 'r[2][0]'): 4000000.0, ('y[4][0]', 'x_full[3][1]'): 48715.729119999996, ('y[4][0]', 'r[0][0]'): 1000000.0, ('y[4][0]', 'x_full[1][1]'): 12178.932279999999, ('y[4][0]', 'y[2][0]'): 1000000400.0, ('y[4][0]', 'x_full[2][0]'): -62801.59376, ('y[4][0]', 'x_full[4][0]'): -251206.37504, ('y[4][0]', 'y[3][0]'): 1000000400.0, ('y[4][0]', 'r[1][0]'): 2000000.0, ('y[4][0]', 'x_full[0][0]'): -15700.39844, ('y[4][0]', 'r[4][0]'): 16000000.0, ('x_full[3][0]', 'x_full[1][2]'): 16001.69784753027, ('x_full[3][0]', 'x_full[1][0]'): 16009.143677183572, ('x_full[3][0]', 'x_full[2][1]'): 32001.074650478902, ('x_full[3][0]', 'B_full[0][4]'): 125.60318751999998, ('x_full[3][0]', 'x_full[2][2]'): 32003.39569506054, ('x_full[3][0]', 'B_full[3][4]'): 1004.8255001599998, ('x_full[3][0]', 'x_full[4][1]'): 128004.29860191561, ('x_full[3][0]', 'r[2][0]'): -502.4127500799999, ('x_full[3][0]', 'x_full[3][1]'): 64002.149300957804, ('x_full[3][0]', 'r[0][0]'): -125.60318751999998, ('x_full[3][0]', 'x_full[1][1]'): 16000.537325239451, ('x_full[3][0]', 'y[2][0]'): -125603.18752, ('x_full[3][0]', 'x_full[2][0]'): 32018.287354367145, ('x_full[3][0]', 'x_full[4][0]'): 128073.14941746858, ('x_full[3][0]', 'y[3][0]'): -125603.18752, ('x_full[3][0]', 'r[1][0]'): -251.20637503999995, ('x_full[3][0]', 'x_full[0][0]'): 8004.571838591786, ('x_full[3][0]', 'r[4][0]'): -2009.6510003199996, ('x_full[3][0]', 'y[4][0]'): -125603.18752, ('x_full[0][2]', 'x_full[1][2]'): 2003.241718024308, ('x_full[0][2]', 'x_full[1][0]'): 2000.2122309412837, ('x_full[0][2]', 'x_full[2][1]'): 4002.5059164798845, ('x_full[0][2]', 'B_full[0][4]'): -17.41893082, ('x_full[0][2]', 'x_full[2][2]'): 4006.483436048616, ('x_full[0][2]', 'B_full[3][4]'): -139.35144656, ('x_full[0][2]', 'x_full[4][1]'): 16010.023665919538, ('x_full[0][2]', 'r[2][0]'): 69.67572328, ('x_full[0][2]', 'x_full[3][1]'): 8005.011832959769, ('x_full[0][2]', 'r[0][0]'): 17.41893082, ('x_full[0][2]', 'x_full[1][1]'): 2001.2529582399422, ('x_full[0][2]', 'y[2][0]'): 17418.93082, ('x_full[0][2]', 'x_full[2][0]'): 4000.4244618825674, ('x_full[0][2]', 'x_full[4][0]'): 16001.69784753027, ('x_full[0][2]', 'y[3][0]'): 17418.93082, ('x_full[0][2]', 'r[1][0]'): 34.83786164, ('x_full[0][2]', 'x_full[0][0]'): 1000.1061154706418, ('x_full[0][2]', 'r[4][0]'): 278.70289312, ('x_full[0][2]', 'y[4][0]'): 17418.93082, ('x_full[0][2]', 'x_full[3][0]'): 8000.848923765135, ('x_full[3][2]', 'x_full[1][2]'): 16025.933744194464, ('x_full[3][2]', 'x_full[1][0]'): 16001.69784753027, ('x_full[3][2]', 'x_full[2][1]'): 32020.047331839076, ('x_full[3][2]', 'B_full[0][4]'): -139.35144656, ('x_full[3][2]', 'x_full[2][2]'): 32051.86748838893, ('x_full[3][2]', 'B_full[3][4]'): -1114.81157248, ('x_full[3][2]', 'x_full[4][1]'): 128080.1893273563, ('x_full[3][2]', 'r[2][0]'): 557.40578624, ('x_full[3][2]', 'x_full[3][1]'): 64040.09466367815, ('x_full[3][2]', 'r[0][0]'): 139.35144656, ('x_full[3][2]', 'x_full[1][1]'): 16010.023665919538, ('x_full[3][2]', 'y[2][0]'): 139351.44656, ('x_full[3][2]', 'x_full[2][0]'): 32003.39569506054, ('x_full[3][2]', 'x_full[4][0]'): 128013.58278024216, ('x_full[3][2]', 'y[3][0]'): 139351.44656, ('x_full[3][2]', 'r[1][0]'): 278.70289312, ('x_full[3][2]', 'x_full[0][0]'): 8000.848923765135, ('x_full[3][2]', 'r[4][0]'): 2229.62314496, ('x_full[3][2]', 'y[4][0]'): 139351.44656, ('x_full[3][2]', 'x_full[3][0]'): 64006.79139012108, ('x_full[3][2]', 'x_full[0][2]'): 8012.966872097232, ('x_full[4][2]', 'x_full[1][2]'): 32051.86748838893, ('x_full[4][2]', 'x_full[1][0]'): 32003.39569506054, ('x_full[4][2]', 'x_full[2][1]'): 64040.09466367815, ('x_full[4][2]', 'B_full[0][4]'): -278.70289312, ('x_full[4][2]', 'x_full[2][2]'): 64103.73497677786, ('x_full[4][2]', 'B_full[3][4]'): -2229.62314496, ('x_full[4][2]', 'x_full[4][1]'): 256160.3786547126, ('x_full[4][2]', 'r[2][0]'): 1114.81157248, ('x_full[4][2]', 'x_full[3][1]'): 128080.1893273563, ('x_full[4][2]', 'r[0][0]'): 278.70289312, ('x_full[4][2]', 'x_full[1][1]'): 32020.047331839076, ('x_full[4][2]', 'y[2][0]'): 278702.89312, ('x_full[4][2]', 'x_full[2][0]'): 64006.79139012108, ('x_full[4][2]', 'x_full[4][0]'): 256027.1655604843, ('x_full[4][2]', 'y[3][0]'): 278702.89312, ('x_full[4][2]', 'r[1][0]'): 557.40578624, ('x_full[4][2]', 'x_full[0][0]'): 16001.69784753027, ('x_full[4][2]', 'r[4][0]'): 4459.24628992, ('x_full[4][2]', 'y[4][0]'): 278702.89312, ('x_full[4][2]', 'x_full[3][0]'): 128013.58278024216, ('x_full[4][2]', 'x_full[0][2]'): 16025.933744194464, ('x_full[4][2]', 'x_full[3][2]'): 128207.46995355572, ('C[2][0]', 'y[2][0]'): -1600.0, ('C[2][0]', 'y[3][0]'): -1600.0, ('C[2][0]', 'y[4][0]'): -1600.0, ('C[0][0]', 'y[2][0]'): -400.0, ('C[0][0]', 'y[3][0]'): -400.0, ('C[0][0]', 'y[4][0]'): -400.0, ('C[0][0]', 'C[2][0]'): 1600.0, ('y[0][0]', 'x_full[1][2]'): 34837.86164, ('y[0][0]', 'x_full[1][0]'): -31400.79688, ('y[0][0]', 'x_full[2][1]'): 24357.864559999998, ('y[0][0]', 'B_full[0][4]'): -1000000.0, ('y[0][0]', 'x_full[2][2]'): 69675.72328, ('y[0][0]', 'B_full[3][4]'): -8000000.0, ('y[0][0]', 'x_full[4][1]'): 97431.45823999999, ('y[0][0]', 'r[2][0]'): 4000000.0, ('y[0][0]', 'x_full[3][1]'): 48715.729119999996, ('y[0][0]', 'r[0][0]'): 1000000.0, ('y[0][0]', 'x_full[1][1]'): 12178.932279999999, ('y[0][0]', 'y[2][0]'): 1000000400.0, ('y[0][0]', 'x_full[2][0]'): -62801.59376, ('y[0][0]', 'x_full[4][0]'): -251206.37504, ('y[0][0]', 'y[3][0]'): 1000000400.0, ('y[0][0]', 'r[1][0]'): 2000000.0, ('y[0][0]', 'x_full[0][0]'): -15700.39844, ('y[0][0]', 'r[4][0]'): 16000000.0, ('y[0][0]', 'y[4][0]'): 1000000400.0, ('y[0][0]', 'x_full[3][0]'): -125603.18752, ('y[0][0]', 'x_full[0][2]'): 17418.93082, ('y[0][0]', 'x_full[3][2]'): 139351.44656, ('y[0][0]', 'x_full[4][2]'): 278702.89312, ('y[0][0]', 'C[2][0]'): -1600.0, ('y[0][0]', 'C[0][0]'): -400.0, ('B_full[4][4]', 'x_full[1][2]'): -557.40578624, ('B_full[4][4]', 'x_full[1][0]'): 502.4127500799999, ('B_full[4][4]', 'x_full[2][1]'): -389.72583296, ('B_full[4][4]', 'B_full[0][4]'): 16000.0, ('B_full[4][4]', 'x_full[2][2]'): -1114.81157248, ('B_full[4][4]', 'B_full[3][4]'): 128000.0, ('B_full[4][4]', 'x_full[4][1]'): -1558.90333184, ('B_full[4][4]', 'r[2][0]'): -64000.0, ('B_full[4][4]', 'x_full[3][1]'): -779.45166592, ('B_full[4][4]', 'r[0][0]'): -16000.0, ('B_full[4][4]', 'x_full[1][1]'): -194.86291648, ('B_full[4][4]', 'y[2][0]'): -16000000.0, ('B_full[4][4]', 'x_full[2][0]'): 1004.8255001599998, ('B_full[4][4]', 'x_full[4][0]'): 4019.3020006399993, ('B_full[4][4]', 'y[3][0]'): -16000000.0, ('B_full[4][4]', 'r[1][0]'): -32000.0, ('B_full[4][4]', 'x_full[0][0]'): 251.20637503999995, ('B_full[4][4]', 'r[4][0]'): -256000.0, ('B_full[4][4]', 'y[4][0]'): -16000000.0, ('B_full[4][4]', 'x_full[3][0]'): 2009.6510003199996, ('B_full[4][4]', 'x_full[0][2]'): -278.70289312, ('B_full[4][4]', 'x_full[3][2]'): -2229.62314496, ('B_full[4][4]', 'x_full[4][2]'): -4459.24628992, ('B_full[4][4]', 'y[0][0]'): -16000000.0, ('r[3][0]', 'x_full[1][2]'): 278.70289312, ('r[3][0]', 'x_full[1][0]'): -251.20637503999995, ('r[3][0]', 'x_full[2][1]'): 194.86291648, ('r[3][0]', 'B_full[0][4]'): -8000.0, ('r[3][0]', 'x_full[2][2]'): 557.40578624, ('r[3][0]', 'B_full[3][4]'): -64000.0, ('r[3][0]', 'x_full[4][1]'): 779.45166592, ('r[3][0]', 'r[2][0]'): 38400.0, ('r[3][0]', 'x_full[3][1]'): 389.72583296, ('r[3][0]', 'r[0][0]'): 9600.0, ('r[3][0]', 'x_full[1][1]'): 97.43145824, ('r[3][0]', 'y[2][0]'): 8000000.0, ('r[3][0]', 'x_full[2][0]'): -502.4127500799999, ('r[3][0]', 'x_full[4][0]'): -2009.6510003199996, ('r[3][0]', 'y[3][0]'): 8000000.0, ('r[3][0]', 'r[1][0]'): 19200.0, ('r[3][0]', 'x_full[0][0]'): -125.60318751999998, ('r[3][0]', 'r[4][0]'): 153600.0, ('r[3][0]', 'y[4][0]'): 8000000.0, ('r[3][0]', 'x_full[3][0]'): -1004.8255001599998, ('r[3][0]', 'x_full[0][2]'): 139.35144656, ('r[3][0]', 'x_full[3][2]'): 1114.81157248, ('r[3][0]', 'x_full[4][2]'): 2229.62314496, ('r[3][0]', 'y[0][0]'): 8000000.0, ('r[3][0]', 'B_full[4][4]'): -128000.0, ('y[1][0]', 'x_full[1][2]'): 34837.86164, ('y[1][0]', 'x_full[1][0]'): -31400.79688, ('y[1][0]', 'x_full[2][1]'): 24357.864559999998, ('y[1][0]', 'B_full[0][4]'): -1000000.0, ('y[1][0]', 'x_full[2][2]'): 69675.72328, ('y[1][0]', 'B_full[3][4]'): -8000000.0, ('y[1][0]', 'x_full[4][1]'): 97431.45823999999, ('y[1][0]', 'r[2][0]'): 4000000.0, ('y[1][0]', 'x_full[3][1]'): 48715.729119999996, ('y[1][0]', 'r[0][0]'): 1000000.0, ('y[1][0]', 'x_full[1][1]'): 12178.932279999999, ('y[1][0]', 'y[2][0]'): 1000000400.0, ('y[1][0]', 'x_full[2][0]'): -62801.59376, ('y[1][0]', 'x_full[4][0]'): -251206.37504, ('y[1][0]', 'y[3][0]'): 1000000400.0, ('y[1][0]', 'r[1][0]'): 2000000.0, ('y[1][0]', 'x_full[0][0]'): -15700.39844, ('y[1][0]', 'r[4][0]'): 16000000.0, ('y[1][0]', 'y[4][0]'): 1000000400.0, ('y[1][0]', 'x_full[3][0]'): -125603.18752, ('y[1][0]', 'x_full[0][2]'): 17418.93082, ('y[1][0]', 'x_full[3][2]'): 139351.44656, ('y[1][0]', 'x_full[4][2]'): 278702.89312, ('y[1][0]', 'C[2][0]'): -1600.0, ('y[1][0]', 'C[0][0]'): -400.0, ('y[1][0]', 'y[0][0]'): 1000000400.0, ('y[1][0]', 'B_full[4][4]'): -16000000.0, ('y[1][0]', 'r[3][0]'): 8000000.0, ('B_full[2][4]', 'x_full[1][2]'): -139.35144656, ('B_full[2][4]', 'x_full[1][0]'): 125.60318751999998, ('B_full[2][4]', 'x_full[2][1]'): -97.43145824, ('B_full[2][4]', 'B_full[0][4]'): 4000.0, ('B_full[2][4]', 'x_full[2][2]'): -278.70289312, ('B_full[2][4]', 'B_full[3][4]'): 32000.0, ('B_full[2][4]', 'x_full[4][1]'): -389.72583296, ('B_full[2][4]', 'r[2][0]'): -16000.0, ('B_full[2][4]', 'x_full[3][1]'): -194.86291648, ('B_full[2][4]', 'r[0][0]'): -4000.0, ('B_full[2][4]', 'x_full[1][1]'): -48.71572912, ('B_full[2][4]', 'y[2][0]'): -4000000.0, ('B_full[2][4]', 'x_full[2][0]'): 251.20637503999995, ('B_full[2][4]', 'x_full[4][0]'): 1004.8255001599998, ('B_full[2][4]', 'y[3][0]'): -4000000.0, ('B_full[2][4]', 'r[1][0]'): -8000.0, ('B_full[2][4]', 'x_full[0][0]'): 62.80159375999999, ('B_full[2][4]', 'r[4][0]'): -64000.0, ('B_full[2][4]', 'y[4][0]'): -4000000.0, ('B_full[2][4]', 'x_full[3][0]'): 502.4127500799999, ('B_full[2][4]', 'x_full[0][2]'): -69.67572328, ('B_full[2][4]', 'x_full[3][2]'): -557.40578624, ('B_full[2][4]', 'x_full[4][2]'): -1114.81157248, ('B_full[2][4]', 'y[0][0]'): -4000000.0, ('B_full[2][4]', 'B_full[4][4]'): 64000.0, ('B_full[2][4]', 'r[3][0]'): -32000.0, ('B_full[2][4]', 'y[1][0]'): -4000000.0, ('A[3][0]', 'r[2][0]'): -6400.0, ('A[3][0]', 'r[0][0]'): -1600.0, ('A[3][0]', 'r[1][0]'): -3200.0, ('A[3][0]', 'r[4][0]'): -25600.0, ('A[3][0]', 'r[3][0]'): -12800.0, ('A[1][0]', 'r[2][0]'): -1600.0, ('A[1][0]', 'r[0][0]'): -400.0, ('A[1][0]', 'r[1][0]'): -800.0, ('A[1][0]', 'r[4][0]'): -6400.0, ('A[1][0]', 'r[3][0]'): -3200.0, ('A[1][0]', 'A[3][0]'): 3200.0, ('B_full[1][4]', 'x_full[1][2]'): -69.67572328, ('B_full[1][4]', 'x_full[1][0]'): 62.80159375999999, ('B_full[1][4]', 'x_full[2][1]'): -48.71572912, ('B_full[1][4]', 'B_full[0][4]'): 2000.0, ('B_full[1][4]', 'x_full[2][2]'): -139.35144656, ('B_full[1][4]', 'B_full[3][4]'): 16000.0, ('B_full[1][4]', 'x_full[4][1]'): -194.86291648, ('B_full[1][4]', 'r[2][0]'): -8000.0, ('B_full[1][4]', 'x_full[3][1]'): -97.43145824, ('B_full[1][4]', 'r[0][0]'): -2000.0, ('B_full[1][4]', 'x_full[1][1]'): -24.35786456, ('B_full[1][4]', 'y[2][0]'): -2000000.0, ('B_full[1][4]', 'x_full[2][0]'): 125.60318751999998, ('B_full[1][4]', 'x_full[4][0]'): 502.4127500799999, ('B_full[1][4]', 'y[3][0]'): -2000000.0, ('B_full[1][4]', 'r[1][0]'): -4000.0, ('B_full[1][4]', 'x_full[0][0]'): 31.400796879999994, ('B_full[1][4]', 'r[4][0]'): -32000.0, ('B_full[1][4]', 'y[4][0]'): -2000000.0, ('B_full[1][4]', 'x_full[3][0]'): 251.20637503999995, ('B_full[1][4]', 'x_full[0][2]'): -34.83786164, ('B_full[1][4]', 'x_full[3][2]'): -278.70289312, ('B_full[1][4]', 'x_full[4][2]'): -557.40578624, ('B_full[1][4]', 'y[0][0]'): -2000000.0, ('B_full[1][4]', 'B_full[4][4]'): 32000.0, ('B_full[1][4]', 'r[3][0]'): -16000.0, ('B_full[1][4]', 'y[1][0]'): -2000000.0, ('B_full[1][4]', 'B_full[2][4]'): 8000.0, ('x_full[0][1]', 'x_full[1][2]'): 2001.2529582399422, ('x_full[0][1]', 'x_full[1][0]'): 2000.0671656549314, ('x_full[0][1]', 'x_full[2][1]'): 4002.4601986908547, ('x_full[0][1]', 'B_full[0][4]'): -6.08946614, ('x_full[0][1]', 'x_full[2][2]'): 4002.5059164798845, ('x_full[0][1]', 'B_full[3][4]'): -48.71572912, ('x_full[0][1]', 'x_full[4][1]'): 16009.840794763419, ('x_full[0][1]', 'r[2][0]'): 24.35786456, ('x_full[0][1]', 'x_full[3][1]'): 8004.920397381709, ('x_full[0][1]', 'r[0][0]'): 6.08946614, ('x_full[0][1]', 'x_full[1][1]'): 2001.2300993454273, ('x_full[0][1]', 'y[2][0]'): 6089.4661399999995, ('x_full[0][1]', 'x_full[2][0]'): 4000.134331309863, ('x_full[0][1]', 'x_full[4][0]'): 16000.537325239451, ('x_full[0][1]', 'y[3][0]'): 6089.4661399999995, ('x_full[0][1]', 'r[1][0]'): 12.17893228, ('x_full[0][1]', 'x_full[0][0]'): 1000.0335828274657, ('x_full[0][1]', 'r[4][0]'): 97.43145824, ('x_full[0][1]', 'y[4][0]'): 6089.4661399999995, ('x_full[0][1]', 'x_full[3][0]'): 8000.268662619726, ('x_full[0][1]', 'x_full[0][2]'): 1000.6264791199711, ('x_full[0][1]', 'x_full[3][2]'): 8005.011832959769, ('x_full[0][1]', 'x_full[4][2]'): 16010.023665919538, ('x_full[0][1]', 'y[0][0]'): 6089.4661399999995, ('x_full[0][1]', 'B_full[4][4]'): -97.43145824, ('x_full[0][1]', 'r[3][0]'): 48.71572912, ('x_full[0][1]', 'y[1][0]'): 6089.4661399999995, ('x_full[0][1]', 'B_full[2][4]'): -24.35786456, ('x_full[0][1]', 'B_full[1][4]'): -12.17893228, ('A[2][0]', 'r[2][0]'): -3200.0, ('A[2][0]', 'r[0][0]'): -800.0, ('A[2][0]', 'r[1][0]'): -1600.0, ('A[2][0]', 'r[4][0]'): -12800.0, ('A[2][0]', 'r[3][0]'): -6400.0, ('A[2][0]', 'A[3][0]'): 6400.0, ('A[2][0]', 'A[1][0]'): 1600.0, ('A[0][0]', 'r[2][0]'): -800.0, ('A[0][0]', 'r[0][0]'): -200.0, ('A[0][0]', 'r[1][0]'): -400.0, ('A[0][0]', 'r[4][0]'): -3200.0, ('A[0][0]', 'r[3][0]'): -1600.0, ('A[0][0]', 'A[3][0]'): 1600.0, ('A[0][0]', 'A[1][0]'): 400.0, ('A[0][0]', 'A[2][0]'): 800.0, ('C[3][0]', 'y[2][0]'): -3200.0, ('C[3][0]', 'y[3][0]'): -3200.0, ('C[3][0]', 'y[4][0]'): -3200.0, ('C[3][0]', 'C[2][0]'): 12800.0, ('C[3][0]', 'C[0][0]'): 3200.0, ('C[3][0]', 'y[0][0]'): -3200.0, ('C[3][0]', 'y[1][0]'): -3200.0, ('C[1][0]', 'y[2][0]'): -800.0, ('C[1][0]', 'y[3][0]'): -800.0, ('C[1][0]', 'y[4][0]'): -800.0, ('C[1][0]', 'C[2][0]'): 3200.0, ('C[1][0]', 'C[0][0]'): 800.0, ('C[1][0]', 'y[0][0]'): -800.0, ('C[1][0]', 'y[1][0]'): -800.0, ('C[1][0]', 'C[3][0]'): 6400.0, ('C[4][0]', 'y[2][0]'): -6400.0, ('C[4][0]', 'y[3][0]'): -6400.0, ('C[4][0]', 'y[4][0]'): -6400.0, ('C[4][0]', 'C[2][0]'): 25600.0, ('C[4][0]', 'C[0][0]'): 6400.0, ('C[4][0]', 'y[0][0]'): -6400.0, ('C[4][0]', 'y[1][0]'): -6400.0, ('C[4][0]', 'C[3][0]'): 51200.0, ('C[4][0]', 'C[1][0]'): 12800.0, ('A[4][0]', 'r[2][0]'): -12800.0, ('A[4][0]', 'r[0][0]'): -3200.0, ('A[4][0]', 'r[1][0]'): -6400.0, ('A[4][0]', 'r[4][0]'): -51200.0, ('A[4][0]', 'r[3][0]'): -25600.0, ('A[4][0]', 'A[3][0]'): 25600.0, ('A[4][0]', 'A[1][0]'): 6400.0, ('A[4][0]', 'A[2][0]'): 12800.0, ('A[4][0]', 'A[0][0]'): 3200.0}, 500005912.5, 'BINARY')\n" + "BinaryQuadraticModel({'y[1][0]': -500001700.0, 'r[4][0]': -15836800.0, 'x[1][2]': -34834.58508411406, 'x[1][0]': 31401.908438851064, 'x[2][2]': -65662.68673217949, 'r[0][0]': -998800.0, 'x[4][0]': 363279.2732510936, 'y[3][0]': -500001700.0, 'x[3][0]': 149621.34927117964, 'r[2][0]': -3988000.0, 'x[0][0]': 15200.668479513544, 'x[2][1]': -20352.919804753736, 'b[1][4]': 2002000.0, 'x[0][2]': -17918.102971563105, 'x[3][1]': -24695.998814744024, 'x[3][2]': -115299.43972016452, 'y[0][0]': -500001700.0, 'x[4][1]': 14647.365549565578, 'y[2][0]': -500001700.0, 'c[2][0]': 10800.0, 'b[2][4]': 4008000.0, 'b[3][4]': 8032000.0, 'b[4][4]': 16128000.0, 'r[3][0]': -7956800.0, 'r[1][0]': -1996400.0, 'x[2][0]': 66806.10279699802, 'c[1][0]': 4600.0, 'y[4][0]': -500001700.0, 'x[4][2]': -166495.14446355117, 'a[3][0]': 1600.0, 'b[0][4]': 1000500.0, 'a[2][0]': -800.0, 'x[0][1]': -6589.152525697503, 'x[1][1]': -12177.690001722289, 'c[4][0]': 81600.0, 'c[0][0]': 2100.0, 'a[1][0]': -800.0, 'a[0][0]': -500.0, 'c[3][0]': 28000.0, 'a[4][0]': 16000.0}, {('r[4][0]', 'y[1][0]'): 16000000.0, ('x[1][2]', 'y[1][0]'): 34837.86164, ('x[1][2]', 'r[4][0]'): 557.40578624, ('x[1][0]', 'y[1][0]'): -31400.79688, ('x[1][0]', 'r[4][0]'): -502.4127500799999, ('x[1][0]', 'x[1][2]'): 4000.4244618825674, ('x[2][2]', 'y[1][0]'): 69675.72328, ('x[2][2]', 'r[4][0]'): 1114.81157248, ('x[2][2]', 'x[1][2]'): 8012.966872097232, ('x[2][2]', 'x[1][0]'): 8000.848923765135, ('r[0][0]', 'y[1][0]'): 1000000.0, ('r[0][0]', 'r[4][0]'): 19200.0, ('r[0][0]', 'x[1][2]'): 34.83786164, ('r[0][0]', 'x[1][0]'): -31.400796879999994, ('r[0][0]', 'x[2][2]'): 69.67572328, ('x[4][0]', 'y[1][0]'): -251206.37504, ('x[4][0]', 'r[4][0]'): -4019.3020006399993, ('x[4][0]', 'x[1][2]'): 32003.39569506054, ('x[4][0]', 'x[1][0]'): 32018.287354367145, ('x[4][0]', 'x[2][2]'): 64006.79139012108, ('x[4][0]', 'r[0][0]'): -251.20637503999995, ('y[3][0]', 'y[1][0]'): 1000000400.0, ('y[3][0]', 'r[4][0]'): 16000000.0, ('y[3][0]', 'x[1][2]'): 34837.86164, ('y[3][0]', 'x[1][0]'): -31400.79688, ('y[3][0]', 'x[2][2]'): 69675.72328, ('y[3][0]', 'r[0][0]'): 1000000.0, ('y[3][0]', 'x[4][0]'): -251206.37504, ('x[3][0]', 'y[1][0]'): -125603.18752, ('x[3][0]', 'r[4][0]'): -2009.6510003199996, ('x[3][0]', 'x[1][2]'): 16001.69784753027, ('x[3][0]', 'x[1][0]'): 16009.143677183572, ('x[3][0]', 'x[2][2]'): 32003.39569506054, ('x[3][0]', 'r[0][0]'): -125.60318751999998, ('x[3][0]', 'x[4][0]'): 128073.14941746858, ('x[3][0]', 'y[3][0]'): -125603.18752, ('r[2][0]', 'y[1][0]'): 4000000.0, ('r[2][0]', 'r[4][0]'): 76800.0, ('r[2][0]', 'x[1][2]'): 139.35144656, ('r[2][0]', 'x[1][0]'): -125.60318751999998, ('r[2][0]', 'x[2][2]'): 278.70289312, ('r[2][0]', 'r[0][0]'): 4800.0, ('r[2][0]', 'x[4][0]'): -1004.8255001599998, ('r[2][0]', 'y[3][0]'): 4000000.0, ('r[2][0]', 'x[3][0]'): -502.4127500799999, ('x[0][0]', 'y[1][0]'): -15700.39844, ('x[0][0]', 'r[4][0]'): -251.20637503999995, ('x[0][0]', 'x[1][2]'): 2000.2122309412837, ('x[0][0]', 'x[1][0]'): 2001.1429596479466, ('x[0][0]', 'x[2][2]'): 4000.4244618825674, ('x[0][0]', 'r[0][0]'): -15.700398439999997, ('x[0][0]', 'x[4][0]'): 16009.143677183572, ('x[0][0]', 'y[3][0]'): -15700.39844, ('x[0][0]', 'x[3][0]'): 8004.571838591786, ('x[0][0]', 'r[2][0]'): -62.80159375999999, ('x[2][1]', 'y[1][0]'): 24357.864559999998, ('x[2][1]', 'r[4][0]'): 389.72583296, ('x[2][1]', 'x[1][2]'): 8005.011832959769, ('x[2][1]', 'x[1][0]'): 8000.268662619726, ('x[2][1]', 'x[2][2]'): 16010.023665919538, ('x[2][1]', 'r[0][0]'): 24.35786456, ('x[2][1]', 'x[4][0]'): 64002.149300957804, ('x[2][1]', 'y[3][0]'): 24357.864559999998, ('x[2][1]', 'x[3][0]'): 32001.074650478902, ('x[2][1]', 'r[2][0]'): 97.43145824, ('x[2][1]', 'x[0][0]'): 4000.134331309863, ('b[1][4]', 'y[1][0]'): -2000000.0, ('b[1][4]', 'r[4][0]'): -32000.0, ('b[1][4]', 'x[1][2]'): -69.67572328, ('b[1][4]', 'x[1][0]'): 62.80159375999999, ('b[1][4]', 'x[2][2]'): -139.35144656, ('b[1][4]', 'r[0][0]'): -2000.0, ('b[1][4]', 'x[4][0]'): 502.4127500799999, ('b[1][4]', 'y[3][0]'): -2000000.0, ('b[1][4]', 'x[3][0]'): 251.20637503999995, ('b[1][4]', 'r[2][0]'): -8000.0, ('b[1][4]', 'x[0][0]'): 31.400796879999994, ('b[1][4]', 'x[2][1]'): -48.71572912, ('x[0][2]', 'y[1][0]'): 17418.93082, ('x[0][2]', 'r[4][0]'): 278.70289312, ('x[0][2]', 'x[1][2]'): 2003.241718024308, ('x[0][2]', 'x[1][0]'): 2000.2122309412837, ('x[0][2]', 'x[2][2]'): 4006.483436048616, ('x[0][2]', 'r[0][0]'): 17.41893082, ('x[0][2]', 'x[4][0]'): 16001.69784753027, ('x[0][2]', 'y[3][0]'): 17418.93082, ('x[0][2]', 'x[3][0]'): 8000.848923765135, ('x[0][2]', 'r[2][0]'): 69.67572328, ('x[0][2]', 'x[0][0]'): 1000.1061154706418, ('x[0][2]', 'x[2][1]'): 4002.5059164798845, ('x[0][2]', 'b[1][4]'): -34.83786164, ('x[3][1]', 'y[1][0]'): 48715.729119999996, ('x[3][1]', 'r[4][0]'): 779.45166592, ('x[3][1]', 'x[1][2]'): 16010.023665919538, ('x[3][1]', 'x[1][0]'): 16000.537325239451, ('x[3][1]', 'x[2][2]'): 32020.047331839076, ('x[3][1]', 'r[0][0]'): 48.71572912, ('x[3][1]', 'x[4][0]'): 128004.29860191561, ('x[3][1]', 'y[3][0]'): 48715.729119999996, ('x[3][1]', 'x[3][0]'): 64002.149300957804, ('x[3][1]', 'r[2][0]'): 194.86291648, ('x[3][1]', 'x[0][0]'): 8000.268662619726, ('x[3][1]', 'x[2][1]'): 32019.681589526837, ('x[3][1]', 'b[1][4]'): -97.43145824, ('x[3][1]', 'x[0][2]'): 8005.011832959769, ('x[3][2]', 'y[1][0]'): 139351.44656, ('x[3][2]', 'r[4][0]'): 2229.62314496, ('x[3][2]', 'x[1][2]'): 16025.933744194464, ('x[3][2]', 'x[1][0]'): 16001.69784753027, ('x[3][2]', 'x[2][2]'): 32051.86748838893, ('x[3][2]', 'r[0][0]'): 139.35144656, ('x[3][2]', 'x[4][0]'): 128013.58278024216, ('x[3][2]', 'y[3][0]'): 139351.44656, ('x[3][2]', 'x[3][0]'): 64006.79139012108, ('x[3][2]', 'r[2][0]'): 557.40578624, ('x[3][2]', 'x[0][0]'): 8000.848923765135, ('x[3][2]', 'x[2][1]'): 32020.047331839076, ('x[3][2]', 'b[1][4]'): -278.70289312, ('x[3][2]', 'x[0][2]'): 8012.966872097232, ('x[3][2]', 'x[3][1]'): 64040.09466367815, ('y[0][0]', 'y[1][0]'): 1000000400.0, ('y[0][0]', 'r[4][0]'): 16000000.0, ('y[0][0]', 'x[1][2]'): 34837.86164, ('y[0][0]', 'x[1][0]'): -31400.79688, ('y[0][0]', 'x[2][2]'): 69675.72328, ('y[0][0]', 'r[0][0]'): 1000000.0, ('y[0][0]', 'x[4][0]'): -251206.37504, ('y[0][0]', 'y[3][0]'): 1000000400.0, ('y[0][0]', 'x[3][0]'): -125603.18752, ('y[0][0]', 'r[2][0]'): 4000000.0, ('y[0][0]', 'x[0][0]'): -15700.39844, ('y[0][0]', 'x[2][1]'): 24357.864559999998, ('y[0][0]', 'b[1][4]'): -2000000.0, ('y[0][0]', 'x[0][2]'): 17418.93082, ('y[0][0]', 'x[3][1]'): 48715.729119999996, ('y[0][0]', 'x[3][2]'): 139351.44656, ('x[4][1]', 'y[1][0]'): 97431.45823999999, ('x[4][1]', 'r[4][0]'): 1558.90333184, ('x[4][1]', 'x[1][2]'): 32020.047331839076, ('x[4][1]', 'x[1][0]'): 32001.074650478902, ('x[4][1]', 'x[2][2]'): 64040.09466367815, ('x[4][1]', 'r[0][0]'): 97.43145824, ('x[4][1]', 'x[4][0]'): 256008.59720383122, ('x[4][1]', 'y[3][0]'): 97431.45823999999, ('x[4][1]', 'x[3][0]'): 128004.29860191561, ('x[4][1]', 'r[2][0]'): 389.72583296, ('x[4][1]', 'x[0][0]'): 16000.537325239451, ('x[4][1]', 'x[2][1]'): 64039.363179053675, ('x[4][1]', 'b[1][4]'): -194.86291648, ('x[4][1]', 'x[0][2]'): 16010.023665919538, ('x[4][1]', 'x[3][1]'): 128078.72635810735, ('x[4][1]', 'x[3][2]'): 128080.1893273563, ('x[4][1]', 'y[0][0]'): 97431.45823999999, ('y[2][0]', 'y[1][0]'): 1000000400.0, ('y[2][0]', 'r[4][0]'): 16000000.0, ('y[2][0]', 'x[1][2]'): 34837.86164, ('y[2][0]', 'x[1][0]'): -31400.79688, ('y[2][0]', 'x[2][2]'): 69675.72328, ('y[2][0]', 'r[0][0]'): 1000000.0, ('y[2][0]', 'x[4][0]'): -251206.37504, ('y[2][0]', 'y[3][0]'): 1000000400.0, ('y[2][0]', 'x[3][0]'): -125603.18752, ('y[2][0]', 'r[2][0]'): 4000000.0, ('y[2][0]', 'x[0][0]'): -15700.39844, ('y[2][0]', 'x[2][1]'): 24357.864559999998, ('y[2][0]', 'b[1][4]'): -2000000.0, ('y[2][0]', 'x[0][2]'): 17418.93082, ('y[2][0]', 'x[3][1]'): 48715.729119999996, ('y[2][0]', 'x[3][2]'): 139351.44656, ('y[2][0]', 'y[0][0]'): 1000000400.0, ('y[2][0]', 'x[4][1]'): 97431.45823999999, ('c[2][0]', 'y[1][0]'): -1600.0, ('c[2][0]', 'y[3][0]'): -1600.0, ('c[2][0]', 'y[0][0]'): -1600.0, ('c[2][0]', 'y[2][0]'): -1600.0, ('b[2][4]', 'y[1][0]'): -4000000.0, ('b[2][4]', 'r[4][0]'): -64000.0, ('b[2][4]', 'x[1][2]'): -139.35144656, ('b[2][4]', 'x[1][0]'): 125.60318751999998, ('b[2][4]', 'x[2][2]'): -278.70289312, ('b[2][4]', 'r[0][0]'): -4000.0, ('b[2][4]', 'x[4][0]'): 1004.8255001599998, ('b[2][4]', 'y[3][0]'): -4000000.0, ('b[2][4]', 'x[3][0]'): 502.4127500799999, ('b[2][4]', 'r[2][0]'): -16000.0, ('b[2][4]', 'x[0][0]'): 62.80159375999999, ('b[2][4]', 'x[2][1]'): -97.43145824, ('b[2][4]', 'b[1][4]'): 8000.0, ('b[2][4]', 'x[0][2]'): -69.67572328, ('b[2][4]', 'x[3][1]'): -194.86291648, ('b[2][4]', 'x[3][2]'): -557.40578624, ('b[2][4]', 'y[0][0]'): -4000000.0, ('b[2][4]', 'x[4][1]'): -389.72583296, ('b[2][4]', 'y[2][0]'): -4000000.0, ('b[3][4]', 'y[1][0]'): -8000000.0, ('b[3][4]', 'r[4][0]'): -128000.0, ('b[3][4]', 'x[1][2]'): -278.70289312, ('b[3][4]', 'x[1][0]'): 251.20637503999995, ('b[3][4]', 'x[2][2]'): -557.40578624, ('b[3][4]', 'r[0][0]'): -8000.0, ('b[3][4]', 'x[4][0]'): 2009.6510003199996, ('b[3][4]', 'y[3][0]'): -8000000.0, ('b[3][4]', 'x[3][0]'): 1004.8255001599998, ('b[3][4]', 'r[2][0]'): -32000.0, ('b[3][4]', 'x[0][0]'): 125.60318751999998, ('b[3][4]', 'x[2][1]'): -194.86291648, ('b[3][4]', 'b[1][4]'): 16000.0, ('b[3][4]', 'x[0][2]'): -139.35144656, ('b[3][4]', 'x[3][1]'): -389.72583296, ('b[3][4]', 'x[3][2]'): -1114.81157248, ('b[3][4]', 'y[0][0]'): -8000000.0, ('b[3][4]', 'x[4][1]'): -779.45166592, ('b[3][4]', 'y[2][0]'): -8000000.0, ('b[3][4]', 'b[2][4]'): 32000.0, ('b[4][4]', 'y[1][0]'): -16000000.0, ('b[4][4]', 'r[4][0]'): -256000.0, ('b[4][4]', 'x[1][2]'): -557.40578624, ('b[4][4]', 'x[1][0]'): 502.4127500799999, ('b[4][4]', 'x[2][2]'): -1114.81157248, ('b[4][4]', 'r[0][0]'): -16000.0, ('b[4][4]', 'x[4][0]'): 4019.3020006399993, ('b[4][4]', 'y[3][0]'): -16000000.0, ('b[4][4]', 'x[3][0]'): 2009.6510003199996, ('b[4][4]', 'r[2][0]'): -64000.0, ('b[4][4]', 'x[0][0]'): 251.20637503999995, ('b[4][4]', 'x[2][1]'): -389.72583296, ('b[4][4]', 'b[1][4]'): 32000.0, ('b[4][4]', 'x[0][2]'): -278.70289312, ('b[4][4]', 'x[3][1]'): -779.45166592, ('b[4][4]', 'x[3][2]'): -2229.62314496, ('b[4][4]', 'y[0][0]'): -16000000.0, ('b[4][4]', 'x[4][1]'): -1558.90333184, ('b[4][4]', 'y[2][0]'): -16000000.0, ('b[4][4]', 'b[2][4]'): 64000.0, ('b[4][4]', 'b[3][4]'): 128000.0, ('r[3][0]', 'y[1][0]'): 8000000.0, ('r[3][0]', 'r[4][0]'): 153600.0, ('r[3][0]', 'x[1][2]'): 278.70289312, ('r[3][0]', 'x[1][0]'): -251.20637503999995, ('r[3][0]', 'x[2][2]'): 557.40578624, ('r[3][0]', 'r[0][0]'): 9600.0, ('r[3][0]', 'x[4][0]'): -2009.6510003199996, ('r[3][0]', 'y[3][0]'): 8000000.0, ('r[3][0]', 'x[3][0]'): -1004.8255001599998, ('r[3][0]', 'r[2][0]'): 38400.0, ('r[3][0]', 'x[0][0]'): -125.60318751999998, ('r[3][0]', 'x[2][1]'): 194.86291648, ('r[3][0]', 'b[1][4]'): -16000.0, ('r[3][0]', 'x[0][2]'): 139.35144656, ('r[3][0]', 'x[3][1]'): 389.72583296, ('r[3][0]', 'x[3][2]'): 1114.81157248, ('r[3][0]', 'y[0][0]'): 8000000.0, ('r[3][0]', 'x[4][1]'): 779.45166592, ('r[3][0]', 'y[2][0]'): 8000000.0, ('r[3][0]', 'b[2][4]'): -32000.0, ('r[3][0]', 'b[3][4]'): -64000.0, ('r[3][0]', 'b[4][4]'): -128000.0, ('r[1][0]', 'y[1][0]'): 2000000.0, ('r[1][0]', 'r[4][0]'): 38400.0, ('r[1][0]', 'x[1][2]'): 69.67572328, ('r[1][0]', 'x[1][0]'): -62.80159375999999, ('r[1][0]', 'x[2][2]'): 139.35144656, ('r[1][0]', 'r[0][0]'): 2400.0, ('r[1][0]', 'x[4][0]'): -502.4127500799999, ('r[1][0]', 'y[3][0]'): 2000000.0, ('r[1][0]', 'x[3][0]'): -251.20637503999995, ('r[1][0]', 'r[2][0]'): 9600.0, ('r[1][0]', 'x[0][0]'): -31.400796879999994, ('r[1][0]', 'x[2][1]'): 48.71572912, ('r[1][0]', 'b[1][4]'): -4000.0, ('r[1][0]', 'x[0][2]'): 34.83786164, ('r[1][0]', 'x[3][1]'): 97.43145824, ('r[1][0]', 'x[3][2]'): 278.70289312, ('r[1][0]', 'y[0][0]'): 2000000.0, ('r[1][0]', 'x[4][1]'): 194.86291648, ('r[1][0]', 'y[2][0]'): 2000000.0, ('r[1][0]', 'b[2][4]'): -8000.0, ('r[1][0]', 'b[3][4]'): -16000.0, ('r[1][0]', 'b[4][4]'): -32000.0, ('r[1][0]', 'r[3][0]'): 19200.0, ('x[2][0]', 'y[1][0]'): -62801.59376, ('x[2][0]', 'r[4][0]'): -1004.8255001599998, ('x[2][0]', 'x[1][2]'): 8000.848923765135, ('x[2][0]', 'x[1][0]'): 8004.571838591786, ('x[2][0]', 'x[2][2]'): 16001.69784753027, ('x[2][0]', 'r[0][0]'): -62.80159375999999, ('x[2][0]', 'x[4][0]'): 64036.57470873429, ('x[2][0]', 'y[3][0]'): -62801.59376, ('x[2][0]', 'x[3][0]'): 32018.287354367145, ('x[2][0]', 'r[2][0]'): -251.20637503999995, ('x[2][0]', 'x[0][0]'): 4002.285919295893, ('x[2][0]', 'x[2][1]'): 16000.537325239451, ('x[2][0]', 'b[1][4]'): 125.60318751999998, ('x[2][0]', 'x[0][2]'): 4000.4244618825674, ('x[2][0]', 'x[3][1]'): 32001.074650478902, ('x[2][0]', 'x[3][2]'): 32003.39569506054, ('x[2][0]', 'y[0][0]'): -62801.59376, ('x[2][0]', 'x[4][1]'): 64002.149300957804, ('x[2][0]', 'y[2][0]'): -62801.59376, ('x[2][0]', 'b[2][4]'): 251.20637503999995, ('x[2][0]', 'b[3][4]'): 502.4127500799999, ('x[2][0]', 'b[4][4]'): 1004.8255001599998, ('x[2][0]', 'r[3][0]'): -502.4127500799999, ('x[2][0]', 'r[1][0]'): -125.60318751999998, ('c[1][0]', 'y[1][0]'): -800.0, ('c[1][0]', 'y[3][0]'): -800.0, ('c[1][0]', 'y[0][0]'): -800.0, ('c[1][0]', 'y[2][0]'): -800.0, ('c[1][0]', 'c[2][0]'): 3200.0, ('y[4][0]', 'y[1][0]'): 1000000400.0, ('y[4][0]', 'r[4][0]'): 16000000.0, ('y[4][0]', 'x[1][2]'): 34837.86164, ('y[4][0]', 'x[1][0]'): -31400.79688, ('y[4][0]', 'x[2][2]'): 69675.72328, ('y[4][0]', 'r[0][0]'): 1000000.0, ('y[4][0]', 'x[4][0]'): -251206.37504, ('y[4][0]', 'y[3][0]'): 1000000400.0, ('y[4][0]', 'x[3][0]'): -125603.18752, ('y[4][0]', 'r[2][0]'): 4000000.0, ('y[4][0]', 'x[0][0]'): -15700.39844, ('y[4][0]', 'x[2][1]'): 24357.864559999998, ('y[4][0]', 'b[1][4]'): -2000000.0, ('y[4][0]', 'x[0][2]'): 17418.93082, ('y[4][0]', 'x[3][1]'): 48715.729119999996, ('y[4][0]', 'x[3][2]'): 139351.44656, ('y[4][0]', 'y[0][0]'): 1000000400.0, ('y[4][0]', 'x[4][1]'): 97431.45823999999, ('y[4][0]', 'y[2][0]'): 1000000400.0, ('y[4][0]', 'c[2][0]'): -1600.0, ('y[4][0]', 'b[2][4]'): -4000000.0, ('y[4][0]', 'b[3][4]'): -8000000.0, ('y[4][0]', 'b[4][4]'): -16000000.0, ('y[4][0]', 'r[3][0]'): 8000000.0, ('y[4][0]', 'r[1][0]'): 2000000.0, ('y[4][0]', 'x[2][0]'): -62801.59376, ('y[4][0]', 'c[1][0]'): -800.0, ('x[4][2]', 'y[1][0]'): 278702.89312, ('x[4][2]', 'r[4][0]'): 4459.24628992, ('x[4][2]', 'x[1][2]'): 32051.86748838893, ('x[4][2]', 'x[1][0]'): 32003.39569506054, ('x[4][2]', 'x[2][2]'): 64103.73497677786, ('x[4][2]', 'r[0][0]'): 278.70289312, ('x[4][2]', 'x[4][0]'): 256027.1655604843, ('x[4][2]', 'y[3][0]'): 278702.89312, ('x[4][2]', 'x[3][0]'): 128013.58278024216, ('x[4][2]', 'r[2][0]'): 1114.81157248, ('x[4][2]', 'x[0][0]'): 16001.69784753027, ('x[4][2]', 'x[2][1]'): 64040.09466367815, ('x[4][2]', 'b[1][4]'): -557.40578624, ('x[4][2]', 'x[0][2]'): 16025.933744194464, ('x[4][2]', 'x[3][1]'): 128080.1893273563, ('x[4][2]', 'x[3][2]'): 128207.46995355572, ('x[4][2]', 'y[0][0]'): 278702.89312, ('x[4][2]', 'x[4][1]'): 256160.3786547126, ('x[4][2]', 'y[2][0]'): 278702.89312, ('x[4][2]', 'b[2][4]'): -1114.81157248, ('x[4][2]', 'b[3][4]'): -2229.62314496, ('x[4][2]', 'b[4][4]'): -4459.24628992, ('x[4][2]', 'r[3][0]'): 2229.62314496, ('x[4][2]', 'r[1][0]'): 557.40578624, ('x[4][2]', 'x[2][0]'): 64006.79139012108, ('x[4][2]', 'y[4][0]'): 278702.89312, ('a[3][0]', 'r[4][0]'): -25600.0, ('a[3][0]', 'r[0][0]'): -1600.0, ('a[3][0]', 'r[2][0]'): -6400.0, ('a[3][0]', 'r[3][0]'): -12800.0, ('a[3][0]', 'r[1][0]'): -3200.0, ('b[0][4]', 'y[1][0]'): -1000000.0, ('b[0][4]', 'r[4][0]'): -16000.0, ('b[0][4]', 'x[1][2]'): -34.83786164, ('b[0][4]', 'x[1][0]'): 31.400796879999994, ('b[0][4]', 'x[2][2]'): -69.67572328, ('b[0][4]', 'r[0][0]'): -1000.0, ('b[0][4]', 'x[4][0]'): 251.20637503999995, ('b[0][4]', 'y[3][0]'): -1000000.0, ('b[0][4]', 'x[3][0]'): 125.60318751999998, ('b[0][4]', 'r[2][0]'): -4000.0, ('b[0][4]', 'x[0][0]'): 15.700398439999997, ('b[0][4]', 'x[2][1]'): -24.35786456, ('b[0][4]', 'b[1][4]'): 2000.0, ('b[0][4]', 'x[0][2]'): -17.41893082, ('b[0][4]', 'x[3][1]'): -48.71572912, ('b[0][4]', 'x[3][2]'): -139.35144656, ('b[0][4]', 'y[0][0]'): -1000000.0, ('b[0][4]', 'x[4][1]'): -97.43145824, ('b[0][4]', 'y[2][0]'): -1000000.0, ('b[0][4]', 'b[2][4]'): 4000.0, ('b[0][4]', 'b[3][4]'): 8000.0, ('b[0][4]', 'b[4][4]'): 16000.0, ('b[0][4]', 'r[3][0]'): -8000.0, ('b[0][4]', 'r[1][0]'): -2000.0, ('b[0][4]', 'x[2][0]'): 62.80159375999999, ('b[0][4]', 'y[4][0]'): -1000000.0, ('b[0][4]', 'x[4][2]'): -278.70289312, ('a[2][0]', 'r[4][0]'): -12800.0, ('a[2][0]', 'r[0][0]'): -800.0, ('a[2][0]', 'r[2][0]'): -3200.0, ('a[2][0]', 'r[3][0]'): -6400.0, ('a[2][0]', 'r[1][0]'): -1600.0, ('a[2][0]', 'a[3][0]'): 6400.0, ('x[0][1]', 'y[1][0]'): 6089.4661399999995, ('x[0][1]', 'r[4][0]'): 97.43145824, ('x[0][1]', 'x[1][2]'): 2001.2529582399422, ('x[0][1]', 'x[1][0]'): 2000.0671656549314, ('x[0][1]', 'x[2][2]'): 4002.5059164798845, ('x[0][1]', 'r[0][0]'): 6.08946614, ('x[0][1]', 'x[4][0]'): 16000.537325239451, ('x[0][1]', 'y[3][0]'): 6089.4661399999995, ('x[0][1]', 'x[3][0]'): 8000.268662619726, ('x[0][1]', 'r[2][0]'): 24.35786456, ('x[0][1]', 'x[0][0]'): 1000.0335828274657, ('x[0][1]', 'x[2][1]'): 4002.4601986908547, ('x[0][1]', 'b[1][4]'): -12.17893228, ('x[0][1]', 'x[0][2]'): 1000.6264791199711, ('x[0][1]', 'x[3][1]'): 8004.920397381709, ('x[0][1]', 'x[3][2]'): 8005.011832959769, ('x[0][1]', 'y[0][0]'): 6089.4661399999995, ('x[0][1]', 'x[4][1]'): 16009.840794763419, ('x[0][1]', 'y[2][0]'): 6089.4661399999995, ('x[0][1]', 'b[2][4]'): -24.35786456, ('x[0][1]', 'b[3][4]'): -48.71572912, ('x[0][1]', 'b[4][4]'): -97.43145824, ('x[0][1]', 'r[3][0]'): 48.71572912, ('x[0][1]', 'r[1][0]'): 12.17893228, ('x[0][1]', 'x[2][0]'): 4000.134331309863, ('x[0][1]', 'y[4][0]'): 6089.4661399999995, ('x[0][1]', 'x[4][2]'): 16010.023665919538, ('x[0][1]', 'b[0][4]'): -6.08946614, ('x[1][1]', 'y[1][0]'): 12178.932279999999, ('x[1][1]', 'r[4][0]'): 194.86291648, ('x[1][1]', 'x[1][2]'): 4002.5059164798845, ('x[1][1]', 'x[1][0]'): 4000.134331309863, ('x[1][1]', 'x[2][2]'): 8005.011832959769, ('x[1][1]', 'r[0][0]'): 12.17893228, ('x[1][1]', 'x[4][0]'): 32001.074650478902, ('x[1][1]', 'y[3][0]'): 12178.932279999999, ('x[1][1]', 'x[3][0]'): 16000.537325239451, ('x[1][1]', 'r[2][0]'): 48.71572912, ('x[1][1]', 'x[0][0]'): 2000.0671656549314, ('x[1][1]', 'x[2][1]'): 8004.920397381709, ('x[1][1]', 'b[1][4]'): -24.35786456, ('x[1][1]', 'x[0][2]'): 2001.2529582399422, ('x[1][1]', 'x[3][1]'): 16009.840794763419, ('x[1][1]', 'x[3][2]'): 16010.023665919538, ('x[1][1]', 'y[0][0]'): 12178.932279999999, ('x[1][1]', 'x[4][1]'): 32019.681589526837, ('x[1][1]', 'y[2][0]'): 12178.932279999999, ('x[1][1]', 'b[2][4]'): -48.71572912, ('x[1][1]', 'b[3][4]'): -97.43145824, ('x[1][1]', 'b[4][4]'): -194.86291648, ('x[1][1]', 'r[3][0]'): 97.43145824, ('x[1][1]', 'r[1][0]'): 24.35786456, ('x[1][1]', 'x[2][0]'): 8000.268662619726, ('x[1][1]', 'y[4][0]'): 12178.932279999999, ('x[1][1]', 'x[4][2]'): 32020.047331839076, ('x[1][1]', 'b[0][4]'): -12.17893228, ('x[1][1]', 'x[0][1]'): 2001.2300993454273, ('c[4][0]', 'y[1][0]'): -6400.0, ('c[4][0]', 'y[3][0]'): -6400.0, ('c[4][0]', 'y[0][0]'): -6400.0, ('c[4][0]', 'y[2][0]'): -6400.0, ('c[4][0]', 'c[2][0]'): 25600.0, ('c[4][0]', 'c[1][0]'): 12800.0, ('c[4][0]', 'y[4][0]'): -6400.0, ('c[0][0]', 'y[1][0]'): -400.0, ('c[0][0]', 'y[3][0]'): -400.0, ('c[0][0]', 'y[0][0]'): -400.0, ('c[0][0]', 'y[2][0]'): -400.0, ('c[0][0]', 'c[2][0]'): 1600.0, ('c[0][0]', 'c[1][0]'): 800.0, ('c[0][0]', 'y[4][0]'): -400.0, ('c[0][0]', 'c[4][0]'): 6400.0, ('a[1][0]', 'r[4][0]'): -6400.0, ('a[1][0]', 'r[0][0]'): -400.0, ('a[1][0]', 'r[2][0]'): -1600.0, ('a[1][0]', 'r[3][0]'): -3200.0, ('a[1][0]', 'r[1][0]'): -800.0, ('a[1][0]', 'a[3][0]'): 3200.0, ('a[1][0]', 'a[2][0]'): 1600.0, ('a[0][0]', 'r[4][0]'): -3200.0, ('a[0][0]', 'r[0][0]'): -200.0, ('a[0][0]', 'r[2][0]'): -800.0, ('a[0][0]', 'r[3][0]'): -1600.0, ('a[0][0]', 'r[1][0]'): -400.0, ('a[0][0]', 'a[3][0]'): 1600.0, ('a[0][0]', 'a[2][0]'): 800.0, ('a[0][0]', 'a[1][0]'): 400.0, ('c[3][0]', 'y[1][0]'): -3200.0, ('c[3][0]', 'y[3][0]'): -3200.0, ('c[3][0]', 'y[0][0]'): -3200.0, ('c[3][0]', 'y[2][0]'): -3200.0, ('c[3][0]', 'c[2][0]'): 12800.0, ('c[3][0]', 'c[1][0]'): 6400.0, ('c[3][0]', 'y[4][0]'): -3200.0, ('c[3][0]', 'c[4][0]'): 51200.0, ('c[3][0]', 'c[0][0]'): 3200.0, ('a[4][0]', 'r[4][0]'): -51200.0, ('a[4][0]', 'r[0][0]'): -3200.0, ('a[4][0]', 'r[2][0]'): -12800.0, ('a[4][0]', 'r[3][0]'): -25600.0, ('a[4][0]', 'r[1][0]'): -6400.0, ('a[4][0]', 'a[3][0]'): 25600.0, ('a[4][0]', 'a[2][0]'): 12800.0, ('a[4][0]', 'a[1][0]'): 6400.0, ('a[4][0]', 'a[0][0]'): 3200.0}, 500005912.5, 'BINARY')\n" ] } ], @@ -592,10 +592,10 @@ "\n", "\n", "\n", - "x_full = Array.create(\"x_full\", shape=(D_X, N), vartype=\"BINARY\")\n", - "A = Array.create(\"A\", shape=(D_A, 1), vartype=\"BINARY\")\n", - "C = Array.create(\"C\", shape=(D_B, 1), vartype=\"BINARY\")\n", - "B = Array.create(\"B_full\", shape=(D_C, T), vartype=\"BINARY\")\n", + "x_full = Array.create(\"x\", shape=(D_X, N), vartype=\"BINARY\")\n", + "A = Array.create(\"a\", shape=(D_A, 1), vartype=\"BINARY\")\n", + "C = Array.create(\"c\", shape=(D_B, 1), vartype=\"BINARY\")\n", + "B = Array.create(\"b\", shape=(D_C, T), vartype=\"BINARY\")\n", "r = Array.create(\"r\", shape=(D_R, 1), vartype=\"BINARY\")\n", "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", "\n", @@ -861,7 +861,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 50, "id": "a359a291-bbb0-4f4f-97fd-acd5f90d8eb6", "metadata": {}, "outputs": [ @@ -869,9 +869,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'A[0][0]': 0, 'A[1][0]': 1, 'A[2][0]': 1, 'A[3][0]': 1, 'B_full[1][4]': 1, 'x_full[4][1]': 0, 'A[4][0]': 0, 'B_full[0][4]': 1, 'x_full[2][2]': 0, 'B_full[2][4]': 0, 'B_full[3][4]': 1, 'y[1][0]': 0, 'x_full[4][2]': 0, 'B_full[4][4]': 0, 'r[1][0]': 1, 'C[0][0]': 0, 'r[0][0]': 1, 'C[1][0]': 0, 'r[3][0]': 1, 'C[2][0]': 0, 'x_full[0][2]': 0, 'r[2][0]': 0, 'C[3][0]': 0, 'x_full[3][2]': 0, 'C[4][0]': 0, 'r[4][0]': 0, 'x_full[0][0]': 1, 'x_full[0][1]': 0, 'x_full[2][1]': 0, 'x_full[1][0]': 0, 'x_full[2][0]': 0, 'x_full[1][1]': 0, 'x_full[1][2]': 0, 'x_full[3][0]': 0, 'x_full[3][1]': 0, 'x_full[4][0]': 0, 'y[0][0]': 1, 'y[2][0]': 0, 'y[3][0]': 0, 'y[4][0]': 0}\n", - "\n", - "['x_full[0][0]', 'x_full[0][1]', 'x_full[0][2]', 'x_full[1][0]', 'x_full[1][1]', 'x_full[1][2]', 'x_full[2][0]', 'x_full[2][1]', 'x_full[2][2]', 'x_full[3][0]', 'x_full[3][1]', 'x_full[3][2]', 'x_full[4][0]', 'x_full[4][1]', 'x_full[4][2]']\n" + "['x[0][0]', 'x[0][1]', 'x[0][2]', 'x[1][0]', 'x[1][1]', 'x[1][2]', 'x[2][0]', 'x[2][1]', 'x[2][2]', 'x[3][0]', 'x[3][1]', 'x[3][2]', 'x[4][0]', 'x[4][1]', 'x[4][2]']\n", + "['a[0][0]', 'a[1][0]', 'a[2][0]', 'a[3][0]', 'a[4][0]']\n", + "['b[0][4]', 'b[1][4]', 'b[2][4]', 'b[3][4]', 'b[4][4]']\n", + "['c[0][0]', 'c[1][0]', 'c[2][0]', 'c[3][0]', 'c[4][0]']\n", + "['r[0][0]', 'r[1][0]', 'r[2][0]', 'r[3][0]', 'r[4][0]']\n", + "['y[0][0]', 'y[1][0]', 'y[2][0]', 'y[3][0]', 'y[4][0]']\n", + "[1.0, 0.0, 0.0]\n", + "[0.5]\n", + "0.01570039844\n", + "0.0004052086194496182\n" ] } ], @@ -885,21 +892,66 @@ "sampleset = sampler.sample(Q)\n", "samples = model.decode_sampleset(sampleset)\n", "best_sample = min(samples, key=lambda s: s.energy)\n", - "print(best_sample.sample)\n", "\n", - "print(type(best_sample.sample))\n", + "sample_dict = best_sample.sample\n", + "# print(sample_dict)\n", + "\n", + "\n", "x_full_stuff = []\n", "a_stuff = []\n", "b_stuff = []\n", "c_stuff = []\n", + "r_stuff = []\n", + "y_stuff = []\n", + "\n", "for key, value in best_sample.sample.items():\n", " if key[0] == \"x\":\n", " x_full_stuff.append(key)\n", + " elif key[0] == \"a\":\n", + " a_stuff.append(key)\n", + " elif key[0] == \"b\":\n", + " b_stuff.append(key)\n", + " elif key[0] == \"c\":\n", + " c_stuff.append(key)\n", + " elif key[0] == \"r\":\n", + " r_stuff.append(key)\n", + " elif key[0] == \"y\":\n", + " y_stuff.append(key)\n", "x_full_stuff.sort()\n", + "a_stuff.sort()\n", + "b_stuff.sort()\n", + "c_stuff.sort()\n", + "r_stuff.sort()\n", + "y_stuff.sort()\n", + "\n", "print(x_full_stuff)\n", + "print(a_stuff)\n", + "print(b_stuff)\n", + "print(c_stuff)\n", + "print(r_stuff)\n", + "print(y_stuff)\n", "\n", "\n", - "# def reassemble_x_weights():\n" + "'''\n", + "x_weights = []\n", + "for i in range(D_X):\n", + " x_weight = 0\n", + " for ii in range(N):\n", + " x_weight += sample_dict['x[' + str(i) + \"][\" + str(ii) + \"]\"] * 1/(2**(ii))\n", + " x_weights.append(x_weight)\n", + "print(x_weights)\n", + "'''\n", + "def reconstruct_weights(letter, D_value, column_value):\n", + " weights = []\n", + " for i in range(column_value):\n", + " weight = 0\n", + " for ii in range(D_value):\n", + " weight += sample_dict[letter + '[' + str(ii) + \"][\" + str(i) + \"]\"] * 1/(2**(ii))\n", + " weights.append(weight)\n", + " print(weights)\n", + " return weights\n", + "\n", + "\n" ] }, { @@ -928,7 +980,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 52, "id": "95761549", "metadata": {}, "outputs": [], @@ -939,7 +991,8 @@ "\n", " VaR = np.percentile(portfolio_returns, 100 * (1 - confidence_level))\n", " \n", - " return -VaR" + " return -VaR\n", + "\n" ] }, { @@ -952,12 +1005,39 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 54, "id": "7c6d5972", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1.0, 0.0, 0.0]\n", + "[0.5]\n", + "0.01570039844\n", + "0.0004052086194496182\n", + "-0.01570039844\n" + ] + } + ], "source": [ - "# write your code here" + "# write your code here\n", + "# reconstruct_weights('a', D_A, 1)\n", + "#reconstruct_weights('b', D_B, 1)\n", + "x_weights = reconstruct_weights('x', D_X, N)\n", + "reconstruct_weights('r',D_R,1)\n", + "\n", + "portfolio_returns = np.dot(expected_returns, np.transpose(np.array(x_weights)))\n", + "print(portfolio_returns)\n", + "\n", + "variance = 0\n", + "for i in range(N):\n", + " for ii in range(N):\n", + " variance += x_weights[i] * x_weights[ii] * covariance_matrix[i][ii]\n", + "print(variance)\n", + "\n", + "print(calculate_historical_VaR(x_weights, expected_returns))" ] }, { @@ -1050,7 +1130,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "aa60be68", "metadata": {}, "outputs": [], @@ -1078,12 +1158,13 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "067cea0c", "metadata": {}, "outputs": [], "source": [ - "# write your code here" + "# write your code here\n", + "\n" ] }, { From ce5069f1d594a3879a1302c7fe041fb6a2e1945c Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 04:45:25 -0500 Subject: [PATCH 13/20] More fixes and additions --- moodys_challenge.ipynb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 3e5a43b..f7daddc 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -430,7 +430,9 @@ "id": "f0d54474", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# Optimization problem has been formulated." + ] }, { "cell_type": "markdown", From affd1d20543c15cbb0c300d64ec90435585d13bc Mon Sep 17 00:00:00 2001 From: Ashrit Date: Sun, 4 Feb 2024 09:48:09 +0000 Subject: [PATCH 14/20] z done --- .ipynb_checkpoints/deriveZ-checkpoint.ipynb | 181 +++++++++++++------- deriveZ.ipynb | 89 ++++++++-- 2 files changed, 186 insertions(+), 84 deletions(-) diff --git a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb index 4dc84dd..3c198ee 100644 --- a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb +++ b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb @@ -12,18 +12,50 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 48, "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", "metadata": { "tags": [] }, "outputs": [ { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "/tmp/ipykernel_512/3483040339.py:10: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", - " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + "VaR at 95% confidence level: -1.9863165e-02 -0.054498\n", + "-1.9305019e-02 -0.052114\n", + "4.3494020e-03 -0.065133\n", + "-6.5832785e-03 -0.073864\n", + "1.1202068e-02 -0.051338\n", + "-1.6676961e-02 -0.080386\n", + "-3.1958164e-03 -0.062213\n", + "-3.6061820e-02 -0.086725\n", + "-6.2434964e-03 -0.068405\n", + "5.3885423e-02 -0.041255\n", + "1.9841270e-02 -0.053425\n", + "5.2570093e-03 -0.048105\n", + "-8.8888889e-03 -0.074956\n", + "-4.9275362e-03 -0.045472\n", + "-2.0058504e-02 -0.061249\n", + "3.0338181e-03 -0.103650\n", + "-2.5770824e-02 -0.057937\n", + "0.0000000e+00 -0.079655\n", + "-1.7283951e-02 -0.096552\n", + "1.1389522e-02 -0.078960\n", + "-1.2942779e-02 -0.049639\n", + "-1.5117830e-02 -0.051520\n", + "8.3017112e-04 -0.040895\n", + "4.6242775e-03 -0.058778\n", + "-2.3952096e-03 -0.069087\n", + "2.4055629e-02 -0.077388\n", + "-1.7333333e-02 -0.073769\n", + "-6.4360418e-03 -0.047025\n", + "1.0585888e-02 -0.116658\n", + "3.3211333e-02 -0.064501\n", + "-2.9184038e-02 -0.035317\n", + "-9.4182825e-03 -0.045576\n", + "dtype: float64\n", + "Z-score for 95% confidence level: 1.6448536269514722\n" ] } ], @@ -37,7 +69,7 @@ "number_assets = 32\n", "T = 392\n", "# Read returns\n", - "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "df = pd.read_csv('returns_data.txt', sep='\\s+')\n", "\n", "Rraw = df.values.T\n", "\n", @@ -48,12 +80,28 @@ "expected_returns = np.mean(R, axis = 1)\n", "\n", "# Covariance matrix of asset returns\n", - "covariance_matrix = np.cov(R)" + "covariance_matrix = np.cov(R)\n", + "\n", + "mean_returns = df.mean()\n", + "std_dev_returns = df.std()\n", + "\n", + "# Set the confidence level\n", + "confidence_level = 0.95\n", + "\n", + "# Calculate the z-score for the confidence level\n", + "from scipy.stats import norm\n", + "z_score = norm.ppf(confidence_level)\n", + "\n", + "# Calculate VaR at the 95% confidence level\n", + "VaR_95 = mean_returns - z_score * std_dev_returns\n", + "\n", + "print(f\"VaR at 95% confidence level: {VaR_95}\")\n", + "print(f\"Z-score for 95% confidence level: {z_score}\")" ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 47, "id": "1454b6b3-e891-4511-8c78-f8396fedce42", "metadata": { "tags": [] @@ -62,18 +110,17 @@ { "data": { "text/plain": [ - "(array([4.51054074e-03, 8.73512634e-05, 7.90540398e-03, 1.40783575e-02,\n", - " 2.07775903e-02, 1.64123539e-03, 1.91034896e-02, 1.28609808e-02,\n", - " 2.80859844e-04, 6.61409780e-02, 6.51414292e-02, 3.98803992e-02,\n", - " 7.00376103e-02, 3.46769279e-02, 4.44522258e-02, 1.80953143e-02,\n", - " 5.18566058e-02, 1.49361414e-02, 2.43586883e-02, 4.35945518e-02,\n", - " 7.52722277e-02, 7.10196380e-02, 1.96061804e-03, 3.71767195e-03,\n", - " 8.65341180e-03, 2.16549540e-02, 4.95875647e-02, 7.07621919e-02,\n", - " 6.89387137e-02, 5.14832364e-03, 5.86121947e-02, 1.02558085e-02]),\n", - " 1.0000000000000002)" + "(array([0.03295262, 0.00357888, 0.02086894, 0.01404641, 0.00207373,\n", + " 0.04048195, 0.03747879, 0.06892786, 0.00658971, 0.07627872,\n", + " 0.03231812, 0.01837159, 0.01969979, 0.00613971, 0.05614756,\n", + " 0.07319379, 0.01233284, 0.0447664 , 0.01589302, 0.00435266,\n", + " 0.02044029, 0.04002689, 0.00849307, 0.01801094, 0.06357421,\n", + " 0.04002009, 0.00848955, 0.05466866, 0.05900047, 0.0350204 ,\n", + " 0.00453561, 0.06122671]),\n", + " 0.9999999999999997)" ] }, - "execution_count": 33, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -94,7 +141,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 41, "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", "metadata": { "tags": [] @@ -112,7 +159,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 42, "id": "d9e7df0d-6d54-41d0-8396-3419e202c41f", "metadata": { "tags": [] @@ -135,7 +182,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 43, "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", "metadata": { "tags": [] @@ -144,49 +191,50 @@ { "data": { "text/plain": [ - "([0.355336682932509,\n", - " 0.3486524751161616,\n", - " 0.3153310798265738,\n", - " 0.3458236857416237,\n", - " 0.3284936295255029],\n", - " [array([0.04355964, 0.00331318, 0.03962351, 0.00446816, 0.02004242,\n", - " 0.0073511 , 0.05799071, 0.04472408, 0.06603708, 0.00088515,\n", - " 0.00764356, 0.0047062 , 0.05034145, 0.00329546, 0.01488312,\n", - " 0.05515591, 0.04475396, 0.01491287, 0.0683395 , 0.07056326,\n", - " 0.03745494, 0.02609458, 0.02203422, 0.01296996, 0.050467 ,\n", - " 0.01384013, 0.04922779, 0.03142448, 0.01422148, 0.00488373,\n", - " 0.05422915, 0.06056223]),\n", - " array([0.04874561, 0.03993086, 0.04785653, 0.05465976, 0.00019219,\n", - " 0.04019522, 0.00810607, 0.06345375, 0.02573358, 0.02708537,\n", - " 0.04455019, 0.04537882, 0.02676014, 0.0433288 , 0.06012773,\n", - " 0.02735331, 0.05145284, 0.01656183, 0.01235625, 0.00276128,\n", - " 0.01715967, 0.04823428, 0.00194451, 0.04691764, 0.05292875,\n", - " 0.02993959, 0.00154663, 0.04849301, 0.01671295, 0.02641807,\n", - " 0.01500313, 0.00811164]),\n", - " array([0.05407912, 0.00771579, 0.03898179, 0.0360573 , 0.04710808,\n", - " 0.02554926, 0.03036403, 0.04929535, 0.03950859, 0.03908034,\n", - " 0.05741809, 0.03047458, 0.05604574, 0.00530165, 0.02561892,\n", - " 0.01067558, 0.02881062, 0.03662797, 0.03708003, 0.01491708,\n", - " 0.03633884, 0.0174767 , 0.03415313, 0.01768372, 0.00391247,\n", - " 0.04466098, 0.06156805, 0.00268126, 0.01123347, 0.03619209,\n", - " 0.03278803, 0.03060133]),\n", - " array([0.01324834, 0.02055701, 0.03873027, 0.05622685, 0.04835239,\n", - " 0.03054007, 0.02763519, 0.05789158, 0.0541646 , 0.00560576,\n", - " 0.00030345, 0.03324706, 0.03112665, 0.02911636, 0.02694478,\n", - " 0.06162621, 0.06285638, 0.00590672, 0.04596359, 0.00188474,\n", - " 0.00836228, 0.04720747, 0.0179548 , 0.03406875, 0.03500403,\n", - " 0.02352799, 0.02899271, 0.0351087 , 0.04108958, 0.00511435,\n", - " 0.02003537, 0.05160599]),\n", - " array([0.03928864, 0.06604121, 0.04455049, 0.05844166, 0.03450061,\n", - " 0.00578215, 0.00745817, 0.04684066, 0.02250656, 0.05992387,\n", - " 0.0013096 , 0.03850481, 0.05485348, 0.00092822, 0.02524379,\n", - " 0.06463862, 0.03410129, 0.01284057, 0.04778934, 0.01594721,\n", - " 0.0531338 , 0.00809094, 0.01694831, 0.03596166, 0.01669566,\n", - " 0.009567 , 0.0003432 , 0.03155908, 0.03194489, 0.06121041,\n", - " 0.02109639, 0.03195769])])" + "([0.3201754590963047,\n", + " 0.3040817367921548,\n", + " 0.33570642951204405,\n", + " 0.324621105208764,\n", + " 0.35256804169434885],\n", + " [array([0.03519249, 0.03290085, 0.01317292, 0.0536117 , 0.05335179,\n", + " 0.0282839 , 0.04913777, 0.03639204, 0.00584395, 0.00473127,\n", + " 0.01556526, 0.05040084, 0.00158258, 0.05193638, 0.01152281,\n", + " 0.0274181 , 0.04280595, 0.02104865, 0.04392498, 0.01501355,\n", + " 0.00104685, 0.03588135, 0.04501165, 0.01426848, 0.03105206,\n", + " 0.04519589, 0.02398323, 0.03468755, 0.03416542, 0.04537202,\n", + " 0.04764116, 0.04785654]),\n", + " array([0.04150249, 0.04927715, 0.04050631, 0.01704145, 0.02052018,\n", + " 0.02598609, 0.03970675, 0.04701215, 0.02573006, 0.03526343,\n", + " 0.0118426 , 0.0231728 , 0.03486327, 0.00175815, 0.02021195,\n", + " 0.04307619, 0.01233438, 0.04928072, 0.0216664 , 0.01882121,\n", + " 0.05057916, 0.02193385, 0.02624362, 0.01480431, 0.04967795,\n", + " 0.01678528, 0.04148598, 0.03736406, 0.03326363, 0.03940288,\n", + " 0.03435846, 0.05452709]),\n", + " array([0.01604598, 0.04529005, 0.01110671, 0.05985637, 0.04223921,\n", + " 0.01186149, 0.04146411, 0.0174574 , 0.04475275, 0.0483324 ,\n", + " 0.01738737, 0.03744703, 0.01976326, 0.06031816, 0.01705841,\n", + " 0.01359626, 0.05254128, 0.03369028, 0.06079489, 0.00420929,\n", + " 0.00269271, 0.02072749, 0.02317871, 0.06133557, 0.05006869,\n", + " 0.03762329, 0.0628452 , 0.00436101, 0.03140263, 0.00272598,\n", + " 0.00502145, 0.04280456]),\n", + " array([3.75143474e-02, 3.56680465e-02, 6.07580111e-02, 1.78872938e-02,\n", + " 4.47482137e-02, 4.36094397e-02, 1.02396118e-02, 3.50628726e-02,\n", + " 5.74763685e-03, 5.88533879e-02, 1.77641933e-02, 2.20699045e-02,\n", + " 2.89828714e-02, 1.46328359e-02, 3.59180566e-02, 1.63276401e-02,\n", + " 3.79104899e-02, 4.88496753e-02, 1.12161826e-03, 3.36144250e-02,\n", + " 5.87756224e-02, 7.50474417e-03, 4.26659419e-02, 3.54226972e-02,\n", + " 5.63592516e-02, 3.53101020e-02, 2.35589519e-02, 1.05982766e-02,\n", + " 5.60329215e-02, 5.09637794e-03, 7.95060325e-05, 6.13150354e-02]),\n", + " array([0.03894738, 0.03544109, 0.00490616, 0.05484427, 0.01377497,\n", + " 0.0112676 , 0.06334437, 0.05774461, 0.04677672, 0.02128168,\n", + " 0.0643672 , 0.01938623, 0.02496569, 0.03924972, 0.05728707,\n", + " 0.04692048, 0.01114821, 0.0088831 , 0.00936529, 0.06442574,\n", + " 0.04214907, 0.0072486 , 0.0600262 , 0.02152251, 0.00609748,\n", + " 0.02466099, 0.00043209, 0.00359317, 0.0311648 , 0.05129704,\n", + " 0.05541976, 0.00206072])])" ] }, - "execution_count": 36, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -210,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 51, "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", "metadata": { "tags": [] @@ -220,8 +268,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "The mean VaR at a 95.0% confidence level is: -0.001003955534921795\n", - "The median VaR at a 95.0% confidence level is: -0.001012017761058015\n" + "The mean VaR at a 95.0% confidence level is: -0.0010204670111313022\n", + "The median VaR at a 95.0% confidence level is: -0.0010196371916901341\n" ] } ], @@ -235,7 +283,10 @@ "\n", "# Assuming mu_R is the matrix of historical returns for the assets\n", "# Replace this with your actual historical returns data\n", - "mu_R = R.T # This should be the historical returns data for your assets\n", + "mu_R = expected_returns # This should be the historical returns data for your assets\n", + "# expected retursn --> z = .001\n", + "# mu_R = R.T # z = .0045\n", + "# z provided from packages == 1.645\n", "\n", "# Generate the random weights for the portfolios and calculate the VaR for each\n", "VaRs = []\n", diff --git a/deriveZ.ipynb b/deriveZ.ipynb index 7aab876..3c198ee 100644 --- a/deriveZ.ipynb +++ b/deriveZ.ipynb @@ -12,18 +12,50 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 48, "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", "metadata": { "tags": [] }, "outputs": [ { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "/tmp/ipykernel_512/3483040339.py:10: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", - " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" + "VaR at 95% confidence level: -1.9863165e-02 -0.054498\n", + "-1.9305019e-02 -0.052114\n", + "4.3494020e-03 -0.065133\n", + "-6.5832785e-03 -0.073864\n", + "1.1202068e-02 -0.051338\n", + "-1.6676961e-02 -0.080386\n", + "-3.1958164e-03 -0.062213\n", + "-3.6061820e-02 -0.086725\n", + "-6.2434964e-03 -0.068405\n", + "5.3885423e-02 -0.041255\n", + "1.9841270e-02 -0.053425\n", + "5.2570093e-03 -0.048105\n", + "-8.8888889e-03 -0.074956\n", + "-4.9275362e-03 -0.045472\n", + "-2.0058504e-02 -0.061249\n", + "3.0338181e-03 -0.103650\n", + "-2.5770824e-02 -0.057937\n", + "0.0000000e+00 -0.079655\n", + "-1.7283951e-02 -0.096552\n", + "1.1389522e-02 -0.078960\n", + "-1.2942779e-02 -0.049639\n", + "-1.5117830e-02 -0.051520\n", + "8.3017112e-04 -0.040895\n", + "4.6242775e-03 -0.058778\n", + "-2.3952096e-03 -0.069087\n", + "2.4055629e-02 -0.077388\n", + "-1.7333333e-02 -0.073769\n", + "-6.4360418e-03 -0.047025\n", + "1.0585888e-02 -0.116658\n", + "3.3211333e-02 -0.064501\n", + "-2.9184038e-02 -0.035317\n", + "-9.4182825e-03 -0.045576\n", + "dtype: float64\n", + "Z-score for 95% confidence level: 1.6448536269514722\n" ] } ], @@ -37,7 +69,7 @@ "number_assets = 32\n", "T = 392\n", "# Read returns\n", - "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "df = pd.read_csv('returns_data.txt', sep='\\s+')\n", "\n", "Rraw = df.values.T\n", "\n", @@ -48,12 +80,28 @@ "expected_returns = np.mean(R, axis = 1)\n", "\n", "# Covariance matrix of asset returns\n", - "covariance_matrix = np.cov(R)" + "covariance_matrix = np.cov(R)\n", + "\n", + "mean_returns = df.mean()\n", + "std_dev_returns = df.std()\n", + "\n", + "# Set the confidence level\n", + "confidence_level = 0.95\n", + "\n", + "# Calculate the z-score for the confidence level\n", + "from scipy.stats import norm\n", + "z_score = norm.ppf(confidence_level)\n", + "\n", + "# Calculate VaR at the 95% confidence level\n", + "VaR_95 = mean_returns - z_score * std_dev_returns\n", + "\n", + "print(f\"VaR at 95% confidence level: {VaR_95}\")\n", + "print(f\"Z-score for 95% confidence level: {z_score}\")" ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 47, "id": "1454b6b3-e891-4511-8c78-f8396fedce42", "metadata": { "tags": [] @@ -62,17 +110,17 @@ { "data": { "text/plain": [ - "(array([0.04530294, 0.03248244, 0.01591696, 0.05819326, 0.05515743,\n", - " 0.0018886 , 0.04133333, 0.04105249, 0.00532421, 0.02746209,\n", - " 0.06062974, 0.02659319, 0.04392856, 0.00207729, 0.05706425,\n", - " 0.00622429, 0.00509702, 0.0474765 , 0.02714145, 0.06078049,\n", - " 0.05412684, 0.00992554, 0.00093757, 0.00173598, 0.01410071,\n", - " 0.02860928, 0.05080505, 0.00879419, 0.01877773, 0.03859788,\n", - " 0.05160082, 0.06086186]),\n", - " 1.0)" + "(array([0.03295262, 0.00357888, 0.02086894, 0.01404641, 0.00207373,\n", + " 0.04048195, 0.03747879, 0.06892786, 0.00658971, 0.07627872,\n", + " 0.03231812, 0.01837159, 0.01969979, 0.00613971, 0.05614756,\n", + " 0.07319379, 0.01233284, 0.0447664 , 0.01589302, 0.00435266,\n", + " 0.02044029, 0.04002689, 0.00849307, 0.01801094, 0.06357421,\n", + " 0.04002009, 0.00848955, 0.05466866, 0.05900047, 0.0350204 ,\n", + " 0.00453561, 0.06122671]),\n", + " 0.9999999999999997)" ] }, - "execution_count": 40, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -210,7 +258,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 51, "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", "metadata": { "tags": [] @@ -220,8 +268,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "The mean VaR at a 95.0% confidence level is: -0.0010112892735360864\n", - "The median VaR at a 95.0% confidence level is: -0.0010098220472010626\n" + "The mean VaR at a 95.0% confidence level is: -0.0010204670111313022\n", + "The median VaR at a 95.0% confidence level is: -0.0010196371916901341\n" ] } ], @@ -236,6 +284,9 @@ "# Assuming mu_R is the matrix of historical returns for the assets\n", "# Replace this with your actual historical returns data\n", "mu_R = expected_returns # This should be the historical returns data for your assets\n", + "# expected retursn --> z = .001\n", + "# mu_R = R.T # z = .0045\n", + "# z provided from packages == 1.645\n", "\n", "# Generate the random weights for the portfolios and calculate the VaR for each\n", "VaRs = []\n", From c1700dd445b8c215f011dd3fc96667384d2159bf Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 05:03:10 -0500 Subject: [PATCH 15/20] Answering questions --- moodys_challenge.ipynb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index f7daddc..a0ef4d9 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -431,7 +431,9 @@ "metadata": {}, "outputs": [], "source": [ - "# Optimization problem has been formulated." + "# Optimization problem has been formulated.\n", + "\n", + "# The problem has been formulated on the screenshot called XXX." ] }, { @@ -512,6 +514,7 @@ { "cell_type": "code", "execution_count": 2, + "id": "e980f661", "metadata": {}, "outputs": [ { From 046550aefe75f3f12f6a428f0198ea1b37f4a91f Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 05:13:47 -0500 Subject: [PATCH 16/20] Fix merge conflicts --- moodys_challenge.ipynb | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 1c98938..5a87ec3 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -37,8 +37,6 @@ ] }, { -<<<<<<< HEAD -======= "cell_type": "code", "execution_count": 1, "id": "15770539", @@ -59,7 +57,6 @@ ] }, { ->>>>>>> origin/ashrit_branch "cell_type": "markdown", "id": "85d339f8-0a2d-4c84-88d2-ae2f5d7e7597", "metadata": {}, @@ -357,7 +354,6 @@ "cell_type": "code", "execution_count": 2, "id": "a78f8aea", -<<<<<<< HEAD "metadata": {}, "outputs": [ { @@ -375,17 +371,6 @@ "output_type": "stream", "text": [ "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_22900\\3824432534.py:2: DeprecationWarning: \n", -======= - "metadata": { - "tags": [] - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/tmp/ipykernel_493/969284350.py:2: DeprecationWarning: \n", ->>>>>>> origin/ashrit_branch "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", "but was not found to be installed on your system.\n", @@ -393,11 +378,7 @@ "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", " \n", " import pandas as pd\n", -<<<<<<< HEAD "C:\\Users\\nicho\\AppData\\Local\\Temp\\ipykernel_22900\\3824432534.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", -======= - "/tmp/ipykernel_493/969284350.py:8: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", ->>>>>>> origin/ashrit_branch " df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n" ] } @@ -1024,11 +1005,7 @@ }, { "cell_type": "code", -<<<<<<< HEAD "execution_count": 52, -======= - "execution_count": 1, ->>>>>>> origin/ashrit_branch "id": "95761549", "metadata": {}, "outputs": [], @@ -1267,11 +1244,7 @@ ], "metadata": { "kernelspec": { -<<<<<<< HEAD - "display_name": "Python 3 (ipykernel)", -======= "display_name": "Python 3 [Moody's]", ->>>>>>> origin/ashrit_branch "language": "python", "name": "python3_moodys_xbto4j" }, From cc544cf442e34c73ae86fd393e01ea22b56b0ed6 Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 07:28:56 -0500 Subject: [PATCH 17/20] Answer questions, add QUBO scratch files --- QUBO (1).ipynb | 597 +++++++++++++++++++++++++++++++++++++++++ QUBO.ipynb | 230 ++++++++++++++++ moodys_challenge.ipynb | 43 +-- 3 files changed, 853 insertions(+), 17 deletions(-) create mode 100644 QUBO (1).ipynb create mode 100644 QUBO.ipynb diff --git a/QUBO (1).ipynb b/QUBO (1).ipynb new file mode 100644 index 0000000..4d0308a --- /dev/null +++ b/QUBO (1).ipynb @@ -0,0 +1,597 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 75, + "id": "a9ff17c4-a1aa-472b-b4e5-8085a9cfb9cb", + "metadata": {}, + "outputs": [], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "\n", + "\n", + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "N, K = 2,3\n", + "T = 2\n", + "z = 0.05\n", + "eps = 0.1\n", + "\n", + "# Data variables\n", + "\n", + "Sigma = np.random.rand(N, N) # UPDATE\n", + "\n", + "Mu = np.random.rand(N) # UPDATE\n", + "\n", + "R = np.random.rand(T,N) # UPDATE\n", + "\n", + "# Slack, nonbasic and other variables\n", + "\n", + "z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + "r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + "A_norm = Array.create(\"A\", shape=(K, 1), vartype=\"BINARY\")\n", + "A_max = z\n", + "A = A_max*binary2integer_norm(A_norm)\n", + "\n", + "B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + "B_max = np.random.rand(T) # UPDATE\n", + "\n", + "C_norm = Array.create(\"C\", shape=(K, 1), vartype=\"BINARY\")\n", + "C = C_max*binary2integer_norm(C_norm)\n", + "C_max = eps*T\n", + "\n", + "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + "r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "id": "984954d0-0ceb-4365-83a6-4aa0a71e480c", + "metadata": {}, + "outputs": [], + "source": [ + "H_var = 0\n", + "for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "001fd2d2-5c9b-4d8e-a372-65360fa58f28", + "metadata": {}, + "outputs": [], + "source": [ + "H_exp = 0\n", + "for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "id": "22f3f7b7-c47d-4a3c-9c6e-725f401e284d", + "metadata": {}, + "outputs": [], + "source": [ + "L1 = 100\n", + "L2s = [100]*T\n", + "L3 = 100\n", + "L4 = 10\n", + "M = 100\n", + "\n", + "H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + "H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + "H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + "H4 = L4*Constraint((binary2integer_norm(z_full[0])-1)**2, \"H4\")" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "id": "54358ac0-efaf-4c10-8b48-6951f12e15ff", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "H = H_var - H_exp + (H2+1-1) + (H4+1-1) + (H3+1-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "id": "f8341ed5-c8d7-4b59-9055-7a5791f72990", + "metadata": {}, + "outputs": [], + "source": [ + "model = H.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "id": "d8231778-cbf6-4f4b-b4e9-61f57f889bd1", + "metadata": {}, + "outputs": [], + "source": [ + "qubo, offset = model.to_qubo() #H_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "id": "643d574c-c36b-484d-92c3-b9d90a6af70b", + "metadata": {}, + "outputs": [], + "source": [ + "from dwave.samplers import SimulatedAnnealingSampler" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "d5b48d50-f295-43ac-8e8c-67bf63432b56", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "150" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(qubo)" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "id": "75ab433b-9d0d-4a31-aa62-e80b465bf4f3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'B_norm[0][0]': 1, 'B_norm[0][1]': 1, 'B_norm[0][2]': 1, 'B_norm[1][0]': 1, 'C[0][0]': 1, 'B_norm[1][1]': 1, 'B_norm[1][2]': 1, 'C[1][0]': 1, 'C[2][0]': 1, 'r_eps[0][0]': 1, 'r_eps[1][0]': 1, 'r_eps[2][0]': 1, 'y[0][0]': 1, 'y[1][0]': 1, 'z_full[0][0]': 1, 'z_full[0][1]': 0, 'z_full[0][2]': 1, 'z_full[1][0]': 0, 'z_full[1][1]': 0, 'z_full[1][2]': 0}\n" + ] + } + ], + "source": [ + "sampler = SimulatedAnnealingSampler()\n", + "sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", + "samples = model.decode_sampleset(sampleset)\n", + "best_sample = min(samples, key=lambda s: s.energy)\n", + "print(best_sample.sample)" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "id": "f0f45162-eb41-4a1f-8dcf-8d3ca00ff01d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'B_norm[0][0]'" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(best_sample.sample.keys())[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "id": "e37eadc1-3598-4b5f-8166-0640bfb46b48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'B_norm[0][0]': 1,\n", + " 'B_norm[0][1]': 1,\n", + " 'B_norm[0][2]': 1,\n", + " 'B_norm[1][0]': 1,\n", + " 'C[0][0]': 1,\n", + " 'B_norm[1][1]': 1,\n", + " 'B_norm[1][2]': 1,\n", + " 'C[1][0]': 1,\n", + " 'C[2][0]': 1,\n", + " 'r_eps[0][0]': 1,\n", + " 'r_eps[1][0]': 1,\n", + " 'r_eps[2][0]': 1,\n", + " 'y[0][0]': 1,\n", + " 'y[1][0]': 1,\n", + " 'z_full[0][0]': 1,\n", + " 'z_full[0][1]': 0,\n", + " 'z_full[0][2]': 1,\n", + " 'z_full[1][0]': 0,\n", + " 'z_full[1][1]': 0,\n", + " 'z_full[1][2]': 0}" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "best_sample.sample" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "id": "ee05072b-cca6-4ea7-9115-b84ba3100079", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'H2_1': (False, 0.00011264725981163792), 'H4': (False, 0.140625), 'H3': (False, 0.0006250000000003197), 'H2_0': (False, 0.00032004287349174376)}\n" + ] + } + ], + "source": [ + "print(best_sample.constraints())" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "id": "97fc4aae-09f7-41c6-bccb-f8d03eac4d85", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Array([[Binary('z_full[0][0]'), Binary('z_full[0][1]'), Binary('z_full[0][2]')],\n", + " [Binary('z_full[1][0]'), Binary('z_full[1][1]'), Binary('z_full[1][2]')]])" + ] + }, + "execution_count": 88, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z_full" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "c8c08839-1f62-44b0-85fd-acbad3c4eab4", + "metadata": {}, + "outputs": [], + "source": [ + "z_full_array = np.zeros(shape=z_full.shape)\n", + "r_eps_norm_array = np.zeros(shape=r_eps_norm.shape)\n", + "A_norm_array = np.zeros(shape=A_norm.shape)\n", + "B_norm_array = np.zeros(shape=B_norm.shape)\n", + "C_norm_array = np.zeros(shape=C_norm.shape)\n", + "y_array = np.zeros(shape=y.shape) \n", + "r_eps_array = np.zeros(shape=r_eps.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "2c9ba4c4-3597-4dbf-9a9b-590d8fecd571", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 0]\n", + "[0, 1]\n", + "[0, 2]\n", + "[1, 0]\n", + "[1, 1]\n", + "[1, 2]\n", + "B_full: [[1, 1, 1], [0, 0, 1]]\n", + "C: [[0, 0, 0], [1, 0, 0], [0, 0, 0]]\n", + "r_eps: [[1], [1], [1]]\n", + "y: [[1], [1]]\n", + "z_full: [[1, 0, 1], [0, 0, 0]]\n" + ] + } + ], + "source": [ + "import re\n", + "\n", + "key = \"B_full[0][1]\"\n", + "\n", + "\n", + "data =best_sample.sample \n", + "\n", + "\n", + "# Iterate through the dictionary and populate arrays\n", + "for key, value in data.items():\n", + " if key.startswith('B_full'):\n", + " # Use regular expression to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " print(indices)\n", + " B_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('C'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " C_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('r_eps'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " r_eps_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('y'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " y_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('z_full'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " z_full_array[indices[0]][indices[1]] = value\n", + "\n", + "# Print the populated arrays\n", + "print(\"B_full:\", B_full)\n", + "print(\"C:\", C)\n", + "print(\"r_eps:\", r_eps)\n", + "print(\"y:\", y)\n", + "print(\"z_full:\", z_full)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56662e9d-475e-4e5e-9245-b491335027c4", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "a2d4a016-f53d-4fab-aded-f0de0ffbf477", + "metadata": {}, + "outputs": [ + { + "ename": "IndexError", + "evalue": "list assignment index out of range", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_22084\\2096571514.py\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 39\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 40\u001b[0m \u001b[1;31m# Populate the array\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 41\u001b[1;33m \u001b[0marrays\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0marray_name\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mindices\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mindices\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mindices\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m>\u001b[0m \u001b[1;36m1\u001b[0m \u001b[1;32melse\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 42\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 43\u001b[0m \u001b[1;31m# Print the populated arrays\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", + "\u001b[1;31mIndexError\u001b[0m: list assignment index out of range" + ] + } + ], + "source": [ + "import re\n", + "\n", + "data = {\n", + " 'B_full[0][0]': 1,\n", + " 'B_full[0][1]': 1,\n", + " 'B_full[0][2]': 1,\n", + " 'B_full[1][0]': 0,\n", + " 'C[1][0]': 1,\n", + " 'B_full[1][1]': 0,\n", + " 'C[0][0]': 0,\n", + " 'r_eps[2][0]': 1,\n", + " 'B_full[1][2]': 1,\n", + " 'C[2][0]': 0,\n", + " 'r_eps[0][0]': 1,\n", + " 'r_eps[1][0]': 1,\n", + " 'y[0][0]': 1,\n", + " 'y[1][0]': 1,\n", + " 'z_full[0][0]': 1,\n", + " 'z_full[0][1]': 0,\n", + " 'z_full[0][2]': 1,\n", + " 'z_full[1][0]': 0,\n", + " 'z_full[1][1]': 0,\n", + " 'z_full[1][2]': 0\n", + "}\n", + "\n", + "# Create arrays dynamically based on the keys\n", + "arrays = {}\n", + "\n", + "# Iterate through the dictionary and populate arrays\n", + "for key, value in data.items():\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " \n", + " # Extract the array name (e.g., B_full, C, r_eps, etc.)\n", + " array_name = key.split('[')[0]\n", + "\n", + " # If the array is not in the dictionary, create an empty array\n", + " if array_name not in arrays:\n", + " arrays[array_name] = [[0] * (max(indices[1] + 1, 1) if len(indices) > 1 else 1) for _ in range(max(indices[0] + 1, 1))]\n", + "\n", + " # Populate the array\n", + " arrays[array_name][indices[0]][indices[1] if len(indices) > 1 else 0] = value\n", + "\n", + "# Print the populated arrays\n", + "for array_name, array in arrays.items():\n", + " print(f\"{array_name}: {array}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "id": "39d984f9-fd93-4852-bbb1-d09efd6bcf67", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'B_full': [0, 0], 'C': [1, 0], 'r_eps': [2, 0], 'y': [0, 0], 'z_full': [0, 0]}\n" + ] + } + ], + "source": [ + "def extract_dimensions(data):\n", + " dimensions = {}\n", + "\n", + " for key in data.keys():\n", + " # Split the key using '[' and ']' to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " \n", + " # Extract dimension name (e.g., 'B_full', 'C', 'r_eps', 'y', 'z_full')\n", + " dimension_name = key.split('[')[0]\n", + "\n", + " # Update the dimensions dictionary\n", + " if dimension_name not in dimensions:\n", + " dimensions[dimension_name] = indices\n", + "\n", + " return dimensions\n", + "\n", + "data = {\n", + " 'B_full[0][0]': 1,\n", + " 'B_full[0][1]': 1,\n", + " 'B_full[0][2]': 1,\n", + " 'B_full[1][0]': 0,\n", + " 'C[1][0]': 1,\n", + " 'B_full[1][1]': 0,\n", + " 'C[0][0]': 0,\n", + " 'r_eps[2][0]': 1,\n", + " 'B_full[1][2]': 1,\n", + " 'C[2][0]': 0,\n", + " 'r_eps[0][0]': 1,\n", + " 'r_eps[1][0]': 1,\n", + " 'y[0][0]': 1,\n", + " 'y[1][0]': 1,\n", + " 'z_full[0][0]': 1,\n", + " 'z_full[0][1]': 0,\n", + " 'z_full[0][2]': 1,\n", + " 'z_full[1][0]': 0,\n", + " 'z_full[1][1]': 0,\n", + " 'z_full[1][2]': 0\n", + "}\n", + "\n", + "dimensions = extract_dimensions(data)\n", + "print(dimensions)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "id": "5cea29c9-5d1f-4512-a57c-cace6307c757", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'B_full': [1, 2], 'C': [2, 0], 'r_eps': [2, 0], 'y': [1, 0], 'z_full': [1, 2]}\n" + ] + } + ], + "source": [ + "import re\n", + "\n", + "def extract_dimensions(data):\n", + " dimensions = {}\n", + "\n", + " for key in data.keys():\n", + " # Split the key using '[' and ']' to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + "\n", + " # Extract dimension name (e.g., 'B_full', 'C', 'r_eps', 'y', 'z_full')\n", + " dimension_name = key.split('[')[0]\n", + "\n", + " # Update the dimensions dictionary with the largest indices\n", + " if dimension_name not in dimensions:\n", + " dimensions[dimension_name] = indices\n", + " else:\n", + " dimensions[dimension_name] = [max(curr, new) for curr, new in zip(dimensions[dimension_name], indices)]\n", + "\n", + " return dimensions\n", + "\n", + "data = {\n", + " 'B_full[0][0]': 1,\n", + " 'B_full[0][1]': 1,\n", + " 'B_full[0][2]': 1,\n", + " 'B_full[1][0]': 0,\n", + " 'C[1][0]': 1,\n", + " 'B_full[1][1]': 0,\n", + " 'C[0][0]': 0,\n", + " 'r_eps[2][0]': 1,\n", + " 'B_full[1][2]': 1,\n", + " 'C[2][0]': 0,\n", + " 'r_eps[0][0]': 1,\n", + " 'r_eps[1][0]': 1,\n", + " 'y[0][0]': 1,\n", + " 'y[1][0]': 1,\n", + " 'z_full[0][0]': 1,\n", + " 'z_full[0][1]': 0,\n", + " 'z_full[0][2]': 1,\n", + " 'z_full[1][0]': 0,\n", + " 'z_full[1][1]': 0,\n", + " 'z_full[1][2]': 0\n", + "}\n", + "\n", + "dimensions = extract_dimensions(data)\n", + "print(dimensions)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb84c2e0-4397-4877-ae02-b68024321fc0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/QUBO.ipynb b/QUBO.ipynb new file mode 100644 index 0000000..c6c3e10 --- /dev/null +++ b/QUBO.ipynb @@ -0,0 +1,230 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 25, + "id": "a9ff17c4-a1aa-472b-b4e5-8085a9cfb9cb", + "metadata": {}, + "outputs": [], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "N, K = 3, 5\n", + "T = 7\n", + "z = 0.05\n", + "eps = 0.1\n", + "\n", + "Sigma = np.random.rand(N, N) # UPDATE\n", + "\n", + "Mu = np.random.rand(N)\n", + "\n", + "\n", + "R = np.random.rand(T,N) # UPDATE\n", + "\n", + "z_full = Array.create(\"z_full\", shape= (N, K), vartype=\"BINARY\")\n", + "\n", + "r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + "A_norm = Array.create(\"A\", shape=(K, 1), vartype=\"BINARY\")\n", + "A_max = z\n", + "\n", + "B_norm = Array.create(\"B_full\", shape=(T, K), vartype=\"BINARY\")\n", + "B_max = np.random.rand(T) # UPDATE\n", + "\n", + "C_norm = Array.create(\"C\", shape=(K, 1), vartype=\"BINARY\")\n", + "C_max = eps*T\n", + "\n", + "\n", + "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + "def binary2integer_norm(z):\n", + " K = len(z)\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "\n", + "r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "A = A_max*binary2integer_norm(A_norm)\n", + "C = C_max*binary2integer_norm(C_norm)\n", + "\n", + "M = 10000\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "984954d0-0ceb-4365-83a6-4aa0a71e480c", + "metadata": {}, + "outputs": [], + "source": [ + "H_var = 0\n", + "for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "001fd2d2-5c9b-4d8e-a372-65360fa58f28", + "metadata": {}, + "outputs": [], + "source": [ + "H_exp = 0\n", + "for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "0c010e01-e680-4f71-8f75-67feadd7c90e", + "metadata": {}, + "outputs": [], + "source": [ + "L1 = 100\n", + "\n", + "H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + "H1_dict = {'L1': 5.0}" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "b3e98794-3326-4003-8e62-22353d853a02", + "metadata": {}, + "outputs": [], + "source": [ + "L2s = ['L2_{}'.format(t) for t in range(T)]\n", + "\n", + "H2 = sum([Placeholder(L2s[t])*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + "M_dict = {\"M\": 1}\n", + "L2s_dict = dict(zip(L2s, [1]*T))\n", + "H2_dict = {**M_dict, **L2s_dict}" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "37ec88b8-e64e-4613-83ec-d4dcade3c98b", + "metadata": {}, + "outputs": [], + "source": [ + "L3 = 100\n", + "\n", + "H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + "H3_dict = {'L3': 5.0}" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "20a04a02-487a-4764-88f8-60172830062a", + "metadata": {}, + "outputs": [], + "source": [ + "L4 = 500\n", + "\n", + "H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)]))**2, \"H4\")\n", + "\n", + "H4_dict = {'L4': 5.0}" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "54358ac0-efaf-4c10-8b48-6951f12e15ff", + "metadata": {}, + "outputs": [], + "source": [ + "H = H_var - H_exp + (H4+1-1) + (H1+1-1) + (H3+1-1) + (H2+1-1)\n", + "H_dict = {**H4_dict ,**H3_dict ,**H2_dict ,**H1_dict}" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "f8341ed5-c8d7-4b59-9055-7a5791f72990", + "metadata": {}, + "outputs": [], + "source": [ + "model = H.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "d8231778-cbf6-4f4b-b4e9-61f57f889bd1", + "metadata": {}, + "outputs": [], + "source": [ + "qubo = model.to_bqm(feed_dict= H_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "643d574c-c36b-484d-92c3-b9d90a6af70b", + "metadata": {}, + "outputs": [], + "source": [ + "from dwave.samplers import SimulatedAnnealingSampler" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "75ab433b-9d0d-4a31-aa62-e80b465bf4f3", + "metadata": {}, + "outputs": [ + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mThe Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click here for more info. View Jupyter log for further details." + ] + } + ], + "source": [ + "sampler = SimulatedAnnealingSampler()\n", + "sampleset = sampler.sample(qubo)\n", + "samples = model.decode_sampleset(sampleset)\n", + "#best_sample = min(samples, key=lambda s: s.energy)\n", + "#print(best_sample.sample)\n", + "\n", + "#print(best_sample.constraints())" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 5a87ec3..776457c 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -453,7 +453,10 @@ "source": [ "# Optimization problem has been formulated.\n", "\n", - "# The problem has been formulated on the screenshot called XXX." + "# The problem has been formulated on our presentation. It has also been converted to QUBO form further down this notebook.\n", + "\n", + "# The total number of variables to be optimized including the slack variables is (T+2) + N + T + 1 = 2T + N + 3\n", + "# (There are also the hyperparmaters lambda_1 thru lambda_4 and M, but those are tuned during the process)\n" ] }, { @@ -548,6 +551,19 @@ } ], "source": [ + "'''We used binary for all 4 of the variable types. y_t was already in binary format so it seemed natural. \n", + "x_i requires a decent amount of precision given that this is the output for the portfolio, \n", + "and there aren't any issues with being unable to define a largest integer with binary as stated by \"Solving the Optimal Trading Trajectory Problem Using a Quantum Annealer.\" \n", + "Therefore there aren't any disadvantages to using binary in this case as it is most efficient. \n", + "The slack variables need a great deal of precision as they encode the constraints, and therefore will need a lot of qubits. \n", + "They are also constrained by the problem, so it makes most sense to use binary in this case as well.\n", + "r_epsilon similarly cannot take on large values and so there aren't any weaknesses to using binary for it either.\n", + "\n", + "The total number of binary variables we had after the mapping is equal to D_X*N + D_A + D_B*T + T + D_C + D_R \n", + "\n", + "The x variables do need to be normalized, as all of their values are between 0 and 1. r_epsilon does not.\n", + "'''\n", + "\n", "import pyqubo\n", "from pyqubo import Array, Binary, Add\n", "D_X = 5\n", @@ -635,9 +651,6 @@ " variance_term = 0\n", " for i in range(N):\n", " for ii in range(N):\n", - " # first_term = binary2integer(x_full[:,i])\n", - " # second_term = first_term * (binary2integer(x_full[:,ii]))\n", - " # variance_term += second_term * (covariance_matrix[i][ii])\n", " variance_term += (binary2integer(x_full[:,i])) * (binary2integer(x_full[:,ii])) * (covariance_matrix[i][ii])\n", " # Mean\n", " the_mean = 0\n", @@ -645,13 +658,8 @@ " the_mean -= expected_returns[i]*binary2integer(x_full[:,i])\n", " ### H_1\n", " m = 0\n", - " # m += (binary2integer(r).mul(-1) + binary2integer(A).mul(1) - z)\n", - " # print(type(binary2integer(r[:,0]) * -1))\n", - " # print(type(binary2integer(A[:,0]) * 1))\n", " m += (binary2integer(r[:,0]) * -1 + binary2integer(A[:,0]) * 1 - float(z))\n", - " # print(\"Type m before is \" + str(type(m)))\n", " m = m**2\n", - " # print(\"Type m after is \" + str(type(m)))\n", " H_1 = lambda_1*m\n", " ### H2_t\n", " H_2 = 0\n", @@ -662,9 +670,6 @@ " y_terms = 0\n", " for i in range(len(y)):\n", " y_terms += y[i][0]\n", - " #print(type(y_terms))\n", - " #print(type(binary2integer(B[:,i])))\n", - " # print(type(((1-y_terms)*M)+1-1))\n", " # The \"+1-1\" is to change it into an Add instance.\n", " # For some reason Add does not have a constructor.\n", " # And addition is not supported between an Add and a Base.\n", @@ -689,8 +694,6 @@ " ### Total\n", " first = variance_term + the_mean\n", " second = first + H_1\n", - " # print(type(H_2))\n", - " # print(type(second))\n", " third = second + H_2\n", " fourth = third + H_3\n", " fifth = fourth + H_4\n", @@ -908,6 +911,12 @@ } ], "source": [ + "'''\n", + "We chose roughly 8 binary variables for each of the slack variables and 7 for each of the x variables. \n", + "For x that allows precision to 1%, allowing the portfolios to be carefully determined without using an excessive amount of qubits. \n", + "We only used 1 binary variable for the y variables since the y variables are already binary.\n", + "'''\n", + "\n", "import pyqubo\n", "from pyqubo import Array\n", "from dwave.samplers import SimulatedAnnealingSampler\n", @@ -1244,9 +1253,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 [Moody's]", + "display_name": "Python 3", "language": "python", - "name": "python3_moodys_xbto4j" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -1258,7 +1267,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.18" + "version": "3.11.7" } }, "nbformat": 4, From 9b38019c6e00a8ba2c769d55f229bcff576888c5 Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 09:19:51 -0500 Subject: [PATCH 18/20] Close out project --- QUBO (1).ipynb | 597 ------------------------------------- QUBO (3).ipynb | 654 +++++++++++++++++++++++++++++++++++++++++ QUBO.ipynb | 230 --------------- moodys_challenge.ipynb | 580 +++++++++++++++++------------------- 4 files changed, 927 insertions(+), 1134 deletions(-) delete mode 100644 QUBO (1).ipynb create mode 100644 QUBO (3).ipynb delete mode 100644 QUBO.ipynb diff --git a/QUBO (1).ipynb b/QUBO (1).ipynb deleted file mode 100644 index 4d0308a..0000000 --- a/QUBO (1).ipynb +++ /dev/null @@ -1,597 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 75, - "id": "a9ff17c4-a1aa-472b-b4e5-8085a9cfb9cb", - "metadata": {}, - "outputs": [], - "source": [ - "import pyqubo\n", - "from pyqubo import Array, Binary, Constraint, Placeholder\n", - "import numpy as np\n", - "\n", - "\n", - "def binary2integer_norm(z):\n", - " try:\n", - " K = len(z)\n", - " except:\n", - " return 1/2 * z\n", - " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", - "\n", - "N, K = 2,3\n", - "T = 2\n", - "z = 0.05\n", - "eps = 0.1\n", - "\n", - "# Data variables\n", - "\n", - "Sigma = np.random.rand(N, N) # UPDATE\n", - "\n", - "Mu = np.random.rand(N) # UPDATE\n", - "\n", - "R = np.random.rand(T,N) # UPDATE\n", - "\n", - "# Slack, nonbasic and other variables\n", - "\n", - "z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", - "\n", - "r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", - "\n", - "A_norm = Array.create(\"A\", shape=(K, 1), vartype=\"BINARY\")\n", - "A_max = z\n", - "A = A_max*binary2integer_norm(A_norm)\n", - "\n", - "B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", - "B_max = np.random.rand(T) # UPDATE\n", - "\n", - "C_norm = Array.create(\"C\", shape=(K, 1), vartype=\"BINARY\")\n", - "C = C_max*binary2integer_norm(C_norm)\n", - "C_max = eps*T\n", - "\n", - "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", - "\n", - "r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", - "\n", - "\n", - "def IV(n):\n", - " return (n*n - n)/2 + n" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "id": "984954d0-0ceb-4365-83a6-4aa0a71e480c", - "metadata": {}, - "outputs": [], - "source": [ - "H_var = 0\n", - "for i in range(N):\n", - " for j in range(N):\n", - " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "id": "001fd2d2-5c9b-4d8e-a372-65360fa58f28", - "metadata": {}, - "outputs": [], - "source": [ - "H_exp = 0\n", - "for i in range(N):\n", - " H_exp += Mu[i]*binary2integer_norm(z_full[i])" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "id": "22f3f7b7-c47d-4a3c-9c6e-725f401e284d", - "metadata": {}, - "outputs": [], - "source": [ - "L1 = 100\n", - "L2s = [100]*T\n", - "L3 = 100\n", - "L4 = 10\n", - "M = 100\n", - "\n", - "H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", - "\n", - "H2 = sum([L2s[t]*Constraint((r_eps[0] \n", - " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", - " - M*(1-y[t][0])+1-1 \n", - " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", - "\n", - "H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", - "\n", - "H4 = L4*Constraint((binary2integer_norm(z_full[0])-1)**2, \"H4\")" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "id": "54358ac0-efaf-4c10-8b48-6951f12e15ff", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "H = H_var - H_exp + (H2+1-1) + (H4+1-1) + (H3+1-1)" - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "id": "f8341ed5-c8d7-4b59-9055-7a5791f72990", - "metadata": {}, - "outputs": [], - "source": [ - "model = H.compile()" - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "id": "d8231778-cbf6-4f4b-b4e9-61f57f889bd1", - "metadata": {}, - "outputs": [], - "source": [ - "qubo, offset = model.to_qubo() #H_dict)" - ] - }, - { - "cell_type": "code", - "execution_count": 82, - "id": "643d574c-c36b-484d-92c3-b9d90a6af70b", - "metadata": {}, - "outputs": [], - "source": [ - "from dwave.samplers import SimulatedAnnealingSampler" - ] - }, - { - "cell_type": "code", - "execution_count": 83, - "id": "d5b48d50-f295-43ac-8e8c-67bf63432b56", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "150" - ] - }, - "execution_count": 83, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "len(qubo)" - ] - }, - { - "cell_type": "code", - "execution_count": 84, - "id": "75ab433b-9d0d-4a31-aa62-e80b465bf4f3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'B_norm[0][0]': 1, 'B_norm[0][1]': 1, 'B_norm[0][2]': 1, 'B_norm[1][0]': 1, 'C[0][0]': 1, 'B_norm[1][1]': 1, 'B_norm[1][2]': 1, 'C[1][0]': 1, 'C[2][0]': 1, 'r_eps[0][0]': 1, 'r_eps[1][0]': 1, 'r_eps[2][0]': 1, 'y[0][0]': 1, 'y[1][0]': 1, 'z_full[0][0]': 1, 'z_full[0][1]': 0, 'z_full[0][2]': 1, 'z_full[1][0]': 0, 'z_full[1][1]': 0, 'z_full[1][2]': 0}\n" - ] - } - ], - "source": [ - "sampler = SimulatedAnnealingSampler()\n", - "sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", - "samples = model.decode_sampleset(sampleset)\n", - "best_sample = min(samples, key=lambda s: s.energy)\n", - "print(best_sample.sample)" - ] - }, - { - "cell_type": "code", - "execution_count": 85, - "id": "f0f45162-eb41-4a1f-8dcf-8d3ca00ff01d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'B_norm[0][0]'" - ] - }, - "execution_count": 85, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "list(best_sample.sample.keys())[0]" - ] - }, - { - "cell_type": "code", - "execution_count": 86, - "id": "e37eadc1-3598-4b5f-8166-0640bfb46b48", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'B_norm[0][0]': 1,\n", - " 'B_norm[0][1]': 1,\n", - " 'B_norm[0][2]': 1,\n", - " 'B_norm[1][0]': 1,\n", - " 'C[0][0]': 1,\n", - " 'B_norm[1][1]': 1,\n", - " 'B_norm[1][2]': 1,\n", - " 'C[1][0]': 1,\n", - " 'C[2][0]': 1,\n", - " 'r_eps[0][0]': 1,\n", - " 'r_eps[1][0]': 1,\n", - " 'r_eps[2][0]': 1,\n", - " 'y[0][0]': 1,\n", - " 'y[1][0]': 1,\n", - " 'z_full[0][0]': 1,\n", - " 'z_full[0][1]': 0,\n", - " 'z_full[0][2]': 1,\n", - " 'z_full[1][0]': 0,\n", - " 'z_full[1][1]': 0,\n", - " 'z_full[1][2]': 0}" - ] - }, - "execution_count": 86, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "best_sample.sample" - ] - }, - { - "cell_type": "code", - "execution_count": 87, - "id": "ee05072b-cca6-4ea7-9115-b84ba3100079", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'H2_1': (False, 0.00011264725981163792), 'H4': (False, 0.140625), 'H3': (False, 0.0006250000000003197), 'H2_0': (False, 0.00032004287349174376)}\n" - ] - } - ], - "source": [ - "print(best_sample.constraints())" - ] - }, - { - "cell_type": "code", - "execution_count": 88, - "id": "97fc4aae-09f7-41c6-bccb-f8d03eac4d85", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Array([[Binary('z_full[0][0]'), Binary('z_full[0][1]'), Binary('z_full[0][2]')],\n", - " [Binary('z_full[1][0]'), Binary('z_full[1][1]'), Binary('z_full[1][2]')]])" - ] - }, - "execution_count": 88, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "z_full" - ] - }, - { - "cell_type": "code", - "execution_count": 73, - "id": "c8c08839-1f62-44b0-85fd-acbad3c4eab4", - "metadata": {}, - "outputs": [], - "source": [ - "z_full_array = np.zeros(shape=z_full.shape)\n", - "r_eps_norm_array = np.zeros(shape=r_eps_norm.shape)\n", - "A_norm_array = np.zeros(shape=A_norm.shape)\n", - "B_norm_array = np.zeros(shape=B_norm.shape)\n", - "C_norm_array = np.zeros(shape=C_norm.shape)\n", - "y_array = np.zeros(shape=y.shape) \n", - "r_eps_array = np.zeros(shape=r_eps.shape)" - ] - }, - { - "cell_type": "code", - "execution_count": 72, - "id": "2c9ba4c4-3597-4dbf-9a9b-590d8fecd571", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[0, 0]\n", - "[0, 1]\n", - "[0, 2]\n", - "[1, 0]\n", - "[1, 1]\n", - "[1, 2]\n", - "B_full: [[1, 1, 1], [0, 0, 1]]\n", - "C: [[0, 0, 0], [1, 0, 0], [0, 0, 0]]\n", - "r_eps: [[1], [1], [1]]\n", - "y: [[1], [1]]\n", - "z_full: [[1, 0, 1], [0, 0, 0]]\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "key = \"B_full[0][1]\"\n", - "\n", - "\n", - "data =best_sample.sample \n", - "\n", - "\n", - "# Iterate through the dictionary and populate arrays\n", - "for key, value in data.items():\n", - " if key.startswith('B_full'):\n", - " # Use regular expression to extract indices\n", - " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", - " print(indices)\n", - " B_norm_array[indices[0]][indices[1]] = value\n", - " elif key.startswith('C'):\n", - " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", - " C_array[indices[0]][indices[1]] = value\n", - " elif key.startswith('r_eps'):\n", - " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", - " r_eps_array[indices[0]][indices[1]] = value\n", - " elif key.startswith('y'):\n", - " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", - " y_array[indices[0]][indices[1]] = value\n", - " elif key.startswith('z_full'):\n", - " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", - " z_full_array[indices[0]][indices[1]] = value\n", - "\n", - "# Print the populated arrays\n", - "print(\"B_full:\", B_full)\n", - "print(\"C:\", C)\n", - "print(\"r_eps:\", r_eps)\n", - "print(\"y:\", y)\n", - "print(\"z_full:\", z_full)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "56662e9d-475e-4e5e-9245-b491335027c4", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 55, - "id": "a2d4a016-f53d-4fab-aded-f0de0ffbf477", - "metadata": {}, - "outputs": [ - { - "ename": "IndexError", - "evalue": "list assignment index out of range", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mIndexError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_22084\\2096571514.py\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 39\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 40\u001b[0m \u001b[1;31m# Populate the array\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 41\u001b[1;33m \u001b[0marrays\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0marray_name\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mindices\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m[\u001b[0m\u001b[0mindices\u001b[0m\u001b[1;33m[\u001b[0m\u001b[1;36m1\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mindices\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m>\u001b[0m \u001b[1;36m1\u001b[0m \u001b[1;32melse\u001b[0m \u001b[1;36m0\u001b[0m\u001b[1;33m]\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 42\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 43\u001b[0m \u001b[1;31m# Print the populated arrays\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", - "\u001b[1;31mIndexError\u001b[0m: list assignment index out of range" - ] - } - ], - "source": [ - "import re\n", - "\n", - "data = {\n", - " 'B_full[0][0]': 1,\n", - " 'B_full[0][1]': 1,\n", - " 'B_full[0][2]': 1,\n", - " 'B_full[1][0]': 0,\n", - " 'C[1][0]': 1,\n", - " 'B_full[1][1]': 0,\n", - " 'C[0][0]': 0,\n", - " 'r_eps[2][0]': 1,\n", - " 'B_full[1][2]': 1,\n", - " 'C[2][0]': 0,\n", - " 'r_eps[0][0]': 1,\n", - " 'r_eps[1][0]': 1,\n", - " 'y[0][0]': 1,\n", - " 'y[1][0]': 1,\n", - " 'z_full[0][0]': 1,\n", - " 'z_full[0][1]': 0,\n", - " 'z_full[0][2]': 1,\n", - " 'z_full[1][0]': 0,\n", - " 'z_full[1][1]': 0,\n", - " 'z_full[1][2]': 0\n", - "}\n", - "\n", - "# Create arrays dynamically based on the keys\n", - "arrays = {}\n", - "\n", - "# Iterate through the dictionary and populate arrays\n", - "for key, value in data.items():\n", - " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", - " \n", - " # Extract the array name (e.g., B_full, C, r_eps, etc.)\n", - " array_name = key.split('[')[0]\n", - "\n", - " # If the array is not in the dictionary, create an empty array\n", - " if array_name not in arrays:\n", - " arrays[array_name] = [[0] * (max(indices[1] + 1, 1) if len(indices) > 1 else 1) for _ in range(max(indices[0] + 1, 1))]\n", - "\n", - " # Populate the array\n", - " arrays[array_name][indices[0]][indices[1] if len(indices) > 1 else 0] = value\n", - "\n", - "# Print the populated arrays\n", - "for array_name, array in arrays.items():\n", - " print(f\"{array_name}: {array}\")\n" - ] - }, - { - "cell_type": "code", - "execution_count": 57, - "id": "39d984f9-fd93-4852-bbb1-d09efd6bcf67", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'B_full': [0, 0], 'C': [1, 0], 'r_eps': [2, 0], 'y': [0, 0], 'z_full': [0, 0]}\n" - ] - } - ], - "source": [ - "def extract_dimensions(data):\n", - " dimensions = {}\n", - "\n", - " for key in data.keys():\n", - " # Split the key using '[' and ']' to extract indices\n", - " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", - " \n", - " # Extract dimension name (e.g., 'B_full', 'C', 'r_eps', 'y', 'z_full')\n", - " dimension_name = key.split('[')[0]\n", - "\n", - " # Update the dimensions dictionary\n", - " if dimension_name not in dimensions:\n", - " dimensions[dimension_name] = indices\n", - "\n", - " return dimensions\n", - "\n", - "data = {\n", - " 'B_full[0][0]': 1,\n", - " 'B_full[0][1]': 1,\n", - " 'B_full[0][2]': 1,\n", - " 'B_full[1][0]': 0,\n", - " 'C[1][0]': 1,\n", - " 'B_full[1][1]': 0,\n", - " 'C[0][0]': 0,\n", - " 'r_eps[2][0]': 1,\n", - " 'B_full[1][2]': 1,\n", - " 'C[2][0]': 0,\n", - " 'r_eps[0][0]': 1,\n", - " 'r_eps[1][0]': 1,\n", - " 'y[0][0]': 1,\n", - " 'y[1][0]': 1,\n", - " 'z_full[0][0]': 1,\n", - " 'z_full[0][1]': 0,\n", - " 'z_full[0][2]': 1,\n", - " 'z_full[1][0]': 0,\n", - " 'z_full[1][1]': 0,\n", - " 'z_full[1][2]': 0\n", - "}\n", - "\n", - "dimensions = extract_dimensions(data)\n", - "print(dimensions)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 58, - "id": "5cea29c9-5d1f-4512-a57c-cace6307c757", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "{'B_full': [1, 2], 'C': [2, 0], 'r_eps': [2, 0], 'y': [1, 0], 'z_full': [1, 2]}\n" - ] - } - ], - "source": [ - "import re\n", - "\n", - "def extract_dimensions(data):\n", - " dimensions = {}\n", - "\n", - " for key in data.keys():\n", - " # Split the key using '[' and ']' to extract indices\n", - " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", - "\n", - " # Extract dimension name (e.g., 'B_full', 'C', 'r_eps', 'y', 'z_full')\n", - " dimension_name = key.split('[')[0]\n", - "\n", - " # Update the dimensions dictionary with the largest indices\n", - " if dimension_name not in dimensions:\n", - " dimensions[dimension_name] = indices\n", - " else:\n", - " dimensions[dimension_name] = [max(curr, new) for curr, new in zip(dimensions[dimension_name], indices)]\n", - "\n", - " return dimensions\n", - "\n", - "data = {\n", - " 'B_full[0][0]': 1,\n", - " 'B_full[0][1]': 1,\n", - " 'B_full[0][2]': 1,\n", - " 'B_full[1][0]': 0,\n", - " 'C[1][0]': 1,\n", - " 'B_full[1][1]': 0,\n", - " 'C[0][0]': 0,\n", - " 'r_eps[2][0]': 1,\n", - " 'B_full[1][2]': 1,\n", - " 'C[2][0]': 0,\n", - " 'r_eps[0][0]': 1,\n", - " 'r_eps[1][0]': 1,\n", - " 'y[0][0]': 1,\n", - " 'y[1][0]': 1,\n", - " 'z_full[0][0]': 1,\n", - " 'z_full[0][1]': 0,\n", - " 'z_full[0][2]': 1,\n", - " 'z_full[1][0]': 0,\n", - " 'z_full[1][1]': 0,\n", - " 'z_full[1][2]': 0\n", - "}\n", - "\n", - "dimensions = extract_dimensions(data)\n", - "print(dimensions)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bb84c2e0-4397-4877-ae02-b68024321fc0", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.11" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/QUBO (3).ipynb b/QUBO (3).ipynb new file mode 100644 index 0000000..0079a65 --- /dev/null +++ b/QUBO (3).ipynb @@ -0,0 +1,654 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 144, + "id": "68ffab4b-fa83-4b74-997a-47dfed539bb0", + "metadata": {}, + "outputs": [], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "\n", + "N, K = 2,6\n", + "T = 4\n", + "z = 0.05\n", + "M = 100\n", + "eps = 0.1\n", + "\n", + "\n", + "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "\n", + "Rraw = df.values.T\n", + "\n", + "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", + "reward = Rraw[:N,:T]\n", + "\n", + "# Expected return of each asset\n", + "Mu = np.mean(reward, axis = 1)\n", + "\n", + "Sigma = np.cov(reward)\n", + "# Covariance matrix of asset returns\n", + "\n", + "R = reward.T" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "id": "dcfa3c78-bcc7-4249-9397-89004f2c6ec3", + "metadata": {}, + "outputs": [], + "source": [ + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "# Slack, nonbasic and other variables\n", + "\n", + "z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + "r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + "A_norm = Array.create(\"A_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + "A_max = z\n", + "A = A_max*binary2integer_norm(A_norm)\n", + "\n", + "B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + "B_max = np.max(R, axis=1) + z + M \n", + "\n", + "C_norm = Array.create(\"C_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + "C_max = eps*T\n", + "C = C_max*binary2integer_norm(C_norm)\n", + "\n", + "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + "r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "984954d0-0ceb-4365-83a6-4aa0a71e480c", + "metadata": {}, + "outputs": [], + "source": [ + "H_var = 0\n", + "for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "id": "001fd2d2-5c9b-4d8e-a372-65360fa58f28", + "metadata": {}, + "outputs": [], + "source": [ + "H_exp = 0\n", + "for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "id": "22f3f7b7-c47d-4a3c-9c6e-725f401e284d", + "metadata": {}, + "outputs": [], + "source": [ + "L1 = 100\n", + "L2s = [100]*T\n", + "L3 = 100\n", + "L4 = 1000\n", + "H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + "H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + "H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + "H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)])-1)**2, \"H4\")" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "id": "54358ac0-efaf-4c10-8b48-6951f12e15ff", + "metadata": {}, + "outputs": [], + "source": [ + "H = H_var - H_exp + (H1 +1-1) + (H2+1-1) + (H4+1-1) + (H3+1-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "id": "f8341ed5-c8d7-4b59-9055-7a5791f72990", + "metadata": {}, + "outputs": [], + "source": [ + "model = H.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "id": "d8231778-cbf6-4f4b-b4e9-61f57f889bd1", + "metadata": {}, + "outputs": [], + "source": [ + "qubo, offset = model.to_qubo() #H_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "id": "643d574c-c36b-484d-92c3-b9d90a6af70b", + "metadata": {}, + "outputs": [], + "source": [ + "from dwave.samplers import SimulatedAnnealingSampler" + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "id": "d5b48d50-f295-43ac-8e8c-67bf63432b56", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "895" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(qubo)" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "id": "75ab433b-9d0d-4a31-aa62-e80b465bf4f3", + "metadata": {}, + "outputs": [], + "source": [ + "sampler = SimulatedAnnealingSampler()\n", + "sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", + "samples = model.decode_sampleset(sampleset)\n", + "best_sample = min(samples, key=lambda s: s.energy)\n", + "#print(best_sample.sample)" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "id": "ee05072b-cca6-4ea7-9115-b84ba3100079", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'H2_2': (False, 2.1224169366451662e-08), 'H2_0': (False, 2.2598342719001514), 'H1': (False, -6.2341624917916505e-19), 'H2_1': (False, 0.0007791606739069312), 'H2_3': (False, 0.0007231105712639518), 'H4': (True, 0.0), 'H3': (False, 0.3600000000000012)}\n" + ] + } + ], + "source": [ + "print(best_sample.constraints())" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "id": "c8c08839-1f62-44b0-85fd-acbad3c4eab4", + "metadata": {}, + "outputs": [], + "source": [ + "z_full_array = np.zeros(shape=z_full.shape)\n", + "r_eps_norm_array = np.zeros(shape=r_eps_norm.shape)\n", + "A_norm_array = np.zeros(shape=A_norm.shape)\n", + "B_norm_array = np.zeros(shape=B_norm.shape)\n", + "C_norm_array = np.zeros(shape=C_norm.shape)\n", + "y_array = np.zeros(shape=y.shape) \n", + "r_eps_array = np.zeros(shape=r_eps_norm.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "2c9ba4c4-3597-4dbf-9a9b-590d8fecd571", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'best_sample' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[1], line 6\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mre\u001b[39;00m\n\u001b[0;32m 3\u001b[0m key \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mB_full[0][1]\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m----> 6\u001b[0m data \u001b[38;5;241m=\u001b[39m\u001b[43mbest_sample\u001b[49m\u001b[38;5;241m.\u001b[39msample \n\u001b[0;32m 9\u001b[0m \u001b[38;5;66;03m# Iterate through the dictionary and populate arrays\u001b[39;00m\n\u001b[0;32m 10\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m key, value \u001b[38;5;129;01min\u001b[39;00m data\u001b[38;5;241m.\u001b[39mitems():\n", + "\u001b[1;31mNameError\u001b[0m: name 'best_sample' is not defined" + ] + } + ], + "source": [ + "import re\n", + "\n", + "key = \"B_full[0][1]\"\n", + "\n", + "\n", + "data =best_sample.sample \n", + "\n", + "\n", + "# Iterate through the dictionary and populate arrays\n", + "for key, value in data.items():\n", + " if key.startswith('B_full'):\n", + " # Use regular expression to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " print(indices)\n", + " B_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('C'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " C_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('A'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " A_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('r_eps'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " r_eps_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('y'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " y_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('z_full'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " z_full_array[indices[0]][indices[1]] = value\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b09c21a", + "metadata": {}, + "outputs": [], + "source": [ + "r_eps = binary2integer_norm([r_eps_array[i][0] for i in range(K)]) \n", + "print(r_eps)\n", + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]\n", + "print(ws)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "id": "56662e9d-475e-4e5e-9245-b491335027c4", + "metadata": {}, + "outputs": [], + "source": [ + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "id": "c936b40f-274a-4e2f-99d7-93b7415c013e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.65625, 0.34375]" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ws" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "id": "4f0ae41c-00d4-47c1-9db1-c66d7d29edc3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(ws)" + ] + }, + { + "cell_type": "markdown", + "id": "758f7c27-cbcc-4317-83b3-facf86cfb2a5", + "metadata": {}, + "source": [ + "# Lagrangian Multiplier Optimization" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "id": "e1b6be3a-b224-43c2-a434-a962c1639a71", + "metadata": {}, + "outputs": [], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "from dwave.samplers import SimulatedAnnealingSampler\n", + "len(qubo)\n", + "\n", + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n\n", + "\n", + "def f(L1, L3, L4, M, *L2s):\n", + " '''\n", + " L1 = 100\n", + " L2s = [100]*T\n", + " L3 = 100\n", + " L4 = 100\n", + " M = 100\n", + " '''\n", + " \n", + " z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + " r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + " A_norm = Array.create(\"A_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + " A_max = z\n", + " A = A_max*binary2integer_norm(A_norm)\n", + "\n", + " B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + " B_max = np.max(R, axis=1) + z + M\n", + "\n", + " C_norm = Array.create(\"C_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + " C_max = eps*T\n", + " C = C_max*binary2integer_norm(C_norm)\n", + "\n", + " y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + " r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + " H_var = 0\n", + " for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]\n", + "\n", + " H_exp = 0\n", + " for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])\n", + "\n", + " H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + " H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + " H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + " H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)])-1)**2, \"H4\")\n", + "\n", + " H = H_var - H_exp + (H1 +1-1) + (H2+1-1) + (H4+1-1) + (H3+1-1)\n", + "\n", + " model = H.compile()\n", + "\n", + " qubo, offset = model.to_qubo() #H_dict)\n", + "\n", + " sampler = SimulatedAnnealingSampler()\n", + " sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", + " samples = model.decode_sampleset(sampleset)\n", + " best_sample = min(samples, key=lambda s: s.energy)\n", + "\n", + " return sum([i for _,i in list(best_sample.constraints().values())])" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "id": "2da01160-9c02-491c-a860-21a054010004", + "metadata": {}, + "outputs": [], + "source": [ + "L1 = 100\n", + "L2s = [100]*T\n", + "L3 = 100\n", + "L4 = 100\n", + "M = 100" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "id": "4de97383-de76-4d7f-8725-5bffc6ec3752", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2.5554789499384083" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f(L1, L3, L4, M, *L2s)" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "id": "d2f070e5-0ca3-49a5-a948-76517d1e0978", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "def numerical_gradient(f, *params, epsilon=1e-7):\n", + " \"\"\"\n", + " Calculate the numerical gradient of the function f with respect to each parameter.\n", + " \"\"\"\n", + " gradients = []\n", + " for i in range(len(params)):\n", + " params_plus_epsilon = list(params)\n", + " params_plus_epsilon[i] += epsilon\n", + " params_minus_epsilon = list(params)\n", + " params_minus_epsilon[i] -= epsilon\n", + "\n", + " gradient_i = (f(*params_plus_epsilon) - f(*params_minus_epsilon)) / (2 * epsilon)\n", + " gradients.append(gradient_i)\n", + "\n", + " return np.array(gradients)\n", + "\n", + "def gradient_descent(f, initial_params, learning_rate=1e-7, num_iterations=100):\n", + " \"\"\"\n", + " Perform gradient descent optimization to minimize the function f.\n", + " \"\"\"\n", + " params = np.array(initial_params, dtype=float) # Explicitly set data type to float\n", + "\n", + " for iteration in range(num_iterations):\n", + " grad = numerical_gradient(f, *params)\n", + " params -= learning_rate * grad\n", + "\n", + " if iteration % 5 == 0:\n", + " print(f'Iteration {iteration}, Value: {f(*params)}')\n", + "\n", + " return params\n", + "\n", + "# Set initial parameter values\n", + "initial_params = [100, 100, 100, 100] + [100] * T # Replace T with the appropriate value\n", + "\n", + "# Perform gradient descent\n", + "optimized_params = gradient_descent(f, initial_params)\n", + "\n", + "# Print the optimized parameters and the corresponding function value\n", + "print(\"Optimized Parameters:\", optimized_params)" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "id": "ecd24c26-e4f9-4040-a347-50e22cb71809", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimized Value: 0.0012257928724465485\n" + ] + } + ], + "source": [ + "print(\"Optimized Value:\", f(*optimized_params))" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "id": "ec0a73b0-edc4-4056-88a0-4967eefe09ef", + "metadata": {}, + "outputs": [], + "source": [ + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "id": "2749ba72-7ed8-409d-b405-ee3e7019200b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.65625, 0.34375]" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ws" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "id": "1ecb0e81-2df6-4ed4-a7ae-2e76b69fcc12", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 152, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(ws)" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "id": "daa06ca7-1e93-453e-b88e-2ef25f03459c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.002 , 0.0449, 0.0108, ..., -0.0327, 0.0167, 0.0186],\n", + " [ 0.0148, -0.0068, -0.0225, ..., 0.0211, 0.0214, 0.0049],\n", + " [-0.0058, 0.0123, 0.0039, ..., 0.024 , 0.047 , 0.0134],\n", + " ...,\n", + " [ 0.0593, 0.0789, 0.0782, ..., 0. , 0. , 0. ],\n", + " [ 0.0184, -0.0253, -0.0006, ..., 0.0148, 0.0251, -0.0143],\n", + " [-0.0006, -0.0123, -0.0045, ..., 0.0541, -0.0044, -0.0013]])" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.round(Rraw, 4)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/QUBO.ipynb b/QUBO.ipynb deleted file mode 100644 index c6c3e10..0000000 --- a/QUBO.ipynb +++ /dev/null @@ -1,230 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 25, - "id": "a9ff17c4-a1aa-472b-b4e5-8085a9cfb9cb", - "metadata": {}, - "outputs": [], - "source": [ - "import pyqubo\n", - "from pyqubo import Array, Binary, Constraint, Placeholder\n", - "import numpy as np\n", - "N, K = 3, 5\n", - "T = 7\n", - "z = 0.05\n", - "eps = 0.1\n", - "\n", - "Sigma = np.random.rand(N, N) # UPDATE\n", - "\n", - "Mu = np.random.rand(N)\n", - "\n", - "\n", - "R = np.random.rand(T,N) # UPDATE\n", - "\n", - "z_full = Array.create(\"z_full\", shape= (N, K), vartype=\"BINARY\")\n", - "\n", - "r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", - "\n", - "A_norm = Array.create(\"A\", shape=(K, 1), vartype=\"BINARY\")\n", - "A_max = z\n", - "\n", - "B_norm = Array.create(\"B_full\", shape=(T, K), vartype=\"BINARY\")\n", - "B_max = np.random.rand(T) # UPDATE\n", - "\n", - "C_norm = Array.create(\"C\", shape=(K, 1), vartype=\"BINARY\")\n", - "C_max = eps*T\n", - "\n", - "\n", - "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", - "\n", - "def binary2integer_norm(z):\n", - " K = len(z)\n", - " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", - "\n", - "\n", - "r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", - "A = A_max*binary2integer_norm(A_norm)\n", - "C = C_max*binary2integer_norm(C_norm)\n", - "\n", - "M = 10000\n", - "\n", - "def IV(n):\n", - " return (n*n - n)/2 + n" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "984954d0-0ceb-4365-83a6-4aa0a71e480c", - "metadata": {}, - "outputs": [], - "source": [ - "H_var = 0\n", - "for i in range(N):\n", - " for j in range(N):\n", - " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "001fd2d2-5c9b-4d8e-a372-65360fa58f28", - "metadata": {}, - "outputs": [], - "source": [ - "H_exp = 0\n", - "for i in range(N):\n", - " H_exp += Mu[i]*binary2integer_norm(z_full[i])" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "0c010e01-e680-4f71-8f75-67feadd7c90e", - "metadata": {}, - "outputs": [], - "source": [ - "L1 = 100\n", - "\n", - "H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", - "\n", - "H1_dict = {'L1': 5.0}" - ] - }, - { - "cell_type": "code", - "execution_count": 29, - "id": "b3e98794-3326-4003-8e62-22353d853a02", - "metadata": {}, - "outputs": [], - "source": [ - "L2s = ['L2_{}'.format(t) for t in range(T)]\n", - "\n", - "H2 = sum([Placeholder(L2s[t])*Constraint((r_eps[0] \n", - " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", - " - M*(1-y[t][0])+1-1 \n", - " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", - "\n", - "M_dict = {\"M\": 1}\n", - "L2s_dict = dict(zip(L2s, [1]*T))\n", - "H2_dict = {**M_dict, **L2s_dict}" - ] - }, - { - "cell_type": "code", - "execution_count": 30, - "id": "37ec88b8-e64e-4613-83ec-d4dcade3c98b", - "metadata": {}, - "outputs": [], - "source": [ - "L3 = 100\n", - "\n", - "H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", - "\n", - "H3_dict = {'L3': 5.0}" - ] - }, - { - "cell_type": "code", - "execution_count": 31, - "id": "20a04a02-487a-4764-88f8-60172830062a", - "metadata": {}, - "outputs": [], - "source": [ - "L4 = 500\n", - "\n", - "H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)]))**2, \"H4\")\n", - "\n", - "H4_dict = {'L4': 5.0}" - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "54358ac0-efaf-4c10-8b48-6951f12e15ff", - "metadata": {}, - "outputs": [], - "source": [ - "H = H_var - H_exp + (H4+1-1) + (H1+1-1) + (H3+1-1) + (H2+1-1)\n", - "H_dict = {**H4_dict ,**H3_dict ,**H2_dict ,**H1_dict}" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "f8341ed5-c8d7-4b59-9055-7a5791f72990", - "metadata": {}, - "outputs": [], - "source": [ - "model = H.compile()" - ] - }, - { - "cell_type": "code", - "execution_count": 34, - "id": "d8231778-cbf6-4f4b-b4e9-61f57f889bd1", - "metadata": {}, - "outputs": [], - "source": [ - "qubo = model.to_bqm(feed_dict= H_dict)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "643d574c-c36b-484d-92c3-b9d90a6af70b", - "metadata": {}, - "outputs": [], - "source": [ - "from dwave.samplers import SimulatedAnnealingSampler" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "75ab433b-9d0d-4a31-aa62-e80b465bf4f3", - "metadata": {}, - "outputs": [ - { - "ename": "", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[1;31mThe Kernel crashed while executing code in the the current cell or a previous cell. Please review the code in the cell(s) to identify a possible cause of the failure. Click here for more info. View Jupyter log for further details." - ] - } - ], - "source": [ - "sampler = SimulatedAnnealingSampler()\n", - "sampleset = sampler.sample(qubo)\n", - "samples = model.decode_sampleset(sampleset)\n", - "#best_sample = min(samples, key=lambda s: s.energy)\n", - "#print(best_sample.sample)\n", - "\n", - "#print(best_sample.constraints())" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.7" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 776457c..560a83d 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -384,13 +384,19 @@ } ], "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", "import numpy as np\n", "import pandas as pd\n", "\n", - "# You may choose to select different parameters/values\n", - "N = 3\n", - "T = 5\n", - "# Read returns\n", + "\n", + "N, K = 2,6\n", + "T = 4\n", + "z = 0.05\n", + "M = 100\n", + "eps = 0.1\n", + "\n", + "\n", "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", "\n", "Rraw = df.values.T\n", @@ -399,13 +405,12 @@ "reward = Rraw[:N,:T]\n", "\n", "# Expected return of each asset\n", - "expected_returns = np.mean(reward, axis = 1)\n", + "Mu = np.mean(reward, axis = 1)\n", "\n", + "Sigma = np.cov(reward)\n", "# Covariance matrix of asset returns\n", - "covariance_matrix = np.cov(reward)\n", "\n", - "print(expected_returns)\n", - "print(covariance_matrix)" + "R = reward.T" ] }, { @@ -564,18 +569,68 @@ "The x variables do need to be normalized, as all of their values are between 0 and 1. r_epsilon does not.\n", "'''\n", "\n", - "import pyqubo\n", - "from pyqubo import Array, Binary, Add\n", - "D_X = 5\n", - "D_A = 5\n", - "x_full = Array.create(\"x_full\", shape=(D_X, N), vartype=\"BINARY\")\n", - "print(x_full[:,0])\n", - "def binary2integer(x):\n", - " return sum([2**i*x[i] for i in range(len(x))])\n", - "print(type(binary2integer(x_full[:,0])))\n", + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "# Slack, nonbasic and other variables\n", + "\n", + "z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + "r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + "A_norm = Array.create(\"A_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + "A_max = z\n", + "A = A_max*binary2integer_norm(A_norm)\n", + "\n", + "B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + "B_max = np.max(R, axis=1) + z + M \n", + "\n", + "C_norm = Array.create(\"C_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + "C_max = eps*T\n", + "C = C_max*binary2integer_norm(C_norm)\n", + "\n", + "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + "r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n\n", + "\n", + "H_var = 0\n", + "for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]\n", + "\n", + "H_exp = 0\n", + "for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])\n", + "\n", + "L1 = 100\n", + "L2s = [100]*T\n", + "L3 = 100\n", + "L4 = 1000\n", + "H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + "H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + "H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + "H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)])-1)**2, \"H4\")\n", "\n", - "A = Array.create(\"A\", shape=(D_A, 1), vartype=\"BINARY\")\n", - "print(type(binary2integer(A[:,0])))" + "H = H_var - H_exp + (H1 +1-1) + (H2+1-1) + (H4+1-1) + (H3+1-1)\n", + "\n", + "model = H.compile()\n", + "\n", + "qubo, offset = model.to_qubo() #H_dict)\n", + "\n", + "\n" ] }, { @@ -613,97 +668,7 @@ } ], "source": [ - "import pyqubo\n", - "from pyqubo import Array, Binary, Add\n", - "import numpy as np\n", - "\n", - "z = 3\n", - "epsilon = 0.05\n", - "M = 1000\n", - "lambda_1 = 100\n", - "lambda_2 = 100\n", - "lambda_3 = 200\n", - "lambda_4 = 500\n", - "\n", - "D_X = 5\n", - "D_A = 5\n", - "D_B = 5\n", - "D_C = 5\n", - "D_R = 5\n", - "\n", - "\n", - "\n", - "x_full = Array.create(\"x\", shape=(D_X, N), vartype=\"BINARY\")\n", - "A = Array.create(\"a\", shape=(D_A, 1), vartype=\"BINARY\")\n", - "C = Array.create(\"c\", shape=(D_B, 1), vartype=\"BINARY\")\n", - "B = Array.create(\"b\", shape=(D_C, T), vartype=\"BINARY\")\n", - "r = Array.create(\"r\", shape=(D_R, 1), vartype=\"BINARY\")\n", - "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", - "\n", - "def binary2integer(x):\n", - " return sum([2**i*x[i] for i in range(len(x))])\n", - "\n", - "# Need new binary 2 integer\n", - "# Normalize the x and r_epsilon\n", - "\n", - "def create_hamiltonian(x_full, A, B, C, r, z, lambda_1, lambda_2, lambda_3, lambda_4, N, T, M, epsilon):\n", - " # Variance\n", - " variance_term = 0\n", - " for i in range(N):\n", - " for ii in range(N):\n", - " variance_term += (binary2integer(x_full[:,i])) * (binary2integer(x_full[:,ii])) * (covariance_matrix[i][ii])\n", - " # Mean\n", - " the_mean = 0\n", - " for i in range(N):\n", - " the_mean -= expected_returns[i]*binary2integer(x_full[:,i])\n", - " ### H_1\n", - " m = 0\n", - " m += (binary2integer(r[:,0]) * -1 + binary2integer(A[:,0]) * 1 - float(z))\n", - " m = m**2\n", - " H_1 = lambda_1*m\n", - " ### H2_t\n", - " H_2 = 0\n", - " for i in range(T):\n", - " the_sum = binary2integer(r[:,0])\n", - " for ii in range(N):\n", - " the_sum -= reward[ii][i]*binary2integer(x_full[:,ii])\n", - " y_terms = 0\n", - " for i in range(len(y)):\n", - " y_terms += y[i][0]\n", - " # The \"+1-1\" is to change it into an Add instance.\n", - " # For some reason Add does not have a constructor.\n", - " # And addition is not supported between an Add and a Base.\n", - " # So this silly workaround is needed to keep this working.\n", - " the_sum -= M*(1-y_terms)+1-1 + binary2integer(B[:,i])\n", - " the_sum = the_sum * the_sum\n", - " the_sum = the_sum*lambda_2\n", - " H_2 += the_sum\n", - " ### H3\n", - " y_terms = 0\n", - " for i in range(len(y)):\n", - " y_terms += y[i][0]\n", - " H_3 = (1-epsilon)*T+1-1 - y_terms + binary2integer(C[:,0])\n", - " H_3 = H_3 * H_3\n", - " H_3 = H_3 * lambda_3\n", - " ### H4\n", - " m = 0\n", - " for i in range(x_full.shape[1]):\n", - " m+= binary2integer(x_full[:,i])\n", - " m = (m - 1)**2\n", - " H_4 = lambda_4*m\n", - " ### Total\n", - " first = variance_term + the_mean\n", - " second = first + H_1\n", - " third = second + H_2\n", - " fourth = third + H_3\n", - " fifth = fourth + H_4\n", - " return variance_term + the_mean + H_1 + H_2 + H_3 + H_4\n", - "\n", - "H = create_hamiltonian(x_full,A,B,C,r,z,lambda_1,lambda_2,lambda_3,lambda_4,N,T,M,epsilon)\n", - "model = H.compile()\n", - "#Q = model.to_qubo()\n", - "Q = model.to_bqm()\n", - "print(Q)\n" + "\n" ] }, { @@ -796,97 +761,6 @@ "
" ] }, - { - "cell_type": "code", - "execution_count": 4, - "id": "fbc5f36d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"\\nfrom pyqubo import Array\\nnumbers = [1,1]\\ns = Array.create('s', shape=len(numbers), vartype='BINARY')\\nH = sum(n * s for s, n in zip(s, numbers))**2\\nmodel = H.compile()\\nqubo = model.to_bqm()\\nprint(qubo) # doctest: +SKIP\\n\"" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "'''\n", - "from pyqubo import Array\n", - "numbers = [1,1]\n", - "s = Array.create('s', shape=len(numbers), vartype='BINARY')\n", - "H = sum(n * s for s, n in zip(s, numbers))**2\n", - "model = H.compile()\n", - "qubo = model.to_bqm()\n", - "print(qubo) # doctest: +SKIP\n", - "'''" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e012568c", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "f7e34fa0", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "36" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "-160+196" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "1b15890f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"\\n# write your code here\\nimport pyqubo\\nfrom pyqubo import Array\\nfrom dwave.samplers import SimulatedAnnealingSampler\\nsampler = SimulatedAnnealingSampler()\\n\\nimport dimod\\nbqm = dimod.generators.gnp_random_bqm(100, .5, 'BINARY')\\nsampleset = sampler.sample(bqm)\\nprint(sampleset.lowest())\\nprint(sampleset.to_pandas_dataframe())\\n\"" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "'''\n", - "# write your code here\n", - "import pyqubo\n", - "from pyqubo import Array\n", - "from dwave.samplers import SimulatedAnnealingSampler\n", - "sampler = SimulatedAnnealingSampler()\n", - "\n", - "import dimod\n", - "bqm = dimod.generators.gnp_random_bqm(100, .5, 'BINARY')\n", - "sampleset = sampler.sample(bqm)\n", - "print(sampleset.lowest())\n", - "print(sampleset.to_pandas_dataframe())\n", - "'''\n" - ] - }, { "cell_type": "code", "execution_count": 50, @@ -911,81 +785,56 @@ } ], "source": [ - "'''\n", - "We chose roughly 8 binary variables for each of the slack variables and 7 for each of the x variables. \n", - "For x that allows precision to 1%, allowing the portfolios to be carefully determined without using an excessive amount of qubits. \n", - "We only used 1 binary variable for the y variables since the y variables are already binary.\n", - "'''\n", - "\n", - "import pyqubo\n", - "from pyqubo import Array\n", "from dwave.samplers import SimulatedAnnealingSampler\n", - "sampler = SimulatedAnnealingSampler()\n", "\n", - "# sampleset = sampler.sample_qubo(Q)\n", - "sampleset = sampler.sample(Q)\n", + "sampler = SimulatedAnnealingSampler()\n", + "sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", "samples = model.decode_sampleset(sampleset)\n", "best_sample = min(samples, key=lambda s: s.energy)\n", "\n", - "sample_dict = best_sample.sample\n", - "# print(sample_dict)\n", - "\n", - "\n", - "x_full_stuff = []\n", - "a_stuff = []\n", - "b_stuff = []\n", - "c_stuff = []\n", - "r_stuff = []\n", - "y_stuff = []\n", - "\n", - "for key, value in best_sample.sample.items():\n", - " if key[0] == \"x\":\n", - " x_full_stuff.append(key)\n", - " elif key[0] == \"a\":\n", - " a_stuff.append(key)\n", - " elif key[0] == \"b\":\n", - " b_stuff.append(key)\n", - " elif key[0] == \"c\":\n", - " c_stuff.append(key)\n", - " elif key[0] == \"r\":\n", - " r_stuff.append(key)\n", - " elif key[0] == \"y\":\n", - " y_stuff.append(key)\n", - "x_full_stuff.sort()\n", - "a_stuff.sort()\n", - "b_stuff.sort()\n", - "c_stuff.sort()\n", - "r_stuff.sort()\n", - "y_stuff.sort()\n", - "\n", - "print(x_full_stuff)\n", - "print(a_stuff)\n", - "print(b_stuff)\n", - "print(c_stuff)\n", - "print(r_stuff)\n", - "print(y_stuff)\n", + "z_full_array = np.zeros(shape=z_full.shape)\n", + "r_eps_norm_array = np.zeros(shape=r_eps_norm.shape)\n", + "A_norm_array = np.zeros(shape=A_norm.shape)\n", + "B_norm_array = np.zeros(shape=B_norm.shape)\n", + "C_norm_array = np.zeros(shape=C_norm.shape)\n", + "y_array = np.zeros(shape=y.shape) \n", + "r_eps_array = np.zeros(shape=r_eps_norm.shape)\n", "\n", + "import re\n", "\n", - "'''\n", - "x_weights = []\n", - "for i in range(D_X):\n", - " x_weight = 0\n", - " for ii in range(N):\n", - " x_weight += sample_dict['x[' + str(i) + \"][\" + str(ii) + \"]\"] * 1/(2**(ii))\n", - " x_weights.append(x_weight)\n", - "print(x_weights)\n", - "'''\n", - "def reconstruct_weights(letter, D_value, column_value):\n", - " weights = []\n", - " for i in range(column_value):\n", - " weight = 0\n", - " for ii in range(D_value):\n", - " weight += sample_dict[letter + '[' + str(ii) + \"][\" + str(i) + \"]\"] * 1/(2**(ii))\n", - " weights.append(weight)\n", - " print(weights)\n", - " return weights\n", + "key = \"B_full[0][1]\"\n", "\n", - "\n" + "\n", + "data =best_sample.sample \n", + "\n", + "\n", + "# Iterate through the dictionary and populate arrays\n", + "for key, value in data.items():\n", + " if key.startswith('B_full'):\n", + " # Use regular expression to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " print(indices)\n", + " B_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('C'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " C_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('A'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " A_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('r_eps'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " r_eps_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('y'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " y_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('z_full'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " z_full_array[indices[0]][indices[1]] = value\n", + "\n", + "r_eps = binary2integer_norm([r_eps_array[i][0] for i in range(K)]) \n", + "print(r_eps)\n", + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]\n", + "print(ws)\n" ] }, { @@ -1037,43 +886,6 @@ "with $\\textit{weights}$ being a list that includes the optimal values (your solution) for the variables $\\{x_{i}\\}$ and $\\textit{mu_R}$ the expected returns of the assets (given by the data). Compare the return value got from this function to $VaR_{\\epsilon}$ calculated above." ] }, - { - "cell_type": "code", - "execution_count": 54, - "id": "7c6d5972", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[1.0, 0.0, 0.0]\n", - "[0.5]\n", - "0.01570039844\n", - "0.0004052086194496182\n", - "-0.01570039844\n" - ] - } - ], - "source": [ - "# write your code here\n", - "# reconstruct_weights('a', D_A, 1)\n", - "#reconstruct_weights('b', D_B, 1)\n", - "x_weights = reconstruct_weights('x', D_X, N)\n", - "reconstruct_weights('r',D_R,1)\n", - "\n", - "portfolio_returns = np.dot(expected_returns, np.transpose(np.array(x_weights)))\n", - "print(portfolio_returns)\n", - "\n", - "variance = 0\n", - "for i in range(N):\n", - " for ii in range(N):\n", - " variance += x_weights[i] * x_weights[ii] * covariance_matrix[i][ii]\n", - "print(variance)\n", - "\n", - "print(calculate_historical_VaR(x_weights, expected_returns))" - ] - }, { "cell_type": "markdown", "id": "e32da760-2d7c-44cc-b541-0fd576eaedf2", @@ -1169,7 +981,132 @@ "metadata": {}, "outputs": [], "source": [ - "# write your code here" + "# Implemented gradient descent for the optimization of the hyperparameters\n", + "\n", + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "from dwave.samplers import SimulatedAnnealingSampler\n", + "len(qubo)\n", + "\n", + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n\n", + "\n", + "def f(L1, L3, L4, M, *L2s):\n", + " '''\n", + " L1 = 100\n", + " L2s = [100]*T\n", + " L3 = 100\n", + " L4 = 100\n", + " M = 100\n", + " '''\n", + " \n", + " z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + " r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + " A_norm = Array.create(\"A_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + " A_max = z\n", + " A = A_max*binary2integer_norm(A_norm)\n", + "\n", + " B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + " B_max = np.max(R, axis=1) + z + M\n", + "\n", + " C_norm = Array.create(\"C_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + " C_max = eps*T\n", + " C = C_max*binary2integer_norm(C_norm)\n", + "\n", + " y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + " r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + " H_var = 0\n", + " for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]\n", + "\n", + " H_exp = 0\n", + " for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])\n", + "\n", + " H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + " H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + " H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + " H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)])-1)**2, \"H4\")\n", + "\n", + " H = H_var - H_exp + (H1 +1-1) + (H2+1-1) + (H4+1-1) + (H3+1-1)\n", + "\n", + " model = H.compile()\n", + "\n", + " qubo, offset = model.to_qubo() #H_dict)\n", + "\n", + " sampler = SimulatedAnnealingSampler()\n", + " sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", + " samples = model.decode_sampleset(sampleset)\n", + " best_sample = min(samples, key=lambda s: s.energy)\n", + "\n", + " return sum([i for _,i in list(best_sample.constraints().values())])\n", + "\n", + "L1 = 100\n", + "L2s = [100]*T\n", + "L3 = 100\n", + "L4 = 100\n", + "M = 100\n", + "\n", + "import numpy as np\n", + "\n", + "def numerical_gradient(f, *params, epsilon=1e-7):\n", + " \"\"\"\n", + " Calculate the numerical gradient of the function f with respect to each parameter.\n", + " \"\"\"\n", + " gradients = []\n", + " for i in range(len(params)):\n", + " params_plus_epsilon = list(params)\n", + " params_plus_epsilon[i] += epsilon\n", + " params_minus_epsilon = list(params)\n", + " params_minus_epsilon[i] -= epsilon\n", + "\n", + " gradient_i = (f(*params_plus_epsilon) - f(*params_minus_epsilon)) / (2 * epsilon)\n", + " gradients.append(gradient_i)\n", + "\n", + " return np.array(gradients)\n", + "\n", + "def gradient_descent(f, initial_params, learning_rate=1e-7, num_iterations=100):\n", + " \"\"\"\n", + " Perform gradient descent optimization to minimize the function f.\n", + " \"\"\"\n", + " params = np.array(initial_params, dtype=float) # Explicitly set data type to float\n", + "\n", + " for iteration in range(num_iterations):\n", + " grad = numerical_gradient(f, *params)\n", + " params -= learning_rate * grad\n", + "\n", + " if iteration % 5 == 0:\n", + " print(f'Iteration {iteration}, Value: {f(*params)}')\n", + "\n", + " return params\n", + "\n", + "# Set initial parameter values\n", + "initial_params = [100, 100, 100, 100] + [100] * T # Replace T with the appropriate value\n", + "\n", + "# Perform gradient descent\n", + "optimized_params = gradient_descent(f, initial_params)\n", + "\n", + "# Print the optimized parameters and the corresponding function value\n", + "print(\"Optimized Parameters:\", optimized_params)" ] }, { @@ -1197,7 +1134,36 @@ "metadata": {}, "outputs": [], "source": [ - "# write your code here\n", + "# Would have been quantum machine learning, but we didn't get to it.\n", + "\n", + "'''\n", + "ALL ANSWERS TO QUESTIONS\n", + "\n", + "The total number of variables to be optimized including the slack variables is (T+2) + N + T + 1 = 2T + N + 3\n", + "(There are also the hyperparmaters lambda_1 thru lambda_4 and M, but those are tuned during the process)\n", + "\n", + "The x variables do need to be normalized, as all of their values are between 0 and 1. r_epsilon does not.\n", + "\n", + "There is an alternative approach for implementing the inequalities, which is a unbalanced penalization approach. This way, we can encode penalty terms without having to set inequalities up with slack variables, thereby saving on variables and becoming more efficient in the process in accordance to \"Unbalanced penalization: A new approach to encode inequality constraints of combinatorial problems for quantum optimization algorithms.\" \n", + "\n", + "We chose roughly 8 binary variables for each of the slack variables and 7 for each of the x variables. For x that allows precision to 1%, allowing the portfolios to be carefully determined without using an excessive amount of qubits. We only used 1 binary variable for the y variables since the y variables are already binary.\n", + "\n", + "\n", + "\n", + "We used binary for all 4 of the variable types. y_t was already in binary format so it seemed natural. x_i requires a decent amount of precision given that this is the output for the portfolio, and there aren't any issues with being unable to define a largest integer with binary as stated by \"Solving the Optimal Trading Trajectory Problem Using a Quantum Annealer.\" Therefore there aren't any disadvantages to using binary in this case as it is most efficient. The slack variables need a great deal of precision as they encode the constraints, and therefore will need a lot of qubits. They are also constrained by the problem, so it makes most sense to use binary in this case as well. r_epsilon similarly cannot take on large values and so there aren't any weaknesses to using binary for it either.\n", + "\n", + "The total number of binary variables we had after the mapping is equal to D_X*N + D_A + D_B*T + T + D_C + D_R \n", + "\n", + "\n", + "\n", + "QUBO is a great way to represent linear optimization problems and can be solved quite nicely by QAOA. (Quantum Optimization: Potential, Challenges, and the Path Forward\n", + "\n", + "There is evidence that QAOA and other quantum algorithms may have scaling advantages when it comes to classically intractable problems (Evidence of Scaling Advantage for the Quantum Approximate Optimization Algorithm on a Classically Intractable Problem)\n", + "\n", + "\n", + "\n", + "'''\n", + "\n", "\n" ] }, From eaf27b9cc52e51ada8514075107c306015300299 Mon Sep 17 00:00:00 2001 From: Ashrit Date: Sun, 4 Feb 2024 14:51:20 +0000 Subject: [PATCH 19/20] clarifying z val --- .ipynb_checkpoints/QUBO (3)-checkpoint.ipynb | 654 +++++++++++++++++++ .ipynb_checkpoints/deriveZ-checkpoint.ipynb | 173 +++-- QUBO (3).ipynb | 2 +- deriveZ.ipynb | 173 +++-- 4 files changed, 877 insertions(+), 125 deletions(-) create mode 100644 .ipynb_checkpoints/QUBO (3)-checkpoint.ipynb diff --git a/.ipynb_checkpoints/QUBO (3)-checkpoint.ipynb b/.ipynb_checkpoints/QUBO (3)-checkpoint.ipynb new file mode 100644 index 0000000..0079a65 --- /dev/null +++ b/.ipynb_checkpoints/QUBO (3)-checkpoint.ipynb @@ -0,0 +1,654 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 144, + "id": "68ffab4b-fa83-4b74-997a-47dfed539bb0", + "metadata": {}, + "outputs": [], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "\n", + "N, K = 2,6\n", + "T = 4\n", + "z = 0.05\n", + "M = 100\n", + "eps = 0.1\n", + "\n", + "\n", + "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "\n", + "Rraw = df.values.T\n", + "\n", + "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", + "reward = Rraw[:N,:T]\n", + "\n", + "# Expected return of each asset\n", + "Mu = np.mean(reward, axis = 1)\n", + "\n", + "Sigma = np.cov(reward)\n", + "# Covariance matrix of asset returns\n", + "\n", + "R = reward.T" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "id": "dcfa3c78-bcc7-4249-9397-89004f2c6ec3", + "metadata": {}, + "outputs": [], + "source": [ + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "# Slack, nonbasic and other variables\n", + "\n", + "z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + "r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + "A_norm = Array.create(\"A_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + "A_max = z\n", + "A = A_max*binary2integer_norm(A_norm)\n", + "\n", + "B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + "B_max = np.max(R, axis=1) + z + M \n", + "\n", + "C_norm = Array.create(\"C_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + "C_max = eps*T\n", + "C = C_max*binary2integer_norm(C_norm)\n", + "\n", + "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + "r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "984954d0-0ceb-4365-83a6-4aa0a71e480c", + "metadata": {}, + "outputs": [], + "source": [ + "H_var = 0\n", + "for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "id": "001fd2d2-5c9b-4d8e-a372-65360fa58f28", + "metadata": {}, + "outputs": [], + "source": [ + "H_exp = 0\n", + "for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "id": "22f3f7b7-c47d-4a3c-9c6e-725f401e284d", + "metadata": {}, + "outputs": [], + "source": [ + "L1 = 100\n", + "L2s = [100]*T\n", + "L3 = 100\n", + "L4 = 1000\n", + "H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + "H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + "H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + "H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)])-1)**2, \"H4\")" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "id": "54358ac0-efaf-4c10-8b48-6951f12e15ff", + "metadata": {}, + "outputs": [], + "source": [ + "H = H_var - H_exp + (H1 +1-1) + (H2+1-1) + (H4+1-1) + (H3+1-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "id": "f8341ed5-c8d7-4b59-9055-7a5791f72990", + "metadata": {}, + "outputs": [], + "source": [ + "model = H.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "id": "d8231778-cbf6-4f4b-b4e9-61f57f889bd1", + "metadata": {}, + "outputs": [], + "source": [ + "qubo, offset = model.to_qubo() #H_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "id": "643d574c-c36b-484d-92c3-b9d90a6af70b", + "metadata": {}, + "outputs": [], + "source": [ + "from dwave.samplers import SimulatedAnnealingSampler" + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "id": "d5b48d50-f295-43ac-8e8c-67bf63432b56", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "895" + ] + }, + "execution_count": 125, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(qubo)" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "id": "75ab433b-9d0d-4a31-aa62-e80b465bf4f3", + "metadata": {}, + "outputs": [], + "source": [ + "sampler = SimulatedAnnealingSampler()\n", + "sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", + "samples = model.decode_sampleset(sampleset)\n", + "best_sample = min(samples, key=lambda s: s.energy)\n", + "#print(best_sample.sample)" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "id": "ee05072b-cca6-4ea7-9115-b84ba3100079", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'H2_2': (False, 2.1224169366451662e-08), 'H2_0': (False, 2.2598342719001514), 'H1': (False, -6.2341624917916505e-19), 'H2_1': (False, 0.0007791606739069312), 'H2_3': (False, 0.0007231105712639518), 'H4': (True, 0.0), 'H3': (False, 0.3600000000000012)}\n" + ] + } + ], + "source": [ + "print(best_sample.constraints())" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "id": "c8c08839-1f62-44b0-85fd-acbad3c4eab4", + "metadata": {}, + "outputs": [], + "source": [ + "z_full_array = np.zeros(shape=z_full.shape)\n", + "r_eps_norm_array = np.zeros(shape=r_eps_norm.shape)\n", + "A_norm_array = np.zeros(shape=A_norm.shape)\n", + "B_norm_array = np.zeros(shape=B_norm.shape)\n", + "C_norm_array = np.zeros(shape=C_norm.shape)\n", + "y_array = np.zeros(shape=y.shape) \n", + "r_eps_array = np.zeros(shape=r_eps_norm.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "2c9ba4c4-3597-4dbf-9a9b-590d8fecd571", + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'best_sample' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[1], line 6\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m \u001b[38;5;21;01mre\u001b[39;00m\n\u001b[0;32m 3\u001b[0m key \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mB_full[0][1]\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m----> 6\u001b[0m data \u001b[38;5;241m=\u001b[39m\u001b[43mbest_sample\u001b[49m\u001b[38;5;241m.\u001b[39msample \n\u001b[0;32m 9\u001b[0m \u001b[38;5;66;03m# Iterate through the dictionary and populate arrays\u001b[39;00m\n\u001b[0;32m 10\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m key, value \u001b[38;5;129;01min\u001b[39;00m data\u001b[38;5;241m.\u001b[39mitems():\n", + "\u001b[1;31mNameError\u001b[0m: name 'best_sample' is not defined" + ] + } + ], + "source": [ + "import re\n", + "\n", + "key = \"B_full[0][1]\"\n", + "\n", + "\n", + "data =best_sample.sample \n", + "\n", + "\n", + "# Iterate through the dictionary and populate arrays\n", + "for key, value in data.items():\n", + " if key.startswith('B_full'):\n", + " # Use regular expression to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " print(indices)\n", + " B_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('C'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " C_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('A'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " A_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('r_eps'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " r_eps_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('y'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " y_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('z_full'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " z_full_array[indices[0]][indices[1]] = value\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b09c21a", + "metadata": {}, + "outputs": [], + "source": [ + "r_eps = binary2integer_norm([r_eps_array[i][0] for i in range(K)]) \n", + "print(r_eps)\n", + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]\n", + "print(ws)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "id": "56662e9d-475e-4e5e-9245-b491335027c4", + "metadata": {}, + "outputs": [], + "source": [ + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "id": "c936b40f-274a-4e2f-99d7-93b7415c013e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.65625, 0.34375]" + ] + }, + "execution_count": 131, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ws" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "id": "4f0ae41c-00d4-47c1-9db1-c66d7d29edc3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 132, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(ws)" + ] + }, + { + "cell_type": "markdown", + "id": "758f7c27-cbcc-4317-83b3-facf86cfb2a5", + "metadata": {}, + "source": [ + "# Lagrangian Multiplier Optimization" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "id": "e1b6be3a-b224-43c2-a434-a962c1639a71", + "metadata": {}, + "outputs": [], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "from dwave.samplers import SimulatedAnnealingSampler\n", + "len(qubo)\n", + "\n", + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n\n", + "\n", + "def f(L1, L3, L4, M, *L2s):\n", + " '''\n", + " L1 = 100\n", + " L2s = [100]*T\n", + " L3 = 100\n", + " L4 = 100\n", + " M = 100\n", + " '''\n", + " \n", + " z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + " r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + " A_norm = Array.create(\"A_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + " A_max = z\n", + " A = A_max*binary2integer_norm(A_norm)\n", + "\n", + " B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + " B_max = np.max(R, axis=1) + z + M\n", + "\n", + " C_norm = Array.create(\"C_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + " C_max = eps*T\n", + " C = C_max*binary2integer_norm(C_norm)\n", + "\n", + " y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + " r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + " H_var = 0\n", + " for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]\n", + "\n", + " H_exp = 0\n", + " for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])\n", + "\n", + " H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + " H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + " H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + " H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)])-1)**2, \"H4\")\n", + "\n", + " H = H_var - H_exp + (H1 +1-1) + (H2+1-1) + (H4+1-1) + (H3+1-1)\n", + "\n", + " model = H.compile()\n", + "\n", + " qubo, offset = model.to_qubo() #H_dict)\n", + "\n", + " sampler = SimulatedAnnealingSampler()\n", + " sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", + " samples = model.decode_sampleset(sampleset)\n", + " best_sample = min(samples, key=lambda s: s.energy)\n", + "\n", + " return sum([i for _,i in list(best_sample.constraints().values())])" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "id": "2da01160-9c02-491c-a860-21a054010004", + "metadata": {}, + "outputs": [], + "source": [ + "L1 = 100\n", + "L2s = [100]*T\n", + "L3 = 100\n", + "L4 = 100\n", + "M = 100" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "id": "4de97383-de76-4d7f-8725-5bffc6ec3752", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2.5554789499384083" + ] + }, + "execution_count": 147, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f(L1, L3, L4, M, *L2s)" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "id": "d2f070e5-0ca3-49a5-a948-76517d1e0978", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "def numerical_gradient(f, *params, epsilon=1e-7):\n", + " \"\"\"\n", + " Calculate the numerical gradient of the function f with respect to each parameter.\n", + " \"\"\"\n", + " gradients = []\n", + " for i in range(len(params)):\n", + " params_plus_epsilon = list(params)\n", + " params_plus_epsilon[i] += epsilon\n", + " params_minus_epsilon = list(params)\n", + " params_minus_epsilon[i] -= epsilon\n", + "\n", + " gradient_i = (f(*params_plus_epsilon) - f(*params_minus_epsilon)) / (2 * epsilon)\n", + " gradients.append(gradient_i)\n", + "\n", + " return np.array(gradients)\n", + "\n", + "def gradient_descent(f, initial_params, learning_rate=1e-7, num_iterations=100):\n", + " \"\"\"\n", + " Perform gradient descent optimization to minimize the function f.\n", + " \"\"\"\n", + " params = np.array(initial_params, dtype=float) # Explicitly set data type to float\n", + "\n", + " for iteration in range(num_iterations):\n", + " grad = numerical_gradient(f, *params)\n", + " params -= learning_rate * grad\n", + "\n", + " if iteration % 5 == 0:\n", + " print(f'Iteration {iteration}, Value: {f(*params)}')\n", + "\n", + " return params\n", + "\n", + "# Set initial parameter values\n", + "initial_params = [100, 100, 100, 100] + [100] * T # Replace T with the appropriate value\n", + "\n", + "# Perform gradient descent\n", + "optimized_params = gradient_descent(f, initial_params)\n", + "\n", + "# Print the optimized parameters and the corresponding function value\n", + "print(\"Optimized Parameters:\", optimized_params)" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "id": "ecd24c26-e4f9-4040-a347-50e22cb71809", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimized Value: 0.0012257928724465485\n" + ] + } + ], + "source": [ + "print(\"Optimized Value:\", f(*optimized_params))" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "id": "ec0a73b0-edc4-4056-88a0-4967eefe09ef", + "metadata": {}, + "outputs": [], + "source": [ + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "id": "2749ba72-7ed8-409d-b405-ee3e7019200b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.65625, 0.34375]" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ws" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "id": "1ecb0e81-2df6-4ed4-a7ae-2e76b69fcc12", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 152, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(ws)" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "id": "daa06ca7-1e93-453e-b88e-2ef25f03459c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.002 , 0.0449, 0.0108, ..., -0.0327, 0.0167, 0.0186],\n", + " [ 0.0148, -0.0068, -0.0225, ..., 0.0211, 0.0214, 0.0049],\n", + " [-0.0058, 0.0123, 0.0039, ..., 0.024 , 0.047 , 0.0134],\n", + " ...,\n", + " [ 0.0593, 0.0789, 0.0782, ..., 0. , 0. , 0. ],\n", + " [ 0.0184, -0.0253, -0.0006, ..., 0.0148, 0.0251, -0.0143],\n", + " [-0.0006, -0.0123, -0.0045, ..., 0.0541, -0.0044, -0.0013]])" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.round(Rraw, 4)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb index 3c198ee..81b614b 100644 --- a/.ipynb_checkpoints/deriveZ-checkpoint.ipynb +++ b/.ipynb_checkpoints/deriveZ-checkpoint.ipynb @@ -12,12 +12,26 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 2, "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", "metadata": { "tags": [] }, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_219/316017550.py:4: DeprecationWarning: \n", + "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", + "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", + "but was not found to be installed on your system.\n", + "If this would cause problems for you,\n", + "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", + " \n", + " import pandas as pd\n" + ] + }, { "name": "stdout", "output_type": "stream", @@ -101,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 3, "id": "1454b6b3-e891-4511-8c78-f8396fedce42", "metadata": { "tags": [] @@ -110,17 +124,17 @@ { "data": { "text/plain": [ - "(array([0.03295262, 0.00357888, 0.02086894, 0.01404641, 0.00207373,\n", - " 0.04048195, 0.03747879, 0.06892786, 0.00658971, 0.07627872,\n", - " 0.03231812, 0.01837159, 0.01969979, 0.00613971, 0.05614756,\n", - " 0.07319379, 0.01233284, 0.0447664 , 0.01589302, 0.00435266,\n", - " 0.02044029, 0.04002689, 0.00849307, 0.01801094, 0.06357421,\n", - " 0.04002009, 0.00848955, 0.05466866, 0.05900047, 0.0350204 ,\n", - " 0.00453561, 0.06122671]),\n", - " 0.9999999999999997)" + "(array([0.00418642, 0.02575848, 0.06693708, 0.06799167, 0.05898317,\n", + " 0.07067119, 0.02501272, 0.01295003, 0.04382892, 0.06962844,\n", + " 0.05880262, 0.02637454, 0.02220247, 0.01063647, 0.01108875,\n", + " 0.00419621, 0.00399815, 0.0395862 , 0.02611385, 0.06111845,\n", + " 0.07534739, 0.01170318, 0.02671506, 0.01058358, 0.04007832,\n", + " 0.00407736, 0.0111234 , 0.01482553, 0.02636917, 0.03315742,\n", + " 0.02972971, 0.00622408]),\n", + " 1.0)" ] }, - "execution_count": 47, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -141,7 +155,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 4, "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", "metadata": { "tags": [] @@ -159,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 5, "id": "d9e7df0d-6d54-41d0-8396-3419e202c41f", "metadata": { "tags": [] @@ -182,7 +196,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 6, "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", "metadata": { "tags": [] @@ -191,50 +205,49 @@ { "data": { "text/plain": [ - "([0.3201754590963047,\n", - " 0.3040817367921548,\n", - " 0.33570642951204405,\n", - " 0.324621105208764,\n", - " 0.35256804169434885],\n", - " [array([0.03519249, 0.03290085, 0.01317292, 0.0536117 , 0.05335179,\n", - " 0.0282839 , 0.04913777, 0.03639204, 0.00584395, 0.00473127,\n", - " 0.01556526, 0.05040084, 0.00158258, 0.05193638, 0.01152281,\n", - " 0.0274181 , 0.04280595, 0.02104865, 0.04392498, 0.01501355,\n", - " 0.00104685, 0.03588135, 0.04501165, 0.01426848, 0.03105206,\n", - " 0.04519589, 0.02398323, 0.03468755, 0.03416542, 0.04537202,\n", - " 0.04764116, 0.04785654]),\n", - " array([0.04150249, 0.04927715, 0.04050631, 0.01704145, 0.02052018,\n", - " 0.02598609, 0.03970675, 0.04701215, 0.02573006, 0.03526343,\n", - " 0.0118426 , 0.0231728 , 0.03486327, 0.00175815, 0.02021195,\n", - " 0.04307619, 0.01233438, 0.04928072, 0.0216664 , 0.01882121,\n", - " 0.05057916, 0.02193385, 0.02624362, 0.01480431, 0.04967795,\n", - " 0.01678528, 0.04148598, 0.03736406, 0.03326363, 0.03940288,\n", - " 0.03435846, 0.05452709]),\n", - " array([0.01604598, 0.04529005, 0.01110671, 0.05985637, 0.04223921,\n", - " 0.01186149, 0.04146411, 0.0174574 , 0.04475275, 0.0483324 ,\n", - " 0.01738737, 0.03744703, 0.01976326, 0.06031816, 0.01705841,\n", - " 0.01359626, 0.05254128, 0.03369028, 0.06079489, 0.00420929,\n", - " 0.00269271, 0.02072749, 0.02317871, 0.06133557, 0.05006869,\n", - " 0.03762329, 0.0628452 , 0.00436101, 0.03140263, 0.00272598,\n", - " 0.00502145, 0.04280456]),\n", - " array([3.75143474e-02, 3.56680465e-02, 6.07580111e-02, 1.78872938e-02,\n", - " 4.47482137e-02, 4.36094397e-02, 1.02396118e-02, 3.50628726e-02,\n", - " 5.74763685e-03, 5.88533879e-02, 1.77641933e-02, 2.20699045e-02,\n", - " 2.89828714e-02, 1.46328359e-02, 3.59180566e-02, 1.63276401e-02,\n", - " 3.79104899e-02, 4.88496753e-02, 1.12161826e-03, 3.36144250e-02,\n", - " 5.87756224e-02, 7.50474417e-03, 4.26659419e-02, 3.54226972e-02,\n", - " 5.63592516e-02, 3.53101020e-02, 2.35589519e-02, 1.05982766e-02,\n", - " 5.60329215e-02, 5.09637794e-03, 7.95060325e-05, 6.13150354e-02]),\n", - " array([0.03894738, 0.03544109, 0.00490616, 0.05484427, 0.01377497,\n", - " 0.0112676 , 0.06334437, 0.05774461, 0.04677672, 0.02128168,\n", - " 0.0643672 , 0.01938623, 0.02496569, 0.03924972, 0.05728707,\n", - " 0.04692048, 0.01114821, 0.0088831 , 0.00936529, 0.06442574,\n", - " 0.04214907, 0.0072486 , 0.0600262 , 0.02152251, 0.00609748,\n", - " 0.02466099, 0.00043209, 0.00359317, 0.0311648 , 0.05129704,\n", - " 0.05541976, 0.00206072])])" + "([0.3128811166148638,\n", + " 0.34202650160735865,\n", + " 0.35334276930136604,\n", + " 0.3410365740045231,\n", + " 0.3165141564733053],\n", + " [array([0.0495565 , 0.04090496, 0.04357528, 0.03138587, 0.04548934,\n", + " 0.04759537, 0.00425981, 0.04478657, 0.03877712, 0.04574071,\n", + " 0.04282936, 0.04846589, 0.04523607, 0.02474504, 0.00173475,\n", + " 0.01009745, 0.04951992, 0.00847554, 0.03235129, 0.02737412,\n", + " 0.02521216, 0.01083173, 0.03016904, 0.0412418 , 0.03148121,\n", + " 0.04565462, 0.00779222, 0.04414392, 0.04107802, 0.00390577,\n", + " 0.03467097, 0.00091758]),\n", + " array([0.01403353, 0.00982509, 0.02215693, 0.04888251, 0.03759727,\n", + " 0.04673139, 0.03929041, 0.02254295, 0.0167434 , 0.03030957,\n", + " 0.03559299, 0.0318543 , 0.02907078, 0.04154272, 0.01493068,\n", + " 0.04450683, 0.04399607, 0.00446831, 0.05255986, 0.03136256,\n", + " 0.05252859, 0.04756222, 0.03254053, 0.01271068, 0.00609097,\n", + " 0.02039448, 0.05474213, 0.04600814, 0.01125323, 0.04347461,\n", + " 0.04532531, 0.00937095]),\n", + " array([0.04276039, 0.05577527, 0.06599738, 0.02122337, 0.01577919,\n", + " 0.01777574, 0.06049104, 0.0060848 , 0.04150497, 0.05034561,\n", + " 0.04371688, 0.01410727, 0.00454085, 0.05396461, 0.03262252,\n", + " 0.05792247, 0.02488471, 0.02395531, 0.04751275, 0.00454294,\n", + " 0.01212546, 0.00017779, 0.02708621, 0.00191693, 0.00403708,\n", + " 0.05340689, 0.0411082 , 0.02861891, 0.06517827, 0.04447769,\n", + " 0.0257816 , 0.01057689]),\n", + " array([0.02446189, 0.04810576, 0.01109954, 0.02208651, 0.04830805,\n", + " 0.06095532, 0.04611498, 0.03494051, 0.02218763, 0.01404973,\n", + " 0.01502475, 0.01276091, 0.00501265, 0.00937838, 0.02563213,\n", + " 0.04173373, 0.03744621, 0.0257746 , 0.01127356, 0.04091599,\n", + " 0.06029149, 0.02897527, 0.06148641, 0.01376761, 0.00746243,\n", + " 0.00780886, 0.03731954, 0.03871881, 0.05245977, 0.05246551,\n", + " 0.04014242, 0.04183903]),\n", + " array([0.05262785, 0.00862903, 0.04958039, 0.02477631, 0.01194494,\n", + " 0.03535696, 0.04781062, 0.05548981, 0.00623476, 0.04606431,\n", + " 0.03779445, 0.05056566, 0.01432316, 0.01568648, 0.04037295,\n", + " 0.03577056, 0.03621072, 0.01843537, 0.0535073 , 0.02829103,\n", + " 0.05666226, 0.03065277, 0.01180278, 0.04231911, 0.03407627,\n", + " 0.00302919, 0.02812648, 0.01991216, 0.05019098, 0.01203927,\n", + " 0.03826156, 0.00345451])])" ] }, - "execution_count": 43, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -258,7 +271,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 7, "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", "metadata": { "tags": [] @@ -268,8 +281,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "The mean VaR at a 95.0% confidence level is: -0.0010204670111313022\n", - "The median VaR at a 95.0% confidence level is: -0.0010196371916901341\n" + "The mean VaR at a 95.0% confidence level is: -0.001012471817893799\n", + "The median VaR at a 95.0% confidence level is: -0.001007872904990408\n" ] } ], @@ -306,11 +319,47 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "96b1afa0-a9b9-4f7c-b891-6dbc0eedf3df", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The mean VaR at a 95.0% confidence level is: 0.0455241269789427\n", + "The median VaR at a 95.0% confidence level is: 0.045520151841198175\n" + ] + } + ], + "source": [ + "# Parameters\n", + "number_of_assets = 32\n", + "number_of_portfolios = 1000 # The number of random portfolios you want to generate\n", + "confidence_level = 0.95\n", + "\n", + "# Assuming mu_R is the matrix of historical returns for the assets\n", + "# Replace this with your actual historical returns data\n", + "# mu_R = expected_returns # This should be the historical returns data for your assets\n", + "# expected retursn --> z = .001\n", + "mu_R = R.T # z = .0045\n", + "# z provided from packages == 1.645\n", + "\n", + "# Generate the random weights for the portfolios and calculate the VaR for each\n", + "VaRs = []\n", + "for _ in range(number_of_portfolios):\n", + " weights = generate_random_weights(number_of_assets)\n", + " VaR = calculate_historical_VaR(weights, mu_R, confidence_level)\n", + " VaRs.append(VaR)\n", + "\n", + "# Calculate z\n", + "mean_VaR = np.mean(VaRs)\n", + "median_VaR = np.median(VaRs)\n", + "z = mean_VaR # or median_VaR, depending on your specific requirements\n", + "\n", + "print(f\"The mean VaR at a {confidence_level*100}% confidence level is: {mean_VaR}\")\n", + "print(f\"The median VaR at a {confidence_level*100}% confidence level is: {median_VaR}\")" + ] }, { "cell_type": "code", diff --git a/QUBO (3).ipynb b/QUBO (3).ipynb index 0079a65..fa5cbe5 100644 --- a/QUBO (3).ipynb +++ b/QUBO (3).ipynb @@ -632,7 +632,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3 [Default]", "language": "python", "name": "python3" }, diff --git a/deriveZ.ipynb b/deriveZ.ipynb index 3c198ee..81b614b 100644 --- a/deriveZ.ipynb +++ b/deriveZ.ipynb @@ -12,12 +12,26 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 2, "id": "8203c1f5-f9fc-472b-b06f-4ca4256bc4ae", "metadata": { "tags": [] }, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_219/316017550.py:4: DeprecationWarning: \n", + "Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),\n", + "(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)\n", + "but was not found to be installed on your system.\n", + "If this would cause problems for you,\n", + "please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466\n", + " \n", + " import pandas as pd\n" + ] + }, { "name": "stdout", "output_type": "stream", @@ -101,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 3, "id": "1454b6b3-e891-4511-8c78-f8396fedce42", "metadata": { "tags": [] @@ -110,17 +124,17 @@ { "data": { "text/plain": [ - "(array([0.03295262, 0.00357888, 0.02086894, 0.01404641, 0.00207373,\n", - " 0.04048195, 0.03747879, 0.06892786, 0.00658971, 0.07627872,\n", - " 0.03231812, 0.01837159, 0.01969979, 0.00613971, 0.05614756,\n", - " 0.07319379, 0.01233284, 0.0447664 , 0.01589302, 0.00435266,\n", - " 0.02044029, 0.04002689, 0.00849307, 0.01801094, 0.06357421,\n", - " 0.04002009, 0.00848955, 0.05466866, 0.05900047, 0.0350204 ,\n", - " 0.00453561, 0.06122671]),\n", - " 0.9999999999999997)" + "(array([0.00418642, 0.02575848, 0.06693708, 0.06799167, 0.05898317,\n", + " 0.07067119, 0.02501272, 0.01295003, 0.04382892, 0.06962844,\n", + " 0.05880262, 0.02637454, 0.02220247, 0.01063647, 0.01108875,\n", + " 0.00419621, 0.00399815, 0.0395862 , 0.02611385, 0.06111845,\n", + " 0.07534739, 0.01170318, 0.02671506, 0.01058358, 0.04007832,\n", + " 0.00407736, 0.0111234 , 0.01482553, 0.02636917, 0.03315742,\n", + " 0.02972971, 0.00622408]),\n", + " 1.0)" ] }, - "execution_count": 47, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -141,7 +155,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 4, "id": "864d0c11-90ac-4fb3-8a06-ddbbf77add41", "metadata": { "tags": [] @@ -159,7 +173,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 5, "id": "d9e7df0d-6d54-41d0-8396-3419e202c41f", "metadata": { "tags": [] @@ -182,7 +196,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 6, "id": "74fdb282-cfd1-4ac7-84f1-2a2c0d79139a", "metadata": { "tags": [] @@ -191,50 +205,49 @@ { "data": { "text/plain": [ - "([0.3201754590963047,\n", - " 0.3040817367921548,\n", - " 0.33570642951204405,\n", - " 0.324621105208764,\n", - " 0.35256804169434885],\n", - " [array([0.03519249, 0.03290085, 0.01317292, 0.0536117 , 0.05335179,\n", - " 0.0282839 , 0.04913777, 0.03639204, 0.00584395, 0.00473127,\n", - " 0.01556526, 0.05040084, 0.00158258, 0.05193638, 0.01152281,\n", - " 0.0274181 , 0.04280595, 0.02104865, 0.04392498, 0.01501355,\n", - " 0.00104685, 0.03588135, 0.04501165, 0.01426848, 0.03105206,\n", - " 0.04519589, 0.02398323, 0.03468755, 0.03416542, 0.04537202,\n", - " 0.04764116, 0.04785654]),\n", - " array([0.04150249, 0.04927715, 0.04050631, 0.01704145, 0.02052018,\n", - " 0.02598609, 0.03970675, 0.04701215, 0.02573006, 0.03526343,\n", - " 0.0118426 , 0.0231728 , 0.03486327, 0.00175815, 0.02021195,\n", - " 0.04307619, 0.01233438, 0.04928072, 0.0216664 , 0.01882121,\n", - " 0.05057916, 0.02193385, 0.02624362, 0.01480431, 0.04967795,\n", - " 0.01678528, 0.04148598, 0.03736406, 0.03326363, 0.03940288,\n", - " 0.03435846, 0.05452709]),\n", - " array([0.01604598, 0.04529005, 0.01110671, 0.05985637, 0.04223921,\n", - " 0.01186149, 0.04146411, 0.0174574 , 0.04475275, 0.0483324 ,\n", - " 0.01738737, 0.03744703, 0.01976326, 0.06031816, 0.01705841,\n", - " 0.01359626, 0.05254128, 0.03369028, 0.06079489, 0.00420929,\n", - " 0.00269271, 0.02072749, 0.02317871, 0.06133557, 0.05006869,\n", - " 0.03762329, 0.0628452 , 0.00436101, 0.03140263, 0.00272598,\n", - " 0.00502145, 0.04280456]),\n", - " array([3.75143474e-02, 3.56680465e-02, 6.07580111e-02, 1.78872938e-02,\n", - " 4.47482137e-02, 4.36094397e-02, 1.02396118e-02, 3.50628726e-02,\n", - " 5.74763685e-03, 5.88533879e-02, 1.77641933e-02, 2.20699045e-02,\n", - " 2.89828714e-02, 1.46328359e-02, 3.59180566e-02, 1.63276401e-02,\n", - " 3.79104899e-02, 4.88496753e-02, 1.12161826e-03, 3.36144250e-02,\n", - " 5.87756224e-02, 7.50474417e-03, 4.26659419e-02, 3.54226972e-02,\n", - " 5.63592516e-02, 3.53101020e-02, 2.35589519e-02, 1.05982766e-02,\n", - " 5.60329215e-02, 5.09637794e-03, 7.95060325e-05, 6.13150354e-02]),\n", - " array([0.03894738, 0.03544109, 0.00490616, 0.05484427, 0.01377497,\n", - " 0.0112676 , 0.06334437, 0.05774461, 0.04677672, 0.02128168,\n", - " 0.0643672 , 0.01938623, 0.02496569, 0.03924972, 0.05728707,\n", - " 0.04692048, 0.01114821, 0.0088831 , 0.00936529, 0.06442574,\n", - " 0.04214907, 0.0072486 , 0.0600262 , 0.02152251, 0.00609748,\n", - " 0.02466099, 0.00043209, 0.00359317, 0.0311648 , 0.05129704,\n", - " 0.05541976, 0.00206072])])" + "([0.3128811166148638,\n", + " 0.34202650160735865,\n", + " 0.35334276930136604,\n", + " 0.3410365740045231,\n", + " 0.3165141564733053],\n", + " [array([0.0495565 , 0.04090496, 0.04357528, 0.03138587, 0.04548934,\n", + " 0.04759537, 0.00425981, 0.04478657, 0.03877712, 0.04574071,\n", + " 0.04282936, 0.04846589, 0.04523607, 0.02474504, 0.00173475,\n", + " 0.01009745, 0.04951992, 0.00847554, 0.03235129, 0.02737412,\n", + " 0.02521216, 0.01083173, 0.03016904, 0.0412418 , 0.03148121,\n", + " 0.04565462, 0.00779222, 0.04414392, 0.04107802, 0.00390577,\n", + " 0.03467097, 0.00091758]),\n", + " array([0.01403353, 0.00982509, 0.02215693, 0.04888251, 0.03759727,\n", + " 0.04673139, 0.03929041, 0.02254295, 0.0167434 , 0.03030957,\n", + " 0.03559299, 0.0318543 , 0.02907078, 0.04154272, 0.01493068,\n", + " 0.04450683, 0.04399607, 0.00446831, 0.05255986, 0.03136256,\n", + " 0.05252859, 0.04756222, 0.03254053, 0.01271068, 0.00609097,\n", + " 0.02039448, 0.05474213, 0.04600814, 0.01125323, 0.04347461,\n", + " 0.04532531, 0.00937095]),\n", + " array([0.04276039, 0.05577527, 0.06599738, 0.02122337, 0.01577919,\n", + " 0.01777574, 0.06049104, 0.0060848 , 0.04150497, 0.05034561,\n", + " 0.04371688, 0.01410727, 0.00454085, 0.05396461, 0.03262252,\n", + " 0.05792247, 0.02488471, 0.02395531, 0.04751275, 0.00454294,\n", + " 0.01212546, 0.00017779, 0.02708621, 0.00191693, 0.00403708,\n", + " 0.05340689, 0.0411082 , 0.02861891, 0.06517827, 0.04447769,\n", + " 0.0257816 , 0.01057689]),\n", + " array([0.02446189, 0.04810576, 0.01109954, 0.02208651, 0.04830805,\n", + " 0.06095532, 0.04611498, 0.03494051, 0.02218763, 0.01404973,\n", + " 0.01502475, 0.01276091, 0.00501265, 0.00937838, 0.02563213,\n", + " 0.04173373, 0.03744621, 0.0257746 , 0.01127356, 0.04091599,\n", + " 0.06029149, 0.02897527, 0.06148641, 0.01376761, 0.00746243,\n", + " 0.00780886, 0.03731954, 0.03871881, 0.05245977, 0.05246551,\n", + " 0.04014242, 0.04183903]),\n", + " array([0.05262785, 0.00862903, 0.04958039, 0.02477631, 0.01194494,\n", + " 0.03535696, 0.04781062, 0.05548981, 0.00623476, 0.04606431,\n", + " 0.03779445, 0.05056566, 0.01432316, 0.01568648, 0.04037295,\n", + " 0.03577056, 0.03621072, 0.01843537, 0.0535073 , 0.02829103,\n", + " 0.05666226, 0.03065277, 0.01180278, 0.04231911, 0.03407627,\n", + " 0.00302919, 0.02812648, 0.01991216, 0.05019098, 0.01203927,\n", + " 0.03826156, 0.00345451])])" ] }, - "execution_count": 43, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -258,7 +271,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 7, "id": "434a4807-8824-48f7-bad9-1b6e8c0c57d9", "metadata": { "tags": [] @@ -268,8 +281,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "The mean VaR at a 95.0% confidence level is: -0.0010204670111313022\n", - "The median VaR at a 95.0% confidence level is: -0.0010196371916901341\n" + "The mean VaR at a 95.0% confidence level is: -0.001012471817893799\n", + "The median VaR at a 95.0% confidence level is: -0.001007872904990408\n" ] } ], @@ -306,11 +319,47 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "96b1afa0-a9b9-4f7c-b891-6dbc0eedf3df", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The mean VaR at a 95.0% confidence level is: 0.0455241269789427\n", + "The median VaR at a 95.0% confidence level is: 0.045520151841198175\n" + ] + } + ], + "source": [ + "# Parameters\n", + "number_of_assets = 32\n", + "number_of_portfolios = 1000 # The number of random portfolios you want to generate\n", + "confidence_level = 0.95\n", + "\n", + "# Assuming mu_R is the matrix of historical returns for the assets\n", + "# Replace this with your actual historical returns data\n", + "# mu_R = expected_returns # This should be the historical returns data for your assets\n", + "# expected retursn --> z = .001\n", + "mu_R = R.T # z = .0045\n", + "# z provided from packages == 1.645\n", + "\n", + "# Generate the random weights for the portfolios and calculate the VaR for each\n", + "VaRs = []\n", + "for _ in range(number_of_portfolios):\n", + " weights = generate_random_weights(number_of_assets)\n", + " VaR = calculate_historical_VaR(weights, mu_R, confidence_level)\n", + " VaRs.append(VaR)\n", + "\n", + "# Calculate z\n", + "mean_VaR = np.mean(VaRs)\n", + "median_VaR = np.median(VaRs)\n", + "z = mean_VaR # or median_VaR, depending on your specific requirements\n", + "\n", + "print(f\"The mean VaR at a {confidence_level*100}% confidence level is: {mean_VaR}\")\n", + "print(f\"The median VaR at a {confidence_level*100}% confidence level is: {median_VaR}\")" + ] }, { "cell_type": "code", From 48519361bf5d58bfc24a7ceb4b65d81dc6ed1ac6 Mon Sep 17 00:00:00 2001 From: Nicholas Pietraszek Date: Sun, 4 Feb 2024 09:57:38 -0500 Subject: [PATCH 20/20] FINAL COMMIT --- QUBO (4).ipynb | 831 +++++++++++++++++++++++++++++++++++++++++ moodys_challenge.ipynb | 488 +++++++++++++++++++----- 2 files changed, 1224 insertions(+), 95 deletions(-) create mode 100644 QUBO (4).ipynb diff --git a/QUBO (4).ipynb b/QUBO (4).ipynb new file mode 100644 index 0000000..a4b22b8 --- /dev/null +++ b/QUBO (4).ipynb @@ -0,0 +1,831 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 191, + "id": "68ffab4b-fa83-4b74-997a-47dfed539bb0", + "metadata": {}, + "outputs": [], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "\n", + "N, K = 2,6\n", + "T = 4\n", + "z = 0.05\n", + "M = 100\n", + "eps = 0.1\n", + "\n", + "\n", + "df = pd.read_csv('returns_data.txt',delim_whitespace=True)\n", + "\n", + "Rraw = df.values.T\n", + "\n", + "# Select the first N,T assets and scenarios, you may choose a different strategy if you would like to do so.\n", + "reward = Rraw[:N,:T]\n", + "\n", + "# Expected return of each asset\n", + "Mu = np.mean(reward, axis = 1)\n", + "\n", + "Sigma = np.cov(reward)\n", + "# Covariance matrix of asset returns\n", + "\n", + "R = reward.T" + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "id": "dcfa3c78-bcc7-4249-9397-89004f2c6ec3", + "metadata": {}, + "outputs": [], + "source": [ + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "# Slack, nonbasic and other variables\n", + "\n", + "z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + "r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + "A_norm = Array.create(\"A_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + "A_max = z\n", + "A = A_max*binary2integer_norm(A_norm)\n", + "\n", + "B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + "B_max = np.max(R, axis=1) + z + M \n", + "\n", + "C_norm = Array.create(\"C_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + "C_max = eps*T\n", + "C = C_max*binary2integer_norm(C_norm)\n", + "\n", + "y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + "r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n" + ] + }, + { + "cell_type": "code", + "execution_count": 193, + "id": "984954d0-0ceb-4365-83a6-4aa0a71e480c", + "metadata": {}, + "outputs": [], + "source": [ + "H_var = 0\n", + "for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "id": "001fd2d2-5c9b-4d8e-a372-65360fa58f28", + "metadata": {}, + "outputs": [], + "source": [ + "H_exp = 0\n", + "for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])" + ] + }, + { + "cell_type": "code", + "execution_count": 195, + "id": "22f3f7b7-c47d-4a3c-9c6e-725f401e284d", + "metadata": {}, + "outputs": [], + "source": [ + "L1 = 100\n", + "L2s = [100]*T\n", + "L3 = 100\n", + "L4 = 1000\n", + "H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + "H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + "H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + "H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)])-1)**2, \"H4\")" + ] + }, + { + "cell_type": "code", + "execution_count": 196, + "id": "54358ac0-efaf-4c10-8b48-6951f12e15ff", + "metadata": {}, + "outputs": [], + "source": [ + "H = H_var - H_exp + (H1 +1-1) + (H2+1-1) + (H4+1-1) + (H3+1-1)" + ] + }, + { + "cell_type": "code", + "execution_count": 197, + "id": "f8341ed5-c8d7-4b59-9055-7a5791f72990", + "metadata": {}, + "outputs": [], + "source": [ + "model = H.compile()" + ] + }, + { + "cell_type": "code", + "execution_count": 198, + "id": "d8231778-cbf6-4f4b-b4e9-61f57f889bd1", + "metadata": {}, + "outputs": [], + "source": [ + "qubo, offset = model.to_qubo() #H_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 199, + "id": "643d574c-c36b-484d-92c3-b9d90a6af70b", + "metadata": {}, + "outputs": [], + "source": [ + "from dwave.samplers import SimulatedAnnealingSampler" + ] + }, + { + "cell_type": "code", + "execution_count": 200, + "id": "d5b48d50-f295-43ac-8e8c-67bf63432b56", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "895" + ] + }, + "execution_count": 200, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(qubo)" + ] + }, + { + "cell_type": "code", + "execution_count": 201, + "id": "75ab433b-9d0d-4a31-aa62-e80b465bf4f3", + "metadata": {}, + "outputs": [], + "source": [ + "sampler = SimulatedAnnealingSampler()\n", + "sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", + "samples = model.decode_sampleset(sampleset)\n", + "best_sample = min(samples, key=lambda s: s.energy)" + ] + }, + { + "cell_type": "code", + "execution_count": 202, + "id": "ee05072b-cca6-4ea7-9115-b84ba3100079", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'H2_2': (False, 0.00011592809759845424), 'H2_0': (False, 0.00010071519761272172), 'H1': (False, -6.2341624917916505e-19), 'H2_1': (False, 0.0001199591688170995), 'H2_3': (False, 0.0006929263919681905), 'H4': (True, 0.0), 'H3': (False, 3.906249999660605e-05)}\n" + ] + } + ], + "source": [ + "print(best_sample.constraints())" + ] + }, + { + "cell_type": "code", + "execution_count": 203, + "id": "c8c08839-1f62-44b0-85fd-acbad3c4eab4", + "metadata": {}, + "outputs": [], + "source": [ + "z_full_array = np.zeros(shape=z_full.shape)\n", + "r_eps_norm_array = np.zeros(shape=r_eps_norm.shape)\n", + "A_norm_array = np.zeros(shape=A_norm.shape)\n", + "B_norm_array = np.zeros(shape=B_norm.shape)\n", + "C_norm_array = np.zeros(shape=C_norm.shape)\n", + "y_array = np.zeros(shape=y.shape) \n", + "r_eps_array = np.zeros(shape=r_eps_norm.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 204, + "id": "2c9ba4c4-3597-4dbf-9a9b-590d8fecd571", + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "\n", + "key = \"B_full[0][1]\"\n", + "\n", + "\n", + "data =best_sample.sample \n", + "\n", + "\n", + "# Iterate through the dictionary and populate arrays\n", + "for key, value in data.items():\n", + " if key.startswith('B_full'):\n", + " # Use regular expression to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " print(indices)\n", + " B_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('C'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " C_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('A'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " A_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('r_eps'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " r_eps_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('y'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " y_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('z_full'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " z_full_array[indices[0]][indices[1]] = value\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 205, + "id": "56662e9d-475e-4e5e-9245-b491335027c4", + "metadata": {}, + "outputs": [], + "source": [ + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]" + ] + }, + { + "cell_type": "code", + "execution_count": 206, + "id": "c936b40f-274a-4e2f-99d7-93b7415c013e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.328125, 0.671875]" + ] + }, + "execution_count": 206, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ws" + ] + }, + { + "cell_type": "code", + "execution_count": 207, + "id": "4f0ae41c-00d4-47c1-9db1-c66d7d29edc3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 207, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(ws)" + ] + }, + { + "cell_type": "code", + "execution_count": 214, + "id": "0ab1fdcd-54c5-4f9e-ad5d-42b8fc94e05c", + "metadata": {}, + "outputs": [], + "source": [ + "H_var = 0\n", + "for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full_array[i])*binary2integer_norm(z_full_array[j])*Sigma[i,j]" + ] + }, + { + "cell_type": "code", + "execution_count": 215, + "id": "ba3b6bf3-24c8-493a-b474-03d853b9e77f", + "metadata": {}, + "outputs": [], + "source": [ + "H_exp = 0\n", + "for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full_array[i])" + ] + }, + { + "cell_type": "code", + "execution_count": 216, + "id": "7841d398-0ef1-4a53-9673-cc4eb6484d08", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.00023188283348470079" + ] + }, + "execution_count": 216, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "H_var" + ] + }, + { + "cell_type": "code", + "execution_count": 217, + "id": "a0a4839b-3656-45ae-a182-5875c41b89ff", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.008354946868359376" + ] + }, + "execution_count": 217, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "H_exp" + ] + }, + { + "cell_type": "markdown", + "id": "758f7c27-cbcc-4317-83b3-facf86cfb2a5", + "metadata": {}, + "source": [ + "# Validating Simulated Annealing Results" + ] + }, + { + "cell_type": "code", + "execution_count": 305, + "id": "e1b6be3a-b224-43c2-a434-a962c1639a71", + "metadata": {}, + "outputs": [], + "source": [ + "import pyqubo\n", + "from pyqubo import Array, Binary, Constraint, Placeholder\n", + "import numpy as np\n", + "from dwave.samplers import SimulatedAnnealingSampler\n", + "len(qubo)\n", + "\n", + "def binary2integer_norm(z):\n", + " try:\n", + " K = len(z)\n", + " except:\n", + " return 1/2 * z\n", + " return sum([2**((k+1)-1-K)*z[k] for k in range(0,K)])\n", + "\n", + "def IV(n):\n", + " return (n*n - n)/2 + n\n", + "\n", + "def f(L1, L3, L4, M, *L2s):\n", + " '''\n", + " L1 = 100\n", + " L2s = [100]*T\n", + " L3 = 100\n", + " L4 = 100\n", + " M = 100\n", + " '''\n", + " \n", + " z_full = Array.create(\"z_full\", shape= (N,K), vartype=\"BINARY\")\n", + "\n", + " r_eps_norm = Array.create(\"r_eps\", shape=(K, 1), vartype=\"BINARY\")\n", + "\n", + " A_norm = Array.create(\"A_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + " A_max = z\n", + " A = A_max*binary2integer_norm(A_norm)\n", + "\n", + " B_norm = Array.create(\"B_norm\", shape=(T, K), vartype=\"BINARY\")\n", + " B_max = np.max(R, axis=1) + z + M\n", + "\n", + " C_norm = Array.create(\"C_norm\", shape=(K, 1), vartype=\"BINARY\")\n", + " C_max = eps*T\n", + " C = C_max*binary2integer_norm(C_norm)\n", + "\n", + " y = Array.create(\"y\", shape=(T, 1), vartype=\"BINARY\")\n", + "\n", + " r_eps = z*(-1 + binary2integer_norm(r_eps_norm))\n", + "\n", + " H_var = 0\n", + " for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full[i])*binary2integer_norm(z_full[j])*Sigma[i,j]\n", + "\n", + " H_exp = 0\n", + " for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full[i])\n", + "\n", + " H1 = L1*Constraint((-r_eps[0] - z + A[0])**2, \"H1\")\n", + "\n", + " H2 = sum([L2s[t]*Constraint((r_eps[0] \n", + " - sum([R[t,j]*binary2integer_norm(z_full[j]) for j in range(N)]) \n", + " - M*(1-y[t][0])+1-1 \n", + " + B_max[t]*binary2integer_norm(B_norm[t]))**2 , \"H2_{}\".format(t)) for t in range(T)])\n", + "\n", + " H3 = L3*Constraint(((1-eps)*T - sum(y)[0] + C[0] )**2, \"H3\")\n", + "\n", + " H4 = L4*Constraint((sum([binary2integer_norm(z_full[i]) for i in range(N)])-1)**2, \"H4\")\n", + "\n", + " H = H_var - H_exp + (H1 +1-1) + (H2+1-1) + (H4+1-1) + (H3+1-1)\n", + "\n", + " model = H.compile()\n", + "\n", + " qubo, offset = model.to_qubo() #H_dict)\n", + "\n", + " sampler = SimulatedAnnealingSampler()\n", + " sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", + " samples = model.decode_sampleset(sampleset)\n", + " best_sample = min(samples, key=lambda s: s.energy)\n", + " \n", + " z_full_array = np.zeros(shape=z_full.shape)\n", + " r_eps_norm_array = np.zeros(shape=r_eps_norm.shape)\n", + " A_norm_array = np.zeros(shape=A_norm.shape)\n", + " B_norm_array = np.zeros(shape=B_norm.shape)\n", + " C_norm_array = np.zeros(shape=C_norm.shape)\n", + " y_array = np.zeros(shape=y.shape) \n", + " r_eps_array = np.zeros(shape=r_eps_norm.shape)\n", + "\n", + " key = \"B_full[0][1]\"\n", + "\n", + "\n", + " data =best_sample.sample \n", + "\n", + "\n", + " # Iterate through the dictionary and populate arrays\n", + " for key, value in data.items():\n", + " if key.startswith('B_full'):\n", + " # Use regular expression to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " print(indices)\n", + " B_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('C'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " C_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('A'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " A_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('r_eps'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " r_eps_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('y'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " y_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('z_full'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " z_full_array[indices[0]][indices[1]] = value\n", + " \n", + " \n", + "\n", + "\n", + " return (sum([i for _,i in list(best_sample.constraints().values())]), z_full_array)" + ] + }, + { + "cell_type": "code", + "execution_count": 326, + "id": "78c14f4e-10a8-4e5b-b64d-6a9e28247f4a", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 339, + "id": "e99ebf5d-9c33-46a6-8f2b-f959127e8dac", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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pW3btuqIs2effbbQco899hgzZszg4MGDJvMfrVy5EkmS1BbAjh078u233/Lzzz+bdH+tWrXKpL7GjRvTsGFDDh8+zPTp0+/ZjnuhcePG1K5dm1WrVhETE6P69u7du6xdu1YdyWcrBVscizp+WZ2LIUOGMGvWLN5++22ioqKoXbu2WZkffvhBDeCCgoL47rvvyM7OVq/tGzdusGvXLqtaao00adKE1q1bs2zZMgwGA9nZ2QwaNMikzJNPPsmGDRuoX79+mQaqgvuHCKQEVZYPPvig2DIvvPACX3/9Nd27d+eNN97goYcewsHBgStXrrBt2zaefvppnn32Wdq1a0f16tUZPnw47777Lg4ODnz99dccPny4zPQfMmQIv/76K4sXL6ZLly4m+TBlpU+nTp1wdHTkxRdf5O233yYrK4uFCxdy69atEtc5atQo1qxZw9NPP83YsWN56KGHyMzMJDY2lieffJKOHTta7QdbsDQxa0HefPNNVq5cyRNPPMF7771HYGAgv/76KwsWLOCVV16hUaNGAPTv35+PP/6Y/v37M23aNBo2bMiGDRv47bffzOr87LPP6NatG126dGHgwIHUrl2bmzdvcvLkSQ4ePMh3331nkx0lRaPRMHPmTPr27cuTTz7Jyy+/THZ2NrNmzSI1NdWq+8MSzZo1Y/v27fzyyy/4+/vj4eFR6I+CsjoXnp6e/PTTTzz55JNERETw2muv0bZtWxwdHfnzzz/56quvOHz4sBpIvfTSS3z22Wf069ePYcOGcePGDWbOnGlTEGVk8ODBvPzyyyQmJtKuXTsz29977z02b95Mu3btGDlyJI0bNyYrK4sLFy6wYcMGFi1aVOiUDQL7RCSbCwRFoNVq+fnnnxk/fjw//PADzz77LM888wwffPABzs7ONGvWDMjrJvn1119xdXWlX79+DB48GHd3d9asWVNmuj355JP4+vqiKIpZsnxZ6RMaGsratWu5desWPXr04PXXX+fBBx80mSPHVjw8PNixYwdDhgzh888/54knnmDYsGGcPn1a7eaw1g+ljbe3N7t27eLRRx9l3LhxPPnkk/z222/MnDmTTz75RC3n6urK1q1befzxxxk7dizPPfccV65c4ZtvvjGrs2PHjuzduxcvLy9GjRrF448/ziuvvML//ve/IhPuy4I+ffqwbt06bty4Qe/evRk0aBDVqlVj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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for G in range(1,4):\n", + " H_vars = []\n", + " H_exps = []\n", + "\n", + " for _ in range(50):\n", + " L1 = 100\n", + " L2s = [100]*T\n", + " L3 = 100\n", + " L4 = 100*G\n", + " M = 100\n", + "\n", + " Loss, z_full_array = f(L1, L3, L4, M, *L2s)\n", + "\n", + " ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]\n", + "\n", + " H_var = 0\n", + " for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full_array[i])*binary2integer_norm(z_full_array[j])*Sigma[i,j]\n", + "\n", + " H_exp = 0\n", + " for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full_array[i])\n", + "\n", + " H_vars.append(H_var)\n", + " H_exps.append(H_exp)\n", + "\n", + "\n", + " # Assuming H_vars and H_exps are your data for variance and expected returns\n", + " # Replace them with your actual data\n", + "\n", + " # Scatter plot\n", + " plt.scatter(H_vars, H_exps, label='Efficient Frontier: L4 = 10*{}'.format(G), marker='o', s=50)\n", + "\n", + " # Title and labels\n", + " plt.title('Mean-Variance Model Frontier Curve')\n", + " plt.xlabel('Variance')\n", + " plt.ylabel('Expected Returns')\n", + "\n", + " # Grid lines\n", + " plt.grid(True, linestyle='--', alpha=0.7)\n", + "\n", + " # Customize the appearance\n", + " plt.axhline(0, color='black',linewidth=0.5)\n", + " plt.axvline(0, color='black',linewidth=0.5)\n", + " plt.xticks(fontsize=10)\n", + " plt.yticks(fontsize=10)\n", + "\n", + " # Show the plot\n", + "\n", + "\n", + "# Add a legend\n", + "plt.legend()\n", + "\n", + "plt.show()\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "755531d7-0735-4d51-9d79-10e4d5e48dc3", + "metadata": {}, + "source": [ + "# Optimizing Lagrange Multipliers" + ] + }, + { + "cell_type": "code", + "execution_count": 345, + "id": "92e1faef-40c1-434c-8442-3d90e9da4dae", + "metadata": {}, + "outputs": [], + "source": [ + "Loss, z_full_array = f(L1, L3, L4, M, *L2s)" + ] + }, + { + "cell_type": "code", + "execution_count": 346, + "id": "6cb2ce88-8281-470d-b3a1-773895a4ffc8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2.6031791873441206" + ] + }, + "execution_count": 346, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Loss" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "989de539-dc14-4a08-934f-23497263dd4d", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def numerical_gradient(f, *params, epsilon=1e-7):\n", + " \"\"\"\n", + " Calculate the numerical gradient of the function f with respect to each parameter.\n", + " \"\"\"\n", + " gradients = []\n", + " for i in range(len(params)):\n", + " params_plus_epsilon = list(params)\n", + " params_plus_epsilon[i] += epsilon\n", + " params_minus_epsilon = list(params)\n", + " params_minus_epsilon[i] -= epsilon\n", + "\n", + " gradient_i = (f(*params_plus_epsilon) - f(*params_minus_epsilon)) / (2 * epsilon)\n", + " gradients.append(gradient_i)\n", + "\n", + " return np.array(gradients)\n", + "\n", + "def gradient_descent(f, initial_params, learning_rate=1e-7, num_iterations=10):\n", + " \"\"\"\n", + " Perform gradient descent optimization to minimize the function f.\n", + " \"\"\"\n", + " params = np.array(initial_params, dtype=float) # Explicitly set data type to float\n", + "\n", + " for iteration in range(num_iterations):\n", + " grad = numerical_gradient(f, *params)\n", + " params -= learning_rate * grad\n", + "\n", + " if iteration % 5 == 0:\n", + " print(f'Iteration {iteration}, Value: {f(*params)}')\n", + "\n", + " return params\n", + "\n", + "# Set initial parameter values\n", + "initial_params = [100, 100, 100, 100] + [100] * T # Replace T with the appropriate value\n", + "\n", + "# Perform gradient descent\n", + "optimized_params = gradient_descent(f, initial_params)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ed57361-fe7d-4c9b-8e64-1f3ccaf0496b", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Optimized Value:\", f(*optimized_params))" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "id": "ecd24c26-e4f9-4040-a347-50e22cb71809", + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimized Value: 0.0012257928724465485\n" + ] + } + ], + "source": [ + "print(\"Optimized Value:\", f(*optimized_params))" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "id": "ec0a73b0-edc4-4056-88a0-4967eefe09ef", + "metadata": {}, + "outputs": [], + "source": [ + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "id": "2749ba72-7ed8-409d-b405-ee3e7019200b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.65625, 0.34375]" + ] + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ws" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "id": "1ecb0e81-2df6-4ed4-a7ae-2e76b69fcc12", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 152, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sum(ws)" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "id": "daa06ca7-1e93-453e-b88e-2ef25f03459c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.002 , 0.0449, 0.0108, ..., -0.0327, 0.0167, 0.0186],\n", + " [ 0.0148, -0.0068, -0.0225, ..., 0.0211, 0.0214, 0.0049],\n", + " [-0.0058, 0.0123, 0.0039, ..., 0.024 , 0.047 , 0.0134],\n", + " ...,\n", + " [ 0.0593, 0.0789, 0.0782, ..., 0. , 0. , 0. ],\n", + " [ 0.0184, -0.0253, -0.0006, ..., 0.0148, 0.0251, -0.0143],\n", + " [-0.0006, -0.0123, -0.0045, ..., 0.0541, -0.0044, -0.0013]])" + ] + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.round(Rraw, 4)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/moodys_challenge.ipynb b/moodys_challenge.ipynb index 560a83d..b02f0a5 100644 --- a/moodys_challenge.ipynb +++ b/moodys_challenge.ipynb @@ -761,6 +761,14 @@ "
" ] }, + { + "cell_type": "markdown", + "id": "c72cd0af", + "metadata": {}, + "source": [ + "## Please refer to second headline under Section 4 for optimization of Hyperparameters as per Note 2. Otherwise continue reading for recovery of variables and portfolio" + ] + }, { "cell_type": "code", "execution_count": 50, @@ -837,6 +845,14 @@ "print(ws)\n" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "2131597b", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", "id": "5e4786ea-1db6-46a6-b9f8-d1bfcdb88b20", @@ -880,15 +896,7 @@ }, { "cell_type": "markdown", - "id": "09d0c42b", - "metadata": {}, - "source": [ - "with $\\textit{weights}$ being a list that includes the optimal values (your solution) for the variables $\\{x_{i}\\}$ and $\\textit{mu_R}$ the expected returns of the assets (given by the data). Compare the return value got from this function to $VaR_{\\epsilon}$ calculated above." - ] - }, - { - "cell_type": "markdown", - "id": "e32da760-2d7c-44cc-b541-0fd576eaedf2", + "id": "d9b697ad", "metadata": {}, "source": [ "**Answer:**" @@ -896,93 +904,22 @@ }, { "cell_type": "markdown", - "id": "9acc19d6-5b85-4ef4-aef4-7cdfe709b566", + "id": "852b1d27", "metadata": {}, "source": [ - "### Section 5: Quantum optimization (Quantum Annealing and/or QAOA)" - ] - }, - { - "cell_type": "markdown", - "id": "ac6a4eab-e29a-470d-ba29-a3995c8eac10", - "metadata": {}, - "source": [ - "\n", - "**Note**: For the exercises below use the $\\hat{H}$ you found in **Section 2**, with the same encoding you used in that exercise and with the use of the **data** provided in the beginning of the challenge.\n", - "\n", - "**STEP 1:** Study the **Mean-Variance-VaR Simplified Version** problem with QAOA and Quantum Annealing. \n", - "\n", - "The Quantum Approximate Optimization Algorithm (QAOA) was first introduced in [A Quantum Approximate Optimization Algorithm](https://arxiv.org/abs/1411.4028). QAOA is a popular method to solve combinatorial optimization problems in noisy intermediate-scale quantum (NISQ) devices.\n", - "\n", - "Useful tutorials on implementing QAOA can be found here: \n", - "- [Pulser tutorial QAOA](https://pulser.readthedocs.io/en/latest/tutorials/qubo.html)\n", - "- [Qiskit QAOA](https://docs.quantum.ibm.com/api/qiskit/qiskit.algorithms.minimum_eigensolvers.QAOA), [Qiskit tutorial QAOA](https://qiskit.org/documentation/stable/0.40/tutorials/algorithms/05_qaoa.html)\n", - "- [pyQAOA tutorial QAOA](https://grove-docs.readthedocs.io/en/latest/qaoa.html)\n", - "- [PennyLane tutorial QAOA](https://pennylane.ai/qml/demos/tutorial_qaoa_intro/)\n", - "- [OpenQAOA-EntropicaLabs](https://openqaoa.entropicalabs.com/)\n", - "\n", - "For Quantum Annealing, you may read this reference [D-Wave, Quantum Annealing](https://docs.dwavesys.com/docs/latest/c_gs_2.html#getting-started-qa) and follow [D-Wave Ocean Software documentation](https://docs.ocean.dwavesys.com/en/stable/index.html) for the implementation. \n", - "In [Solving the Optimal Trading Trajectory Problem Using a Quantum Annealer](https://arxiv.org/abs/1508.06182), the authors explain how the choice of encoding might differ when considering solving an optimization problem with quantum annealing instead of simulated annealing.\n", - " \n", - "Do not implement anything at the moment. This step is for introducing you to different quantum or quantum-inspired algorithms one could utilize for this problem.\n", - "\n", - "**STEP 2:** In **Section 3** you used [D-Wave neal](https://docs.ocean.dwavesys.com/projects/neal/en/latest/) to solve the problem with Simulated Annealing. As also seen in the previous step, [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) gives you immediate, secure access to D-Wave quantum and hybrid solvers. Study the capabilities of [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) and answer the following questions:\n", - "\n", - "- For reasonable granularity (i.e. number of qubits per encoded variable) how many assets $n$ and events $T$ you can study with a quantum device and D-Wave? For that you will need to find the total number of qubits that you can explore with [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) for the algorithm of your choice. Notice that the resources available to the public are much less than the capabilities of these devices.\n", - "\n", - "**STEP 3:** After exploring the capabilities of [D-Wave Ocean Software documentation](https://docs.ocean.dwavesys.com/en/stable/index.html), you are encouraged to use a **quantum simulator/backend** to study the **Mean-Variance-VaR Simplified Version** problem with the **data** provided in the beginning of the challenge (feel free to change $T$ and $n$ if needed) and any method of your choice. For this, you can use your qBraid account. Explore the options below:\n", - "\n", - "- [Qiskit](https://docs.quantum.ibm.com/api/qiskit)\n", - "- [Amazon Braket](https://docs.aws.amazon.com/braket/latest/developerguide/what-is-braket.html)\n", - "\n", - "Or even more, you may also opt to explore the usage of GPUs for simulating quantum algorithms. For example, see a recent [work](https://www.jpmorgan.com/technology/technology-blog/quantum-optimization-research) that studies QAOA scaling performance on a fast GPU-specific QAOA simulator.\n", - "\n", - "No matter what algorithm you choose, **you are encouraged to run small-scale simulations of the problems on quantum backends and simulators, always keeping in mind the resources that you are utilizing and their cost**. What problems/limitations do you encounter in comparison with simulated annealing?" - ] - }, - { - "cell_type": "markdown", - "id": "dbc17d65-627d-4805-ac9f-3a3803bfc70d", - "metadata": {}, - "source": [ - "
Note 1 (Hint):\n", - " \n", - " One could read the following references: \n", - " \n", - "- [Quantum Optimization: Potential, Challenges, and the Path Forward](https://arxiv.org/abs/2312.02279) and references within.\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "id": "8b4a5f08-14de-4d26-8031-8c1b2cd4f003", - "metadata": {}, - "source": [ - "
Note 2 (Hint):\n", - " \n", - "One can take a look at the [IBM Quantum Development Roadmap to 2033](https://newsroom.ibm.com/2023-12-04-IBM-Debuts-Next-Generation-Quantum-Processor-IBM-Quantum-System-Two,-Extends-Roadmap-to-Advance-Era-of-Quantum-Utility) and [QuEra's Quantum Computing Roadmap](https://www.quera.com/press-releases/quera-computing-releases-a-groundbreaking-roadmap-for-advanced-error-corrected-quantum-computers-pioneering-the-next-frontier-in-quantum-innovation) for an idea about the current and predicted quantum capabilities.\n", - " \n", - "
" - ] - }, - { - "cell_type": "markdown", - "id": "990a82c7-6ccc-4552-8b86-97a95fece3cd", - "metadata": {}, - "source": [ - "**Answer:**" + "# Validating Simulated Annealing Results with Expected Efficient Frontier Curve" ] }, { "cell_type": "code", "execution_count": null, - "id": "aa60be68", + "id": "34946f11", "metadata": {}, "outputs": [], "source": [ "# Implemented gradient descent for the optimization of the hyperparameters\n", "\n", + "\n", "import pyqubo\n", "from pyqubo import Array, Binary, Constraint, Placeholder\n", "import numpy as np\n", @@ -1057,17 +994,181 @@ " sampleset = sampler.sample_qubo(qubo, num_reads=10)\n", " samples = model.decode_sampleset(sampleset)\n", " best_sample = min(samples, key=lambda s: s.energy)\n", + " \n", + " z_full_array = np.zeros(shape=z_full.shape)\n", + " r_eps_norm_array = np.zeros(shape=r_eps_norm.shape)\n", + " A_norm_array = np.zeros(shape=A_norm.shape)\n", + " B_norm_array = np.zeros(shape=B_norm.shape)\n", + " C_norm_array = np.zeros(shape=C_norm.shape)\n", + " y_array = np.zeros(shape=y.shape) \n", + " r_eps_array = np.zeros(shape=r_eps_norm.shape)\n", + "\n", + " key = \"B_full[0][1]\"\n", + "\n", + "\n", + " data =best_sample.sample \n", + "\n", + "\n", + " # Iterate through the dictionary and populate arrays\n", + " for key, value in data.items():\n", + " if key.startswith('B_full'):\n", + " # Use regular expression to extract indices\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " print(indices)\n", + " B_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('C'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " C_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('A'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " A_norm_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('r_eps'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " r_eps_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('y'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " y_array[indices[0]][indices[1]] = value\n", + " elif key.startswith('z_full'):\n", + " indices = [int(idx) for idx in re.findall(r'\\[(\\d+)\\]', key)]\n", + " z_full_array[indices[0]][indices[1]] = value\n", + " \n", + " \n", "\n", - " return sum([i for _,i in list(best_sample.constraints().values())])\n", "\n", - "L1 = 100\n", - "L2s = [100]*T\n", - "L3 = 100\n", - "L4 = 100\n", - "M = 100\n", + " return (sum([i for _,i in list(best_sample.constraints().values())]), z_full_array)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "929a4091", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da5a4c44", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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pW3btuqIs2effbbQco899hgzZszg4MGDJvMfrVy5EkmS1BbAjh078u233/Lzzz+bdH+tWrXKpL7GjRvTsGFDDh8+zPTp0+/ZjnuhcePG1K5dm1WrVhETE6P69u7du6xdu1YdyWcrBVscizp+WZ2LIUOGMGvWLN5++22ioqKoXbu2WZkffvhBDeCCgoL47rvvyM7OVq/tGzdusGvXLqtaao00adKE1q1bs2zZMgwGA9nZ2QwaNMikzJNPPsmGDRuoX79+mQaqgvuHCKQEVZYPPvig2DIvvPACX3/9Nd27d+eNN97goYcewsHBgStXrrBt2zaefvppnn32Wdq1a0f16tUZPnw47777Lg4ODnz99dccPny4zPQfMmQIv/76K4sXL6ZLly4m+TBlpU+nTp1wdHTkxRdf5O233yYrK4uFCxdy69atEtc5atQo1qxZw9NPP83YsWN56KGHyMzMJDY2lieffJKOHTta7QdbsDQxa0HefPNNVq5cyRNPPMF7771HYGAgv/76KwsWLOCVV16hUaNGAPTv35+PP/6Y/v37M23aNBo2bMiGDRv47bffzOr87LPP6NatG126dGHgwIHUrl2bmzdvcvLkSQ4ePMh3331nkx0lRaPRMHPmTPr27cuTTz7Jyy+/THZ2NrNmzSI1NdWq+8MSzZo1Y/v27fzyyy/4+/vj4eFR6I+CsjoXnp6e/PTTTzz55JNERETw2muv0bZtWxwdHfnzzz/56quvOHz4sBpIvfTSS3z22Wf069ePYcOGcePGDWbOnGlTEGVk8ODBvPzyyyQmJtKuXTsz29977z02b95Mu3btGDlyJI0bNyYrK4sLFy6wYcMGFi1aVOiUDQL7RCSbCwRFoNVq+fnnnxk/fjw//PADzz77LM888wwffPABzs7ONGvWDMjrJvn1119xdXWlX79+DB48GHd3d9asWVNmuj355JP4+vqiKIpZsnxZ6RMaGsratWu5desWPXr04PXXX+fBBx80mSPHVjw8PNixYwdDhgzh888/54knnmDYsGGcPn1a7eaw1g+ljbe3N7t27eLRRx9l3LhxPPnkk/z222/MnDmTTz75RC3n6urK1q1befzxxxk7dizPPfccV65c4ZtvvjGrs2PHjuzduxcvLy9GjRrF448/ziuvvML//ve/IhPuy4I+ffqwbt06bty4Qe/evRk0aBDVqlVj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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for G in range(1,4):\n", + " H_vars = []\n", + " H_exps = []\n", "\n", - "import numpy as np\n", + " for _ in range(50):\n", + " L1 = 100\n", + " L2s = [100]*T\n", + " L3 = 100\n", + " L4 = 100*G\n", + " M = 100\n", + "\n", + " Loss, z_full_array = f(L1, L3, L4, M, *L2s)\n", "\n", + " ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]\n", + "\n", + " H_var = 0\n", + " for i in range(N):\n", + " for j in range(N):\n", + " H_var += binary2integer_norm(z_full_array[i])*binary2integer_norm(z_full_array[j])*Sigma[i,j]\n", + "\n", + " H_exp = 0\n", + " for i in range(N):\n", + " H_exp += Mu[i]*binary2integer_norm(z_full_array[i])\n", + "\n", + " H_vars.append(H_var)\n", + " H_exps.append(H_exp)\n", + "\n", + "\n", + " # Assuming H_vars and H_exps are your data for variance and expected returns\n", + " # Replace them with your actual data\n", + "\n", + " # Scatter plot\n", + " plt.scatter(H_vars, H_exps, label='Efficient Frontier: L4 = 10*{}'.format(G), marker='o', s=50)\n", + "\n", + " # Title and labels\n", + " plt.title('Mean-Variance Model Frontier Curve')\n", + " plt.xlabel('Variance')\n", + " plt.ylabel('Expected Returns')\n", + "\n", + " # Grid lines\n", + " plt.grid(True, linestyle='--', alpha=0.7)\n", + "\n", + " # Customize the appearance\n", + " plt.axhline(0, color='black',linewidth=0.5)\n", + " plt.axvline(0, color='black',linewidth=0.5)\n", + " plt.xticks(fontsize=10)\n", + " plt.yticks(fontsize=10)\n", + "\n", + " # Show the plot\n", + "\n", + "\n", + "# Add a legend\n", + "plt.legend()\n", + "\n", + "plt.show()\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "id": "07a3c3bd", + "metadata": {}, + "source": [ + "# Optimizing Lagrange Multipliers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70b882f8", + "metadata": {}, + "outputs": [], + "source": [ + "Loss, z_full_array = f(L1, L3, L4, M, *L2s)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc651ba6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2.6031791873441206" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "Loss" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "977ebe53", + "metadata": {}, + "outputs": [], + "source": [ "def numerical_gradient(f, *params, epsilon=1e-7):\n", " \"\"\"\n", " Calculate the numerical gradient of the function f with respect to each parameter.\n", @@ -1084,7 +1185,7 @@ "\n", " return np.array(gradients)\n", "\n", - "def gradient_descent(f, initial_params, learning_rate=1e-7, num_iterations=100):\n", + "def gradient_descent(f, initial_params, learning_rate=1e-7, num_iterations=10):\n", " \"\"\"\n", " Perform gradient descent optimization to minimize the function f.\n", " \"\"\"\n", @@ -1103,10 +1204,207 @@ "initial_params = [100, 100, 100, 100] + [100] * T # Replace T with the appropriate value\n", "\n", "# Perform gradient descent\n", - "optimized_params = gradient_descent(f, initial_params)\n", + "optimized_params = gradient_descent(f, initial_params)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19b7613f", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Optimized Value:\", f(*optimized_params))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22ff2864", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Optimized Value: 0.0012257928724465485\n" + ] + } + ], + "source": [ + "print(\"Optimized Value:\", f(*optimized_params))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4039718f", + "metadata": {}, + "outputs": [], + "source": [ + "ws = [binary2integer_norm(z_full_array[i]) for i in range(N)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62e6d2df", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.65625, 0.34375]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ws" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2757b5bb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "sum(ws)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e04bb120", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.002 , 0.0449, 0.0108, ..., -0.0327, 0.0167, 0.0186],\n", + " [ 0.0148, -0.0068, -0.0225, ..., 0.0211, 0.0214, 0.0049],\n", + " [-0.0058, 0.0123, 0.0039, ..., 0.024 , 0.047 , 0.0134],\n", + " ...,\n", + " [ 0.0593, 0.0789, 0.0782, ..., 0. , 0. , 0. ],\n", + " [ 0.0184, -0.0253, -0.0006, ..., 0.0148, 0.0251, -0.0143],\n", + " [-0.0006, -0.0123, -0.0045, ..., 0.0541, -0.0044, -0.0013]])" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "np.round(Rraw, 4)" + ] + }, + { + "cell_type": "markdown", + "id": "09d0c42b", + "metadata": {}, + "source": [ + "with $\\textit{weights}$ being a list that includes the optimal values (your solution) for the variables $\\{x_{i}\\}$ and $\\textit{mu_R}$ the expected returns of the assets (given by the data). Compare the return value got from this function to $VaR_{\\epsilon}$ calculated above." + ] + }, + { + "cell_type": "markdown", + "id": "e32da760-2d7c-44cc-b541-0fd576eaedf2", + "metadata": {}, + "source": [ + "**Answer:**" + ] + }, + { + "cell_type": "markdown", + "id": "9acc19d6-5b85-4ef4-aef4-7cdfe709b566", + "metadata": {}, + "source": [ + "### Section 5: Quantum optimization (Quantum Annealing and/or QAOA)" + ] + }, + { + "cell_type": "markdown", + "id": "ac6a4eab-e29a-470d-ba29-a3995c8eac10", + "metadata": {}, + "source": [ + "\n", + "**Note**: For the exercises below use the $\\hat{H}$ you found in **Section 2**, with the same encoding you used in that exercise and with the use of the **data** provided in the beginning of the challenge.\n", + "\n", + "**STEP 1:** Study the **Mean-Variance-VaR Simplified Version** problem with QAOA and Quantum Annealing. \n", + "\n", + "The Quantum Approximate Optimization Algorithm (QAOA) was first introduced in [A Quantum Approximate Optimization Algorithm](https://arxiv.org/abs/1411.4028). QAOA is a popular method to solve combinatorial optimization problems in noisy intermediate-scale quantum (NISQ) devices.\n", + "\n", + "Useful tutorials on implementing QAOA can be found here: \n", + "- [Pulser tutorial QAOA](https://pulser.readthedocs.io/en/latest/tutorials/qubo.html)\n", + "- [Qiskit QAOA](https://docs.quantum.ibm.com/api/qiskit/qiskit.algorithms.minimum_eigensolvers.QAOA), [Qiskit tutorial QAOA](https://qiskit.org/documentation/stable/0.40/tutorials/algorithms/05_qaoa.html)\n", + "- [pyQAOA tutorial QAOA](https://grove-docs.readthedocs.io/en/latest/qaoa.html)\n", + "- [PennyLane tutorial QAOA](https://pennylane.ai/qml/demos/tutorial_qaoa_intro/)\n", + "- [OpenQAOA-EntropicaLabs](https://openqaoa.entropicalabs.com/)\n", + "\n", + "For Quantum Annealing, you may read this reference [D-Wave, Quantum Annealing](https://docs.dwavesys.com/docs/latest/c_gs_2.html#getting-started-qa) and follow [D-Wave Ocean Software documentation](https://docs.ocean.dwavesys.com/en/stable/index.html) for the implementation. \n", + "In [Solving the Optimal Trading Trajectory Problem Using a Quantum Annealer](https://arxiv.org/abs/1508.06182), the authors explain how the choice of encoding might differ when considering solving an optimization problem with quantum annealing instead of simulated annealing.\n", + " \n", + "Do not implement anything at the moment. This step is for introducing you to different quantum or quantum-inspired algorithms one could utilize for this problem.\n", + "\n", + "**STEP 2:** In **Section 3** you used [D-Wave neal](https://docs.ocean.dwavesys.com/projects/neal/en/latest/) to solve the problem with Simulated Annealing. As also seen in the previous step, [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) gives you immediate, secure access to D-Wave quantum and hybrid solvers. Study the capabilities of [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) and answer the following questions:\n", + "\n", + "- For reasonable granularity (i.e. number of qubits per encoded variable) how many assets $n$ and events $T$ you can study with a quantum device and D-Wave? For that you will need to find the total number of qubits that you can explore with [D-Wave Ocean](https://docs.ocean.dwavesys.com/en/stable/getting_started.html) for the algorithm of your choice. Notice that the resources available to the public are much less than the capabilities of these devices.\n", + "\n", + "**STEP 3:** After exploring the capabilities of [D-Wave Ocean Software documentation](https://docs.ocean.dwavesys.com/en/stable/index.html), you are encouraged to use a **quantum simulator/backend** to study the **Mean-Variance-VaR Simplified Version** problem with the **data** provided in the beginning of the challenge (feel free to change $T$ and $n$ if needed) and any method of your choice. For this, you can use your qBraid account. Explore the options below:\n", + "\n", + "- [Qiskit](https://docs.quantum.ibm.com/api/qiskit)\n", + "- [Amazon Braket](https://docs.aws.amazon.com/braket/latest/developerguide/what-is-braket.html)\n", + "\n", + "Or even more, you may also opt to explore the usage of GPUs for simulating quantum algorithms. For example, see a recent [work](https://www.jpmorgan.com/technology/technology-blog/quantum-optimization-research) that studies QAOA scaling performance on a fast GPU-specific QAOA simulator.\n", "\n", - "# Print the optimized parameters and the corresponding function value\n", - "print(\"Optimized Parameters:\", optimized_params)" + "No matter what algorithm you choose, **you are encouraged to run small-scale simulations of the problems on quantum backends and simulators, always keeping in mind the resources that you are utilizing and their cost**. What problems/limitations do you encounter in comparison with simulated annealing?" + ] + }, + { + "cell_type": "markdown", + "id": "dbc17d65-627d-4805-ac9f-3a3803bfc70d", + "metadata": {}, + "source": [ + "
Note 1 (Hint):\n", + " \n", + " One could read the following references: \n", + " \n", + "- [Quantum Optimization: Potential, Challenges, and the Path Forward](https://arxiv.org/abs/2312.02279) and references within.\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "8b4a5f08-14de-4d26-8031-8c1b2cd4f003", + "metadata": {}, + "source": [ + "
Note 2 (Hint):\n", + " \n", + "One can take a look at the [IBM Quantum Development Roadmap to 2033](https://newsroom.ibm.com/2023-12-04-IBM-Debuts-Next-Generation-Quantum-Processor-IBM-Quantum-System-Two,-Extends-Roadmap-to-Advance-Era-of-Quantum-Utility) and [QuEra's Quantum Computing Roadmap](https://www.quera.com/press-releases/quera-computing-releases-a-groundbreaking-roadmap-for-advanced-error-corrected-quantum-computers-pioneering-the-next-frontier-in-quantum-innovation) for an idea about the current and predicted quantum capabilities.\n", + " \n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "990a82c7-6ccc-4552-8b86-97a95fece3cd", + "metadata": {}, + "source": [ + "**Answer:**" ] }, {