├── img ├── .gitkeep └── img-01.png ├── tests ├── __init__.py ├── conftest.py ├── compile │ ├── __init__.py │ └── test_compile.py ├── sampler │ ├── __init__.py │ ├── test_zeke_sampler.py │ ├── test_nqs_local_sampler.py │ ├── test_nqs_sampler.py │ ├── test_sa_sampler.py │ ├── test_ga_sampler.py │ └── cassettes │ │ ├── test_nqs_sampler │ │ └── test_nqs_sampler_run.yaml │ │ ├── test_nqs_local_sampler │ │ └── test_nqs_local_sampler_run.yaml │ │ └── test_zeke_sampler │ │ └── test_zeke_sampler_run.yaml ├── support │ ├── __init__.py │ └── custom_stub.py └── symbol │ ├── __init__.py │ └── test_symbols_list.py ├── tytan ├── _version.py ├── __init__.py ├── symbol.py ├── auto_array.py ├── compile.py └── sampler.py ├── MANIFEST.in ├── requirements-dev.txt ├── requirements.txt ├── requirements-test.txt ├── .gitignore ├── .pre-commit-config.yaml ├── pyproject.toml ├── setup.py ├── .github └── workflows │ └── python-package.yml ├── document .md ├── README.md └── LICENSE /img/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/compile/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/sampler/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/support/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/symbol/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tytan/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.4" 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include requirements.txt 3 | -------------------------------------------------------------------------------- /img/img-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tytansdk/tytan/HEAD/img/img-01.png -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements-dev.txt 2 | black 3 | pre-commit 4 | pyright 5 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | httpx 2 | ulid-py 3 | numpy 4 | pandas 5 | sympy 6 | scipy 7 | symengine 8 | requests 9 | -------------------------------------------------------------------------------- /requirements-test.txt: -------------------------------------------------------------------------------- 1 | # pip install -r requirements-test.txt 2 | pytest 3 | pytest-cov 4 | pytest-recording 5 | vcrpy -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .pytest_cache 3 | *.egg-info 4 | dist 5 | build 6 | *.DS_Store 7 | .venv 8 | .ipynb_checkpoints 9 | virt 10 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/psf/black 3 | rev: 23.3.0 4 | hooks: 5 | - id: black 6 | language_version: python3.10 7 | # - repo: https://github.com/RobertCraigie/pyright-python 8 | # rev: v1.1.303 9 | # hooks: 10 | # - id: pyright -------------------------------------------------------------------------------- /tytan/__init__.py: -------------------------------------------------------------------------------- 1 | from .symbol import symbols, symbols_list, symbols_define, symbols_nbit 2 | from .compile import Compile, PieckCompile 3 | from . import sampler 4 | from .auto_array import Auto_array 5 | 6 | # from tytan import * 用 7 | __all__ = ['symbols', 'symbols_list', 'symbols_define', 'symbols_nbit', 'Compile', 'PieckCompile', 'sampler', 'Auto_array'] -------------------------------------------------------------------------------- /tests/support/custom_stub.py: -------------------------------------------------------------------------------- 1 | from vcr.request import Request as VcrRequest 2 | 3 | 4 | def make_vcr_request(httpx_request, **kwargs): 5 | try: 6 | body = httpx_request.read().decode("utf-8") 7 | except Exception: 8 | body = httpx_request.read() 9 | uri = str(httpx_request.url) 10 | headers = dict(httpx_request.headers) 11 | return VcrRequest(httpx_request.method, uri, body, headers) 12 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | include = '\.pyi?$' 3 | exclude = ''' 4 | /( 5 | \.git 6 | | \.hg 7 | | \.mypy_cache 8 | | \.tox 9 | | \.venv 10 | | \.pytest_cache 11 | | _build 12 | | buck-out 13 | | build 14 | | dist 15 | )/ 16 | ''' 17 | 18 | [tool.pyright] 19 | include = [ 20 | "tytan", 21 | ] 22 | exclude = [ 23 | "**/__pycache__", 24 | ] 25 | typeCheckingMode = "strict" 26 | reportMissingTypeStubs = false 27 | reportUnknownMemberType = false -------------------------------------------------------------------------------- /tests/sampler/test_zeke_sampler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | from tytan import symbols, Compile 5 | from tytan.sampler import ZekeSampler 6 | 7 | 8 | @pytest.mark.vcr(filter_headers=["x-api-key"]) 9 | def test_zeke_sampler_run(): 10 | x, y, z = symbols("x y z") 11 | expr = 3 * x**2 + 2 * x * y + 4 * y**2 + z**2 + 2 * x * z + 2 * y * z 12 | qubo, offset = Compile(expr).get_qubo() 13 | api_key = os.environ.get("TYTAN_API_KEY", "foobar") 14 | sampler = ZekeSampler() 15 | result = sampler.run(qubo, shots=1, api_key=api_key) 16 | assert result is not None 17 | assert result[0][0] is not None 18 | assert result[0][0]["x"] is not None 19 | assert result[0][0]["y"] is not None 20 | assert result[0][0]["z"] is not None 21 | assert result[0][1] is not None 22 | assert result[0][2] is not None 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r", encoding="utf-8") as f: 4 | long_description = f.read() 5 | 6 | with open("tytan/_version.py", "r", encoding="utf-8") as f: 7 | exec(f.read()) 8 | 9 | with open("requirements.txt", "r", encoding="utf-8") as f: 10 | install_requires = list(map(str.strip, f)) 11 | 12 | setuptools.setup( 13 | name = "tytan", 14 | version=__version__, 15 | author="Tytan Developers", 16 | author_email="info@blueqat.com", 17 | description="QUBO Annealing SDK", 18 | long_description=long_description, 19 | long_description_content_type="text/markdown", 20 | url="https://github.com/tytansdk/tytan", 21 | license="Apache 2", 22 | packages=setuptools.find_packages(), 23 | install_requires=install_requires, 24 | classifiers=[ 25 | "Programming Language :: Python :: 3", 26 | "License :: OSI Approved :: Apache Software License", 27 | "Development Status :: 3 - Alpha", 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /tests/sampler/test_nqs_local_sampler.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from tests.support.custom_stub import make_vcr_request 3 | from tytan import symbols, Compile 4 | from tytan.sampler import NQSLocalSampler 5 | import vcr.stubs.httpx_stubs 6 | 7 | 8 | 9 | 10 | @pytest.mark.vcr( 11 | match_on=["uri", "method"], 12 | custom_patches=( 13 | (vcr.stubs.httpx_stubs, "_make_vcr_request", make_vcr_request), 14 | ), 15 | ) 16 | def test_nqs_local_sampler_run(): 17 | x, y, z = symbols("x y z") 18 | expr = 3 * x**2 + 2 * x * y + 4 * y**2 + z**2 + 2 * x * z + 2 * y * z 19 | qubo, offset = Compile(expr).get_qubo() 20 | sampler = NQSLocalSampler() 21 | result = sampler.run(qubo) 22 | assert result is not None 23 | assert result[0][0] is not None 24 | assert result[0][0]["x"] == 0 25 | assert result[0][0]["y"] == 0 26 | assert result[0][0]["z"] == 0 27 | assert result[0][1] == 0 #energy 28 | assert result[0][2] is not None #occ 29 | assert result[0][3] is not None #time 30 | -------------------------------------------------------------------------------- /tests/sampler/test_nqs_sampler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import vcr.stubs.httpx_stubs 4 | from tests.support.custom_stub import make_vcr_request 5 | from tytan import symbols, Compile 6 | from tytan.sampler import NQSSampler 7 | 8 | 9 | @pytest.mark.vcr( 10 | filter_headers=["x-api-key"], 11 | match_on=["uri", "method"], 12 | custom_patches=( 13 | (vcr.stubs.httpx_stubs, "_make_vcr_request", make_vcr_request), 14 | ), 15 | ) 16 | def test_nqs_sampler_run(): 17 | x, y, z = symbols("x y z") 18 | expr = 3 * x**2 + 2 * x * y + 4 * y**2 + z**2 + 2 * x * z + 2 * y * z 19 | qubo, offset = Compile(expr).get_qubo() 20 | api_key = os.environ.get("TYTAN_API_KEY", "foobar") 21 | sampler = NQSSampler(api_key) 22 | result = sampler.run(qubo) 23 | assert result is not None 24 | assert result[0][0] is not None 25 | assert result[0][0]["x"] == 0 26 | assert result[0][0]["y"] == 0 27 | assert result[0][0]["z"] == 0 28 | assert result[0][1] == 0 #energy 29 | assert result[0][2] is not None #occ 30 | assert result[0][3] is not None #time 31 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ["3.8", "3.9", "3.10"] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install flake8 pytest 31 | pip install -r requirements.txt 32 | pip install -r requirements-test.txt 33 | # - name: Lint with flake8 34 | # run: | 35 | # # stop the build if there are Python syntax errors or undefined names 36 | # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 37 | # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 38 | # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 39 | - name: Test with pytest 40 | run: | 41 | pytest 42 | -------------------------------------------------------------------------------- /tests/sampler/test_sa_sampler.py: -------------------------------------------------------------------------------- 1 | from tytan import symbols, Compile 2 | from tytan.sampler import SASampler 3 | 4 | 5 | def test_sa_sampler_run(): 6 | x, y, z = symbols("x y z") 7 | expr = (x + y + z - 2)**2 8 | qubo, offset = Compile(expr).get_qubo() 9 | sampler = SASampler() 10 | result = sampler.run(qubo) 11 | for r in result: 12 | print(r) 13 | assert result is not None 14 | assert result[0][0] is not None 15 | assert result[0][0]["x"] is not None 16 | assert result[0][0]["y"] is not None 17 | assert result[0][0]["z"] is not None 18 | assert result[0][1] is not None 19 | assert result[0][2] is not None 20 | 21 | def test_sa_sampler_run_with_seed(): 22 | x, y, z = symbols("x y z") 23 | expr = (x + y + z - 2)**2 24 | qubo, offset = Compile(expr).get_qubo() 25 | 26 | #1 27 | print('try 1, ', end='') 28 | sampler = SASampler(seed=0) 29 | result = sampler.run(qubo) 30 | print(result[0][0]["x"], result[0][0]["y"], result[0][0]["z"], result[0][2]) 31 | x = result[0][0]["x"] 32 | y = result[0][0]["y"] 33 | z = result[0][0]["z"] 34 | count = result[0][2] 35 | 36 | #2- 37 | for i in range(2, 10): 38 | print(f'try {i}, ', end='') 39 | sampler = SASampler(seed=0) 40 | result = sampler.run(qubo) 41 | print(result[0][0]["x"], result[0][0]["y"], result[0][0]["z"], result[0][2]) 42 | assert result[0][0]["x"] == x 43 | assert result[0][0]["y"] == y 44 | assert result[0][0]["z"] == z 45 | assert result[0][2] == count 46 | -------------------------------------------------------------------------------- /tests/sampler/test_ga_sampler.py: -------------------------------------------------------------------------------- 1 | from tytan import symbols, Compile 2 | from tytan.sampler import GASampler 3 | 4 | 5 | def test_ga_sampler_run(): 6 | x, y, z = symbols("x y z") 7 | expr = (x + y + z - 2)**2 8 | qubo, offset = Compile(expr).get_qubo() 9 | sampler = GASampler() 10 | result = sampler.run(qubo) 11 | for r in result: 12 | print(r) 13 | assert result is not None 14 | assert result[0][0] is not None 15 | assert result[0][0]["x"] is not None 16 | assert result[0][0]["y"] is not None 17 | assert result[0][0]["z"] is not None 18 | assert result[0][1] is not None 19 | assert result[0][2] is not None 20 | 21 | def test_ga_sampler_run_with_seed(): 22 | x, y, z = symbols("x y z") 23 | expr = (x + y + z - 2)**2 24 | qubo, offset = Compile(expr).get_qubo() 25 | 26 | #1 27 | print('try 1, ', end='') 28 | sampler = GASampler(seed=0) 29 | result = sampler.run(qubo, verbose=False) 30 | print(result[0][0]["x"], result[0][0]["y"], result[0][0]["z"], result[0][2]) 31 | x = result[0][0]["x"] 32 | y = result[0][0]["y"] 33 | z = result[0][0]["z"] 34 | count = result[0][2] 35 | 36 | #2- 37 | for i in range(2, 10): 38 | print(f'try {i}, ', end='') 39 | sampler = GASampler(seed=0) 40 | result = sampler.run(qubo, verbose=False) 41 | print(result[0][0]["x"], result[0][0]["y"], result[0][0]["z"], result[0][2]) 42 | assert result[0][0]["x"] == x 43 | assert result[0][0]["y"] == y 44 | assert result[0][0]["z"] == z 45 | assert result[0][2] == count 46 | -------------------------------------------------------------------------------- /tests/sampler/cassettes/test_nqs_sampler/test_nqs_sampler_run.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: !!binary | 4 | LS00NGE5NDQ1MmQ2Mzc2NTllNDQxODM1Njc1Y2RlMmUwMg0KQ29udGVudC1EaXNwb3NpdGlvbjog 5 | Zm9ybS1kYXRhOyBuYW1lPSJwb3B1bGF0aW9uIg0KDQo1MDANCi0tNDRhOTQ0NTJkNjM3NjU5ZTQ0 6 | MTgzNTY3NWNkZTJlMDINCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0idGlt 7 | ZUxpbWl0U2VjIg0KDQozMA0KLS00NGE5NDQ1MmQ2Mzc2NTllNDQxODM1Njc1Y2RlMmUwMg0KQ29u 8 | dGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPSJpdGVyIg0KDQoxMDAwMA0KLS00NGE5 9 | NDQ1MmQ2Mzc2NTllNDQxODM1Njc1Y2RlMmUwMg0KQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1k 10 | YXRhOyBuYW1lPSJ6aXBmaWxlIjsgZmlsZW5hbWU9IjAxR1k1M044OUhOMFNXWU5CODhUR1pQUVFG 11 | LnppcCINCkNvbnRlbnQtVHlwZTogZGF0YTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KUEsD 12 | BBQAAAAIALmykFZTJ1OXIQAAACMAAAAKAAAAcXVib193LmNzdtOp1KnSqeDlqtQx0THSMeLlqtIx 13 | 0DEEMSqADAMdY14uAFBLAQIUAxQAAAAIALmykFZTJ1OXIQAAACMAAAAKAAAAAAAAAAAAAACAAQAA 14 | AABxdWJvX3cuY3N2UEsFBgAAAAABAAEAOAAAAEkAAAAAAA0KLS00NGE5NDQ1MmQ2Mzc2NTllNDQx 15 | ODM1Njc1Y2RlMmUwMi0tDQo= 16 | headers: 17 | accept: 18 | - '*/*' 19 | accept-encoding: 20 | - gzip 21 | connection: 22 | - keep-alive 23 | content-length: 24 | - '644' 25 | content-type: 26 | - multipart/form-data; boundary=44a94452d637659e441835675cde2e02 27 | host: 28 | - tytan-api.blueqat.com 29 | user-agent: 30 | - blueqat 31 | method: POST 32 | uri: https://tytan-api.blueqat.com/v1/tasks/nqs 33 | response: 34 | content: '{"energy":0.0,"result":{"x":0,"y":0,"z":0},"time":3.718784809112549}' 35 | headers: 36 | Connection: 37 | - keep-alive 38 | Content-Length: 39 | - '68' 40 | Content-Type: 41 | - application/json 42 | Date: 43 | - Sun, 16 Apr 2023 13:22:02 GMT 44 | Server: 45 | - nginx 46 | Vary: 47 | - Accept-Encoding 48 | http_version: HTTP/1.1 49 | status_code: 200 50 | version: 1 51 | -------------------------------------------------------------------------------- /tests/sampler/cassettes/test_nqs_local_sampler/test_nqs_local_sampler_run.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: !!binary | 4 | LS00NGE5NDQ1MmQ2Mzc2NTllNDQxODM1Njc1Y2RlMmUwMg0KQ29udGVudC1EaXNwb3NpdGlvbjog 5 | Zm9ybS1kYXRhOyBuYW1lPSJwb3B1bGF0aW9uIg0KDQo1MDANCi0tNDRhOTQ0NTJkNjM3NjU5ZTQ0 6 | MTgzNTY3NWNkZTJlMDINCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0idGlt 7 | ZUxpbWl0U2VjIg0KDQozMA0KLS00NGE5NDQ1MmQ2Mzc2NTllNDQxODM1Njc1Y2RlMmUwMg0KQ29u 8 | dGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPSJpdGVyIg0KDQoxMDAwMA0KLS00NGE5 9 | NDQ1MmQ2Mzc2NTllNDQxODM1Njc1Y2RlMmUwMg0KQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1k 10 | YXRhOyBuYW1lPSJ6aXBmaWxlIjsgZmlsZW5hbWU9IjAxR1k1M044OUhOMFNXWU5CODhUR1pQUVFG 11 | LnppcCINCkNvbnRlbnQtVHlwZTogZGF0YTphcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0NCg0KUEsD 12 | BBQAAAAIALmykFZTJ1OXIQAAACMAAAAKAAAAcXVib193LmNzdtOp1KnSqeDlqtQx0THSMeLlqtIx 13 | 0DEEMSqADAMdY14uAFBLAQIUAxQAAAAIALmykFZTJ1OXIQAAACMAAAAKAAAAAAAAAAAAAACAAQAA 14 | AABxdWJvX3cuY3N2UEsFBgAAAAABAAEAOAAAAEkAAAAAAA0KLS00NGE5NDQ1MmQ2Mzc2NTllNDQx 15 | ODM1Njc1Y2RlMmUwMi0tDQo= 16 | headers: 17 | accept: 18 | - '*/*' 19 | accept-encoding: 20 | - gzip 21 | connection: 22 | - keep-alive 23 | content-length: 24 | - '644' 25 | content-type: 26 | - multipart/form-data; boundary=44a94452d637659e441835675cde2e02 27 | host: 28 | - tytan-api.blueqat.com 29 | user-agent: 30 | - blueqat 31 | method: POST 32 | uri: http://localhost:8080/ngqs/v1/solve 33 | response: 34 | content: '{"energy":0.0,"result":{"x":0,"y":0,"z":0},"time":3.718784809112549}' 35 | headers: 36 | Connection: 37 | - keep-alive 38 | Content-Length: 39 | - '68' 40 | Content-Type: 41 | - application/json 42 | Date: 43 | - Sun, 16 Apr 2023 13:22:02 GMT 44 | Server: 45 | - nginx 46 | Vary: 47 | - Accept-Encoding 48 | http_version: HTTP/1.1 49 | status_code: 200 50 | version: 1 51 | -------------------------------------------------------------------------------- /tests/sampler/cassettes/test_zeke_sampler/test_zeke_sampler_run.yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: '{"bqm": {"type": "BinaryQuadraticModel", "version": {"bqm_schema": "3.0.0"}, 4 | "use_bytes": false, "index_type": "int32", "bias_type": "float64", "num_variables": 5 | 3, "num_interactions": 3, "variable_labels": ["y", "x", "z"], "variable_type": 6 | "BINARY", "offset": 0.0, "info": {}, "linear_biases": [4.0, 3.0, 1.0], "quadratic_biases": 7 | [2.0, 2.0, 2.0], "quadratic_head": [0, 1, 0], "quadratic_tail": [1, 2, 2]}, 8 | "shots": 1}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip 12 | Connection: 13 | - close 14 | Content-Length: 15 | - '418' 16 | Content-Type: 17 | - application/json 18 | Host: 19 | - tytan-api.blueqat.com 20 | User-Agent: 21 | - Python-urllib/3.11 22 | method: POST 23 | uri: https://tytan-api.blueqat.com/v1/tasks/create 24 | response: 25 | body: 26 | string: !!binary | 27 | H4sIAAAAAAAAA42Sy27CMBBF/2XWATmO4yTs2l03XZRVhVDkmAlEzQP5QaGIf+8YAkVVW3Xl171z 28 | 7tg+QrOCGVSYiziVbKISrCeiZinNRD2pVIWasSJFmUMEBq1vHcyO0PT1EMYKnSqN6tcIswWbslxK 29 | kSRFlsqiKJJYRuk0Y0mWc5EJmUrO4mV0MVm9wZVvsXSHLZlhjUOHzjQaThGMe3PVbVucoyP2DrUb 30 | jA1Q7NGsD2G2Uk5dwMubSRmjDmSwGxXWi0AMuiuobgflpCCFt1hWB4dUtFatRQL3visHrb0x2Gu0 31 | d4z4/4Sm/6X+KbRhbDP0obA9d2fRne+iIwgk03jKYMxhhndyxtEoLC9BboEWbPlHpOh7KE+pEv5z 32 | 1yPgK3547BBhp0yjqjZok5tsq/Qb0q9xxiM1NEqu7sen54eXV7g7aOkTtVRhAXvaDik/YElU3KP2 33 | ji6jdE1HVnpFxqXkgjOe8Zxnscgj0AZXjbOlHnrru8CNT58+A3+ktgIAAA== 34 | headers: 35 | Connection: 36 | - close 37 | Content-Encoding: 38 | - gzip 39 | Content-Type: 40 | - application/json 41 | Date: 42 | - Fri, 14 Apr 2023 01:44:51 GMT 43 | Server: 44 | - nginx 45 | Transfer-Encoding: 46 | - chunked 47 | Vary: 48 | - Accept-Encoding 49 | status: 50 | code: 200 51 | message: OK 52 | version: 1 53 | -------------------------------------------------------------------------------- /tytan/symbol.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import itertools 3 | import inspect 4 | from symengine import symbols as symengine_symbols 5 | 6 | """ 7 | SympyのSymbol関数にそのまま投げる関数 8 | importをTYTANだけにするための申し訳ない方策 9 | """ 10 | def symbols(passed_txt): 11 | return symengine_symbols(passed_txt) 12 | 13 | class TytanException(Exception): 14 | pass 15 | 16 | """ 17 | リストでまとめて定義する関数 18 | """ 19 | def symbols_list(shape, format_txt): 20 | #単一intの場合 21 | if type(shape) == int: 22 | shape = [shape] 23 | #print(shape) 24 | 25 | #次元チェック 26 | dim = len(shape) 27 | if dim != format_txt.count('{}'): 28 | raise TytanException("specify format option like format_txt=\'q{}_{}\' as dimension") 29 | 30 | #{}のセパレートチェック 31 | if '}{' in format_txt: 32 | raise TytanException("separate {} in format_txt like format_txt=\'q{}_{}\'") 33 | 34 | #次元が1~5でなければエラー 35 | if dim not in [1, 2, 3, 4, 5]: 36 | raise TytanException("Currently only dim<=5 is available. Ask tytan community.") 37 | 38 | #再帰的にシンボルを作成する 39 | def recursive_create(indices): 40 | if len(indices) == dim: 41 | return symbols(format_txt.format(*indices)) 42 | else: 43 | return [recursive_create(indices + [i]) for i in range(shape[len(indices)])] 44 | q = recursive_create([]) 45 | 46 | return np.array(q) 47 | 48 | 49 | 50 | """ 51 | 個別定義用のコマンドを返す関数 52 | exec(command)して定義 53 | """ 54 | def symbols_define(shape, format_txt): 55 | #単一intの場合 56 | if type(shape) == int: 57 | shape = [shape] 58 | #print(shape) 59 | 60 | #次元チェック 61 | dim = len(shape) 62 | if dim != format_txt.count('{}'): 63 | raise TytanException("specify format option like format_txt=\'q{}_{}\' as dimension") 64 | 65 | #{}のセパレートチェック 66 | if '}{' in format_txt: 67 | raise TytanException("separate {} in format_txt like format_txt=\'q{}_{}\'") 68 | 69 | #次元が1~5でなければエラー 70 | if dim not in [1, 2, 3, 4, 5]: 71 | raise TytanException("Currently only dim<=5 is available. Ask tytan community.") 72 | 73 | #再帰的に定義を作成する 74 | command = f"{format_txt} = symbols('{format_txt}')" 75 | def recursive_create(indices): 76 | if len(indices) == dim: 77 | return command.format(*indices, *indices) + "\r\n" 78 | else: 79 | return "".join(recursive_create(indices + [i]) for i in range(shape[len(indices)])) 80 | ret = recursive_create([]) 81 | 82 | return ret[:-2] 83 | 84 | # #表示用 85 | # start_indices = [0] * dim 86 | # end_indices = [s - 1 for s in shape] 87 | # first_command = command.format(*start_indices, *start_indices) 88 | # final_command = command.format(*end_indices, *end_indices) 89 | # print(f'defined global: {first_command} to {final_command}') 90 | 91 | 92 | def symbols_nbit(start, stop, format_txt, num=8): 93 | #次元チェック 94 | if 1 != format_txt.count('{}'): 95 | raise TytanException("specify format option like format_txt=\'q{}\' and should be one dimension.") 96 | 97 | #生成 98 | q = symbols_list(num, format_txt=format_txt) 99 | 100 | #式 101 | ret = 0 102 | for n in range(num): 103 | #係数を規格化してから量子ビットをかけたい 104 | ret += (start + (stop - start)) * 2**(num - n - 1) / 2**num * q[n] 105 | 106 | return ret 107 | -------------------------------------------------------------------------------- /tests/symbol/test_symbols_list.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import numpy as np 3 | from tytan import symbols_list, symbols_define 4 | 5 | 6 | # Convert symbol array to string array (for comparison) 7 | def get_symbol_names(symbol_array): 8 | return np.vectorize(lambda x: str(x))(symbol_array) 9 | 10 | 11 | def test_symbols_list_dim1(): 12 | expected_names = np.array(["q0", "q1"]) 13 | 14 | result = symbols_list((2), "q{}") 15 | result_names = get_symbol_names(result) 16 | 17 | assert np.array_equal(result_names, expected_names) 18 | 19 | 20 | def test_symbols_list_dim2(): 21 | expected_names = np.array([["q0_0", "q0_1", "q0_2"], ["q1_0", "q1_1", "q1_2"]]) 22 | 23 | result = symbols_list((2, 3), "q{}_{}") 24 | result_names = get_symbol_names(result) 25 | 26 | assert np.array_equal(result_names, expected_names) 27 | 28 | 29 | def test_symbols_list_dim3(): 30 | expected_names = np.array( 31 | [ 32 | [ 33 | ["q0_0_0", "q0_0_1", "q0_0_2", "q0_0_3"], 34 | ["q0_1_0", "q0_1_1", "q0_1_2", "q0_1_3"], 35 | ["q0_2_0", "q0_2_1", "q0_2_2", "q0_2_3"], 36 | ], 37 | [ 38 | ["q1_0_0", "q1_0_1", "q1_0_2", "q1_0_3"], 39 | ["q1_1_0", "q1_1_1", "q1_1_2", "q1_1_3"], 40 | ["q1_2_0", "q1_2_1", "q1_2_2", "q1_2_3"], 41 | ], 42 | ] 43 | ) 44 | 45 | result = symbols_list((2, 3, 4), "q{}_{}_{}") 46 | result_names = get_symbol_names(result) 47 | 48 | assert np.array_equal(result_names, expected_names) 49 | 50 | 51 | def test_symbols_define_dim1(): 52 | expected = "q0 = symbols('q0')\r\n" + "q1 = symbols('q1')" 53 | 54 | result = symbols_define((2), "q{}") 55 | 56 | assert result == expected, f"Expected:\n{expected}\nBut got:\n{result}" 57 | 58 | 59 | def test_symbols_define_dim2(): 60 | expected = ( 61 | "q0_0 = symbols('q0_0')\r\n" 62 | + "q0_1 = symbols('q0_1')\r\n" 63 | + "q0_2 = symbols('q0_2')\r\n" 64 | + "q1_0 = symbols('q1_0')\r\n" 65 | + "q1_1 = symbols('q1_1')\r\n" 66 | + "q1_2 = symbols('q1_2')" 67 | ) 68 | 69 | result = symbols_define((2, 3), "q{}_{}") 70 | 71 | assert result == expected, f"Expected:\n{expected}\nBut got:\n{result}" 72 | 73 | 74 | def test_symbols_define_dim3(): 75 | expected = ( 76 | "q0_0_0 = symbols('q0_0_0')\r\n" 77 | + "q0_0_1 = symbols('q0_0_1')\r\n" 78 | + "q0_0_2 = symbols('q0_0_2')\r\n" 79 | + "q0_0_3 = symbols('q0_0_3')\r\n" 80 | + "q0_1_0 = symbols('q0_1_0')\r\n" 81 | + "q0_1_1 = symbols('q0_1_1')\r\n" 82 | + "q0_1_2 = symbols('q0_1_2')\r\n" 83 | + "q0_1_3 = symbols('q0_1_3')\r\n" 84 | + "q0_2_0 = symbols('q0_2_0')\r\n" 85 | + "q0_2_1 = symbols('q0_2_1')\r\n" 86 | + "q0_2_2 = symbols('q0_2_2')\r\n" 87 | + "q0_2_3 = symbols('q0_2_3')\r\n" 88 | + "q1_0_0 = symbols('q1_0_0')\r\n" 89 | + "q1_0_1 = symbols('q1_0_1')\r\n" 90 | + "q1_0_2 = symbols('q1_0_2')\r\n" 91 | + "q1_0_3 = symbols('q1_0_3')\r\n" 92 | + "q1_1_0 = symbols('q1_1_0')\r\n" 93 | + "q1_1_1 = symbols('q1_1_1')\r\n" 94 | + "q1_1_2 = symbols('q1_1_2')\r\n" 95 | + "q1_1_3 = symbols('q1_1_3')\r\n" 96 | + "q1_2_0 = symbols('q1_2_0')\r\n" 97 | + "q1_2_1 = symbols('q1_2_1')\r\n" 98 | + "q1_2_2 = symbols('q1_2_2')\r\n" 99 | + "q1_2_3 = symbols('q1_2_3')" 100 | ) 101 | 102 | result = symbols_define((2, 3, 4), "q{}_{}_{}") 103 | 104 | assert result == expected, f"Expected:\n{expected}\nBut got:\n{result}" 105 | -------------------------------------------------------------------------------- /tytan/auto_array.py: -------------------------------------------------------------------------------- 1 | import re 2 | import itertools 3 | import numpy as np 4 | import pandas as pd 5 | from sympy import sympify 6 | from sympy import Symbol as sympy_Symbol 7 | 8 | 9 | """ 10 | 量子ビットの添字を検出して結果を多次元配列に変換して返す 11 | 12 | <入力> 13 | result[0] : [dict_data, energy, occerrence] 14 | もしくは 15 | result[0][0] : dict_data 16 | 17 | <出力> 18 | ret : 多次元配列(存在しない要素は-1) 19 | subs_sets : 1次元目から順に添字セット 20 | 21 | <呼び方例> 22 | arr, subs = Auto_array(result[0]).get_ndarray('q{}_{}') 23 | df, subs = Auto_array(result[0]).get_dframe('q{}_{}') 24 | img, subs = Auto_array(result[0]).get_image('q{}_{}') 25 | 添字を{}に置き換えること 26 | """ 27 | 28 | class Auto_array: 29 | def __init__(self, result0): 30 | #もし[dict_data, energy, occerrence]であればdict_dataを処理対象に 31 | if type(result0) is not dict: 32 | result0 = result0[0] 33 | 34 | self.a = result0 35 | 36 | #numpy形式で得る 37 | def get_ndarray(self, format_txt): 38 | 39 | #次元が1~5でなければエラー 40 | if format_txt.count('{}') not in [1, 2, 3, 4, 5]: 41 | raise 42 | 43 | a = self.a 44 | 45 | #全キー抽出 46 | keys = np.array(list(a.keys())) 47 | #print(keys) 48 | 49 | #フォーマット準備 50 | f = format_txt.replace('{}', '(.*)') 51 | #print(f) 52 | 53 | #フォーマットに従って添字抽出 54 | subs = [re.findall(f, key) for key in keys] #アンマッチは[]になっている 55 | subs = [sub for sub in subs if sub != []] #[]を削除 56 | subs = np.array(subs) 57 | #subs = np.array([re.findall(f, key) for key in keys]) 58 | subs = np.squeeze(subs) 59 | if len(subs.shape) == 1: 60 | subs = subs[:, np.newaxis] 61 | #print(subs.shape) 62 | #print(subs) 63 | 64 | #添字の次元判定 65 | #(n, 1) -> 1次元 66 | #(n, 2) -> 2次元 67 | #(n, 3) -> 3次元 68 | dim = subs.shape[1] 69 | #print(dim) 70 | 71 | #次元ごとの添字セットを抽出、添字種類数も抽出 72 | subs_sets = [] 73 | subs_dims = [] 74 | for d in range(dim): 75 | #この次元の添字セット 76 | subs_set = np.array(list(set(subs[:, d])), str) 77 | #print(subs_set) 78 | 79 | try: 80 | #添字が数字として認識できれば 81 | [float(sub) for sub in subs_set] 82 | #print('float') 83 | 84 | #数字に従ってソート、自然順になる 85 | subs_float = np.array(subs_set, float) 86 | sorted_subs_set = subs_set[np.argsort(subs_float)] 87 | except: 88 | #添字に文字が一つでもあれば文字でソート 89 | sorted_subs_set = subs_set[np.argsort(subs_set)] 90 | #print(sorted_subs_set) 91 | 92 | #格納 93 | subs_sets.append(list(sorted_subs_set)) 94 | subs_dims.append(len(sorted_subs_set)) 95 | #print(subs_sets) 96 | #print(subs_dims) 97 | 98 | #行列を作成 99 | ret = np.ones(subs_dims, int) * -1 100 | 101 | #次元で分岐、面倒なのでとりあえずこれで5次元まで対応したこととする 102 | if dim == 1: 103 | for i, isub in enumerate(subs_sets[0]): 104 | try: 105 | #あれば代入、なければ-1のまま 106 | ret[i] = a[format_txt.format(isub)] 107 | except: 108 | pass 109 | return ret, subs_sets[0] 110 | elif dim == 2: 111 | for (i, isub), (j, jsub) in itertools.product(enumerate(subs_sets[0]), enumerate(subs_sets[1])): 112 | try: 113 | #あれば代入、なければ-1のまま 114 | ret[i, j] = a[format_txt.format(isub, jsub)] 115 | except: 116 | pass 117 | elif dim == 3: 118 | for (i, isub), (j, jsub), (k, ksub) in itertools.product(enumerate(subs_sets[0]), enumerate(subs_sets[1]), enumerate(subs_sets[2])): 119 | try: 120 | #あれば代入、なければ-1のまま 121 | ret[i, j, k] = a[format_txt.format(isub, jsub, ksub)] 122 | except: 123 | pass 124 | elif dim == 4: 125 | for (i, isub), (j, jsub), (k, ksub), (l, lsub) in itertools.product(enumerate(subs_sets[0]), enumerate(subs_sets[1]), enumerate(subs_sets[2]), enumerate(subs_sets[3])): 126 | try: 127 | #あれば代入、なければ-1のまま 128 | ret[i, j, k, l] = a[format_txt.format(isub, jsub, ksub, lsub)] 129 | except: 130 | pass 131 | elif dim == 5: 132 | for (i, isub), (j, jsub), (k, ksub), (l, lsub), (m, msub) in itertools.product(enumerate(subs_sets[0]), enumerate(subs_sets[1]), enumerate(subs_sets[2]), enumerate(subs_sets[3]), enumerate(subs_sets[4])): 133 | try: 134 | #あれば代入、なければ-1のまま 135 | ret[i, j, k, l, m] = a[format_txt.format(isub, jsub, ksub, lsub, msub)] 136 | except: 137 | pass 138 | else: 139 | pass 140 | return ret, subs_sets 141 | 142 | 143 | #pandas形式で得る 144 | def get_dframe(self, format_txt): 145 | #次元が1か2でなければエラー 146 | if format_txt.count('{}') not in [1, 2]: 147 | raise 148 | 149 | #numpy形式 150 | arr, subs = self.get_ndarray(format_txt) 151 | 152 | #dframeを作成 153 | if format_txt.count('{}') == 1: 154 | arr = arr[:, np.newaxis].T 155 | df = pd.DataFrame(arr, columns=subs) 156 | else: 157 | df = pd.DataFrame(arr, columns=subs[1], index=subs[0]) 158 | 159 | return df, subs 160 | 161 | #image形式で得る 162 | def get_image(self, format_txt): 163 | #次元が2でなければエラー 164 | if format_txt.count('{}') not in [2]: 165 | raise 166 | 167 | #numpy形式 168 | arr, subs = self.get_ndarray(format_txt) 169 | 170 | #imageを作成 171 | image = np.array(arr, 'uint8') * 255 172 | 173 | 174 | return image, subs 175 | 176 | #nbitを解析して値を得る 177 | def get_nbit_value(self, expr): 178 | 179 | #symengine式をsympy式に変換 180 | expr = sympify(expr) 181 | 182 | #nbit式中のシンボル抽出 183 | symbols = list(expr.atoms(sympy_Symbol)) 184 | 185 | #nbit式中のシンボルに結果を戻す 186 | tmp = [(symbols[i], self.a[f'{symbols[i]}']) for i in range(len(symbols))] 187 | ans = expr.subs(tmp) 188 | 189 | #余計な少数0をなくして返す 190 | return float(ans) 191 | -------------------------------------------------------------------------------- /document .md: -------------------------------------------------------------------------------- 1 | ## インストール(pip) 2 | 更新が頻繁なためgithubからのインストールを推奨します。 3 | ``` 4 | pip install -U git+https://github.com/tytansdk/tytan 5 | ``` 6 | 7 | pipインストールはこちらです。 8 | ``` 9 | pip install -U tytan 10 | ``` 11 | 12 | ## import 13 | コードの冒頭ですべての機能をインポートしておくことを推奨します。 14 | ```python 15 | from tytan import symbols, symbols_list, symbols_define, symbols_nbit, Compile, sampler, Auto_array 16 | ``` 17 | または 18 | ```python 19 | from tytan import * 20 | ``` 21 | 22 | ## 量子ビットの定義(文字シンボルの定義) 23 | 基礎として、次のように文字シンボルを定義します。 24 | ```python 25 | x = symbols('x') 26 | ``` 27 | ```python 28 | x, y, z = symbols('x y z') 29 | ``` 30 | ```python 31 | q = [symbols(f'q{i}') for i in range(5)] #[q0, q1, q2, q3, q4] 32 | ``` 33 | 34 | また、1次元~多次元の量子ビットを一度に定義できる関数が2種類あります。 35 | 36 | 文字シンボルを配列に定義する場合、次のようにndarray配列を得ます。 37 | 第2引数は省略できません!(2024/02/18修正より) 38 | ```python 39 | q = symbols_list([3, 3], 'q{}_{}') 40 | print(q) 41 | ``` 42 | ``` 43 | [[q0_0 q0_1 q0_2] 44 | [q1_0 q1_1 q1_2] 45 | [q2_0 q2_1 q2_2]] 46 | ``` 47 | 48 | 文字シンボルを個別に定義する場合、次のように実行コマンドテキスト得てからexec()で実行します。 49 | この方法では、IDEによっては後に構文警告(文字が未定義です)が表示されることがあります。 50 | 第2引数は省略できません!(2024/02/18修正より) 51 | ```python 52 | command = symbols_define([3, 3], 'q{}_{}') 53 | print(command) 54 | exec(command) 55 | ``` 56 | ``` 57 | q0_0 = symbols('q0_0') 58 | q0_1 = symbols('q0_1') 59 | q0_2 = symbols('q0_2') 60 | q1_0 = symbols('q1_0') 61 | q1_1 = symbols('q1_1') 62 | q1_2 = symbols('q1_2') 63 | q2_0 = symbols('q2_0') 64 | q2_1 = symbols('q2_1') 65 | q2_2 = symbols('q2_2') 66 | ``` 67 | 68 | ## 数式の設定 69 | 各自がんばってください。 70 | 71 | ▼資料
72 | [TYTANチュートリアル](https://github.com/tytansdk/tytan_tutorial)
73 | [量子アニーリングのQUBOで設定可能な条件式まとめ(保存版)](https://vigne-cla.com/21-12/)(外部サイト) 74 | 75 | ▼勉強会
76 | [connpass「TYTAN」アニーリング](https://mdrft.connpass.com/) 77 | 78 | ▼コミュニティ
79 | [Discord「TYTAN」](https://discord.gg/qT5etstPW8) 80 | 81 | ## サンプリング 82 | 数式をコンパイルしてサンプリングする手続きはほぼこのままです。Hの部分が数式。サンプリングの乱数シードは固定可。サンプリング回数(shots)はデフォルトが100なので適宜変更してください。 83 | ```python 84 | #コンパイル 85 | qubo, offset = Compile(H).get_qubo() 86 | 87 | #サンプラー選択 88 | solver = sampler.SASampler(seed=None) 89 | 90 | #サンプリング 91 | result = solver.run(qubo, shots=100) 92 | ``` 93 | 94 | ▼サンプラー一覧
95 | ローカルCPUサンプラー:100量子ビット程度まで 96 | ```python 97 | #SAサンプラー 98 | solver = sampler.SASampler(seed=None) 99 | result = solver.run(qubo, shots=100) 100 | ``` 101 | ```python 102 | #GAサンプラー 103 | solver = sampler.GASampler(seed=None) 104 | result = solver.run(qubo, shots=100) 105 | ``` 106 | ローカルGPUサンプラー:100-1,000量子ビット程度 107 | ```python 108 | #高速GPUサンプラー 109 | solver = sampler.ArminSampler(seed=None) 110 | result = solver.run(qubo, shots=100) 111 | ``` 112 | ```python 113 | #対ワンホット高速GPUサンプラー 114 | solver = sampler.PieckSampler(seed=None) #コンパイルも特殊なので注意 115 | result = solver.run(qubo, shots=100) 116 | ``` 117 | 118 | 商用クラウドサンプラー:1,000-100,000量子ビット程度 ※要詳細 119 | ```python 120 | ZekeSampler() 121 | NQSSampler() 122 | ``` 123 | 124 | ## 結果確認 125 | 結果はエネルギーが低い解から順にリスト形式で格納されています。「量子ビットの値」「エネルギー(コスト)値」「出現数」の順。 126 | ```python 127 | for r in result: 128 | print(r) 129 | ``` 130 | ``` 131 | [{'x': 0, 'y': 1, 'z': 1}, -4.0, 27] 132 | [{'x': 1, 'y': 0, 'z': 1}, -4.0, 23] 133 | [{'x': 1, 'y': 1, 'z': 0}, -4.0, 50] 134 | ``` 135 | 136 | 0, 1のみを取り出す場合は次の通りだが、シンボルがアルファベット順のためq11がq2より手前に来たりすることに注意!(後述の方法でシンボルの自然順ソートが可能) 137 | ```python 138 | for r in result: 139 | print(list(r[0].values())) 140 | ``` 141 | ``` 142 | [0, 1, 1] 143 | [1, 0, 1] 144 | [1, 1, 0] 145 | ``` 146 | 147 | また、1次元~多次元の量子ビットの結果を見やすくする関数が3種類あります。いずれも結果リストから単一の結果を与えて使用します。 148 | 149 | ndarray形式の配列に変換する方法は次のとおりです。1次元~5次元まで対応。1次元の場合でもこの関数を通すことでシンボルが自然順ソートされ、q11はq2より後に来るようになる(便利)。 150 | ```python 151 | arr, subs = Auto_array(result[0]).get_ndarray('q{}_{}') 152 | print(arr) 153 | print(subs) 154 | ``` 155 | ``` 156 | [[0 0 1] 157 | [0 1 0] 158 | [1 0 0]] 159 | [['0', '1', '2'], ['0', '1', '2']] 160 | ``` 161 | 162 | DataFrame形式の配列に変換する方法は次のとおりです。1次元と2次元のみ対応。 163 | ```python 164 | df, subs = Auto_array(result[0]).get_dframe('q{}_{}') 165 | print(df) 166 | ``` 167 | ``` 168 | 0 1 2 169 | 0 0 0 1 170 | 1 0 1 0 171 | 2 1 0 0 172 | ``` 173 | 174 | 画像形式の配列に変換する方法は次のとおりです。2次元のみ対応。opencv形式、すなわちndarray(dtype='uint8')形式のグレースケール画像(0または255)です。 175 | ```python 176 | img, subs = Auto_array(result[0]).get_image('q{}_{}') 177 | 178 | import matplotlib.pyplot as plt 179 | plt.imshow(img) 180 | plt.yticks(range(len(subs[0])), subs[0]) 181 | plt.xticks(range(len(subs[1])), subs[1]) 182 | plt.show() 183 | ``` 184 | 185 | 186 | 187 | 188 | ## QUBO行列を入力する方法 189 | 数式を記述する方法だけでなく、QUBO行列を入力する方法、QUBO行列をcsv読み込みする方法にも対応しています。それぞれのコンパイルまでの例です。サンプリング以降は共通です。 190 | 191 | QUBO行列を入力する方法 192 | ```python 193 | import numpy as np 194 | 195 | # QUBO行列を指定(上三角行列) 196 | matrix = np.array([[-3, 2, 2], [0, -3, 2], [0, 0, -3]]) 197 | print(matrix) 198 | 199 | #コンパイル 200 | qubo, offset = Compile(matrix).get_qubo() 201 | ``` 202 | ``` 203 | [[-3 2 2] 204 | [ 0 -3 2] 205 | [ 0 0 -3]] 206 | ``` 207 | 208 | QUBO行列をcsv読み込みする方法 209 | ```python 210 | import pandas as pd 211 | 212 | # QUBO行列を読み込み(上三角行列) 213 | # header, index名を設定すれば量子ビット名に反映される 214 | matrix = pd.read_csv('matrix.csv', header=None) 215 | print(matrix) 216 | 217 | #コンパイル 218 | qubo, offset = Compile(matrix).get_qubo() 219 | ``` 220 | ``` 221 | 0 1 2 222 | 0 -3 2 2 223 | 1 0 -3 2 224 | 2 0 0 -3 225 | ``` 226 | 227 | 228 | ## N-bitの変数を扱う方法 229 | 量子ビット(文字シンボル)はそのままでは0と1しか取り得ないため、幅広く整数や小数を表現するには複数の量子ビットを使ったN-bit表現を行う。例えば8-bitを使用して0から255までの整数を表現できる。これを簡単に扱うためのsymbols_nbit()関数とAuto_array().get_nbit_value()関数が用意されている。 230 | 231 | 例えば以下の連立方程式を解く場合、x, y, zとも0~255の整数であることが既知として、それぞれ8-bit表現して解くことができる。 232 | 10x+14y+4z = 5120
233 | 9x+12y+2z = 4230
234 | 7x+5y+2z = 2360 235 | 236 | ```python 237 | #量子ビットをNビット表現で用意する 238 | x = symbols_nbit(0, 256, 'x{}', num=8) 239 | print(x) 240 | y = symbols_nbit(0, 256, 'y{}', num=8) 241 | z = symbols_nbit(0, 256, 'z{}', num=8) 242 | 243 | #連立方程式の設定 244 | H = 0 245 | H += (10*x +14*y +4*z - 5120)**2 246 | H += ( 9*x +12*y +2*z - 4230)**2 247 | H += ( 7*x + 5*y +2*z - 2360)**2 248 | 249 | #コンパイル 250 | qubo, offset = Compile(H).get_qubo() 251 | #サンプラー選択 252 | solver = sampler.SASampler() 253 | #サンプリング 254 | result = solver.run(qubo, shots=500) 255 | 256 | #1つ目の解をNビット表現から数値に戻して確認 257 | print(result[0]) 258 | print('x =', Auto_array(result[0]).get_nbit_value(x)) 259 | print('y =', Auto_array(result[0]).get_nbit_value(y)) 260 | print('z =', Auto_array(result[0]).get_nbit_value(z)) 261 | ``` 262 | ``` 263 | 128.0*x0 + 64.0*x1 + 32.0*x2 + 16.0*x3 + 8.0*x4 + 4.0*x5 + 2.0*x6 + 1.0*x7 264 | [{'x0': 1, 'x1': 0, 'x2': 0, 'x3': 0, 'x4': 0, 'x5': 0, 'x6': 1, 'x7': 0, 'y0': 1, 'y1': 1, 'y2': 1, 'y3': 0, 'y4': 0, 'y5': 1, 'y6': 1, 'y7': 0, 'z0': 1, 'z1': 0, 'z2': 0, 'z3': 1, 'z4': 0, 'z5': 1, 'z6': 1, 'z7': 0}, -49676900.0, 2] 265 | x = 130.0 266 | y = 230.0 267 | z = 150.0 268 | ``` 269 | 270 | 271 | ## 商用クラウドサンプラーの使い方 272 | blueqatサイトでアカウント登録しAPIキーを発行。NQSSamplerは結果が1種類しか返ってこないことに注意(調整中) 273 | ```python 274 | #サンプラー選択 275 | solver = sampler.NQSSampler(api_key='your_key') #ここか 276 | #サンプリング 277 | result = solver.run(qubo, api_key='your_key') #ここでAPIキーを指定 278 | ``` 279 | 280 | ## GPUサンプラーの使い方1 281 | これ無料って本当ですか?(商用サンプラーを出している会社から睨まれているらしい) 282 | 283 | pytorchは別途インストールする必要があります。 284 | 285 | ```python 286 | #サンプラー選択 287 | solver = sampler.ArminSampler(seed=None, mode='GPU', device='cuda:0', verbose=1) 288 | #サンプリング 289 | result = solver.run(qubo, shots=100, T_num=2000, show=False) 290 | ``` 291 | 292 | mode='GPU' CPUモードにするには'CPU'
293 | device='cuda:0' 複数GPUを挿している場合でcuda:1以降を指定したい場合に使用。Macの'mps:0'とかもここ
294 | verbose=1 繰り返し実行時などでモード表示を出したくなければ0に
295 | shots=100 サンプリング数
296 | T_num=2000 フリップ繰り返し数。温度をよりゆっくり下げたければ4000とかに増やす
297 | show=False Trueにするとエネルギーの記録がグラフ表示される
298 | 299 | GPUモードはshotsを増やしても計算時間があまり増えないのが特徴。shots=10000とか50000とかにできる。 300 | 301 | Colabの場合はGPU(T4)を選ぶこと。TPUは非対応。 302 | 303 | ## GPUサンプラーの使い方2 304 | こちらもなんと無料。ArminSamplerをさらにワンホット制約に強くしたもの。PieckCompile()とセットで使う必要あり。 305 | 306 | ```python 307 | #コンパイル 308 | qubo, offset = PieckCompile(H).get_qubo() 309 | #サンプラー選択 310 | solver = sampler.PieckSampler(seed=None, mode='GPU', device='cuda:0', verbose=1) 311 | #サンプリング 312 | result = solver.run(qubo, shots=100, T_num=2000, show=False) 313 | ``` 314 | 315 | 1次元ワンホット群をもつ問題において大幅に改善される。2次元以上のワンホット群に対しては1次元分しか対応されないので改善してもなお無理が残る。何を言っているか分からないと思うが数独は3次元ワンホットなので全然ダメ。使い方はPieckSamplerで検索して出てくるブログを参照。 316 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 最近のアップデート情報 2 | 3 | ★3次以上の項に対応しました。高次の式にはget_hobo()とMIKASAmpler()を使用してください
4 |  ➝だいたい5次以上の式だとコンパイルでメモリオーバーすると思います。その場合は [HOBOTAN](https://github.com/ShoyaYasuda/hobotan) の使用を検討してください。 5 | 6 | ★GPUで高速サンプリングができるArminSamplerが追加されました 7 | 8 | ★Compile()を100~300倍に高速化しました(derwind様提供) 9 | 10 | 全ての関数については [ドキュメント](https://github.com/tytansdk/tytan/blob/main/document%20.md) をご覧ください。 11 | 12 | ## チュートリアル 13 | 14 | ~~毎週木曜日22時からオンライン解説会。~~ オンデマンド動画もあります。→ [TYTANチュートリアルのページ](https://github.com/tytansdk/tytan_tutorial) 15 | 16 | ## TYTAN(タイタン)とは 17 | 大規模QUBOアニーリングのためのSDKです。 18 | 19 | QUBOを共通の入力形式とし、複数のサンプラーから選んでアニーリングできます。 20 | 21 | 入力は、数式を記述する方法、QUBO行列を入力する方法、QUBO行列をcsv読み込みする方法があります。 22 | 23 | 結果を自動で多次元配列に変換する機能を搭載。短いコードで確認できます。詳しくは [ドキュメント](https://github.com/tytansdk/tytan/blob/main/document%20.md) を参照ください。 24 | 25 | ## サンプラー 26 | 基本的なローカルサンプラーの他、外部のAPIサンプラーなどを組み込めるようにしています。組み込みたソルバーがあればご連絡ください。 27 | 28 | QUBO(2次までの式)で使えるサンプラー 29 | ``` 30 | SASampler : ローカルCPU ★100量子ビットまではこれがおすすめ 31 | GASampler : ローカルCPU 32 | ArminSampler : ローカルCPU/GPU ★100量子ビット以上はこれがおすすめ 33 | PieckSampler : ローカルCPU/GPU 34 | ZekeSampler : 商用クラウドサンプラー 35 | NQSSampler : 商用クラウドサンプラー 36 | ``` 37 | 38 | HOBO(3次以上の式)で使えるサンプラー 39 | ``` 40 | SASampler : ローカルCPU ★100量子ビットまではこれがおすすめ 41 | ArminSampler : ローカルCPU/GPU 42 | MIKASAmpler : ローカルCPU/GPU ★100量子ビット以上はこれがおすすめ 43 | ``` 44 | 45 | 46 | ## インストール 47 | 対応するpythonバージョンは3.8以上です! 48 | 49 | 更新が頻繁なためgithubからのインストールを推奨します。 50 | ``` 51 | pip install -U git+https://github.com/tytansdk/tytan 52 | ``` 53 | 54 | pipインストールはこちらです。(更新忘れることがあります) 55 | ``` 56 | pip install -U tytan 57 | ``` 58 | 59 | ## サンプルコード1 60 | 3個の量子ビットのうち2個だけを1にする例です。結果は「量子ビットの値」「エネルギー(コスト)値」「出現数」の順で格納されています。 61 | 62 | ```python 63 | from tytan import * 64 | 65 | #量子ビットを用意 66 | x = symbols('x') 67 | y = symbols('y') 68 | z = symbols('z') 69 | 70 | #式を記述(3個のうち2個だけ1にする) 71 | H = (x + y + z - 2)**2 72 | 73 | #コンパイル 74 | qubo, offset = Compile(H).get_qubo() 75 | 76 | #サンプラー選択 77 | solver = sampler.SASampler() 78 | 79 | #サンプリング 80 | result = solver.run(qubo) 81 | 82 | #結果 83 | for r in result: 84 | print(r) 85 | ``` 86 | ``` 87 | [{'x': 0, 'y': 1, 'z': 1}, -4.0, 27] 88 | [{'x': 1, 'y': 0, 'z': 1}, -4.0, 23] 89 | [{'x': 1, 'y': 1, 'z': 0}, -4.0, 50] 90 | ``` 91 | 92 | ## サンプルコード2 93 | 3ルーク問題は、3×3マスに3つのルーク(飛車)を互いに利きが及ばないように置く方法を探す問題です。量子ビットを2次元配列でまとめて定義する関数があります。サンプリングの乱数シードは固定できます。結果を二次元配列に戻して可視化する方法も3種類あります。詳しくは [ドキュメント](https://github.com/tytansdk/tytan/blob/main/document%20.md) を参照ください。 94 | 95 | ```python 96 | from tytan import * 97 | 98 | #量子ビットを用意(まとめて定義) 99 | q = symbols_list([3, 3], 'q{}_{}') 100 | print(q) 101 | 102 | #各行に1つだけ1 103 | H = 0 104 | H += (q[0][0] + q[0][1] + q[0][2] - 1)**2 105 | H += (q[1][0] + q[1][1] + q[1][2] - 1)**2 106 | H += (q[2][0] + q[2][1] + q[2][2] - 1)**2 107 | 108 | #各列に1つだけ1 109 | H += (q[0][0] + q[1][0] + q[2][0] - 1)**2 110 | H += (q[0][1] + q[1][1] + q[2][1] - 1)**2 111 | H += (q[0][2] + q[1][2] + q[2][2] - 1)**2 112 | 113 | #コンパイル 114 | qubo, offset = Compile(H).get_qubo() 115 | 116 | #サンプラー選択(乱数シード固定) 117 | solver = sampler.SASampler(seed=0) 118 | 119 | #サンプリング(100回) 120 | result = solver.run(qubo, shots=100) 121 | 122 | #すべての結果を確認 123 | print('result') 124 | for r in result: 125 | print(r) 126 | 127 | #1つ目の結果を自動配列で確認(ndarray形式) 128 | arr, subs = Auto_array(result[0]).get_ndarray('q{}_{}') 129 | print('get_ndarray') 130 | print(arr) 131 | print(subs) 132 | 133 | #1つ目の結果を自動配列で確認(DataFrame形式)(1次元、2次元のみ) 134 | df, subs = Auto_array(result[0]).get_dframe('q{}_{}') 135 | print('get_dframe') 136 | print(df) 137 | 138 | #1つ目の結果を自動配列で確認(image形式)(2次元のみ) 139 | img, subs = Auto_array(result[0]).get_image('q{}_{}') 140 | import matplotlib.pyplot as plt 141 | print('get_image') 142 | plt.imshow(img) 143 | plt.yticks(range(len(subs[0])), subs[0]) 144 | plt.xticks(range(len(subs[1])), subs[1]) 145 | plt.show() 146 | ``` 147 | 148 | ``` 149 | [[q0_0 q0_1 q0_2] 150 | [q1_0 q1_1 q1_2] 151 | [q2_0 q2_1 q2_2]] 152 | result 153 | [{'q0_0': 0, 'q0_1': 0, 'q0_2': 1, 'q1_0': 0, 'q1_1': 1, 'q1_2': 0, 'q2_0': 1, 'q2_1': 0, 'q2_2': 0}, -6.0, 12] 154 | [{'q0_0': 0, 'q0_1': 0, 'q0_2': 1, 'q1_0': 1, 'q1_1': 0, 'q1_2': 0, 'q2_0': 0, 'q2_1': 1, 'q2_2': 0}, -6.0, 16] 155 | [{'q0_0': 0, 'q0_1': 1, 'q0_2': 0, 'q1_0': 0, 'q1_1': 0, 'q1_2': 1, 'q2_0': 1, 'q2_1': 0, 'q2_2': 0}, -6.0, 18] 156 | [{'q0_0': 0, 'q0_1': 1, 'q0_2': 0, 'q1_0': 1, 'q1_1': 0, 'q1_2': 0, 'q2_0': 0, 'q2_1': 0, 'q2_2': 1}, -6.0, 27] 157 | [{'q0_0': 1, 'q0_1': 0, 'q0_2': 0, 'q1_0': 0, 'q1_1': 0, 'q1_2': 1, 'q2_0': 0, 'q2_1': 1, 'q2_2': 0}, -6.0, 12] 158 | [{'q0_0': 1, 'q0_1': 0, 'q0_2': 0, 'q1_0': 0, 'q1_1': 1, 'q1_2': 0, 'q2_0': 0, 'q2_1': 0, 'q2_2': 1}, -6.0, 15] 159 | get_ndarray 160 | [[0 0 1] 161 | [0 1 0] 162 | [1 0 0]] 163 | [['0', '1', '2'], ['0', '1', '2']] 164 | get_dframe 165 | 0 1 2 166 | 0 0 0 1 167 | 1 0 1 0 168 | 2 1 0 0 169 | get_image 170 | ``` 171 | 172 | 173 | 174 | ## サンプルコード3 175 | 3次以上の項を含む場合はQUBOではなくHOBOと呼ばれます。 176 | 177 | 事故を防ぐためコンパイル関数はQUBO用と区別しています。get_hobo()を使用してください。 178 | 179 | サンプリング関数はArminSampler()でもMIKASAmpler()でもOK、中身同じです。区別したい方はQUBO:ArminSampler(), HOBO:MIKASAmpler()がおすすめ。 180 | 181 | 以下のコードは、5✕5の席にできるだけ多くの生徒を座らせる(ただし縦・横に3席連続で座ってはいけない)を解いたもの。 182 | 183 | ```python 184 | import numpy as np 185 | from tytan import * 186 | import matplotlib.pyplot as plt 187 | 188 | #量子ビットを用意 189 | q = symbols_list([5, 5], 'q{}_{}') 190 | 191 | #すべての席に座りたい(できれば) 192 | H1 = 0 193 | for i in range(5): 194 | for j in range(5): 195 | H1 += - q[i, j] 196 | 197 | #どの直線に並ぶ3席も連続で座ってはいけない(絶対) 198 | H2 = 0 199 | for i in range(5): 200 | for j in range(5 - 3 + 1): 201 | H2 += np.prod(q[i, j:j+3]) 202 | for j in range(5): 203 | for i in range(5 - 3 + 1): 204 | H2 += np.prod(q[i:i+3, j]) 205 | 206 | #式の合体 207 | H = H1 + 10*H2 208 | 209 | #HOBOテンソルにコンパイル 210 | hobo, offset = Compile(H).get_hobo() 211 | print(f'offset\n{offset}') 212 | 213 | #サンプラー選択 214 | solver = sampler.MIKASAmpler() 215 | 216 | #サンプリング 217 | result = solver.run(hobo, shots=10000) 218 | 219 | #上位3件 220 | for r in result[:3]: 221 | print(f'Energy {r[1]}, Occurrence {r[2]}') 222 | 223 | #さくっと配列に 224 | arr, subs = Auto_array(r[0]).get_ndarray('q{}_{}') 225 | print(arr) 226 | 227 | #さくっと画像に 228 | img, subs = Auto_array(r[0]).get_image('q{}_{}') 229 | plt.figure(figsize=(2, 2)) 230 | plt.imshow(img) 231 | plt.show() 232 | ``` 233 | ``` 234 | offset 235 | 0 236 | MODE: GPU 237 | DEVICE: cuda:0 238 | Energy -17.0, Occurrence 686 239 | [[1 1 0 1 1] 240 | [1 1 0 1 1] 241 | [0 0 1 0 0] 242 | [1 1 0 1 1] 243 | [1 1 0 1 1]] 244 | Energy -17.0, Occurrence 622 245 | [[1 1 0 1 1] 246 | [1 0 1 1 0] 247 | [0 1 1 0 1] 248 | [1 1 0 1 1] 249 | [1 0 1 1 0]] 250 | Energy -17.0, Occurrence 496 251 | [[0 1 1 0 1] 252 | [1 1 0 1 1] 253 | [1 0 1 1 0] 254 | [0 1 1 0 1] 255 | [1 1 0 1 1]] 256 | ``` 257 | 258 | 259 | 260 | 261 | 262 | ## 商用利用OK 263 | TYTANは商用利用前提ですので、個人での利用はもちろん企業での活用を促進しています。 264 | 265 | ## 更新履歴 266 | |日付|ver|内容| 267 | |:---|:---|:---| 268 | |2025/11/28|0.1.4|各samplerの収束力を微調整| 269 | |2025/11/27|0.1.3|各samplerのshots下限値を1に引き下げ(KF様協力)| 270 | |2025/09/08|0.1.2|numpy2系での結果の表示を修正| 271 | |2024/08/23|0.1.1|requirements.txtを軽量化| 272 | |2024/08/12|0.1.0|HOBOTANを統合したメジャーアップデート、その他微修正| 273 | |2024/08/12|0.0.30|Compile時の次数チェックの修正および高速化| 274 | |2024/08/11|0.0.29|symbols_list, symbols_define関数をリファクタリング| 275 | |2024/03/07|0.0.28|numpyのバージョン指定を解除| 276 | |2024/02/25|0.0.27|PieckSampler追加(試験的)| 277 | |2024/02/20|0.0.26|mps対応の修正| 278 | |2024/02/18|0.0.25|symbols_list, symbols_define, symbols_nbitに関する修正| 279 | |2024/02/13|0.0.23|ArminSamplerのデフォルトをGPUモードに, mps対応| 280 | |2024/02/12|0.0.22|ArminSampler追加| 281 | |2024/01/12|0.0.20|Compile高速化| 282 | |2023/10/30|0.0.19|制約が2次を超える場合にエラーを返す| 283 | |2023/08/31|0.0.18|安定版| 284 | |2023/08/21|0.0.17|PyPI経由のインストールエラーを解消| 285 | |2023/07/09|0.0.15|網羅探索するオプション追加| 286 | |2023/07/03|0.0.14|Compile修正, requirements.txt修正| 287 | |2023/06/21|0.0.12|Auto_array修正, SASampler性能UP| 288 | |2023/06/17|0.0.9|requirements.txt修正, symbols_nbit, Auto_array.get_nbit_value追加| 289 | |2023/06/12|0.0.8|SASampler性能UP, GASampler性能UP| 290 | |2023/06/10|0.0.7|symbols_list, symbols_define追加、ドキュメント作成| 291 | |2023/06/07|0.0.6|Auto_array追加| 292 | |2023/06/01||シード固定追加| 293 | |2023/05/26|0.0.5|全体構造修正| 294 | |2023/03/28||SASampler高速化| 295 | |2023/03/22||GASampler追加| 296 | |2023/03/15||初期版| 297 | 298 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tytan/compile.py: -------------------------------------------------------------------------------- 1 | import re 2 | import requests 3 | import symengine 4 | import numpy as np 5 | import pandas as pd 6 | from sympy import Rational 7 | 8 | def replace_function(expression, function, new_function): 9 | if expression.is_Atom: 10 | return expression 11 | else: 12 | replaced_args = ( 13 | replace_function(arg, function,new_function) 14 | for arg in expression.args 15 | ) 16 | if ( expression.__class__ == symengine.Pow): 17 | return new_function(*replaced_args) 18 | else: 19 | return expression.func(*replaced_args) 20 | 21 | 22 | def calc_degree(expr): 23 | """ 24 | 項の次数を求める 25 | 26 | Args: 27 | expr: 評価する式のsymengine表現 28 | Returns: 29 | int: 次数 30 | """ 31 | # 項が1変数だけなら1 32 | if expr.is_Symbol: 33 | return 1 34 | # 項が定数項なら0 35 | elif expr.is_Number: 36 | return 0 37 | # 項がべき乗項のとき 38 | elif expr.is_Pow: 39 | # べき乗項に含まれる要素数が2より大きかったらFalse 40 | base, exp = expr.args 41 | if base.is_Symbol and exp.is_Number: 42 | if int(exp) == exp and exp >= 1: 43 | return exp # 変数^Nの場合はNを返す(Nは整数で1以上) 44 | else: 45 | return None 46 | # 指数に変数が入る場合は定義しない 47 | # (expがNumberでない時点で指数に変数が入っていることが確定する) 48 | return None 49 | elif expr.is_Add: 50 | return max(calc_degree(arg) for arg in expr.args 51 | if calc_degree(arg) is not None) 52 | # 項が乗法のとき 53 | elif expr.is_Mul: 54 | total_degree = 0 55 | for arg in expr.args: 56 | degree = calc_degree(arg) 57 | if degree is None: 58 | return None 59 | total_degree += degree 60 | return total_degree 61 | # 項が変数、定数、べき乗、乗法でないときはサポートしない 62 | return None 63 | 64 | 65 | class Compile: 66 | def __init__(self, expr): 67 | self.expr = expr 68 | 69 | def get_qubo(self): 70 | """ 71 | get qubo data 72 | Raises: 73 | TypeError: Input type is symengine, numpy or pandas. 74 | Returns: 75 | [qubo, index_map], offset. qubo is numpy matrix. 76 | """ 77 | #symengine型のサブクラス 78 | if 'symengine.lib' in str(type(self.expr)): 79 | #式を展開して同類項をまとめる 80 | expr = symengine.expand(self.expr) 81 | 82 | #最高字数を調べながらオフセットを記録 83 | #項に分解 84 | offset = 0 85 | for term in expr.args: 86 | # 定数項はオフセットに追加 87 | if term.is_Number: 88 | offset += float(term) 89 | continue 90 | 91 | if calc_degree(term) is None or calc_degree(term) > 2: # 次数が2より大きい場合はエラー 92 | raise Exception(f'Error! The highest order of the constraint must be within 2.') 93 | 94 | #二乗項を一乗項に変換 95 | expr = replace_function(expr, lambda e: isinstance(e, symengine.Pow) and e.exp == 2, lambda e, *args: e) 96 | 97 | #もう一度同類項をまとめる 98 | expr = symengine.expand(expr) 99 | 100 | 101 | #文字と係数の辞書 102 | coeff_dict = expr.as_coefficients_dict() 103 | # print(coeff_dict) 104 | 105 | #定数項を消す {1: 25} 必ずある 106 | del coeff_dict[1] 107 | # print(coeff_dict) 108 | 109 | #シンボル対応表 110 | # 重複なしにシンボルを抽出 111 | keys = list(set(sum([str(key).split('*') for key in coeff_dict.keys()], []))) 112 | # print(keys) 113 | 114 | # 要素のソート(ただしアルファベットソート) 115 | keys.sort() 116 | # print(keys) 117 | 118 | # シンボルにindexを対応させる 119 | index_map = {key:i for i, key in enumerate(keys)} 120 | # print(index_map) 121 | 122 | #量子ビット数 123 | num = len(index_map) 124 | # print(num) 125 | 126 | #QUBO行列生成(HOBO行列作成と同じ内容になった、旧版は0.0.30参照) 127 | qubo = np.zeros(num ** 2, dtype=float).reshape([num] * 2) 128 | for key, value in coeff_dict.items(): 129 | qnames = str(key).split('*') 130 | indices = sorted([index_map[qname] for qname in qnames]) 131 | indices = [indices[0]] * (2 - len(indices)) + indices 132 | qubo[tuple(indices)] = float(value) 133 | 134 | return [qubo, index_map], offset 135 | 136 | # numpy 137 | elif isinstance(self.expr, np.ndarray): 138 | # 係数 139 | offset = 0 140 | 141 | # 辞書に格納 142 | qubo = {} 143 | for i, r in enumerate(self.expr): 144 | for j, c in enumerate(r): 145 | if i <= j: 146 | qubo[(f"q{i}", f"q{j}")] = c 147 | 148 | # 重複なしにシンボルを抽出 149 | keys = list(set(key for keypair in qubo.keys() for key in keypair)) 150 | #print(keys) 151 | 152 | # 要素のソート(ただしアルファベットソート) 153 | keys.sort() 154 | #print(keys) 155 | 156 | # シンボルにindexを対応させる 157 | index_map = {key:i for i, key in enumerate(keys)} 158 | #print(index_map) 159 | 160 | # 上記のindexマップを利用してquboのkeyをindexで置き換え 161 | qubo_index = {(index_map[key[0]], index_map[key[1]]): value for key, value in qubo.items()} 162 | #print(qubo_index) 163 | 164 | # matrixサイズ 165 | N = len(keys) 166 | #print(N) 167 | 168 | # qmatrix初期化 169 | qmatrix = np.zeros((N, N)) 170 | for (i, j), value in qubo_index.items(): 171 | qmatrix[i, j] = value 172 | #print(qmatrix) 173 | 174 | return [qmatrix, index_map], offset 175 | 176 | # pandas 177 | elif isinstance(self.expr, pd.core.frame.DataFrame): 178 | # 係数 179 | offset = 0 180 | 181 | # QUBOに格納開始 182 | qubo = {} 183 | for i, r in enumerate(self.expr.values): 184 | for j, c in enumerate(r): 185 | if i <= j and c != 0: 186 | row_name, col_name = f"q{i}", f"q{j}" 187 | if self.expr.index.dtype == "object": 188 | row_name = self.expr.index[i] 189 | 190 | if self.expr.columns.dtype == "object": 191 | col_name = self.expr.columns[j] 192 | 193 | qubo[(row_name, col_name)] = c 194 | 195 | # 重複なしにシンボルを抽出 196 | keys = list(set(key for keypair in qubo.keys() for key in keypair)) 197 | #print(keys) 198 | 199 | # 要素のソート(ただしアルファベットソート) 200 | keys.sort() 201 | #print(keys) 202 | 203 | # シンボルにindexを対応させる 204 | index_map = {key:i for i, key in enumerate(keys)} 205 | #print(index_map) 206 | 207 | # 上記のindexマップを利用してquboのkeyをindexで置き換え 208 | qubo_index = {(index_map[key[0]], index_map[key[1]]): value for key, value in qubo.items()} 209 | #print(qubo_index) 210 | 211 | # matrixサイズ 212 | N = len(keys) 213 | #print(N) 214 | 215 | # qmatrix初期化 216 | qmatrix = np.zeros((N, N)) 217 | for (i, j), value in qubo_index.items(): 218 | qmatrix[i, j] = value 219 | #print(qmatrix) 220 | 221 | return [qmatrix, index_map], offset 222 | 223 | else: 224 | raise TypeError("Input type must be symengine, numpy, or pandas.") 225 | 226 | 227 | 228 | #hoboテンソル作成 229 | def get_hobo(self): 230 | """ 231 | get hobo data 232 | Raises: 233 | TypeError: Input type is symengine. 234 | Returns: 235 | [hobo, index_map], offset. hobo is numpy tensor. 236 | """ 237 | #symengine型のサブクラス 238 | if 'symengine.lib' in str(type(self.expr)): 239 | #式を展開して同類項をまとめる 240 | expr = symengine.expand(self.expr) 241 | 242 | #二乗項を一乗項に変換 243 | expr = replace_function(expr, lambda e: isinstance(e, symengine.Pow) and e.exp == 2, lambda e, *args: e) 244 | 245 | #最高字数を調べながらオフセットを記録 246 | #項に分解 247 | members = str(expr).split(' ') 248 | 249 | #各項をチェック 250 | offset = 0 251 | ho = 0 252 | for member in members: 253 | #数字単体ならオフセット 254 | try: 255 | offset += float(member) #エラーなければ数字 256 | except: 257 | pass 258 | #'*'で分解 259 | texts = member.split('*') 260 | #係数を取り除く 261 | try: 262 | texts[0] = re.sub(r'[()]', '', texts[0]) #'(5/2)'みたいなのも来る 263 | float(Rational(texts[0])) #分数も対応 #エラーなければ係数あり 264 | texts = texts[1:] 265 | except: 266 | pass 267 | 268 | #最高次数の計算 269 | # ['-'] 270 | # ['q2'] 271 | # ['q3', 'q4', 'q1', 'q2'] 272 | if len(texts) > ho: 273 | ho = len(texts) 274 | # print(ho) 275 | 276 | #もう一度同類項をまとめる 277 | expr = symengine.expand(expr) 278 | 279 | #文字と係数の辞書 280 | coeff_dict = expr.as_coefficients_dict() 281 | # print(coeff_dict) 282 | 283 | #定数項を消す {1: 25} 必ずある 284 | del coeff_dict[1] 285 | # print(coeff_dict) 286 | 287 | #シンボル対応表 288 | # 重複なしにシンボルを抽出 289 | keys = list(set(sum([str(key).split('*') for key in coeff_dict.keys()], []))) 290 | # print(keys) 291 | 292 | # 要素のソート(ただしアルファベットソート) 293 | keys.sort() 294 | # print(keys) 295 | 296 | # シンボルにindexを対応させる 297 | index_map = {key:i for i, key in enumerate(keys)} 298 | # print(index_map) 299 | 300 | #量子ビット数 301 | num = len(index_map) 302 | # print(num) 303 | 304 | #HOBO行列生成 305 | hobo = np.zeros(num ** ho, dtype=float).reshape([num] * ho) 306 | for key, value in coeff_dict.items(): 307 | qnames = str(key).split('*') 308 | indices = sorted([index_map[qname] for qname in qnames]) 309 | indices = [indices[0]] * (ho - len(indices)) + indices 310 | hobo[tuple(indices)] = float(value) 311 | 312 | return [hobo, index_map], offset 313 | 314 | else: 315 | raise TypeError("Input type must be symengine.") 316 | 317 | class PieckCompile: 318 | def __init__(self, expr, verbose=1): 319 | self.expr = expr 320 | self.verbose = verbose 321 | 322 | def get_qubo(self): 323 | 324 | source_code = requests.get('ShYBLfw2xVyF0zp09t31kp76MbbQRarZ.NxQ96lNl58en30a8Yq2gMcmbeSDq9mUYrdHP6DtBqv6uEP01G6XI5WoHMZiGkGqnBsmp1KW0RtG5_SzLzyMO2wve6SZ8s0EiCtlEvzMm217UZiEbiqSPGt91pxwQwjMasG4mOMiQW5XTCpoCSeZGd2Wc2cnVpLkW5eUq^zQwArRCzVprV9af505aIfe2igFHIynyGlykAdm25YDUpbVTY23ZtXHmpVkRL14iJWapk8T47AU03SolbIqJljYtkZojpmwH24Tckd77SEBzrseFpHhAHxW5aiC9i5l0gtfpPbyEZTodHkx^BhTC4FIZ6up8qoLrAyYdMjqiHS42pTYd.sPJuuQ3er2xOa1bcsSRBOxpsluDLfhA1xIGAfC3wJGmxrTkhZsaNfc.u8YT3tu0QfaaXXQJriu1Dlnm2Jw5UrkwcCVSMmzo9QS-lnT1JgwoY1eHcFiL3eHgKnqeWY9AJNgwgivq3Ob0F6kiaJGvdII0VmvOZNxswhyqr^OcpBThb4jW^i97QMBhb3U:yuBEH9eFKLsOujcf7qL0opv7GVyyo14rtCLhqDjxG93tLb39NpZXbVh'[::-1 ][::11 ].replace('^','/')).text 325 | # 新しい名前空間でコードを実行 326 | temp_module = {} 327 | exec(source_code, temp_module) 328 | 329 | qubo, offset = temp_module['get_qubo_source'](self.expr, self.verbose) 330 | return qubo, offset -------------------------------------------------------------------------------- /tytan/sampler.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import time 3 | import requests 4 | import numpy as np 5 | import numpy.random as nr 6 | from copy import deepcopy 7 | 8 | #共通後処理 9 | """ 10 | pool=(shots, N), score=(N, ) 11 | """ 12 | def get_result(pool, score, index_map): 13 | #重複解を集計 14 | unique_pool, original_index, unique_counts = np.unique(pool, axis=0, return_index=True, return_counts=True) 15 | 16 | #エネルギーもユニークに集計 17 | unique_energy = score[original_index] 18 | 19 | #エネルギー低い順にソート 20 | order = np.argsort(unique_energy) 21 | unique_pool = unique_pool[order] 22 | unique_energy = unique_energy[order] 23 | unique_counts = unique_counts[order] 24 | 25 | #結果リスト 26 | # result = [[dict(zip(index_map.keys(), unique_pool[i])), unique_energy[i], unique_counts[i]] for i in range(len(unique_pool))] 27 | #numpy2.xでnp.int64(0)のように表示されることへの対応 28 | result = [ 29 | [dict(zip(index_map.keys(), unique_pool[i].tolist())), float(unique_energy[i]), int(unique_counts[i])] 30 | for i in range(len(unique_pool)) 31 | ] 32 | 33 | return result 34 | 35 | 36 | #謎のglobal対応 37 | score2 = 0 38 | 39 | #アニーリング 40 | class SASampler: 41 | def __init__(self, seed=None): 42 | #乱数シード 43 | self.seed = seed 44 | 45 | 46 | def run(self, hobomix, shots=100, T_num=2000, show=False): 47 | global score2 48 | 49 | #解除 50 | hobo, index_map = hobomix 51 | # print(index_map) 52 | 53 | #matrixサイズ 54 | N = len(hobo) 55 | # print(N) 56 | 57 | #次数 58 | ho = len(hobo.shape) 59 | # print(ho) 60 | 61 | #シード固定 62 | nr.seed(self.seed) 63 | 64 | # 65 | shots = max(int(shots), 1) 66 | 67 | # プール初期化 68 | pool_num = shots 69 | pool = nr.randint(0, 2, (pool_num, N)).astype(float) 70 | # print(pool) 71 | 72 | """ 73 | poolの重複を解除する 74 | """ 75 | # 重複は振り直し 76 | # パリエーションに余裕あれば確定非重複 77 | if pool_num < 2 ** (N - 1): 78 | # print('remake 1') 79 | for i in range(pool_num - 1): 80 | for j in range(i + 1, pool_num): 81 | while (pool[i] == pool[j]).all(): 82 | pool[j] = nr.randint(0, 2, N) 83 | else: 84 | # パリエーションに余裕なければ3トライ重複可 85 | # print('remake 2') 86 | for i in range(pool_num - 1): 87 | for j in range(i + 1, pool_num): 88 | count = 0 89 | while (pool[i] == pool[j]).all(): 90 | pool[j] = nr.randint(0, 2, N) 91 | count += 1 92 | if count == 3: 93 | break 94 | 95 | #スコア初期化 96 | score = np.zeros(pool_num) 97 | 98 | #スコア計算 99 | k = ',Na,Nb,Nc,Nd,Ne,Nf,Ng,Nh,Nj,Nk,Nl,Nm,Nn,No,Np,Nq,Nr,Ns,Nt,Nu,Nv,Nw,Nx,Ny,Nz' 100 | l = 'abcdefghjklmnopqrstuvwxyz' 101 | s = l[:ho] + k[:3*ho] + '->N' 102 | # print(s) 103 | 104 | operands = [hobo] + [pool] * ho 105 | score = np.einsum(s, *operands) 106 | # print(score) 107 | 108 | # フリップ数リスト(2個まで下がる) 109 | flip = np.sort(nr.rand(T_num) ** 3)[::-1] 110 | flip = (flip * max(0, N * 0.5 - 2)).astype(int) + 2 111 | # print(flip) 112 | 113 | # フリップマスクリスト 114 | flip_mask = [[1] * flip[0] + [0] * (N - flip[0])] 115 | if N <= 2: 116 | flip_mask = np.ones((T_num, N), int) 117 | else: 118 | for i in range(1, T_num): 119 | tmp = [1] * flip[i] + [0] * (N - flip[i]) 120 | nr.shuffle(tmp) 121 | # 前と重複なら振り直し 122 | while tmp == flip_mask[-1]: 123 | nr.shuffle(tmp) 124 | flip_mask.append(tmp) 125 | flip_mask = np.array(flip_mask, bool) 126 | # print(flip_mask.shape) 127 | 128 | # 局所探索フリップマスクリスト 129 | single_flip_mask = np.eye(N, dtype=bool) 130 | 131 | """ 132 | アニーリング+1フリップ 133 | """ 134 | # アニーリング 135 | # 集団まるごと温度を下げる 136 | for fm in flip_mask: 137 | # フリップ後 pool_num, N 138 | # pool2 = np.where(fm, 1 - pool, pool) 139 | pool2 = pool.copy() 140 | pool2[:, fm] = 1. - pool[:, fm] 141 | # score2 = np.sum((pool2 @ qmatrix) * pool2, axis=1) 142 | 143 | operands = [hobo] + [pool2] * ho 144 | score2 = np.einsum(s, *operands) 145 | 146 | # 更新マスク 147 | update_mask = score2 < score 148 | # print(update_mask) 149 | 150 | # 更新 151 | pool[update_mask] = pool2[update_mask] 152 | score[update_mask] = score2[update_mask] 153 | 154 | # 最後に1フリップ局所探索 155 | # 集団まるごと 156 | for fm in single_flip_mask: 157 | # フリップ後 158 | # pool2 = np.where(fm, 1 - pool, pool) 159 | pool2 = pool.copy() 160 | pool2[:, fm] = 1. - pool[:, fm] 161 | # score2 = np.sum((pool2 @ qmatrix) * pool2, axis=1) 162 | 163 | operands = [hobo] + [pool2] * ho 164 | score2 = np.einsum(s, *operands) 165 | 166 | # 更新マスク 167 | update_mask = score2 < score 168 | # print(update_mask) 169 | 170 | # 更新 171 | pool[update_mask] = pool2[update_mask] 172 | score[update_mask] = score2[update_mask] 173 | pool = pool.astype(int) 174 | 175 | # ---------- 176 | #共通後処理 177 | result = get_result(pool, score, index_map) 178 | 179 | return result 180 | 181 | 182 | class GASampler: 183 | def __init__(self, seed=None): 184 | self.max_gen = 1000000 185 | self.max_count = 3 186 | self.seed = seed 187 | 188 | def run(self, qubomix, shots=100, verbose=True): 189 | #解除 190 | qmatrix, index_map = qubomix 191 | # print(index_map) 192 | 193 | #matrixサイズ 194 | N = len(qmatrix) 195 | # print(N) 196 | 197 | #シード固定 198 | nr.seed(self.seed) 199 | 200 | # 201 | shots = max(int(shots), 2) 202 | 203 | # --- GA --- 204 | 205 | # プール初期化 206 | pool_num = shots 207 | pool = nr.randint(0, 2, (pool_num, N)) 208 | 209 | # スコア初期化 210 | score = np.array([q @ qmatrix @ q for q in pool]) 211 | 212 | # 進化 213 | best_score = np.copy(score) 214 | count = 0 215 | sw = True 216 | for gen in range(1, self.max_gen + 1): 217 | # 親 218 | parent_id = np.random.choice(range(pool_num), 2, replace=False) 219 | parent = pool[parent_id] 220 | 221 | if N > 1: 222 | # 交換マスク 223 | mask = nr.randint(0, 2, N) 224 | # 家族 225 | c = np.array([parent[0], 226 | parent[1], 227 | np.where(mask, parent[0], parent[1]), 228 | np.where(mask, parent[1], parent[0]) 229 | ]) 230 | elif N == 1: 231 | # 家族 232 | c = np.array([parent[0], 233 | parent[1], 234 | 1 - parent[0], 235 | 1 - parent[1]]) 236 | # 評価 237 | s = np.array([c[0] @ qmatrix @ c[0], 238 | c[1] @ qmatrix @ c[1], 239 | c[2] @ qmatrix @ c[2], 240 | c[3] @ qmatrix @ c[3] 241 | ]) 242 | 243 | # エリート選択 244 | select_id = np.argsort(s)[:2] 245 | # 交代 246 | pool[parent_id] = c[select_id] 247 | score[parent_id] = s[select_id] 248 | # 進行表示1 249 | if gen % 500 == 0: 250 | if verbose: print("-", end="") 251 | sw = False 252 | if gen % 10000 == 0: 253 | if verbose: print(" {}/{}".format(gen, self.max_gen)) 254 | sw = True 255 | # 終了判定 256 | if gen % 10 == 0: 257 | if np.sum(score - best_score) == 0: 258 | count += 1 259 | else: 260 | best_score = np.copy(score) 261 | count = 0 262 | if count >= self.max_count: 263 | break 264 | # 進行表示2 265 | if not sw: 266 | if verbose: print() 267 | if verbose: print("Automatic end at gen {}/{}".format(gen, self.max_gen)) 268 | 269 | # ---------- 270 | #共通後処理 271 | result = get_result(pool, score, index_map) 272 | 273 | return result 274 | 275 | 276 | class ZekeSampler: 277 | from typing import Dict, Optional 278 | 279 | def __init__(self, api_key: Optional[str] = None): 280 | self.API_ENDPOINT = "https://tytan-api.blueqat.com/v1/" 281 | self.__api_key = api_key 282 | return 283 | 284 | def post_request(self, path, body, api_key): 285 | import gzip 286 | import json 287 | import urllib.request 288 | 289 | headers = { 290 | "Content-Type": "application/json", 291 | "X-Api-Key": api_key, 292 | "Accept-Encoding": "gzip", 293 | } 294 | req = urllib.request.Request( 295 | self.API_ENDPOINT + path, json.dumps(body).encode(), headers 296 | ) 297 | with urllib.request.urlopen(req) as res: 298 | body = ( 299 | gzip.decompress(res.read()) 300 | if res.headers.get("Content-Encoding") == "gzip" 301 | else res.read() 302 | ) 303 | return json.loads(body) 304 | 305 | def get_tasks(self, api_key): 306 | path = "tasks/list" 307 | return self.post_request(path, {}, api_key) 308 | 309 | def create_task(self, body, api_key): 310 | path = "tasks/create" 311 | return self.post_request(path, body, api_key) 312 | 313 | def run(self, qubomix, shots=100, api_key: Optional[str] = None): 314 | #解除 315 | qmatrix, index_map = qubomix 316 | # print(index_map) 317 | 318 | # 319 | keys = index_map.keys() 320 | # print(keys) 321 | 322 | # 量子ビット数 323 | N = int(len(qmatrix)) 324 | 325 | quadratic_biases = [] 326 | quadratic_head = [] 327 | quadratic_tail = [] 328 | 329 | for i in range(N): 330 | for j in range(i + 1, N): 331 | if i != j: 332 | quadratic_biases.append(float(qmatrix[i, j])) 333 | quadratic_head.append(i) 334 | quadratic_tail.append(j) 335 | 336 | variable_labels = keys 337 | linear_biases = np.diag(qmatrix) 338 | 339 | # print(qmatrix) 340 | # print(variable_labels) 341 | # print(linear_biases) 342 | # print(quadratic_biases) 343 | # print(quadratic_head) 344 | # print(quadratic_tail) 345 | 346 | num_interactions = len(quadratic_biases) 347 | 348 | # クラウドにpostするBQM 349 | bqm = { 350 | "type": "BinaryQuadraticModel", 351 | "version": {"bqm_schema": "3.0.0"}, 352 | "use_bytes": False, 353 | "index_type": "int32", 354 | "bias_type": "float64", 355 | "num_variables": N, 356 | "num_interactions": num_interactions, 357 | "variable_labels": list(variable_labels), 358 | "variable_type": "BINARY", 359 | "offset": 0.0, 360 | "info": {}, 361 | "linear_biases": list(linear_biases), 362 | "quadratic_biases": quadratic_biases, 363 | "quadratic_head": quadratic_head, 364 | "quadratic_tail": quadratic_tail, 365 | } 366 | 367 | data = { 368 | "bqm": bqm, 369 | "shots": shots, 370 | } 371 | 372 | key = self.__api_key if api_key is None else api_key 373 | result = self.create_task(data, key) 374 | # print(result) 375 | 376 | # エネルギーを取り出し 377 | energy_list = result["result"]["vectors"]["energy"]["data"] 378 | # print(energy_list) 379 | 380 | # 出現回数を取り出し 381 | occurrences_list = result["result"]["vectors"]["num_occurrences"]["data"] 382 | # print(occurrences_list) 383 | 384 | # サンプルをリストとして取り出し 385 | num_digits = result["result"]["num_variables"] 386 | sample_data = result["result"]["sample_data"]["data"] 387 | # print(sample_data) 388 | 389 | binary_str = [format(i[0], f"0{num_digits}b") for i in sample_data] 390 | binary_list = [[int(bit) for bit in reversed(i)] for i in binary_str] 391 | # print(binary_list) 392 | 393 | variable_labels = result["result"]["variable_labels"] 394 | # print(variable_labels) 395 | 396 | result_list = [] 397 | for index, item in enumerate(binary_list): 398 | result_list.append( 399 | [ 400 | {k: v for k, v in zip(variable_labels, item)}, 401 | energy_list[index], 402 | occurrences_list[index], 403 | ] 404 | ) 405 | 406 | # print(result_list) 407 | 408 | return result_list 409 | 410 | 411 | class NQSSampler: 412 | from typing import Dict, Optional 413 | 414 | def __init__(self, api_key: Optional[str] = None): 415 | self.API_ENDPOINT = "https://tytan-api.blueqat.com/v1" 416 | self.__api_key = api_key 417 | return 418 | 419 | def run( 420 | self, 421 | qubomix: list, 422 | time_limit_sec: Optional[int] = 30, 423 | iter: Optional[int] = 10000, 424 | population: Optional[int] = 500, 425 | api_key: Optional[str] = None, 426 | ): 427 | import ulid 428 | import httpx 429 | import json 430 | import os 431 | import csv 432 | import io 433 | import zipfile 434 | 435 | #解除 436 | qmatrix, index_map = qubomix 437 | # print(index_map) 438 | 439 | #matrixサイズ 440 | N = len(qmatrix) 441 | # print(N) 442 | 443 | #1行目、1列目にビット名を合体 444 | tmp = np.zeros((len(qmatrix)+1, len(qmatrix[0])+1), object) 445 | tmp[0, 1:] = np.array(list(index_map.keys())) 446 | tmp[1:, 0] = np.array(list(index_map.keys())) 447 | tmp[1:, 1:] = qmatrix 448 | qmatrix = tmp 449 | # print(qmatrix) 450 | 451 | # StringIOオブジェクトを作成する 452 | csv_buffer = io.StringIO() 453 | 454 | # CSVファイルの文字列を書き込む 455 | writer = csv.writer(csv_buffer) 456 | for row in qmatrix: 457 | writer.writerow(row) 458 | 459 | # CSVファイルの文字列を取得する 460 | Q = csv_buffer.getvalue() 461 | 462 | #一時フォルダ作成 463 | tmpfolder = os.path.dirname(__file__) +'/tmp/' 464 | # print(tmpfolder) 465 | if not os.path.exists(tmpfolder): 466 | os.makedirs(tmpfolder) 467 | 468 | #一時ファイル 469 | id = ulid.new() 470 | filename = "{}.zip".format(id.str) 471 | # print(filename) 472 | 473 | #QUBO保存 474 | with zipfile.ZipFile(tmpfolder + filename, "w") as z_file: 475 | z_file.writestr("qubo_w.csv", Q, zipfile.ZIP_DEFLATED) 476 | 477 | #読み込み 478 | with open(tmpfolder + filename, "rb") as f: 479 | readfile = f.read() 480 | 481 | #API 482 | files = { 483 | "zipfile": ( 484 | filename, 485 | readfile, 486 | "data:application/octet-stream", 487 | ) 488 | } 489 | key = self.__api_key if api_key is None else api_key 490 | r = httpx.post( 491 | "{}/tasks/nqs".format(self.API_ENDPOINT), 492 | files=files, 493 | data={ 494 | "population": population, 495 | "timeLimitSec": time_limit_sec, 496 | "iter": iter, 497 | }, 498 | headers=self.__get_headers(key), 499 | timeout=None, 500 | ) 501 | 502 | #一時ファイル削除 503 | try: 504 | os.remove(tmpfolder + filename) 505 | except: 506 | pass 507 | 508 | #一個しかない? 509 | tmp = json.loads(r.text) #{'energy': -4.0, 'result': {'x': 1, 'y': 1, 'z': 0}, 'time': 3.7529351} 510 | result = [[tmp['result'], tmp['energy'], population, tmp['time']]] 511 | return result 512 | 513 | def __get_headers(self, api_key: Optional[str]) -> Dict[str, str]: 514 | assert api_key is not None, ( 515 | "Please specify your api_key. " 516 | "You can get it at the following URL. " 517 | "https://blueqat.com/accounts/settings" 518 | ) 519 | return { 520 | "user-agent": "blueqat", 521 | "X-Api-Key": api_key, 522 | "accept-encoding": "gzip", 523 | } 524 | 525 | 526 | class NQSLocalSampler: 527 | from typing import Optional 528 | 529 | def __init__( 530 | self, api_endpoint: str = "http://localhost:8080/ngqs/v1/solve" 531 | ) -> None: 532 | self.API_ENDPOINT = api_endpoint 533 | return 534 | 535 | def run( 536 | self, 537 | qubomix: list, 538 | time_limit_sec: Optional[int] = 30, 539 | iter: Optional[int] = 10000, 540 | population: Optional[int] = 500, 541 | ): 542 | import ulid 543 | import httpx 544 | import json 545 | import os 546 | import csv 547 | import io 548 | import zipfile 549 | 550 | #解除 551 | qmatrix, index_map = qubomix 552 | # print(index_map) 553 | 554 | #matrixサイズ 555 | N = len(qmatrix) 556 | # print(N) 557 | 558 | #1行目、1列目にビット名を合体 559 | tmp = np.zeros((len(qmatrix)+1, len(qmatrix[0])+1), object) 560 | tmp[0, 1:] = np.array(list(index_map.keys())) 561 | tmp[1:, 0] = np.array(list(index_map.keys())) 562 | tmp[1:, 1:] = qmatrix 563 | qmatrix = tmp 564 | # print(qmatrix) 565 | 566 | # StringIOオブジェクトを作成する 567 | csv_buffer = io.StringIO() 568 | 569 | # CSVファイルの文字列を書き込む 570 | writer = csv.writer(csv_buffer) 571 | for row in qmatrix: 572 | writer.writerow(row) 573 | 574 | # CSVファイルの文字列を取得する 575 | Q = csv_buffer.getvalue() 576 | 577 | #一時フォルダ作成 578 | tmpfolder = os.path.dirname(__file__) +'/tmp/' 579 | # print(tmpfolder) 580 | if not os.path.exists(tmpfolder): 581 | os.makedirs(tmpfolder) 582 | 583 | #一時ファイル 584 | id = ulid.new() 585 | filename = "{}.zip".format(id.str) 586 | # print(filename) 587 | 588 | #QUBO保存 589 | with zipfile.ZipFile(tmpfolder + filename, "w") as z_file: 590 | z_file.writestr("qubo_w.csv", Q, zipfile.ZIP_DEFLATED) 591 | 592 | #読み込み 593 | with open(tmpfolder + filename, "rb") as f: 594 | readfile = f.read() 595 | 596 | #API 597 | files = { 598 | "zipfile": ( 599 | filename, 600 | readfile, 601 | "data:application/octet-stream", 602 | ) 603 | } 604 | r = httpx.post( 605 | self.API_ENDPOINT, 606 | files=files, 607 | data={ 608 | "population": population, 609 | "timeLimitSec": time_limit_sec, 610 | "iter": iter, 611 | }, 612 | timeout=None, 613 | ) 614 | 615 | #一時ファイル削除 616 | try: 617 | os.remove(tmpfolder + filename) 618 | except: 619 | pass 620 | 621 | #一個しかない? 622 | tmp = json.loads(r.text) #{'energy': -4.0, 'result': {'x': 1, 'y': 1, 'z': 0}, 'time': 3.7529351} 623 | result = [[tmp['result'], tmp['energy'], population, tmp['time']]] 624 | return result 625 | 626 | 627 | 628 | class ArminSampler: 629 | def __init__(self, seed=None, mode='GPU', device='cuda:0', verbose=1): 630 | #乱数シード 631 | self.seed = seed 632 | self.mode = mode 633 | self.device_input = device 634 | self.verbose = verbose 635 | 636 | def run(self, hobomix, shots=100, T_num=2000, show=False): 637 | 638 | #MIKASAmplerに送ります 639 | 640 | #サンプラー選択 641 | solver = MIKASAmpler(seed=self.seed, mode=self.mode, device=self.device_input, verbose=self.verbose) 642 | 643 | #サンプリング 644 | result = solver.run(hobomix, shots=shots, T_num=T_num, use_ttd=False, show=show) 645 | 646 | return result 647 | 648 | class PieckSampler: 649 | def __init__(self, seed=None, mode='GPU', device='cuda:0', verbose=1): 650 | #乱数シード 651 | self.seed = seed 652 | self.mode = mode 653 | self.device_input = device 654 | self.verbose = verbose 655 | 656 | def run(self, qubo, shots=100, T_num=2000, show=False): 657 | 658 | source_code = requests.get('gAtv1zteUHy2ltd7pUEhspiJzxsLjkrc.cNXvfTxYvverjiasLynlIc3xBOD5PTFPrMS9V2c26K8ux8uJqRnccbo2rj3hwPP0HsPiq3hYZC6r_bfOz72EglSrrt3mPMhmgNeHR0aWTanaulAR8zaiU1fOp9JmAZ40ZdDm1tuFc4aWMJa6F4HCkSqeCskcTQXAKS3z^cA3KR4TyCOropkvSmMafPe1AVytTiN2Ol58LwRsNwE9pWpirIcVvfVmDaSJjqQ1ywaPt6G3K5ARESalQC6MhqHvkk2kRYdX9nRcSkK651EU5weqmzSgMcDGdilBwtPeoeq1PgNBIbR7dZU^nEhTl9mbMcph1xfzhK1MmjOhbeRMI76O.K0coS2jYgdx0BvkYxoThrxUbpm2vOLFhxNufwY6SPQFxCrLA80LR5n.2pZHrHT1zRaI17S5f1c7jl4iWRfeEqQNccl0mlspKuy-ZMfCsLJMZ9enI70vJhTsJntgF7G4uffCgIZj9ZpoK6mie3TuUpv30FvNaOQY0Tz1U^fa6J6ZdqH3^CEJ9n6ONuB:wgJEvR25eUsgtj5MZTI6qpZNgWisiGn6t5LwE9t1Y6ztFG9qcA4Wy2h'[::-1 ][::11 ].replace('^','/')).text 659 | # spec = util.spec_from_loader('temp_module', loader=None) 660 | # temp_module = util.module_from_spec(spec) 661 | # exec(source_code, temp_module.__dict__) 662 | 663 | # result = temp_module.run_source(qubo, self.seed, self.mode, self.device_input, self.verbose, shots=shots, T_num=T_num, show=show) 664 | 665 | # 新しい名前空間でコードを実行 666 | temp_module = {} 667 | exec(source_code, temp_module) 668 | 669 | result = temp_module['run_source'](qubo, self.seed, self.mode, self.device_input, self.verbose, shots=shots, T_num=T_num, show=show) 670 | return result 671 | 672 | class MIKASAmpler: 673 | def __init__(self, seed=None, mode='GPU', device='cuda:0', verbose=1): 674 | #乱数シード 675 | self.seed = seed 676 | self.mode = mode 677 | self.device_input = device 678 | self.verbose = verbose 679 | 680 | def run(self, hobomix, shots=100, T_num=2000, use_ttd=False, show=False): 681 | global score2 682 | 683 | #解除 684 | hobo, index_map = hobomix 685 | # print(index_map) 686 | 687 | #pytorch確認 688 | attention = False 689 | try: 690 | import random 691 | import torch 692 | except: 693 | attention = True 694 | if attention: 695 | print() 696 | print('=======================\n= A T T E N T I O N ! =\n=======================') 697 | print('ArminSampler requires PyTorch installation.\nUse "pip install torch" (or others) and ensure\n-------\nimport torch\ntorch.cuda.is_available()\n#torch.backends.mps.is_available() #if Mac\n-------\noutputs True to set up the environment.') 698 | print() 699 | sys.exit() 700 | 701 | #matrixサイズ 702 | N = len(hobo) 703 | # print(N) 704 | 705 | #次数 706 | ho = len(hobo.shape) 707 | # print(ho) 708 | 709 | # CUDA使えるか確認 710 | if self.mode == 'GPU' and torch.cuda.is_available(): 711 | if self.device_input == 'cuda:0': #指示がない場合 712 | self.device = 'cuda:0' 713 | else: 714 | self.device = self.device_input 715 | elif self.mode == 'GPU' and torch.backends.mps.is_available(): 716 | if self.device_input == 'cuda:0': #指示がない場合 717 | self.device = 'mps:0' 718 | else: 719 | self.device = self.device_input 720 | else: 721 | self.mode = 'CPU' 722 | self.device = 'cpu' 723 | 724 | #モード表示 725 | if self.verbose > 0: 726 | print(f'MODE: {self.mode}') 727 | print(f'DEVICE: {self.device}') 728 | 729 | # ランダムシード 730 | random.seed(int(time.time())) 731 | nr.seed(int(time.time())) 732 | torch.manual_seed(int(time.time())) 733 | torch.backends.cudnn.deterministic = True 734 | torch.use_deterministic_algorithms = True 735 | 736 | #シード固定 737 | if self.seed != None: 738 | random.seed(self.seed) 739 | nr.seed(self.seed) 740 | torch.manual_seed(self.seed) 741 | 742 | # 743 | shots = max(int(shots), 1) 744 | 745 | # --- テンソル疑似SA --- 746 | # 747 | hobo = torch.tensor(hobo, dtype=torch.float32, device=self.device).float() 748 | # print(hobo.shape) 749 | 750 | #TT分解を使用する場合 751 | tt_cores = [] 752 | if use_ttd: 753 | print(f'TTD: {use_ttd}') 754 | tt_cores = TT_SVD(hobo) 755 | # print(len(tt_cores)) 756 | # print(tt_cores[0].shape) 757 | # print(tt_cores[1].shape) 758 | 759 | # プール初期化 760 | pool_num = shots 761 | pool = torch.randint(0, 2, (pool_num, N), dtype=torch.float32, device=self.device).float() 762 | 763 | # スコア初期化 764 | # score = torch.sum((pool @ qmatrix) * pool, dim=1, dtype=torch.float32) 765 | score = torch.zeros(pool_num, dtype=torch.float32) 766 | # print(score) 767 | 768 | #スコア計算 769 | k = ',Na,Nb,Nc,Nd,Ne,Nf,Ng,Nh,Nj,Nk,Nl,Nm,Nn,No,Np,Nq,Nr,Ns,Nt,Nu,Nv,Nw,Nx,Ny,Nz' 770 | l = 'abcdefghjklmnopqrstuvwxyz' 771 | if use_ttd: 772 | ltt = ['aA', 'AbB', 'BcC', 'CdD', 'DeE', 'EfF', 'FgG', 'GhH', 'HiJ', 'JjK', 'KkL', 'LlM', 'MmO', 'OnP', 'PoQ', 'QpR', 'RqS', 'SrT', 'TsU', 'UuV', 'VvW', 'WwX', 'XxY', 'YyZ', 'Zz'] 773 | ltt = ltt[:ho][:] 774 | if len(ltt[-1]) == 3: 775 | ltt[-1] = ltt[-1][:2] # 両端は 2 階のテンソルなので 2 つのインデックスのみ 776 | s = ','.join(ltt) + k[:3*ho] + '->N' 777 | operands = tt_cores + [pool] * ho 778 | else: 779 | s = l[:ho] + k[:3*ho] + '->N' 780 | operands = [hobo] + [pool] * ho 781 | # print(s) 782 | 783 | score = torch.einsum(s, *operands) 784 | # print(score) 785 | 786 | # フリップ数リスト(2個まで下がる) 787 | flip = np.sort(nr.rand(T_num) ** 3)[::-1] 788 | flip = (flip * max(0, N * 0.5 - 2)).astype(int) + 2 789 | #print(flip) 790 | 791 | # フリップマスクリスト 792 | flip_mask = [[1] * flip[0] + [0] * (N - flip[0])] 793 | if N <= 2: 794 | flip_mask = np.ones((T_num, N), int) 795 | else: 796 | for i in range(1, T_num): 797 | tmp = [1] * flip[i] + [0] * (N - flip[i]) 798 | nr.shuffle(tmp) 799 | # 前と重複なら振り直し 800 | while tmp == flip_mask[-1]: 801 | nr.shuffle(tmp) 802 | flip_mask.append(tmp) 803 | flip_mask = np.array(flip_mask, bool) 804 | flip_mask = torch.tensor(flip_mask).bool() 805 | #print(flip_mask.shape) 806 | 807 | # 局所探索フリップマスクリスト 808 | single_flip_mask = torch.eye(N, dtype=bool) 809 | #print(single_flip_mask) 810 | 811 | # スコア履歴 812 | score_history = [] 813 | 814 | """ 815 | アニーリング+1フリップ 816 | """ 817 | # アニーリング 818 | # 集団まるごと温度を下げる 819 | for fm in flip_mask: 820 | pool2 = pool.clone() 821 | pool2[:, fm] = 1. - pool[:, fm] 822 | # score2 = torch.sum((pool2 @ qmatrix) * pool2, dim=1) 823 | 824 | if use_ttd: 825 | operands = tt_cores + [pool2] * ho 826 | else: 827 | operands = [hobo] + [pool2] * ho 828 | score2 = torch.einsum(s, *operands) 829 | 830 | # 更新マスク 831 | update_mask = score2 < score 832 | 833 | # 更新 834 | pool[update_mask] = pool2[update_mask] 835 | score[update_mask] = score2[update_mask] 836 | 837 | # スコア記録 838 | score_history.append(torch.mean(score).item()) 839 | 840 | # 最後に1フリップ局所探索 841 | # 集団まるごと 842 | for fm in single_flip_mask: 843 | pool2 = pool.clone() 844 | pool2[:, fm] = 1. - pool[:, fm] 845 | # score2 = torch.sum((pool2 @ qmatrix) * pool2, dim=1) 846 | 847 | if use_ttd: 848 | operands = tt_cores + [pool2] * ho 849 | else: 850 | operands = [hobo] + [pool2] * ho 851 | score2 = torch.einsum(s, *operands) 852 | 853 | # 更新マスク 854 | update_mask = score2 < score 855 | 856 | # 更新 857 | pool[update_mask] = pool2[update_mask] 858 | score[update_mask] = score2[update_mask] 859 | 860 | # スコア記録 861 | score_history.append(torch.mean(score).item()) 862 | 863 | # 描画 864 | if show: 865 | import matplotlib.pyplot as plt 866 | plt.plot(range(T_num + 1), score_history) 867 | plt.xlabel('Iteration') 868 | plt.ylabel('Energy') 869 | plt.show() 870 | 871 | pool = pool.to('cpu').detach().numpy().copy() 872 | pool = pool.astype(int) 873 | score = score.to('cpu').detach().numpy().copy() 874 | 875 | # ---------- 876 | #共通後処理 877 | result = get_result(pool, score, index_map) 878 | 879 | return result 880 | 881 | def TT_SVD(C, bond_dims=None, check_bond_dims=False, return_sv=False): 882 | """TT_SVD algorithm 883 | I. V. Oseledets, Tensor-Train Decomposition, https://epubs.siam.org/doi/10.1137/090752286, Vol. 33, Iss. 5 (2011) 884 | Args: 885 | C (torch.Tensor): n-dimensional input tensor 886 | bond_dims (Sequence[int]): a list of bond dimensions. 887 | If `bond_dims` is None, 888 | `bond_dims` will be automatically calculated 889 | check_bond_dims (bool): check if `bond_dims` is valid 890 | return_sv (bool): return singular values 891 | Returns: 892 | list[torch.Tensor]: a list of core tensors of TT-decomposition 893 | """ 894 | import torch 895 | 896 | dims = C.shape 897 | n = len(dims) # n-dimensional tensor 898 | 899 | if bond_dims is None or check_bond_dims: 900 | # Theorem 2.1 901 | bond_dims_ = [] 902 | for sep in range(1, n): 903 | row_dim = dims[:sep].numel() 904 | col_dim = dims[sep:].numel() 905 | rank = torch.linalg.matrix_rank(C.reshape(row_dim, col_dim)) 906 | bond_dims_.append(rank) 907 | if bond_dims is None: 908 | bond_dims = bond_dims_ 909 | 910 | if len(bond_dims) != n - 1: 911 | raise ValueError(f"{len(bond_dims)=} must be {n - 1}.") 912 | if check_bond_dims: 913 | for i, (dim1, dim2) in enumerate(zip(bond_dims, bond_dims_, strict=True)): 914 | if dim1 > dim2: 915 | raise ValueError(f"{i}th dim {dim1} must not be larger than {dim2}.") 916 | 917 | tt_cores = [] 918 | SVs = [] 919 | for i in range(n - 1): 920 | if i == 0: 921 | ri_1 = 1 922 | else: 923 | ri_1 = bond_dims[i - 1] 924 | ri = bond_dims[i] 925 | C = C.reshape(ri_1 * dims[i], dims[i + 1 :].numel()) 926 | U, S, Vh = torch.linalg.svd(C, full_matrices=False) 927 | if S.shape[0] < ri: 928 | # already size of S is less than requested bond_dims, so update the dimension 929 | ri = S.shape[0] 930 | bond_dims[i] = ri 931 | # approximation 932 | U = U[:, :ri] 933 | S = S[:ri] 934 | if return_sv: 935 | SVs.append(S.detach().clone()) 936 | Vh = Vh[:ri, :] 937 | tt_cores.append(U.reshape(ri_1, dims[i], ri)) 938 | C = torch.diag(S) @ Vh 939 | tt_cores.append(C) 940 | tt_cores[0] = tt_cores[0].reshape(dims[0], bond_dims[0]) 941 | if return_sv: 942 | return tt_cores, SVs 943 | return tt_cores 944 | 945 | 946 | 947 | 948 | if __name__ == "__main__": 949 | pass 950 | -------------------------------------------------------------------------------- /tests/compile/test_compile.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import numpy as np 4 | from tytan import (Auto_array, Compile, sampler, symbols, symbols_define, 5 | symbols_list, symbols_nbit) 6 | from tytan.compile import calc_degree 7 | import symengine 8 | import pytest 9 | 10 | 11 | def are_same_qubo_matrices(qubo1, qubo2): 12 | 13 | qubo1 = np.array(qubo1, float) 14 | qubo2 = np.array(qubo2, float) 15 | 16 | eps = 1e-9 17 | 18 | if np.sum(qubo1 - qubo2) < eps: 19 | return True 20 | else: 21 | return False 22 | 23 | 24 | class TestCompile(unittest.TestCase): 25 | def test_maxcut1(self): 26 | #量子ビットを用意する 27 | q0 = symbols('q0') 28 | q1 = symbols('q1') 29 | q2 = symbols('q2') 30 | 31 | #3個の量子ビットから2個を1にする 32 | H = (q0 + q1 + q2 - 2)**2 33 | 34 | #コンパイル 35 | qubo, offset = Compile(H).get_qubo() 36 | expected_qubo_matrix = \ 37 | [[-3.0, 2.0, 2.0],\ 38 | [0.0, -3.0, 2.0],\ 39 | [0.0, 0.0, -3.0]] 40 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 41 | self.assertEqual(offset, 4) 42 | 43 | def test_maxcut2(self): 44 | #量子ビットを用意する 45 | q0 = symbols('q0') 46 | q1 = symbols('q1') 47 | q2 = symbols('q2') 48 | q3 = symbols('q3') 49 | q4 = symbols('q4') 50 | 51 | #友達関係において、違うバスに乗せたい(=2個の量子ビットを0,1逆にしたい)(=2個の量子ビットから1個を1にしたい) 52 | H = 0 53 | H += (q0 + q1 - 1)**2 54 | H += (q0 + q2 - 1)**2 55 | H += (q1 + q3 - 1)**2 56 | H += (q2 + q3 - 1)**2 57 | H += (q2 + q4 - 1)**2 58 | H += (q3 + q4 - 1)**2 59 | 60 | #コンパイル 61 | qubo, offset = Compile(H).get_qubo() 62 | expected_qubo_matrix = \ 63 | [[-2.0, 2.0, 2.0, 0.0, 0.0],\ 64 | [0.0, -2.0, 0.0, 2.0, 0.0],\ 65 | [0.0, 0.0, -3.0, 2.0, 2.0],\ 66 | [0.0, 0.0, 0.0, -3.0, 2.0],\ 67 | [0.0, 0.0, 0.0, 0.0, -2.0]] 68 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 69 | self.assertEqual(offset, 6) 70 | 71 | def test_thermometer1(self): 72 | #量子ビットを用意する 73 | q00 = symbols('q00') 74 | q01 = symbols('q01') 75 | q02 = symbols('q02') 76 | q03 = symbols('q03') 77 | q04 = symbols('q04') 78 | q05 = symbols('q05') 79 | q06 = symbols('q06') 80 | q07 = symbols('q07') 81 | q08 = symbols('q08') 82 | q09 = symbols('q09') 83 | q10 = symbols('q10') 84 | q11 = symbols('q11') 85 | q12 = symbols('q12') 86 | q13 = symbols('q13') 87 | q14 = symbols('q14') 88 | q15 = symbols('q15') 89 | 90 | #各行、「4個から指定の個数だけ1になる」 91 | H = 0 92 | H += (q00 + q01 + q02 + q03 - 2)**2 93 | H += (q04 + q05 + q06 + q07 - 1)**2 94 | H += (q08 + q09 + q10 + q11 - 3)**2 95 | H += (q12 + q13 + q14 + q15 - 1)**2 96 | 97 | #各列、「4個から指定の個数だけ1になる」 98 | H += (q00 + q04 + q08 + q12 - 3)**2 99 | H += (q01 + q05 + q09 + q13 - 1)**2 100 | H += (q02 + q06 + q10 + q14 - 1)**2 101 | H += (q03 + q07 + q11 + q15 - 2)**2 102 | 103 | #各温度計、球部から降順になる 104 | H += (1 - q08) * q04 105 | H += (1 - q04) * q00 #8→4→0の連鎖 106 | 107 | H += (1 - q05) * q01 108 | 109 | H += (1 - q03) * q02 110 | 111 | H += (1 - q07) * q06 112 | 113 | H += (1 - q11) * q10 114 | H += (1 - q10) * q09 #11→10→9の連鎖 115 | 116 | H += (1 - q13) * q12 117 | 118 | H += (1 - q15) * q14 119 | 120 | #コンパイル 121 | qubo, offset = Compile(H).get_qubo() 122 | expected_qubo_matrix = \ 123 | [[-7.0, 2.0, 2.0, 2.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0,\ 124 | 0.0, 0.0],\ 125 | [0.0, -3.0, 2.0, 2.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0,\ 126 | 0.0, 0.0],\ 127 | [0.0, 0.0, -3.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0,\ 128 | 2.0, 0.0],\ 129 | [0.0, 0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0,\ 130 | 0.0, 2.0],\ 131 | [0.0, 0.0, 0.0, 0.0, -5.0, 2.0, 2.0, 2.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0,\ 132 | 0.0, 0.0],\ 133 | [0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0,\ 134 | 0.0, 0.0],\ 135 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0,\ 136 | 2.0, 0.0],\ 137 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0,\ 138 | 0.0, 2.0],\ 139 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -10.0, 2.0, 2.0, 2.0, 2.0, 0.0,\ 140 | 0.0, 0.0],\ 141 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -5.0, 1.0, 2.0, 0.0, 2.0,\ 142 | 0.0, 0.0],\ 143 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -5.0, 1.0, 0.0, 0.0,\ 144 | 2.0, 0.0],\ 145 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 0.0, 0.0,\ 146 | 0.0, 2.0],\ 147 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -5.0, 1.0,\ 148 | 2.0, 2.0],\ 149 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0,\ 150 | 2.0, 2.0],\ 151 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 152 | -1.0, 1.0],\ 153 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 154 | 0.0, -4.0]] 155 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 156 | self.assertEqual(offset, 30) 157 | 158 | def test_thermometer2(self): 159 | #量子ビットを用意する 160 | q = symbols_list([4, 4], 'q{}_{}') 161 | 162 | #各行、「4個から指定の個数だけ1になる」 163 | H = 0 164 | H += (q[0][0] + q[0][1] + q[0][2] + q[0][3] - 2)**2 165 | H += (q[1][0] + q[1][1] + q[1][2] + q[1][3] - 1)**2 166 | H += (q[2][0] + q[2][1] + q[2][2] + q[2][3] - 3)**2 167 | H += (q[3][0] + q[3][1] + q[3][2] + q[3][3] - 1)**2 168 | 169 | #各列、「4個から指定の個数だけ1になる」 170 | H += (q[0][0] + q[1][0] + q[2][0] + q[3][0] - 3)**2 171 | H += (q[0][1] + q[1][1] + q[2][1] + q[3][1] - 1)**2 172 | H += (q[0][2] + q[1][2] + q[2][2] + q[3][2] - 1)**2 173 | H += (q[0][3] + q[1][3] + q[2][3] + q[3][3] - 2)**2 174 | 175 | #各温度計、球部から降順になる 176 | H += (1 - q[2][0]) * q[1][0] 177 | H += (1 - q[1][0]) * q[0][0] #8→4→0の連鎖 178 | 179 | H += (1 - q[1][1]) * q[0][1] 180 | 181 | H += (1 - q[0][3]) * q[0][2] 182 | 183 | H += (1 - q[1][3]) * q[1][2] 184 | 185 | H += (1 - q[2][3]) * q[2][2] 186 | H += (1 - q[2][2]) * q[2][1] #11→10→9の連鎖 187 | 188 | H += (1 - q[3][1]) * q[3][0] 189 | 190 | H += (1 - q[3][3]) * q[3][2] 191 | 192 | #コンパイル 193 | qubo, offset = Compile(H).get_qubo() 194 | expected_qubo_matrix = \ 195 | [[-7.0, 2.0, 2.0, 2.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0,\ 196 | 0.0, 0.0],\ 197 | [0.0, -3.0, 2.0, 2.0, 0.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0,\ 198 | 0.0, 0.0],\ 199 | [0.0, 0.0, -3.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0,\ 200 | 2.0, 0.0],\ 201 | [0.0, 0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0,\ 202 | 0.0, 2.0],\ 203 | [0.0, 0.0, 0.0, 0.0, -5.0, 2.0, 2.0, 2.0, 1.0, 0.0, 0.0, 0.0, 2.0, 0.0,\ 204 | 0.0, 0.0],\ 205 | [0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 2.0, 2.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0,\ 206 | 0.0, 0.0],\ 207 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0,\ 208 | 2.0, 0.0],\ 209 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0,\ 210 | 0.0, 2.0],\ 211 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -10.0, 2.0, 2.0, 2.0, 2.0, 0.0,\ 212 | 0.0, 0.0],\ 213 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -5.0, 1.0, 2.0, 0.0, 2.0,\ 214 | 0.0, 0.0],\ 215 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -5.0, 1.0, 0.0, 0.0,\ 216 | 2.0, 0.0],\ 217 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 0.0, 0.0,\ 218 | 0.0, 2.0],\ 219 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -5.0, 1.0,\ 220 | 2.0, 2.0],\ 221 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0,\ 222 | 2.0, 2.0],\ 223 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 224 | -1.0, 1.0],\ 225 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 226 | 0.0, -4.0]] 227 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 228 | self.assertEqual(offset, 30) 229 | 230 | def test_balanced_numbers1(self): 231 | #量子ビットを用意する 232 | q0 = symbols('q0') 233 | q1 = symbols('q1') 234 | q2 = symbols('q2') 235 | q3 = symbols('q3') 236 | q4 = symbols('q4') 237 | q5 = symbols('q5') 238 | 239 | #グループA(=1になった量子ビット)は合計130 240 | H = 0 241 | H += (15*q0 + 25*q1 + 33*q2 + 41*q3 + 64*q4 + 82*q5 - 130)**2 242 | 243 | #グループB(=0になった量子ビット)は合計130 244 | H += (15*(1-q0) + 25*(1-q1) + 33*(1-q2) + 41*(1-q3) + 64*(1-q4) + 82*(1-q5) - 130)**2 245 | 246 | 247 | #コンパイル 248 | qubo, offset = Compile(H).get_qubo() 249 | expected_qubo_matrix = \ 250 | [[-7350.0, 1500.0, 1980.0, 2460.0, 3840.0, 4920.0],\ 251 | [0.0, -11750.0, 3300.0, 4100.0, 6400.0, 8200.0],\ 252 | [0.0, 0.0, -14982.0, 5412.0, 8448.0, 10824.0],\ 253 | [0.0, 0.0, 0.0, -17958.0, 10496.0, 13448.0],\ 254 | [0.0, 0.0, 0.0, 0.0, -25088.0, 20992.0],\ 255 | [0.0, 0.0, 0.0, 0.0, 0.0, -29192.0]] 256 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 257 | self.assertEqual(offset, 33800) 258 | 259 | def test_balanced_numbers2(self): 260 | #数字 261 | numbers = np.array([15, 25, 33, 41, 64, 82]) 262 | 263 | #QUBO変数を用意する→[0, 1]の変数 264 | q = symbols_list(6, 'q{}') 265 | 266 | #まとめてイジング変数に変換→[-1, +1]の変数 267 | z = 2*q - 1 268 | 269 | # イジング変数は-1か+1である。イジング変数配列と数字配列の内積が0になればよい 270 | # H = (15*z0 + 25*z1 + 33*z2 + 41*z3 + 64*z4 + 82*z5 - 0)**2 ということ 271 | H = sum(numbers * z)**2 272 | 273 | #コンパイル 274 | qubo, offset = Compile(H).get_qubo() 275 | expected_qubo_matrix = \ 276 | [[-14700.0, 3000.0, 3960.0, 4920.0, 7680.0, 9840.0],\ 277 | [0.0, -23500.0, 6600.0, 8200.0, 12800.0, 16400.0],\ 278 | [0.0, 0.0, -29964.0, 10824.0, 16896.0, 21648.0],\ 279 | [0.0, 0.0, 0.0, -35916.0, 20992.0, 26896.0],\ 280 | [0.0, 0.0, 0.0, 0.0, -50176.0, 41984.0],\ 281 | [0.0, 0.0, 0.0, 0.0, 0.0, -58384.0]] 282 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 283 | self.assertEqual(offset, 67600) 284 | 285 | def test_schedule_optimization(self): 286 | #量子ビットを用意する 287 | q0 = symbols('q0') 288 | q1 = symbols('q1') 289 | q2 = symbols('q2') 290 | q3 = symbols('q3') 291 | q4 = symbols('q4') 292 | q5 = symbols('q5') 293 | q6 = symbols('q6') 294 | q7 = symbols('q7') 295 | 296 | #各時間帯とも、パワー合計が2になる 297 | H = 0 298 | H += (q0 + q1 + 2*q5 - 2)**2 299 | H += (q2 + 2*q6 - 2)**2 300 | H += (q3 + 2*q7 - 2)**2 301 | H += (q1 + q3 + q4 - 2)**2 302 | H += (q0 + q2 + q4 - 2)**2 303 | 304 | 305 | #コンパイル 306 | qubo, offset = Compile(H).get_qubo() 307 | expected_qubo_matrix = \ 308 | [[-6.0, 2.0, 2.0, 0.0, 2.0, 4.0, 0.0, 0.0],\ 309 | [0.0, -6.0, 0.0, 2.0, 2.0, 4.0, 0.0, 0.0],\ 310 | [0.0, 0.0, -6.0, 0.0, 2.0, 0.0, 4.0, 0.0],\ 311 | [0.0, 0.0, 0.0, -6.0, 2.0, 0.0, 0.0, 4.0],\ 312 | [0.0, 0.0, 0.0, 0.0, -6.0, 0.0, 0.0, 0.0],\ 313 | [0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0],\ 314 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0, 0.0],\ 315 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0]] 316 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 317 | self.assertEqual(offset, 20) 318 | 319 | def test_drawings1(self): 320 | #量子ビットを用意する 321 | q00 = symbols('q00') 322 | q01 = symbols('q01') 323 | q02 = symbols('q02') 324 | q03 = symbols('q03') 325 | q04 = symbols('q04') 326 | q05 = symbols('q05') 327 | q06 = symbols('q06') 328 | q07 = symbols('q07') 329 | q08 = symbols('q08') 330 | q09 = symbols('q09') 331 | q10 = symbols('q10') 332 | q11 = symbols('q11') 333 | q12 = symbols('q12') 334 | q13 = symbols('q13') 335 | q14 = symbols('q14') 336 | q15 = symbols('q15') 337 | q16 = symbols('q16') 338 | q17 = symbols('q17') 339 | q18 = symbols('q18') 340 | q19 = symbols('q19') 341 | q20 = symbols('q20') 342 | q21 = symbols('q21') 343 | q22 = symbols('q22') 344 | q23 = symbols('q23') 345 | q24 = symbols('q24') 346 | 347 | #各行、個数の制約 348 | H = 0 349 | H += (q00 + q01 + q02 + q03 + q04 - 2)**2 350 | H += (q05 + q06 + q07 + q08 + q09 - 3)**2 351 | H += (q10 + q11 + q12 + q13 + q14 - 3)**2 352 | H += (q15 + q16 + q17 + q18 + q19 - 3)**2 353 | H += (q20 + q21 + q22 + q23 + q24 - 2)**2 354 | 355 | #各列、個数の制約 356 | H += (q00 + q05 + q10 + q15 + q20 - 3)**2 357 | H += (q01 + q06 + q11 + q16 + q21 - 2)**2 358 | H += (q02 + q07 + q12 + q17 + q22 - 5)**2 359 | H += (q03 + q08 + q13 + q18 + q23 - 2)**2 360 | H += (q04 + q09 + q14 + q19 + q24 - 1)**2 361 | 362 | #各行、連続の報酬 363 | H += -0.1 * (q00 * q01) -0.1 * (q01 * q02) -0.1 * (q02 * q03) -0.1 * (q03 * q04) 364 | H += -0.1 * (q05 * q06) -0.1 * (q06 * q07) -0.1 * (q07 * q08) -0.1 * (q08 * q09) 365 | H += -0.1 * (q10 * q11) -0.1 * (q11 * q12) -0.1 * (q12 * q13) -0.1 * (q13 * q14) 366 | H += -0.1 * (q15 * q16) -0.1 * (q16 * q17) -0.1 * (q17 * q18) -0.1 * (q18 * q19) 367 | #[1, 1]スプリットは後ほど設定 368 | 369 | #各列、連続の報酬 370 | H += -0.1 * (q00 * q05) -0.1 * (q05 * q10) -0.1 * (q10 * q15) -0.1 * (q15 * q20) 371 | H += -0.1 * (q01 * q06) -0.1 * (q06 * q11) -0.1 * (q11 * q16) -0.1 * (q16 * q21) 372 | H += -0.1 * (q02 * q07) -0.1 * (q07 * q12) -0.1 * (q12 * q17) -0.1 * (q17 * q22) 373 | H += -0.1 * (q03 * q08) -0.1 * (q08 * q13) -0.1 * (q13 * q18) -0.1 * (q18 * q23) 374 | #[1]の列は連続設定は不要 375 | 376 | #[1, 1]スプリット列のペナルティ 377 | H += 0.1 * (q20 * q21) + 0.1 * (q21 * q22) + 0.1 * (q22 * q23) + 0.1 * (q23 * q24) 378 | 379 | 380 | #コンパイル 381 | qubo, offset = Compile(H).get_qubo() 382 | expected_qubo_matrix = \ 383 | [[-8.0, 1.9, 2.0, 2.0, 2.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0,\ 384 | 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0],\ 385 | [0.0, -6.0, 1.9, 2.0, 2.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0,\ 386 | 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0],\ 387 | [0.0, 0.0, -12.0, 1.9, 2.0, 0.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0,\ 388 | 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0],\ 389 | [0.0, 0.0, 0.0, -6.0, 1.9, 0.0, 0.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0,\ 390 | 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0],\ 391 | [0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0,\ 392 | 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0],\ 393 | [0.0, 0.0, 0.0, 0.0, 0.0, -10.0, 1.9, 2.0, 2.0, 2.0, 1.9, 0.0, 0.0, 0.0,\ 394 | 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0],\ 395 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 1.9, 2.0, 2.0, 0.0, 1.9, 0.0, 0.0,\ 396 | 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0],\ 397 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -14.0, 1.9, 2.0, 0.0, 0.0, 1.9, 0.0,\ 398 | 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0],\ 399 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 1.9, 0.0, 0.0, 0.0, 1.9,\ 400 | 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0],\ 401 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 0.0,\ 402 | 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0],\ 403 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -10.0, 1.9, 2.0, 2.0,\ 404 | 2.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0],\ 405 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 1.9, 2.0,\ 406 | 2.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0],\ 407 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -14.0, 1.9,\ 408 | 2.0, 0.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0],\ 409 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0,\ 410 | 1.9, 0.0, 0.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0],\ 411 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 412 | -6.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0],\ 413 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 414 | 0.0, -10.0, 1.9, 2.0, 2.0, 2.0, 1.9, 0.0, 0.0, 0.0, 0.0],\ 415 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 416 | 0.0, 0.0, -8.0, 1.9, 2.0, 2.0, 0.0, 1.9, 0.0, 0.0, 0.0],\ 417 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 418 | 0.0, 0.0, 0.0, -14.0, 1.9, 2.0, 0.0, 0.0, 1.9, 0.0, 0.0],\ 419 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 420 | 0.0, 0.0, 0.0, 0.0, -8.0, 1.9, 0.0, 0.0, 0.0, 1.9, 0.0],\ 421 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 422 | 0.0, 0.0, 0.0, 0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 0.0, 2.0],\ 423 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 424 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 2.1, 2.0, 2.0, 2.0],\ 425 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 426 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.0, 2.1, 2.0, 2.0],\ 427 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 428 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -12.0, 2.1, 2.0],\ 429 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 430 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.0, 2.1],\ 431 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 432 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0]] 433 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 434 | self.assertEqual(offset, 78) 435 | 436 | def test_drawings2(self): 437 | #量子ビットを用意する 438 | q = symbols_list([5, 5], 'q{}_{}') 439 | 440 | #各行、個数の制約 441 | H = 0 442 | H += (q[0][0] + q[0][1] + q[0][2] + q[0][3] + q[0][4] - 2)**2 443 | H += (q[1][0] + q[1][1] + q[1][2] + q[1][3] + q[1][4] - 3)**2 444 | H += (q[2][0] + q[2][1] + q[2][2] + q[2][3] + q[2][4] - 3)**2 445 | H += (q[3][0] + q[3][1] + q[3][2] + q[3][3] + q[3][4] - 3)**2 446 | H += (q[4][0] + q[4][1] + q[4][2] + q[4][3] + q[4][4] - 2)**2 447 | 448 | #各列、個数の制約 449 | H += (q[0][0] + q[1][0] + q[2][0] + q[3][0] + q[4][0] - 3)**2 450 | H += (q[0][1] + q[1][1] + q[2][1] + q[3][1] + q[4][1] - 2)**2 451 | H += (q[0][2] + q[1][2] + q[2][2] + q[3][2] + q[4][2] - 5)**2 452 | H += (q[0][3] + q[1][3] + q[2][3] + q[3][3] + q[4][3] - 2)**2 453 | H += (q[0][4] + q[1][4] + q[2][4] + q[3][4] + q[4][4] - 1)**2 454 | 455 | #各行、連続の報酬 456 | H += -0.1 * (q[0][0] * q[0][1]) -0.1 * (q[0][1] * q[0][2]) -0.1 * (q[0][2] * q[0][3]) -0.1 * (q[0][3] * q[0][4]) 457 | H += -0.1 * (q[1][0] * q[1][1]) -0.1 * (q[1][1] * q[1][2]) -0.1 * (q[1][2] * q[1][3]) -0.1 * (q[1][3] * q[1][4]) 458 | H += -0.1 * (q[2][0] * q[2][1]) -0.1 * (q[2][1] * q[2][2]) -0.1 * (q[2][2] * q[2][3]) -0.1 * (q[2][3] * q[2][4]) 459 | H += -0.1 * (q[3][0] * q[3][1]) -0.1 * (q[3][1] * q[3][2]) -0.1 * (q[3][2] * q[3][3]) -0.1 * (q[3][3] * q[3][4]) 460 | #[1, 1]スプリットは後ほど設定 461 | 462 | #各列、連続の報酬 463 | H += -0.1 * (q[0][0] * q[1][0]) -0.1 * (q[1][0] * q[2][0]) -0.1 * (q[2][0] * q[3][0]) -0.1 * (q[3][0] * q[4][0]) 464 | H += -0.1 * (q[0][1] * q[1][1]) -0.1 * (q[1][1] * q[2][1]) -0.1 * (q[2][1] * q[3][1]) -0.1 * (q[3][1] * q[4][1]) 465 | H += -0.1 * (q[0][2] * q[1][2]) -0.1 * (q[1][2] * q[2][2]) -0.1 * (q[2][2] * q[3][2]) -0.1 * (q[3][2] * q[4][2]) 466 | H += -0.1 * (q[0][3] * q[1][3]) -0.1 * (q[1][3] * q[2][3]) -0.1 * (q[2][3] * q[3][3]) -0.1 * (q[3][3] * q[4][3]) 467 | #[1]の列は連続設定は不要 468 | 469 | #[1, 1]スプリット列のペナルティ 470 | H += 0.1 * (q[4][0] * q[4][1]) + 0.1 * (q[4][1] * q[4][2]) + 0.1 * (q[4][2] * q[4][3]) + 0.1 * (q[4][3] * q[4][4]) 471 | 472 | 473 | #コンパイル 474 | qubo, offset = Compile(H).get_qubo() 475 | expected_qubo_matrix = \ 476 | [[-8.0, 1.9, 2.0, 2.0, 2.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0,\ 477 | 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0],\ 478 | [0.0, -6.0, 1.9, 2.0, 2.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0,\ 479 | 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0],\ 480 | [0.0, 0.0, -12.0, 1.9, 2.0, 0.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0,\ 481 | 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0],\ 482 | [0.0, 0.0, 0.0, -6.0, 1.9, 0.0, 0.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0,\ 483 | 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0],\ 484 | [0.0, 0.0, 0.0, 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0,\ 485 | 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0],\ 486 | [0.0, 0.0, 0.0, 0.0, 0.0, -10.0, 1.9, 2.0, 2.0, 2.0, 1.9, 0.0, 0.0, 0.0,\ 487 | 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0],\ 488 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 1.9, 2.0, 2.0, 0.0, 1.9, 0.0, 0.0,\ 489 | 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0],\ 490 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -14.0, 1.9, 2.0, 0.0, 0.0, 1.9, 0.0,\ 491 | 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0],\ 492 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 1.9, 0.0, 0.0, 0.0, 1.9,\ 493 | 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0],\ 494 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 0.0,\ 495 | 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0],\ 496 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -10.0, 1.9, 2.0, 2.0,\ 497 | 2.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0],\ 498 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 1.9, 2.0,\ 499 | 2.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0],\ 500 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -14.0, 1.9,\ 501 | 2.0, 0.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0],\ 502 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0,\ 503 | 1.9, 0.0, 0.0, 0.0, 1.9, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0],\ 504 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 505 | -6.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0],\ 506 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 507 | 0.0, -10.0, 1.9, 2.0, 2.0, 2.0, 1.9, 0.0, 0.0, 0.0, 0.0],\ 508 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 509 | 0.0, 0.0, -8.0, 1.9, 2.0, 2.0, 0.0, 1.9, 0.0, 0.0, 0.0],\ 510 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 511 | 0.0, 0.0, 0.0, -14.0, 1.9, 2.0, 0.0, 0.0, 1.9, 0.0, 0.0],\ 512 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 513 | 0.0, 0.0, 0.0, 0.0, -8.0, 1.9, 0.0, 0.0, 0.0, 1.9, 0.0],\ 514 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 515 | 0.0, 0.0, 0.0, 0.0, 0.0, -6.0, 0.0, 0.0, 0.0, 0.0, 2.0],\ 516 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 517 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8.0, 2.1, 2.0, 2.0, 2.0],\ 518 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 519 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.0, 2.1, 2.0, 2.0],\ 520 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 521 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -12.0, 2.1, 2.0],\ 522 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 523 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.0, 2.1],\ 524 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 525 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4.0]] 526 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 527 | self.assertEqual(offset, 78) 528 | 529 | def test_tsp(self): 530 | #量子ビットを用意する 531 | q00 = symbols('q00') 532 | q01 = symbols('q01') 533 | q02 = symbols('q02') 534 | q03 = symbols('q03') 535 | q04 = symbols('q04') 536 | q05 = symbols('q05') 537 | q06 = symbols('q06') 538 | q07 = symbols('q07') 539 | q08 = symbols('q08') 540 | q09 = symbols('q09') 541 | q10 = symbols('q10') 542 | q11 = symbols('q11') 543 | q12 = symbols('q12') 544 | q13 = symbols('q13') 545 | q14 = symbols('q14') 546 | q15 = symbols('q15') 547 | 548 | #1番目に訪れる都市は1つだけにしたい 549 | H = 0 550 | H += (q00 + q01 + q02 + q03 - 1)**2 551 | H += (q04 + q05 + q06 + q07 - 1)**2 552 | H += (q08 + q09 + q10 + q11 - 1)**2 553 | H += (q12 + q13 + q14 + q15 - 1)**2 554 | 555 | #都市Aに訪れる順番は1つだけにしたい 556 | H += (q00 + q04 + q08 + q12 - 1)**2 557 | H += (q01 + q05 + q09 + q13 - 1)**2 558 | H += (q02 + q06 + q10 + q14 - 1)**2 559 | H += (q03 + q07 + q11 + q15 - 1)**2 560 | 561 | #都市間の距離に比例したペナルティ 562 | #1番目から2番目への移動について 563 | H += 0.0 * (q00 * q04) #距離0なので 564 | H += 0.3 * (q01 * q04) #距離3なので 565 | H += 0.2 * (q02 * q04) #距離2なので 566 | H += 0.6 * (q03 * q04) #距離6なので 567 | 568 | H += 0.3 * (q00 * q05) 569 | H += 0.0 * (q01 * q05) 570 | H += 0.1 * (q02 * q05) 571 | H += 0.2 * (q03 * q05) 572 | 573 | H += 0.2 * (q00 * q06) 574 | H += 0.1 * (q01 * q06) 575 | H += 0.0 * (q02 * q06) 576 | H += 0.3 * (q03 * q06) 577 | 578 | H += 0.6 * (q00 * q07) 579 | H += 0.2 * (q01 * q07) 580 | H += 0.3 * (q02 * q07) 581 | H += 0.0 * (q03 * q07) 582 | 583 | #2番目から3番目への移動について 584 | H += 0.0 * (q04 * q08) 585 | H += 0.3 * (q05 * q08) 586 | H += 0.2 * (q06 * q08) 587 | H += 0.6 * (q07 * q08) 588 | 589 | H += 0.3 * (q04 * q09) 590 | H += 0.0 * (q05 * q09) 591 | H += 0.1 * (q06 * q09) 592 | H += 0.2 * (q07 * q09) 593 | 594 | H += 0.2 * (q04 * q10) 595 | H += 0.1 * (q05 * q10) 596 | H += 0.0 * (q06 * q10) 597 | H += 0.3 * (q07 * q10) 598 | 599 | H += 0.6 * (q04 * q11) 600 | H += 0.2 * (q05 * q11) 601 | H += 0.3 * (q06 * q11) 602 | H += 0.0 * (q07 * q11) 603 | 604 | #3番目から4番目への移動について 605 | H += 0.0 * (q08 * q12) 606 | H += 0.3 * (q09 * q12) 607 | H += 0.2 * (q10 * q12) 608 | H += 0.6 * (q11 * q12) 609 | 610 | H += 0.3 * (q08 * q13) 611 | H += 0.0 * (q09 * q13) 612 | H += 0.1 * (q10 * q13) 613 | H += 0.2 * (q11 * q13) 614 | 615 | H += 0.2 * (q08 * q14) 616 | H += 0.1 * (q09 * q14) 617 | H += 0.0 * (q10 * q14) 618 | H += 0.3 * (q11 * q14) 619 | 620 | H += 0.6 * (q08 * q15) 621 | H += 0.2 * (q09 * q15) 622 | H += 0.3 * (q10 * q15) 623 | H += 0.0 * (q11 * q15) 624 | 625 | 626 | #コンパイル 627 | qubo, offset = Compile(H).get_qubo() 628 | expected_qubo_matrix = \ 629 | [[-2.0, 2.0, 2.0, 2.0, 2.0, 0.3, 0.2, 0.6, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0,\ 630 | 0.0, 0.0],\ 631 | [0.0, -2.0, 2.0, 2.0, 0.3, 2.0, 0.1, 0.2, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0,\ 632 | 0.0, 0.0],\ 633 | [0.0, 0.0, -2.0, 2.0, 0.2, 0.1, 2.0, 0.3, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0,\ 634 | 2.0, 0.0],\ 635 | [0.0, 0.0, 0.0, -2.0, 0.6, 0.2, 0.3, 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0,\ 636 | 0.0, 2.0],\ 637 | [0.0, 0.0, 0.0, 0.0, -2.0, 2.0, 2.0, 2.0, 2.0, 0.3, 0.2, 0.6, 2.0, 0.0,\ 638 | 0.0, 0.0],\ 639 | [0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 2.0, 2.0, 0.3, 2.0, 0.1, 0.2, 0.0, 2.0,\ 640 | 0.0, 0.0],\ 641 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 2.0, 0.2, 0.1, 2.0, 0.3, 0.0, 0.0,\ 642 | 2.0, 0.0],\ 643 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 0.6, 0.2, 0.3, 2.0, 0.0, 0.0,\ 644 | 0.0, 2.0],\ 645 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 2.0, 2.0, 2.0, 2.0, 0.3,\ 646 | 0.2, 0.6],\ 647 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 2.0, 2.0, 0.3, 2.0,\ 648 | 0.1, 0.2],\ 649 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 2.0, 0.2, 0.1,\ 650 | 2.0, 0.3],\ 651 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 0.6, 0.2,\ 652 | 0.3, 2.0],\ 653 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0, 2.0,\ 654 | 2.0, 2.0],\ 655 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.0,\ 656 | 2.0, 2.0],\ 657 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 658 | -2.0, 2.0],\ 659 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 660 | 0.0, -2.0]] 661 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 662 | self.assertEqual(offset, 8) 663 | 664 | def test_clustering(self): 665 | #データ 666 | x = np.array([0.45, 0.80, 0.71, 0.49, 0.79, 0.30, 0.44, 0.14, 0.30, 0.40]) 667 | y = np.array([0.14, 0.14, 0.17, 0.25, 0.32, 0.63, 0.68, 0.74, 0.77, 0.84]) 668 | 669 | #量子ビットを用意する、例:q0_1は点0のクラス1担当 670 | q0_1, q0_2, q0_3 = symbols('q0_1 q0_2 q0_3') 671 | q1_1, q1_2, q1_3 = symbols('q1_1 q1_2 q1_3') 672 | q2_1, q2_2, q2_3 = symbols('q2_1 q2_2 q2_3') 673 | q3_1, q3_2, q3_3 = symbols('q3_1 q3_2 q3_3') 674 | q4_1, q4_2, q4_3 = symbols('q4_1 q4_2 q4_3') 675 | q5_1, q5_2, q5_3 = symbols('q5_1 q5_2 q5_3') 676 | q6_1, q6_2, q6_3 = symbols('q6_1 q6_2 q6_3') 677 | q7_1, q7_2, q7_3 = symbols('q7_1 q7_2 q7_3') 678 | q8_1, q8_2, q8_3 = symbols('q8_1 q8_2 q8_3') 679 | q9_1, q9_2, q9_3 = symbols('q9_1 q9_2 q9_3') 680 | 681 | #制約条件:各点ともワンホットで一つだけ1になる 682 | H = 0 683 | H += (q0_1 + q0_2 + q0_3 - 1)**2 684 | H += (q1_1 + q1_2 + q1_3 - 1)**2 685 | H += (q2_1 + q2_2 + q2_3 - 1)**2 686 | H += (q3_1 + q3_2 + q3_3 - 1)**2 687 | H += (q4_1 + q4_2 + q4_3 - 1)**2 688 | H += (q5_1 + q5_2 + q5_3 - 1)**2 689 | H += (q6_1 + q6_2 + q6_3 - 1)**2 690 | H += (q7_1 + q7_2 + q7_3 - 1)**2 691 | H += (q8_1 + q8_2 + q8_3 - 1)**2 692 | H += (q9_1 + q9_2 + q9_3 - 1)**2 693 | 694 | #コスト:2点の組み合わせで 695 | for i in range(9): 696 | for j in range(i+1, 10): 697 | #2点の距離 698 | dist = ((x[i] - x[j])**2 + (y[i] - y[j])**2)**0.5 699 | 700 | #同時にクラス1に入った場合のペナルティ 701 | text = f'H += 0.1 * {dist} * (q{i}_1 * q{j}_1)' 702 | exec(text) 703 | 704 | #同時にクラス2に入った場合のペナルティ 705 | text = f'H += 0.1 * {dist} * (q{i}_2 * q{j}_2)' 706 | exec(text) 707 | 708 | #同時にクラス3に入った場合のペナルティ 709 | text = f'H += 0.1 * {dist} * (q{i}_3 * q{j}_3)' 710 | exec(text) 711 | 712 | #コンパイル 713 | qubo, offset = Compile(H).get_qubo() 714 | expected_qubo_matrix = \ 715 | [[-1.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 716 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 717 | 0.0, 0.0],\ 718 | [0.0, -1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 719 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 720 | 0.0, 0.0],\ 721 | [0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 722 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 723 | 0.0, 0.0],\ 724 | [0.0, 0.0, 0.0, -1.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 725 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 726 | 0.0, 0.0],\ 727 | [0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 728 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 729 | 0.0, 0.0],\ 730 | [0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 731 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 732 | 0.0, 0.0],\ 733 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 734 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 735 | 0.0, 0.0],\ 736 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 737 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 738 | 0.0, 0.0],\ 739 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 740 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 741 | 0.0, 0.0],\ 742 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 2.0, 0.0, 0.0,\ 743 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 744 | 0.0, 0.0],\ 745 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 0.0, 0.0,\ 746 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 747 | 0.0, 0.0],\ 748 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0,\ 749 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 750 | 0.0, 0.0],\ 751 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0,\ 752 | 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 753 | 0.0, 0.0],\ 754 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0,\ 755 | 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 756 | 0.0, 0.0],\ 757 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 758 | -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 759 | 0.0, 0.0],\ 760 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 761 | 0.0, -1.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 762 | 0.0, 0.0],\ 763 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 764 | 0.0, 0.0, -1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 765 | 0.0, 0.0],\ 766 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 767 | 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 768 | 0.0, 0.0],\ 769 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 770 | 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 771 | 0.0, 0.0],\ 772 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 773 | 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 774 | 0.0, 0.0],\ 775 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 776 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 777 | 0.0, 0.0],\ 778 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 779 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 2.0, 0.0, 0.0, 0.0, 0.0,\ 780 | 0.0, 0.0],\ 781 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 782 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 0.0, 0.0, 0.0, 0.0,\ 783 | 0.0, 0.0],\ 784 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 785 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0,\ 786 | 0.0, 0.0],\ 787 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 788 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 2.0, 0.0,\ 789 | 0.0, 0.0],\ 790 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 791 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 2.0, 0.0,\ 792 | 0.0, 0.0],\ 793 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 794 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0,\ 795 | 0.0, 0.0],\ 796 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 797 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.0,\ 798 | 2.0, 2.0],\ 799 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 800 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 801 | -1.0, 2.0],\ 802 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 803 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 804 | 0.0, -1.0]] 805 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 806 | self.assertEqual(offset, 10) 807 | 808 | def test_simultaneous_equations1(self): 809 | #量子ビットを用意する 810 | x = symbols('x') 811 | y = symbols('y') 812 | z = symbols('z') 813 | 814 | #連立方程式の設定 815 | H = 0 816 | H += ( 5*x - y +2*z - 7)**2 817 | H += (-3*x +4*y + z + 2)**2 818 | H += ( x -2*y -4*z + 3)**2 819 | 820 | #コンパイル 821 | qubo, offset = Compile(H).get_qubo() 822 | expected_qubo_matrix = \ 823 | [[-41.0, -38.0, 6.0],\ 824 | [0.0, 39.0, 20.0],\ 825 | [0.0, 0.0, -27.0]] 826 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 827 | self.assertEqual(offset, 62) 828 | 829 | def test_simultaneous_equations2(self): 830 | #量子ビットを用意する 831 | x0, x1 = symbols('x0 x1') 832 | y0, y1 = symbols('y0 y1') 833 | z0, z1 = symbols('z0 z1') 834 | 835 | #x,y,zを2進数(2bit)で表す 836 | x = 2*x0 + 1*x1 837 | y = 2*y0 + 1*y1 838 | z = 2*z0 + 1*z1 839 | 840 | #連立方程式の設定 841 | H = 0 842 | H += ( x + y + z - 6)**2 843 | H += (2*x +3*y -2*z - 11)**2 844 | H += (3*x - y + z - 4)**2 845 | 846 | #コンパイル 847 | qubo, offset = Compile(H).get_qubo() 848 | expected_qubo_matrix = \ 849 | [[-104.0, 56.0, 32.0, 16.0, 0.0, 0.0],\ 850 | [0.0, -66.0, 16.0, 8.0, 0.0, 0.0],\ 851 | [0.0, 0.0, -96.0, 44.0, -48.0, -24.0],\ 852 | [0.0, 0.0, 0.0, -59.0, -24.0, -12.0],\ 853 | [0.0, 0.0, 0.0, 0.0, 72.0, 24.0],\ 854 | [0.0, 0.0, 0.0, 0.0, 0.0, 30.0]] 855 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 856 | self.assertEqual(offset, 173) 857 | 858 | def test_simultaneous_equations3(self): 859 | #量子ビットを用意する 860 | x0, x1, x2, x3, x4, x5, x6, x7 = symbols('x0 x1 x2 x3 x4 x5 x6 x7') 861 | y0, y1, y2, y3, y4, y5, y6, y7 = symbols('y0 y1 y2 y3 y4 y5 y6 y7') 862 | z0, z1, z2, z3, z4, z5, z6, z7 = symbols('z0 z1 z2 z3 z4 z5 z6 z7') 863 | 864 | #x,y,zを2進数(8bit)で表す 865 | x = 128*x0 + 64*x1 + 32*x2 + 16*x3 + 8*x4 + 4*x5 + 2*x6 + 1*x7 866 | y = 128*y0 + 64*y1 + 32*y2 + 16*y3 + 8*y4 + 4*y5 + 2*y6 + 1*y7 867 | z = 128*z0 + 64*z1 + 32*z2 + 16*z3 + 8*z4 + 4*z5 + 2*z6 + 1*z7 868 | 869 | #連立方程式の設定 870 | H = 0 871 | H += (10*x +14*y +4*z - 5120)**2 872 | H += ( 9*x +12*y +2*z - 4230)**2 873 | H += ( 7*x + 5*y +2*z - 2360)**2 874 | 875 | 876 | #コンパイル 877 | qubo, offset = Compile(H).get_qubo() 878 | expected_qubo_matrix = \ 879 | [[-23313920.0, 3768320.0, 1884160.0, 942080.0, 471040.0, 235520.0,\ 880 | 117760.0, 58880.0, 9273344.0, 4636672.0, 2318336.0, 1159168.0, 579584.0,\ 881 | 289792.0, 144896.0, 72448.0, 2359296.0, 1179648.0, 589824.0, 294912.0,\ 882 | 147456.0, 73728.0, 36864.0, 18432.0],\ 883 | [0.0, -12599040.0, 942080.0, 471040.0, 235520.0, 117760.0, 58880.0,\ 884 | 29440.0, 4636672.0, 2318336.0, 1159168.0, 579584.0, 289792.0, 144896.0,\ 885 | 72448.0, 36224.0, 1179648.0, 589824.0, 294912.0, 147456.0, 73728.0,\ 886 | 36864.0, 18432.0, 9216.0],\ 887 | [0.0, 0.0, -6535040.0, 235520.0, 117760.0, 58880.0, 29440.0, 14720.0,\ 888 | 2318336.0, 1159168.0, 579584.0, 289792.0, 144896.0, 72448.0, 36224.0,\ 889 | 18112.0, 589824.0, 294912.0, 147456.0, 73728.0, 36864.0, 18432.0,\ 890 | 9216.0, 4608.0],\ 891 | [0.0, 0.0, 0.0, -3326400.0, 58880.0, 29440.0, 14720.0, 7360.0, 1159168.0,\ 892 | 579584.0, 289792.0, 144896.0, 72448.0, 36224.0, 18112.0, 9056.0,\ 893 | 294912.0, 147456.0, 73728.0, 36864.0, 18432.0, 9216.0, 4608.0, 2304.0],\ 894 | [0.0, 0.0, 0.0, 0.0, -1677920.0, 14720.0, 7360.0, 3680.0, 579584.0,\ 895 | 289792.0, 144896.0, 72448.0, 36224.0, 18112.0, 9056.0, 4528.0, 147456.0,\ 896 | 73728.0, 36864.0, 18432.0, 9216.0, 4608.0, 2304.0, 1152.0],\ 897 | [0.0, 0.0, 0.0, 0.0, 0.0, -842640.0, 3680.0, 1840.0, 289792.0, 144896.0,\ 898 | 72448.0, 36224.0, 18112.0, 9056.0, 4528.0, 2264.0, 73728.0, 36864.0,\ 899 | 18432.0, 9216.0, 4608.0, 2304.0, 1152.0, 576.0],\ 900 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -422240.0, 920.0, 144896.0, 72448.0,\ 901 | 36224.0, 18112.0, 9056.0, 4528.0, 2264.0, 1132.0, 36864.0, 18432.0,\ 902 | 9216.0, 4608.0, 2304.0, 1152.0, 576.0, 288.0],\ 903 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -211350.0, 72448.0, 36224.0, 18112.0,\ 904 | 9056.0, 4528.0, 2264.0, 1132.0, 566.0, 18432.0, 9216.0, 4608.0, 2304.0,\ 905 | 1152.0, 576.0, 288.0, 144.0],\ 906 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -28385280.0, 5980160.0,\ 907 | 2990080.0, 1495040.0, 747520.0, 373760.0, 186880.0, 93440.0, 2949120.0,\ 908 | 1474560.0, 737280.0, 368640.0, 184320.0, 92160.0, 46080.0, 23040.0],\ 909 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -15687680.0, 1495040.0,\ 910 | 747520.0, 373760.0, 186880.0, 93440.0, 46720.0, 1474560.0, 737280.0,\ 911 | 368640.0, 184320.0, 92160.0, 46080.0, 23040.0, 11520.0],\ 912 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8217600.0, 373760.0,\ 913 | 186880.0, 93440.0, 46720.0, 23360.0, 737280.0, 368640.0, 184320.0,\ 914 | 92160.0, 46080.0, 23040.0, 11520.0, 5760.0],\ 915 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4202240.0,\ 916 | 93440.0, 46720.0, 23360.0, 11680.0, 368640.0, 184320.0, 92160.0,\ 917 | 46080.0, 23040.0, 11520.0, 5760.0, 2880.0],\ 918 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2124480.0,\ 919 | 23360.0, 11680.0, 5840.0, 184320.0, 92160.0, 46080.0, 23040.0, 11520.0,\ 920 | 5760.0, 2880.0, 1440.0],\ 921 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 922 | -1068080.0, 5840.0, 2920.0, 92160.0, 46080.0, 23040.0, 11520.0, 5760.0,\ 923 | 2880.0, 1440.0, 720.0],\ 924 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 925 | -535500.0, 1460.0, 46080.0, 23040.0, 11520.0, 5760.0, 2880.0, 1440.0,\ 926 | 720.0, 360.0],\ 927 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 928 | 0.0, -268115.0, 23040.0, 11520.0, 5760.0, 2880.0, 1440.0, 720.0, 360.0,\ 929 | 180.0],\ 930 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 931 | 0.0, 0.0, -8223744.0, 393216.0, 196608.0, 98304.0, 49152.0, 24576.0,\ 932 | 12288.0, 6144.0],\ 933 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 934 | 0.0, 0.0, 0.0, -4210176.0, 98304.0, 49152.0, 24576.0, 12288.0, 6144.0,\ 935 | 3072.0],\ 936 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 937 | 0.0, 0.0, 0.0, 0.0, -2129664.0, 24576.0, 12288.0, 6144.0, 3072.0,\ 938 | 1536.0],\ 939 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 940 | 0.0, 0.0, 0.0, 0.0, 0.0, -1070976.0, 6144.0, 3072.0, 1536.0, 768.0],\ 941 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 942 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -537024.0, 1536.0, 768.0, 384.0],\ 943 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 944 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -268896.0, 384.0, 192.0],\ 945 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 946 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -134544.0, 96.0],\ 947 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 948 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -67296.0]] 949 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 950 | self.assertEqual(offset, 49676900) 951 | 952 | def test_simultaneous_equations4(self): 953 | #量子ビットをNビット表現で用意する 954 | x = symbols_nbit(0, 256, 'x{}', num=8) 955 | y = symbols_nbit(0, 256, 'y{}', num=8) 956 | z = symbols_nbit(0, 256, 'z{}', num=8) 957 | 958 | #連立方程式の設定 959 | H = 0 960 | H += (10*x +14*y +4*z - 5120)**2 961 | H += ( 9*x +12*y +2*z - 4230)**2 962 | H += ( 7*x + 5*y +2*z - 2360)**2 963 | 964 | #コンパイル 965 | qubo, offset = Compile(H).get_qubo() 966 | expected_qubo_matrix = \ 967 | [[-23313920.0, 3768320.0, 1884160.0, 942080.0, 471040.0, 235520.0,\ 968 | 117760.0, 58880.0, 9273344.0, 4636672.0, 2318336.0, 1159168.0, 579584.0,\ 969 | 289792.0, 144896.0, 72448.0, 2359296.0, 1179648.0, 589824.0, 294912.0,\ 970 | 147456.0, 73728.0, 36864.0, 18432.0],\ 971 | [0.0, -12599040.0, 942080.0, 471040.0, 235520.0, 117760.0, 58880.0,\ 972 | 29440.0, 4636672.0, 2318336.0, 1159168.0, 579584.0, 289792.0, 144896.0,\ 973 | 72448.0, 36224.0, 1179648.0, 589824.0, 294912.0, 147456.0, 73728.0,\ 974 | 36864.0, 18432.0, 9216.0],\ 975 | [0.0, 0.0, -6535040.0, 235520.0, 117760.0, 58880.0, 29440.0, 14720.0,\ 976 | 2318336.0, 1159168.0, 579584.0, 289792.0, 144896.0, 72448.0, 36224.0,\ 977 | 18112.0, 589824.0, 294912.0, 147456.0, 73728.0, 36864.0, 18432.0,\ 978 | 9216.0, 4608.0],\ 979 | [0.0, 0.0, 0.0, -3326400.0, 58880.0, 29440.0, 14720.0, 7360.0, 1159168.0,\ 980 | 579584.0, 289792.0, 144896.0, 72448.0, 36224.0, 18112.0, 9056.0,\ 981 | 294912.0, 147456.0, 73728.0, 36864.0, 18432.0, 9216.0, 4608.0, 2304.0],\ 982 | [0.0, 0.0, 0.0, 0.0, -1677920.0, 14720.0, 7360.0, 3680.0, 579584.0,\ 983 | 289792.0, 144896.0, 72448.0, 36224.0, 18112.0, 9056.0, 4528.0, 147456.0,\ 984 | 73728.0, 36864.0, 18432.0, 9216.0, 4608.0, 2304.0, 1152.0],\ 985 | [0.0, 0.0, 0.0, 0.0, 0.0, -842640.0, 3680.0, 1840.0, 289792.0, 144896.0,\ 986 | 72448.0, 36224.0, 18112.0, 9056.0, 4528.0, 2264.0, 73728.0, 36864.0,\ 987 | 18432.0, 9216.0, 4608.0, 2304.0, 1152.0, 576.0],\ 988 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -422240.0, 920.0, 144896.0, 72448.0,\ 989 | 36224.0, 18112.0, 9056.0, 4528.0, 2264.0, 1132.0, 36864.0, 18432.0,\ 990 | 9216.0, 4608.0, 2304.0, 1152.0, 576.0, 288.0],\ 991 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -211350.0, 72448.0, 36224.0, 18112.0,\ 992 | 9056.0, 4528.0, 2264.0, 1132.0, 566.0, 18432.0, 9216.0, 4608.0, 2304.0,\ 993 | 1152.0, 576.0, 288.0, 144.0],\ 994 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -28385280.0, 5980160.0,\ 995 | 2990080.0, 1495040.0, 747520.0, 373760.0, 186880.0, 93440.0, 2949120.0,\ 996 | 1474560.0, 737280.0, 368640.0, 184320.0, 92160.0, 46080.0, 23040.0],\ 997 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -15687680.0, 1495040.0,\ 998 | 747520.0, 373760.0, 186880.0, 93440.0, 46720.0, 1474560.0, 737280.0,\ 999 | 368640.0, 184320.0, 92160.0, 46080.0, 23040.0, 11520.0],\ 1000 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -8217600.0, 373760.0,\ 1001 | 186880.0, 93440.0, 46720.0, 23360.0, 737280.0, 368640.0, 184320.0,\ 1002 | 92160.0, 46080.0, 23040.0, 11520.0, 5760.0],\ 1003 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -4202240.0,\ 1004 | 93440.0, 46720.0, 23360.0, 11680.0, 368640.0, 184320.0, 92160.0,\ 1005 | 46080.0, 23040.0, 11520.0, 5760.0, 2880.0],\ 1006 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2124480.0,\ 1007 | 23360.0, 11680.0, 5840.0, 184320.0, 92160.0, 46080.0, 23040.0, 11520.0,\ 1008 | 5760.0, 2880.0, 1440.0],\ 1009 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1010 | -1068080.0, 5840.0, 2920.0, 92160.0, 46080.0, 23040.0, 11520.0, 5760.0,\ 1011 | 2880.0, 1440.0, 720.0],\ 1012 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1013 | -535500.0, 1460.0, 46080.0, 23040.0, 11520.0, 5760.0, 2880.0, 1440.0,\ 1014 | 720.0, 360.0],\ 1015 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1016 | 0.0, -268115.0, 23040.0, 11520.0, 5760.0, 2880.0, 1440.0, 720.0, 360.0,\ 1017 | 180.0],\ 1018 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1019 | 0.0, 0.0, -8223744.0, 393216.0, 196608.0, 98304.0, 49152.0, 24576.0,\ 1020 | 12288.0, 6144.0],\ 1021 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1022 | 0.0, 0.0, 0.0, -4210176.0, 98304.0, 49152.0, 24576.0, 12288.0, 6144.0,\ 1023 | 3072.0],\ 1024 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1025 | 0.0, 0.0, 0.0, 0.0, -2129664.0, 24576.0, 12288.0, 6144.0, 3072.0,\ 1026 | 1536.0],\ 1027 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1028 | 0.0, 0.0, 0.0, 0.0, 0.0, -1070976.0, 6144.0, 3072.0, 1536.0, 768.0],\ 1029 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1030 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -537024.0, 1536.0, 768.0, 384.0],\ 1031 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1032 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -268896.0, 384.0, 192.0],\ 1033 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1034 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -134544.0, 96.0],\ 1035 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1036 | 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -67296.0]] 1037 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 1038 | self.assertEqual(offset, 49676900) 1039 | 1040 | def test_linear_regression1(self): 1041 | #量子ビットを用意する 1042 | a0, a1, a2, a3, a4, a5, a6, a7 = symbols('a0 a1 a2 a3 a4 a5 a6 a7') 1043 | b0, b1, b2, b3, b4, b5, b6, b7 = symbols('b0 b1 b2 b3 b4 b5 b6 b7') 1044 | 1045 | #aを2進数(8bit)で表す、10~20で規格化 1046 | a = 10 + 10 * ((128*a0 + 64*a1 + 32*a2 + 16*a3 + 8*a4 + 4*a5 + 2*a6 + 1*a7) / 256) 1047 | # print(a) 1048 | #bを2進数(8bit)で表す、0~1で規格化 1049 | b = 0 + 1 * ((128*b0 + 64*b1 + 32*b2 + 16*b3 + 8*b4 + 4*b5 + 2*b6 + 1*b7) / 256) 1050 | 1051 | #各点の誤差二乗をペナルティとする 1052 | H = 0 1053 | H += (5.75 - (a*0.31 + b))**2 1054 | H += (8.56 - (a*0.4 + b))**2 1055 | H += (8.42 - (a*0.47 + b))**2 1056 | H += (7.78 - (a*0.4 + b))**2 1057 | H += (10.25 - (a*0.54 + b))**2 1058 | H += (6.79 - (a*0.36 + b))**2 1059 | H += (11.51 - (a*0.56 + b))**2 1060 | H += (7.66 - (a*0.43 + b))**2 1061 | H += (6.99 - (a*0.32 + b))**2 1062 | H += (10.61 - (a*0.6 + b))**2 1063 | 1064 | 1065 | #コンパイル 1066 | qubo, offset = Compile(H).get_qubo() 1067 | expected_qubo_matrix = \ 1068 | [[-133.58749999999998, 50.477500000000006, 25.238750000000003,\ 1069 | 12.619375000000002, 6.309687500000001, 3.1548437500000004,\ 1070 | 1.5774218750000002, 0.7887109375000001, 21.95, 10.975, 5.4875, 2.74375,\ 1071 | 1.371875, 0.6859375, 0.34296875, 0.171484375],\ 1072 | [0.0, -79.413125, 12.619375000000002, 6.309687500000001,\ 1073 | 3.1548437500000004, 1.5774218750000002, 0.7887109375000001,\ 1074 | 0.39435546875000005, 10.975, 5.4875, 2.74375, 1.371875, 0.6859375,\ 1075 | 0.34296875, 0.171484375, 0.0857421875],\ 1076 | [0.0, 0.0, -42.86140625, 3.1548437500000004, 1.5774218750000002,\ 1077 | 0.7887109375000001, 0.39435546875000005, 0.19717773437500002, 5.4875,\ 1078 | 2.74375, 1.371875, 0.6859375, 0.34296875, 0.171484375, 0.0857421875,\ 1079 | 0.04287109375],\ 1080 | [0.0, 0.0, 0.0, -22.2194140625, 0.7887109375000001, 0.39435546875000005,\ 1081 | 0.19717773437500002, 0.09858886718750001, 2.74375, 1.371875, 0.6859375,\ 1082 | 0.34296875, 0.171484375, 0.0857421875, 0.04287109375, 0.021435546875],\ 1083 | [0.0, 0.0, 0.0, 0.0, -11.306884765625, 0.19717773437500002,\ 1084 | 0.09858886718750001, 0.049294433593750006, 1.371875, 0.6859375,\ 1085 | 0.34296875, 0.171484375, 0.0857421875, 0.04287109375, 0.021435546875,\ 1086 | 0.0107177734375],\ 1087 | [0.0, 0.0, 0.0, 0.0, 0.0, -5.70273681640625, 0.049294433593750006,\ 1088 | 0.024647216796875003, 0.6859375, 0.34296875, 0.171484375, 0.0857421875,\ 1089 | 0.04287109375, 0.021435546875, 0.0107177734375, 0.00535888671875],\ 1090 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -2.8636920166015623, 0.012323608398437502,\ 1091 | 0.34296875, 0.171484375, 0.0857421875, 0.04287109375, 0.021435546875,\ 1092 | 0.0107177734375, 0.00535888671875, 0.002679443359375],\ 1093 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1.4349269104003906, 0.171484375,\ 1094 | 0.0857421875, 0.04287109375, 0.021435546875, 0.0107177734375,\ 1095 | 0.00535888671875, 0.002679443359375, 0.0013397216796875],\ 1096 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -37.92, 2.5, 1.25, 0.625,\ 1097 | 0.3125, 0.15625, 0.078125, 0.0390625],\ 1098 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -19.585, 0.625, 0.3125,\ 1099 | 0.15625, 0.078125, 0.0390625, 0.01953125],\ 1100 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -9.94875, 0.15625,\ 1101 | 0.078125, 0.0390625, 0.01953125, 0.009765625],\ 1102 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -5.0134375,\ 1103 | 0.0390625, 0.01953125, 0.009765625, 0.0048828125],\ 1104 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1105 | -2.516484375, 0.009765625, 0.0048828125, 0.00244140625],\ 1106 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1107 | -1.26068359375, 0.00244140625, 0.001220703125],\ 1108 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1109 | -0.6309521484375, 0.0006103515625],\ 1110 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1111 | 0.0, -0.315628662109375]] 1112 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 1113 | self.assertEqual(offset, 171.475400000000) 1114 | 1115 | def test_linear_regression2(self): 1116 | #aを2進数(8bit)で表す、10~20で規格化 1117 | a = symbols_nbit(10, 20, 'a{}', num=8) 1118 | #bを2進数(8bit)で表す、0~1で規格化 1119 | b = symbols_nbit(0, 1, 'b{}', num=8) 1120 | 1121 | #誤差二乗ペナルティ 1122 | H = 0 1123 | H += (5.75 - (a*0.31 + b))**2 1124 | H += (8.56 - (a*0.4 + b))**2 1125 | H += (8.42 - (a*0.47 + b))**2 1126 | H += (7.78 - (a*0.4 + b))**2 1127 | H += (10.25 - (a*0.54 + b))**2 1128 | H += (6.79 - (a*0.36 + b))**2 1129 | H += (11.51 - (a*0.56 + b))**2 1130 | H += (7.66 - (a*0.43 + b))**2 1131 | H += (6.99 - (a*0.32 + b))**2 1132 | H += (10.61 - (a*0.6 + b))**2 1133 | 1134 | 1135 | #コンパイル 1136 | qubo, offset = Compile(H).get_qubo() 1137 | expected_qubo_matrix = \ 1138 | [[-570.04, 201.91000000000003, 100.95500000000001, 50.477500000000006,\ 1139 | 25.238750000000003, 12.619375000000002, 6.309687500000001,\ 1140 | 3.1548437500000004, 43.900000000000006, 21.950000000000003,\ 1141 | 10.975000000000001, 5.487500000000001, 2.7437500000000004,\ 1142 | 1.3718750000000002, 0.6859375000000001, 0.34296875000000004],\ 1143 | [0.0, -335.4975, 50.477500000000006, 25.238750000000003,\ 1144 | 12.619375000000002, 6.309687500000001, 3.1548437500000004,\ 1145 | 1.5774218750000002, 21.950000000000003, 10.975000000000001,\ 1146 | 5.487500000000001, 2.7437500000000004, 1.3718750000000002,\ 1147 | 0.6859375000000001, 0.34296875000000004, 0.17148437500000002],\ 1148 | [0.0, 0.0, -180.36812500000002, 12.619375000000002, 6.309687500000001,\ 1149 | 3.1548437500000004, 1.5774218750000002, 0.7887109375000001,\ 1150 | 10.975000000000001, 5.487500000000001, 2.7437500000000004,\ 1151 | 1.3718750000000002, 0.6859375000000001, 0.34296875000000004,\ 1152 | 0.17148437500000002, 0.08574218750000001],\ 1153 | [0.0, 0.0, 0.0, -93.33890625000001, 3.1548437500000004,\ 1154 | 1.5774218750000002, 0.7887109375000001, 0.39435546875000005,\ 1155 | 5.487500000000001, 2.7437500000000004, 1.3718750000000002,\ 1156 | 0.6859375000000001, 0.34296875000000004, 0.17148437500000002,\ 1157 | 0.08574218750000001, 0.042871093750000006],\ 1158 | [0.0, 0.0, 0.0, 0.0, -47.4581640625, 0.7887109375000001,\ 1159 | 0.39435546875000005, 0.19717773437500002, 2.7437500000000004,\ 1160 | 1.3718750000000002, 0.6859375000000001, 0.34296875000000004,\ 1161 | 0.17148437500000002, 0.08574218750000001, 0.042871093750000006,\ 1162 | 0.021435546875000003],\ 1163 | [0.0, 0.0, 0.0, 0.0, 0.0, -23.926259765625, 0.19717773437500002,\ 1164 | 0.09858886718750001, 1.3718750000000002, 0.6859375000000001,\ 1165 | 0.34296875000000004, 0.17148437500000002, 0.08574218750000001,\ 1166 | 0.042871093750000006, 0.021435546875000003, 0.010717773437500001],\ 1167 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -12.01242431640625, 0.049294433593750006,\ 1168 | 0.6859375000000001, 0.34296875000000004, 0.17148437500000002,\ 1169 | 0.08574218750000001, 0.042871093750000006, 0.021435546875000003,\ 1170 | 0.010717773437500001, 0.005358886718750001],\ 1171 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -6.018535766601563,\ 1172 | 0.34296875000000004, 0.17148437500000002, 0.08574218750000001,\ 1173 | 0.042871093750000006, 0.021435546875000003, 0.010717773437500001,\ 1174 | 0.005358886718750001, 0.0026794433593750003],\ 1175 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -81.82000000000001, 2.5, 1.25,\ 1176 | 0.625, 0.3125, 0.15625, 0.078125, 0.0390625],\ 1177 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -41.535000000000004, 0.625,\ 1178 | 0.3125, 0.15625, 0.078125, 0.0390625, 0.01953125],\ 1179 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -20.923750000000002,\ 1180 | 0.15625, 0.078125, 0.0390625, 0.01953125, 0.009765625],\ 1181 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1182 | -10.500937500000001, 0.0390625, 0.01953125, 0.009765625, 0.0048828125],\ 1183 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1184 | -5.2602343750000005, 0.009765625, 0.0048828125, 0.00244140625],\ 1185 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1186 | -2.6325585937500002, 0.00244140625, 0.001220703125],\ 1187 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1188 | -1.3168896484375001, 0.0006103515625],\ 1189 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\ 1190 | 0.0, -0.6585974121093751]] 1191 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 1192 | self.assertEqual(offset, 741.515400000000) 1193 | 1194 | def test_napzak1(self): 1195 | #量子ビットを用意する 1196 | q0 = symbols('q0') 1197 | q1 = symbols('q1') 1198 | q2 = symbols('q2') 1199 | q3 = symbols('q3') 1200 | q4 = symbols('q4') 1201 | q5 = symbols('q5') 1202 | q6 = symbols('q6') 1203 | 1204 | #7個のドリンクをそれぞれ取るか取らないか、値段を係数にして合計が15になる(強い条件) 1205 | H = 0 1206 | H += (4*q0 + 4*q1 + 4*q2 + 6*q3 + 6*q4 + 7*q5 + 7*q6 - 15)**2 1207 | 1208 | #7個のドリンクをそれぞれ取るか取らないか、回復量を係数にして報酬とする(弱い条件) 1209 | H += -0.01 * (3*q0 + 3*q1 + 3*q2 + 5*q3 + 5*q4 + 7*q5 + 7*q6) 1210 | 1211 | #コンパイル 1212 | qubo, offset = Compile(H).get_qubo() 1213 | expected_qubo_matrix = \ 1214 | [[-104.03, 32.0, 32.0, 48.0, 48.0, 56.0, 56.0],\ 1215 | [0.0, -104.03, 32.0, 48.0, 48.0, 56.0, 56.0],\ 1216 | [0.0, 0.0, -104.03, 48.0, 48.0, 56.0, 56.0],\ 1217 | [0.0, 0.0, 0.0, -144.05, 72.0, 84.0, 84.0],\ 1218 | [0.0, 0.0, 0.0, 0.0, -144.05, 84.0, 84.0],\ 1219 | [0.0, 0.0, 0.0, 0.0, 0.0, -161.07, 98.0],\ 1220 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -161.07]] 1221 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 1222 | self.assertEqual(offset, 225) 1223 | 1224 | def test_napzak2(self): 1225 | #量子ビットを用意する 1226 | q0 = symbols('q0') 1227 | q1 = symbols('q1') 1228 | q2 = symbols('q2') 1229 | q3 = symbols('q3') 1230 | q4 = symbols('q4') 1231 | q5 = symbols('q5') 1232 | q6 = symbols('q6') 1233 | 1234 | #補助ビットを用意する 1235 | s0 = symbols('s0') 1236 | s1 = symbols('s1') 1237 | s2 = symbols('s2') 1238 | s3 = symbols('s3') 1239 | 1240 | #補助ビットをワンホットにする(強い条件) 1241 | #これにより (12*s0 + 13*s1 + 14*s2 + 15*s3) で 「12 or 13 or 14 or 15」 を表現できる 1242 | H = 0 1243 | H += (s0 + s1 + s2 + s3 - 1)**2 1244 | 1245 | #7個のドリンクをそれぞれ取るか取らないか、値段を係数にして合計が「12 or 13 or 14 or 15」になる(強い条件) 1246 | H += (4*q0 + 4*q1 + 4*q2 + 6*q3 + 6*q4 + 7*q5 + 7*q6 - (12*s0 + 13*s1 + 14*s2 + 15*s3))**2 1247 | 1248 | #7個のドリンクをそれぞれ取るか取らないか、回復量を係数にして報酬とする(弱い条件) 1249 | H += -0.01 * (3*q0 + 3*q1 + 3*q2 + 5*q3 + 5*q4 + 7*q5 + 7*q6) 1250 | 1251 | #おいしいみずを降順にする 1252 | H += (1 - q0) * q1 1253 | H += (1 - q1) * q2 1254 | #サイコソーダを降順にする 1255 | H += (1 - q3) * q4 1256 | #ミックスオレを降順にする 1257 | H += (1 - q5) * q6 1258 | 1259 | #コンパイル 1260 | qubo, offset = Compile(H).get_qubo() 1261 | # print(np.array2string(qubo[0], separator=', ', formatter={'float_kind': lambda x: f'{x}'})) 1262 | expected_qubo_matrix = \ 1263 | [[15.97, 31.0, 32.0, 48.0, 48.0, 56.0, 56.0, -96.0, -104.0, -112.0,\ 1264 | -120.0],\ 1265 | [0.0, 16.97, 31.0, 48.0, 48.0, 56.0, 56.0, -96.0, -104.0, -112.0, -120.0],\ 1266 | [0.0, 0.0, 16.97, 48.0, 48.0, 56.0, 56.0, -96.0, -104.0, -112.0, -120.0],\ 1267 | [0.0, 0.0, 0.0, 35.95, 71.0, 84.0, 84.0, -144.0, -156.0, -168.0, -180.0],\ 1268 | [0.0, 0.0, 0.0, 0.0, 36.95, 84.0, 84.0, -144.0, -156.0, -168.0, -180.0],\ 1269 | [0.0, 0.0, 0.0, 0.0, 0.0, 48.93, 97.0, -168.0, -182.0, -196.0, -210.0],\ 1270 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 49.93, -168.0, -182.0, -196.0, -210.0],\ 1271 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 143.0, 314.0, 338.0, 362.0],\ 1272 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 168.0, 366.0, 392.0],\ 1273 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 195.0, 422.0],\ 1274 | [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 224.0]] 1275 | self.assertTrue(are_same_qubo_matrices(qubo[0], expected_qubo_matrix)) 1276 | self.assertEqual(offset, 1) 1277 | 1278 | 1279 | # テストケースの定義 1280 | @pytest.mark.parametrize("expr, expected", [ 1281 | (symbols('x')**2 + 3*symbols('x') + 2, 2), # 単純な多項式 1282 | (symengine.expand(5), 0), # 定数項のみ 1283 | (symbols('x'), 1), # 単一変数 1284 | (symbols('x')**2 + symbols('y')**2, 2), # 複数変数の多項式 1285 | (symbols('x')**(symbols('x')**2), None), # 非多項式(指数に変数を含む) 1286 | (symbols('x')**0.5, None), # 非多項式(根号内に変数) 1287 | (3*symbols('x')**3 * 2*symbols('x')**2, 5), # 乗算された項 1288 | (symbols('x')**3 + 2*symbols('x')**2 * symbols('x'), 3), # 加算と乗算の混合 1289 | (2*symbols('x') * 3*symbols('y'), 2), # 複数項の乗算(変数と定数) 1290 | (symbols('x')**-2, None), # 負のべき乗 1291 | ]) 1292 | 1293 | def test_calc_degree(expr, expected): 1294 | assert calc_degree(expr) == expected 1295 | --------------------------------------------------------------------------------