├── .all-contributorsrc
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── inject_solutions.bash
├── problems
├── __init__.py
├── ada_lovelaces_note_g.py
├── almost_pi.py
├── babylonian_spiral.py
├── babylonian_square_roots.py
├── blood_types.py
├── caesar_cipher.py
├── chaos.py
├── colorful_resistors.py
├── compound_interest.py
├── correlation_does_not_imply_causation.py
├── definite_integrals.py
├── dna_transcription.py
├── earthquake_epicenters.py
├── el_nino_intensities.py
├── exponential_growth.py
├── flight_paths.py
├── game_of_life.py
├── habitable_exoplanets.py
├── molecular_mass_calculator.py
├── nand_gate.py
├── numerical_diff.py
├── plump_moose.py
├── rna_translation.py
├── rock_star_climate.py
├── rocket_science.py
├── scientific_temperatures.py
├── sha_256.py
├── speed_of_light.py
├── temperature_variations.py
├── test_case.py
└── wind_chill.py
├── pytest.ini
├── requirements.txt
├── resources
├── correlation_does_not_imply_causation
│ └── spurious_xy.csv
├── el_nino_intensities
│ └── mei.ext_index.txt
├── game_of_life
│ ├── oscillators.txt
│ ├── spaceships.txt
│ └── still_life.txt
└── molecular_mass_calculator
│ └── periodic_table.csv
├── setup.py
└── tests
└── test_problems.py
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "ali-ramadhan",
10 | "name": "Ali Ramadhan",
11 | "avatar_url": "https://avatars.githubusercontent.com/u/20099589?v=4",
12 | "profile": "http://aliramadhan.me",
13 | "contributions": [
14 | "content",
15 | "test",
16 | "code"
17 | ]
18 | },
19 | {
20 | "login": "basimr",
21 | "name": "br",
22 | "avatar_url": "https://avatars.githubusercontent.com/u/9298270?v=4",
23 | "profile": "https://github.com/basimr",
24 | "contributions": [
25 | "content",
26 | "code"
27 | ]
28 | }
29 | ],
30 | "contributorsPerLine": 7,
31 | "projectName": "lovelace-problems",
32 | "projectOwner": "project-lovelace",
33 | "repoType": "github",
34 | "repoHost": "https://github.com",
35 | "skipCi": true
36 | }
37 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: tests
2 |
3 | on:
4 | - push
5 | - pull_request
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-latest
10 | strategy:
11 | matrix:
12 | python-version: [3.7, 3.8, 3.9]
13 |
14 | steps:
15 | - uses: actions/checkout@v2
16 |
17 | - name: Set up Python ${{ matrix.python-version }}
18 | uses: actions/setup-python@v2
19 | with:
20 | python-version: ${{ matrix.python-version }}
21 |
22 | - name: Install Python dependencies
23 | run: |
24 | pip install --upgrade pip
25 | pip install -r requirements.txt
26 |
27 | - name: Set up package
28 | run: python setup.py develop
29 |
30 | - name: Clone lovelace-solutions
31 | run: |
32 | git clone https://${{ secrets.LOVELACE_GITHUB_TOKEN }}@github.com/project-lovelace/lovelace-solutions.git
33 | ln -s ../lovelace-solutions/python problems/solutions
34 |
35 | - name: Run tests
36 | run: pytest --capture=no --verbose tests/
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 | local_settings.py
55 |
56 | # Flask stuff:
57 | instance/
58 | .webassets-cache
59 |
60 | # Scrapy stuff:
61 | .scrapy
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # PyBuilder
67 | target/
68 |
69 | # IPython Notebook
70 | .ipynb_checkpoints
71 |
72 | # pyenv
73 | .python-version
74 |
75 | # celery beat schedule file
76 | celerybeat-schedule
77 |
78 | # dotenv
79 | .env
80 |
81 | # virtualenv
82 | venv/
83 | ENV/
84 |
85 | # Spyder project settings
86 | .spyderproject
87 |
88 | # Rope project settings
89 | .ropeproject
90 |
91 | # IDE files
92 | .idea
93 |
94 | # Symbolic links to secret stuff
95 | lovelace-solutions
96 | problems/solutions
97 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [v3.2.0](https://github.com/project-lovelace/lovelace-problems/tree/v3.2.0) (2021-04-11)
4 |
5 | [Full Changelog](https://github.com/project-lovelace/lovelace-problems/compare/v3.1.0...v3.2.0)
6 |
7 | **Closed issues:**
8 |
9 | - Record version of problems repo [\#46](https://github.com/project-lovelace/lovelace-problems/issues/46)
10 |
11 | **Merged pull requests:**
12 |
13 | - New problem: Exponential growth [\#67](https://github.com/project-lovelace/lovelace-problems/pull/67) ([ali-ramadhan](https://github.com/ali-ramadhan))
14 | - Bump urllib3 from 1.26.3 to 1.26.4 [\#66](https://github.com/project-lovelace/lovelace-problems/pull/66) ([dependabot[bot]](https://github.com/apps/dependabot))
15 | - Flesh out RNA translation tests [\#65](https://github.com/project-lovelace/lovelace-problems/pull/65) ([ali-ramadhan](https://github.com/ali-ramadhan))
16 | - Parameterize test cases too [\#64](https://github.com/project-lovelace/lovelace-problems/pull/64) ([ali-ramadhan](https://github.com/ali-ramadhan))
17 | - Test cases for RNA translation [\#63](https://github.com/project-lovelace/lovelace-problems/pull/63) ([ali-ramadhan](https://github.com/ali-ramadhan))
18 | - Cleanup problem module logic [\#61](https://github.com/project-lovelace/lovelace-problems/pull/61) ([ali-ramadhan](https://github.com/ali-ramadhan))
19 |
20 | ## [v3.1.0](https://github.com/project-lovelace/lovelace-problems/tree/v3.1.0) (2021-03-27)
21 |
22 | [Full Changelog](https://github.com/project-lovelace/lovelace-problems/compare/v3.0.0...v3.1.0)
23 |
24 | **Closed issues:**
25 |
26 | - Test suite that just makes sure we can generate every test case? [\#54](https://github.com/project-lovelace/lovelace-problems/issues/54)
27 | - Change output for El Niño intensities when no El Nino or La Nina is present [\#32](https://github.com/project-lovelace/lovelace-problems/issues/32)
28 | - Rewrite README.me [\#11](https://github.com/project-lovelace/lovelace-problems/issues/11)
29 |
30 | **Merged pull requests:**
31 |
32 | - Run CI tests with Python 3.9 [\#58](https://github.com/project-lovelace/lovelace-problems/pull/58) ([ali-ramadhan](https://github.com/ali-ramadhan))
33 | - Add GitHub Actions CI pipeline [\#57](https://github.com/project-lovelace/lovelace-problems/pull/57) ([ali-ramadhan](https://github.com/ali-ramadhan))
34 | - Add a test suite [\#56](https://github.com/project-lovelace/lovelace-problems/pull/56) ([ali-ramadhan](https://github.com/ali-ramadhan))
35 | - Revise El Nino intensities problem [\#55](https://github.com/project-lovelace/lovelace-problems/pull/55) ([ali-ramadhan](https://github.com/ali-ramadhan))
36 |
37 | ## [v3.0.0](https://github.com/project-lovelace/lovelace-problems/tree/v3.0.0) (2021-03-25)
38 |
39 | [Full Changelog](https://github.com/project-lovelace/lovelace-problems/compare/v2.0...v3.0.0)
40 |
41 | **Fixed bugs:**
42 |
43 | - Game of Life: Oscillator test case expected result is wrong. [\#41](https://github.com/project-lovelace/lovelace-problems/issues/41)
44 |
45 | **Closed issues:**
46 |
47 | - Blood types: Add more test cases that return False [\#42](https://github.com/project-lovelace/lovelace-problems/issues/42)
48 | - Bug in verifying flight paths solution [\#37](https://github.com/project-lovelace/lovelace-problems/issues/37)
49 | - RNA string needs to be reversed! [\#36](https://github.com/project-lovelace/lovelace-problems/issues/36)
50 | - Share verify\_user\_solution function [\#35](https://github.com/project-lovelace/lovelace-problems/issues/35)
51 | - Arrays should not be verified recursively [\#26](https://github.com/project-lovelace/lovelace-problems/issues/26)
52 | - Remove jargon from problem descriptions [\#10](https://github.com/project-lovelace/lovelace-problems/issues/10)
53 |
54 | **Merged pull requests:**
55 |
56 | - Plump moose and compound interest [\#52](https://github.com/project-lovelace/lovelace-problems/pull/52) ([ali-ramadhan](https://github.com/ali-ramadhan))
57 | - Clean up README + sandbox and remove `output_str` [\#51](https://github.com/project-lovelace/lovelace-problems/pull/51) ([ali-ramadhan](https://github.com/ali-ramadhan))
58 | - Fix typo in `almost_pi.py` [\#50](https://github.com/project-lovelace/lovelace-problems/pull/50) ([ali-ramadhan](https://github.com/ali-ramadhan))
59 | - Fix: constant should be float [\#49](https://github.com/project-lovelace/lovelace-problems/pull/49) ([ali-ramadhan](https://github.com/ali-ramadhan))
60 | - Remove `verify_user_solution` boilerplate [\#48](https://github.com/project-lovelace/lovelace-problems/pull/48) ([ali-ramadhan](https://github.com/ali-ramadhan))
61 | - Deal in Python lists, not numpy arrays [\#47](https://github.com/project-lovelace/lovelace-problems/pull/47) ([ali-ramadhan](https://github.com/ali-ramadhan))
62 | - blood\_types: Add more test cases where no donors are available [\#44](https://github.com/project-lovelace/lovelace-problems/pull/44) ([benallan](https://github.com/benallan))
63 | - More robust test case and value checking [\#43](https://github.com/project-lovelace/lovelace-problems/pull/43) ([ali-ramadhan](https://github.com/ali-ramadhan))
64 | - Solve test cases when they're created [\#39](https://github.com/project-lovelace/lovelace-problems/pull/39) ([ali-ramadhan](https://github.com/ali-ramadhan))
65 |
66 | ## [v2.0](https://github.com/project-lovelace/lovelace-problems/tree/v2.0) (2019-07-22)
67 |
68 | [Full Changelog](https://github.com/project-lovelace/lovelace-problems/compare/v1.1...v2.0)
69 |
70 | **Fixed bugs:**
71 |
72 | - Bug in Note G solution [\#30](https://github.com/project-lovelace/lovelace-problems/issues/30)
73 | - Caesar cipher should not use a shift of zero [\#12](https://github.com/project-lovelace/lovelace-problems/issues/12)
74 |
75 | **Closed issues:**
76 |
77 | - Shave down the extreme test cases in game of life and the Note G problems [\#29](https://github.com/project-lovelace/lovelace-problems/issues/29)
78 | - Rename test case class names for each problem module [\#20](https://github.com/project-lovelace/lovelace-problems/issues/20)
79 | - Generic verify\_user\_solution function [\#17](https://github.com/project-lovelace/lovelace-problems/issues/17)
80 | - Store more physical constants in the PHYSICAL\_CONSTANTS dictionary [\#16](https://github.com/project-lovelace/lovelace-problems/issues/16)
81 | - Engine should use the solutions repo [\#21](https://github.com/project-lovelace/lovelace-problems/issues/21)
82 | - Recalibrate difficulty scale on the problems [\#9](https://github.com/project-lovelace/lovelace-problems/issues/9)
83 | - Move solutions to a private lovelace-solutions repo [\#8](https://github.com/project-lovelace/lovelace-problems/issues/8)
84 | - Warmup problem modules [\#6](https://github.com/project-lovelace/lovelace-problems/issues/6)
85 |
86 | **Merged pull requests:**
87 |
88 | - More test cases for warmup problems [\#34](https://github.com/project-lovelace/lovelace-problems/pull/34) ([ali-ramadhan](https://github.com/ali-ramadhan))
89 | - No huge cases [\#33](https://github.com/project-lovelace/lovelace-problems/pull/33) ([ali-ramadhan](https://github.com/ali-ramadhan))
90 | - Revise test cases [\#31](https://github.com/project-lovelace/lovelace-problems/pull/31) ([ali-ramadhan](https://github.com/ali-ramadhan))
91 | - More cleanup and better documentation/references for physical constants [\#28](https://github.com/project-lovelace/lovelace-problems/pull/28) ([ali-ramadhan](https://github.com/ali-ramadhan))
92 | - Remove template as there are plenty of examples [\#27](https://github.com/project-lovelace/lovelace-problems/pull/27) ([ali-ramadhan](https://github.com/ali-ramadhan))
93 | - Generic and more sophisticated test case verification [\#25](https://github.com/project-lovelace/lovelace-problems/pull/25) ([ali-ramadhan](https://github.com/ali-ramadhan))
94 | - Cleanup [\#24](https://github.com/project-lovelace/lovelace-problems/pull/24) ([ali-ramadhan](https://github.com/ali-ramadhan))
95 | - Less dumb class names [\#23](https://github.com/project-lovelace/lovelace-problems/pull/23) ([ali-ramadhan](https://github.com/ali-ramadhan))
96 | - No more publicly visible solutions [\#22](https://github.com/project-lovelace/lovelace-problems/pull/22) ([ali-ramadhan](https://github.com/ali-ramadhan))
97 | - Nuke solutions [\#19](https://github.com/project-lovelace/lovelace-problems/pull/19) ([ali-ramadhan](https://github.com/ali-ramadhan))
98 | - Small improvements to warmup problem modules [\#15](https://github.com/project-lovelace/lovelace-problems/pull/15) ([ali-ramadhan](https://github.com/ali-ramadhan))
99 | - Fix warmup problems [\#14](https://github.com/project-lovelace/lovelace-problems/pull/14) ([ali-ramadhan](https://github.com/ali-ramadhan))
100 | - Warmup problems [\#5](https://github.com/project-lovelace/lovelace-problems/pull/5) ([ali-ramadhan](https://github.com/ali-ramadhan))
101 |
102 | ## [v1.1](https://github.com/project-lovelace/lovelace-problems/tree/v1.1) (2019-01-05)
103 |
104 | [Full Changelog](https://github.com/project-lovelace/lovelace-problems/compare/v1.0...v1.1)
105 |
106 | ## [v1.0](https://github.com/project-lovelace/lovelace-problems/tree/v1.0) (2018-11-11)
107 |
108 | [Full Changelog](https://github.com/project-lovelace/lovelace-problems/compare/4ba7fefc64d9cbec91878a41ae5739bff8ba56ab...v1.0)
109 |
110 | **Closed issues:**
111 |
112 | - As a developer, I want a consistent and scalable way of describing the expected input/outputs for a TestCase object in each problem module. [\#1](https://github.com/project-lovelace/lovelace-problems/issues/1)
113 |
114 | **Merged pull requests:**
115 |
116 | - Alpha2018 [\#4](https://github.com/project-lovelace/lovelace-problems/pull/4) ([ali-ramadhan](https://github.com/ali-ramadhan))
117 | - Updating master/production with Basim's work. [\#3](https://github.com/project-lovelace/lovelace-problems/pull/3) ([ali-ramadhan](https://github.com/ali-ramadhan))
118 | - First d3 test works. [\#2](https://github.com/project-lovelace/lovelace-problems/pull/2) ([ali-ramadhan](https://github.com/ali-ramadhan))
119 |
120 |
121 |
122 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
123 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Basim Ramadhan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Project Lovelace problem modules
2 |
3 | [](#contributors-)
4 |
5 |
6 | [](https://github.com/project-lovelace/lovelace-problems/actions/workflows/ci.yml)
7 |
8 | This repository contains modules for generating test cases (inputs and outputs) for
9 | [Project Lovelace](http://projectlovelace.net) problems.
10 |
11 | Note that it relies on a private solutions repository to generate solutions.
12 | The solutions are kept private to avoid providing copy-pasteable solutions to every problem.
13 |
14 | ## New problem submission guide
15 |
16 | If you have an idea for a new problem please consider submitting a new problem, we love receiving new contributions!
17 | We discuss new problems on Discord mostly but you can also open a GitHub issue or post on Discourse to discuss. Also feel free to ask us any questions at all!
18 | Let us know if you're interested in submitting new problems so we can invite you to the [Project Lovelace GitHub organization](https://github.com/project-lovelace).
19 |
20 | There are three steps to submitting a new problem:
21 |
22 | 1. Open a pull request to [lovelace-solutions](https://github.com/project-lovelace/lovelace-solutions#new-problem-submission-guide) with the solution to the problem.
23 | 2. Open a pull request to lovelace-problems with code to generate test cases for the problem. (You are here!)
24 | 3. Open a pull request to [lovelace-website](https://github.com/project-lovelace/lovelace-website#new-problem-submission-guide) with the problem description, code stubs, and any visualization code.
25 |
26 | ### How to submit new problem test cases
27 |
28 | 1. Add a new problem module under the [`problems/`](https://github.com/project-lovelace/lovelace-problems/tree/main/problems) directory. Structure it similarly to the other problem modules. You can generate as many test cases as you want although it's good to cover all the interesting cases.
29 | 2. Open a pull request! Once all the tests pass it can be merged.
30 |
31 | ## Contributors ✨
32 |
33 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
34 |
35 |
36 |
37 |
38 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
--------------------------------------------------------------------------------
/inject_solutions.bash:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | gh repo clone project-lovelace/lovelace-solutions
4 | ln -s ../lovelace-solutions/python problems/solutions
5 |
--------------------------------------------------------------------------------
/problems/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/problems/ada_lovelaces_note_g.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import randint
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.ada_lovelaces_note_g import bernoulli
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "bernoulli"
12 | INPUT_VARS = ['n']
13 | OUTPUT_VARS = ['numerator', 'denominator']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {}
18 | ATOL = {}
19 | RTOL = {}
20 |
21 |
22 | class TestCaseType(TestCaseTypeEnum):
23 | ZEROTH = ("Zeroth Bernoulli number", 1)
24 | FIRST = ("First Bernoulli number", 1)
25 | SECOND = ("Second Bernoulli number", 1)
26 | THIRD = ("Third Bernoulli number", 1)
27 | RANDOM_EVEN = ("Even 2 < n < 50", 2)
28 | RANDOM_ODD = ("Odd 3 < n < 51", 2)
29 | LARGE_EVEN = ("Even 50 < n < 125", 1)
30 | LARGE_ODD = ("Odd 51 < n < 126", 1)
31 |
32 |
33 | class ProblemTestCase(TestCase):
34 | def input_tuple(self) -> tuple:
35 | return self.input['n'],
36 |
37 | def output_tuple(self) -> tuple:
38 | numerator = self.output['numerator']
39 | denominator = self.output['denominator']
40 | return numerator, denominator
41 |
42 |
43 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
44 | test_case = ProblemTestCase(test_type)
45 |
46 | if test_type is TestCaseType.ZEROTH:
47 | n = 0
48 |
49 | elif test_type is TestCaseType.FIRST:
50 | n = 1
51 |
52 | elif test_type is TestCaseType.SECOND:
53 | n = 2
54 |
55 | elif test_type is TestCaseType.THIRD:
56 | n = 3
57 |
58 | elif test_type is TestCaseType.RANDOM_EVEN:
59 | n = 2 * randint(2, 50)
60 |
61 | elif test_type is TestCaseType.RANDOM_ODD:
62 | n = 2 * randint(2, 50) + 1
63 |
64 | elif test_type is TestCaseType.LARGE_EVEN:
65 | n = 2 * randint(50, 125)
66 |
67 | elif test_type is TestCaseType.LARGE_ODD:
68 | n = 2 * randint(50, 125) + 1
69 |
70 | else:
71 | raise ValueError(f"Unrecognized test case: {test_type}")
72 |
73 | test_case.input['n'] = n
74 | test_case.output['numerator'], test_case.output['denominator'] = bernoulli(n)
75 |
76 | return test_case
77 |
--------------------------------------------------------------------------------
/problems/almost_pi.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import randint
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.almost_pi import almost_pi
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "almost_pi"
12 | INPUT_VARS = ['N']
13 | OUTPUT_VARS = ['pi']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {}
18 | ATOL = {}
19 | RTOL = {
20 | 'pi': 1e-5
21 | }
22 |
23 |
24 | class TestCaseType(TestCaseTypeEnum):
25 | SMALL_N = ("1 < n < 10", 2)
26 | MEDIUM_N = ("10 < n < 100", 2)
27 | LARGE_N = ("100 < n < 1,000", 1)
28 | HUGE_N = ("10,000 < n < 100,000", 1)
29 |
30 |
31 | class ProblemTestCase(TestCase):
32 | def input_tuple(self) -> tuple:
33 | return self.input['N'],
34 |
35 | def output_tuple(self) -> tuple:
36 | return self.output['pi'],
37 |
38 |
39 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
40 | test_case = ProblemTestCase(test_type)
41 |
42 | if test_type is TestCaseType.SMALL_N:
43 | N = randint(2, 10)
44 |
45 | elif test_type is TestCaseType.MEDIUM_N:
46 | N = randint(11, 100)
47 |
48 | elif test_type is TestCaseType.LARGE_N:
49 | N = randint(101, 1000)
50 |
51 | elif test_type is TestCaseType.HUGE_N:
52 | N = randint(10000, 100000)
53 |
54 | else:
55 | raise ValueError(f"Unrecognized test case: {test_type}")
56 |
57 | test_case.input['N'] = N
58 | test_case.output['pi'] = almost_pi(N)
59 |
60 | return test_case
61 |
--------------------------------------------------------------------------------
/problems/babylonian_spiral.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from numpy.random import randint
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.babylonian_spiral import babylonian_spiral
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "babylonian_spiral"
11 | INPUT_VARS = ["n_steps"]
12 | OUTPUT_VARS = ["x", "y"]
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {}
19 |
20 |
21 | class TestCaseType(TestCaseTypeEnum):
22 | FEW_STEPS = ("few steps", 3)
23 | MANY_STEPS = ("many steps", 2)
24 |
25 |
26 | class ProblemTestCase(TestCase):
27 | def input_tuple(self):
28 | return self.input["n_steps"],
29 |
30 | def output_tuple(self) -> tuple:
31 | return (self.output["x"], self.output["y"])
32 |
33 |
34 | def generate_test_case(test_type):
35 | test_case = ProblemTestCase(test_type)
36 |
37 | if test_type is TestCaseType.FEW_STEPS:
38 | n = randint(0, 20)
39 |
40 | elif test_type is TestCaseType.MANY_STEPS:
41 | n = randint(20, 1000)
42 |
43 | else:
44 | raise ValueError(f"Unrecognized test case: {test_type}")
45 |
46 | x, y = babylonian_spiral(n)
47 |
48 | test_case.input["n_steps"] = n
49 | test_case.output = {"x": x, "y": y}
50 |
51 | return test_case
52 |
--------------------------------------------------------------------------------
/problems/babylonian_square_roots.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from math import isclose
3 | from typing import Tuple
4 |
5 | from numpy.random import uniform, randint
6 |
7 | from problems.test_case import TestCase, TestCaseTypeEnum
8 | from problems.solutions.babylonian_square_roots import babylonian_sqrt
9 |
10 | logger = logging.getLogger(__name__)
11 |
12 | FUNCTION_NAME = "babylonian_sqrt"
13 | INPUT_VARS = ['n']
14 | OUTPUT_VARS = ['sqrt_n']
15 |
16 | STATIC_RESOURCES = []
17 |
18 | PHYSICAL_CONSTANTS = {}
19 | ATOL = {}
20 | RTOL = {
21 | 'sqrt_n': 1e-10
22 | }
23 |
24 |
25 | class TestCaseType(TestCaseTypeEnum):
26 | ZERO = ("zero", 1)
27 | SMALL = ("1 < n < 10", 2)
28 | LARGE = ("10 < n < 1,000,000", 2)
29 | SQUARE = ("square number", 1)
30 |
31 |
32 | class ProblemTestCase(TestCase):
33 | def input_tuple(self) -> tuple:
34 | return self.input['n'],
35 |
36 | def output_tuple(self) -> tuple:
37 | return self.output['sqrt_n'],
38 |
39 |
40 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
41 | test_case = ProblemTestCase(test_type)
42 |
43 | if test_type is TestCaseType.ZERO:
44 | n = 0
45 |
46 | elif test_type is TestCaseType.SMALL:
47 | n = uniform(1, 10)
48 |
49 | elif test_type is TestCaseType.LARGE:
50 | n = uniform(10, 1000000)
51 |
52 | elif test_type is TestCaseType.SQUARE:
53 | n = randint(5, 100)**2
54 |
55 | else:
56 | raise ValueError(f"Unrecognized test case: {test_type}")
57 |
58 | test_case.input['n'] = float(n)
59 | test_case.output['sqrt_n'] = float(babylonian_sqrt(n))
60 |
61 | return test_case
62 |
--------------------------------------------------------------------------------
/problems/blood_types.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from random import choice, choices, randint
3 | from typing import Tuple
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.blood_types import survive
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "survive"
11 | INPUT_VARS = ["patient_blood_type", "donated_blood"]
12 | OUTPUT_VARS = ["survive"]
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {"blood_types": ["A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+"]}
17 |
18 | ATOL = {}
19 | RTOL = {}
20 |
21 |
22 | class TestCaseType(TestCaseTypeEnum):
23 | NO_DONATIONS = ("No donations have been made.", 0)
24 | LUCKY_PATIENT = ("Lucky patient (O- is available)", 1)
25 | LUCKY_ABP_PATIENT = ("Lucky AB+ patient", 1)
26 | SLIM_PICKINGS = ("Slim pickings (few donations)", 2)
27 | WELL_STOCKED = ("Well stocked hospital (many donations)", 2)
28 | UNLUCKY_ONEG_PATIENT = ("Unlucky O- patient (No suitable donors available)", 1)
29 | UNLUCKY_OPOS_PATIENT = ("Unlucky O+ patient (No suitable donors available)", 1)
30 | UNLUCKY_ANEG_PATIENT = ("Unlucky A- patient (No suitable donors available)", 1)
31 | UNLUCKY_APOS_PATIENT = ("Unlucky A+ patient (No suitable donors available)", 1)
32 | UNLUCKY_BNEG_PATIENT = ("Unlucky B- patient (No suitable donors available)", 1)
33 | UNLUCKY_BPOS_PATIENT = ("Unlucky B+ patient (No suitable donors available)", 1)
34 | UNLUCKY_ABNEG_PATIENT = ("Unlucky AB- patient (No suitable donors available)", 1)
35 | UNLUCKY_ABPOS_PATIENT = ("Unlucky AB+ patient (No suitable donors available)", 1)
36 |
37 |
38 | class ProblemTestCase(TestCase):
39 | def input_tuple(self) -> tuple:
40 | return self.input["patient_blood_type"], self.input["donated_blood"]
41 |
42 | def output_tuple(self) -> tuple:
43 | return (self.output["survive"],)
44 |
45 |
46 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
47 | test_case = ProblemTestCase(test_type)
48 | blood_types = PHYSICAL_CONSTANTS["blood_types"]
49 |
50 | if test_type is TestCaseType.NO_DONATIONS:
51 | patient_blood_type = choice(blood_types)
52 | donated_blood = []
53 |
54 | elif test_type is TestCaseType.LUCKY_PATIENT:
55 | patient_blood_type = choice(blood_types)
56 | donated_blood = ["O-", "O-", "O-"]
57 |
58 | elif test_type is TestCaseType.LUCKY_ABP_PATIENT:
59 | patient_blood_type = "AB+"
60 | donated_blood = [choice(blood_types)]
61 |
62 | elif test_type is TestCaseType.SLIM_PICKINGS:
63 | patient_blood_type = choice(blood_types)
64 | donated_blood = choices(blood_types, k=randint(2, 3))
65 |
66 | elif test_type is TestCaseType.WELL_STOCKED:
67 | patient_blood_type = choice(blood_types)
68 | donated_blood = choices(blood_types, k=randint(8, 25))
69 |
70 | elif test_type is TestCaseType.UNLUCKY_ONEG_PATIENT:
71 | patient_blood_type = "O-"
72 | donated_blood = ["A-", "B-", "AB-", "A+", "B+", "AB+", "O+"]
73 |
74 | elif test_type is TestCaseType.UNLUCKY_OPOS_PATIENT:
75 | patient_blood_type = "O+"
76 | donated_blood = ["A-", "B-", "AB-", "A+", "B+", "AB+"]
77 |
78 | elif test_type is TestCaseType.UNLUCKY_ANEG_PATIENT:
79 | patient_blood_type = "A-"
80 | donated_blood = ["B-", "AB-", "A+", "B+", "AB+", "O+"]
81 |
82 | elif test_type is TestCaseType.UNLUCKY_APOS_PATIENT:
83 | patient_blood_type = "A+"
84 | donated_blood = ["B-", "AB-", "B+", "AB+"]
85 |
86 | elif test_type is TestCaseType.UNLUCKY_BNEG_PATIENT:
87 | patient_blood_type = "B-"
88 | donated_blood = ["A-", "AB-", "A+", "B+", "AB+", "O+"]
89 |
90 | elif test_type is TestCaseType.UNLUCKY_BPOS_PATIENT:
91 | patient_blood_type = "B+"
92 | donated_blood = ["A-", "AB-", "A+", "AB+"]
93 |
94 | elif test_type is TestCaseType.UNLUCKY_ABNEG_PATIENT:
95 | patient_blood_type = "AB-"
96 | donated_blood = ["A+", "B+", "AB+", "O+"]
97 |
98 | elif test_type is TestCaseType.UNLUCKY_ABPOS_PATIENT:
99 | patient_blood_type = "AB+"
100 | donated_blood = []
101 |
102 | else:
103 | raise ValueError(f"Unrecognized test case: {test_type}")
104 |
105 | test_case.input = {"patient_blood_type": patient_blood_type, "donated_blood": donated_blood}
106 |
107 | test_case.output["survive"] = survive(patient_blood_type, donated_blood)
108 |
109 | return test_case
110 |
--------------------------------------------------------------------------------
/problems/caesar_cipher.py:
--------------------------------------------------------------------------------
1 | import string
2 | import random
3 | import logging
4 | from typing import Tuple
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.caesar_cipher import break_caesar_cipher
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "break_caesar_cipher"
12 | INPUT_VARS = ['ciphertext', 'known_word']
13 | OUTPUT_VARS = ['decrypted_message']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {}
18 | ATOL = {}
19 | RTOL = {}
20 |
21 |
22 | class TestCaseType(TestCaseTypeEnum):
23 | MOBY_DICK = ('Moby dick', 1)
24 | THE_WIRE = ('Bunny Colvin (The Wire)', 1)
25 | RANDOM_STRING = ('random string', 1)
26 |
27 |
28 | class ProblemTestCase(TestCase):
29 | def input_tuple(self) -> tuple:
30 | return self.input['ciphertext'], self.input['known_word']
31 |
32 | def output_tuple(self) -> tuple:
33 | return self.output['decrypted_message'],
34 |
35 |
36 | def generate_random_string(length):
37 | return "".join(random.choice(string.ascii_lowercase) for _ in range(length))
38 |
39 |
40 | def randomly_insert_spaces(s):
41 | length = len(s)
42 | n = random.randint(int(length/10), int(length/4))
43 | for _ in range(n):
44 | pos = random.randint(0, length-1)
45 | s = s[:pos] + " " + s[pos:]
46 | return s
47 |
48 |
49 | def caesar_cipher(plaintext, shift):
50 | alphabet = string.ascii_lowercase
51 | shifted_alphabet = alphabet[shift:] + alphabet[:shift]
52 | table = str.maketrans(alphabet, shifted_alphabet)
53 | return plaintext.translate(table)
54 |
55 |
56 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
57 | test_case = ProblemTestCase(test_type)
58 |
59 | if test_type is TestCaseType.RANDOM_STRING:
60 | length = random.randint(50, 400)
61 | plaintext = randomly_insert_spaces(generate_random_string(length))
62 |
63 | elif test_type is TestCaseType.MOBY_DICK:
64 | plaintext = "Call me Ishmael Some years ago never mind how long precisely having little or no money in my purse and nothing particular to interest me on shore I thought I would sail about a little and see the watery part of the world".lower()
65 |
66 | elif test_type is TestCaseType.THE_WIRE:
67 | plaintext = "This drug thing this aint police work I mean I can send any fool with a badge and a gun to a corner to jack a crew and grab vials But policing I mean you call something a war and pretty soon everyone is going to be running around acting like warriors They gonna be running around on a damn crusade storming corners racking up body counts And when you at war you need a fucking enemy And pretty soon damn near everybody on every corner is your fucking enemy And soon, the neighborhood youre supposed to be policing thats just occupied territory".lower()
68 |
69 | else:
70 | raise ValueError(f"Unrecognized test case: {test_type}")
71 |
72 | known_word = random.choice(plaintext.split())
73 |
74 | shift = random.randint(1, 25)
75 | ciphertext = caesar_cipher(plaintext, shift).upper()
76 |
77 | test_case.input['ciphertext'] = ciphertext
78 | test_case.input['known_word'] = known_word
79 | test_case.output['decrypted_message'] = break_caesar_cipher(ciphertext, known_word)
80 |
81 | return test_case
82 |
--------------------------------------------------------------------------------
/problems/chaos.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from problems.test_case import TestCase, TestCaseTypeEnum
5 | from problems.solutions.chaos import logistic_map
6 |
7 | logger = logging.getLogger(__name__)
8 |
9 | FUNCTION_NAME = "logistic_map"
10 | INPUT_VARS = ['r']
11 | OUTPUT_VARS = ['x']
12 |
13 | STATIC_RESOURCES = []
14 |
15 | PHYSICAL_CONSTANTS = {}
16 | ATOL = {}
17 | RTOL = {
18 | 'x': 0.0001
19 | }
20 |
21 |
22 | class TestCaseType(TestCaseTypeEnum):
23 | DEATH = ('death', 1)
24 | QUICK_STABLE = ('quick stable', 1)
25 | FLUCTUATE_STABLE = ('fluctuate stable', 1)
26 | CHAOS = ('chaos', 1)
27 | DIVERGENCE = ('divergence', 1)
28 |
29 |
30 | class ProblemTestCase(TestCase):
31 | def input_tuple(self) -> tuple:
32 | return self.input['r'],
33 |
34 | def output_tuple(self) -> tuple:
35 | return self.output['x'],
36 |
37 |
38 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
39 | test_case = ProblemTestCase(test_type)
40 |
41 | if test_type is TestCaseType.DEATH:
42 | r = 1
43 |
44 | elif test_type is TestCaseType.QUICK_STABLE:
45 | r = 2
46 |
47 | elif test_type is TestCaseType.FLUCTUATE_STABLE:
48 | r = 3
49 |
50 | elif test_type is TestCaseType.CHAOS:
51 | r = 3.5
52 |
53 | elif test_type is TestCaseType.DIVERGENCE:
54 | r = 3.6
55 |
56 | else:
57 | raise ValueError(f"Unrecognized test case: {test_type}")
58 |
59 | test_case.input['r'] = float(r)
60 | test_case.output['x'] = logistic_map(r)
61 |
62 | return test_case
63 |
--------------------------------------------------------------------------------
/problems/colorful_resistors.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | import numpy as np
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.colorful_resistors import resistance
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "resistance"
12 | STATIC_RESOURCES = []
13 |
14 | INPUT_VARS = ['colors']
15 | OUTPUT_VARS = ['nominal_resistance', 'minimum_resistance', 'maximum_resistance']
16 |
17 | PHYSICAL_CONSTANTS = {
18 | 'digits': {
19 | 'black': 0,
20 | 'brown': 1,
21 | 'red': 2,
22 | 'orange': 3,
23 | 'yellow': 4,
24 | 'green': 5,
25 | 'blue': 6,
26 | 'violet': 7,
27 | 'grey': 8,
28 | 'white': 9
29 | },
30 | 'multiplier': {
31 | 'pink': 0.001,
32 | 'silver': 0.01,
33 | 'gold': 0.1,
34 | 'black': 1,
35 | 'brown': 10,
36 | 'red': 100,
37 | 'orange': 10**3,
38 | 'yellow': 10**4,
39 | 'green': 10**5,
40 | 'blue': 10**6,
41 | 'violet': 10**7,
42 | 'grey': 10**8,
43 | 'white': 10**9
44 | },
45 | 'tolerance': {
46 | 'none': 0.2,
47 | 'silver': 0.1,
48 | 'gold': 0.05,
49 | 'brown': 0.01,
50 | 'red': 0.02,
51 | 'green': 0.005,
52 | 'blue': 0.0025,
53 | 'violet': 0.001,
54 | 'grey': 0.0005
55 | }
56 | }
57 |
58 | ATOL = {}
59 | RTOL = {
60 | 'nominal_resistance': 1e-6,
61 | 'minimum_resistance': 1e-6,
62 | 'maximum_resistance': 1e-6
63 | }
64 |
65 |
66 | class TestCaseType(TestCaseTypeEnum):
67 | ZERO_RESISTOR = ('zero resistor', 1)
68 | FOUR_BAND = ('four band resistor', 3)
69 | FIVE_BAND = ('five band resistor', 3)
70 |
71 |
72 | class ProblemTestCase(TestCase):
73 | def input_tuple(self) -> tuple:
74 | return self.input['colors'],
75 |
76 | def output_tuple(self) -> tuple:
77 | nominal_R = self.output['nominal_resistance']
78 | minimum_R = self.output['minimum_resistance']
79 | maximum_R = self.output['maximum_resistance']
80 | return nominal_R, minimum_R, maximum_R
81 |
82 |
83 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
84 | digits = PHYSICAL_CONSTANTS['digits']
85 | multiplier = PHYSICAL_CONSTANTS['multiplier']
86 | tolerance = PHYSICAL_CONSTANTS['tolerance']
87 |
88 | test_case = ProblemTestCase(test_type)
89 |
90 | # We use the str() of each random choice as np.random.choice()
91 | # returns an np.str_ object and the test cases will fail if the user
92 | # hasn't imported numpy: we want a string, not an np.str_!
93 | if test_type is TestCaseType.ZERO_RESISTOR:
94 | colors = ['black']
95 |
96 | elif test_type is TestCaseType.FOUR_BAND:
97 | band_color1 = str(np.random.choice(list(digits.keys())))
98 | band_color2 = str(np.random.choice(list(digits.keys())))
99 | multiplier_color = str(np.random.choice(list(multiplier.keys())))
100 | tolerance_color = str(np.random.choice(list(tolerance.keys())))
101 | colors = [band_color1, band_color2, multiplier_color, tolerance_color]
102 |
103 | elif test_type is TestCaseType.FIVE_BAND:
104 | band_color1 = str(np.random.choice(list(digits.keys())))
105 | band_color2 = str(np.random.choice(list(digits.keys())))
106 | band_color3 = str(np.random.choice(list(digits.keys())))
107 | multiplier_color = str(np.random.choice(list(multiplier.keys())))
108 | tolerance_color = str(np.random.choice(list(tolerance.keys())))
109 | colors = [band_color1, band_color2, band_color3, multiplier_color, tolerance_color]
110 |
111 | else:
112 | raise ValueError(f"Unrecognized test case: {test_type}")
113 |
114 | test_case.input['colors'] = colors
115 |
116 | nominal_R, minimum_R, maximum_R = resistance(colors)
117 | test_case.output['nominal_resistance'] = nominal_R
118 | test_case.output['minimum_resistance'] = minimum_R
119 | test_case.output['maximum_resistance'] = maximum_R
120 |
121 | return test_case
122 |
--------------------------------------------------------------------------------
/problems/compound_interest.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from numpy.random import uniform, randint
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.compound_interest import compound_interest
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "compound_interest"
11 | INPUT_VARS = ["amount", "rate", "years"]
12 | OUTPUT_VARS = ["new_amount"]
13 |
14 | STATIC_RESOURCES = []
15 | PHYSICAL_CONSTANTS = {}
16 |
17 | ATOL = {}
18 | RTOL = {
19 | "new_amount": 1e-6
20 | }
21 |
22 |
23 | class TestCaseType(TestCaseTypeEnum):
24 | NO_INTEREST = ("No interest", 1)
25 | SAVINGS = ("High-rate savings account", 1)
26 | SP_500 = ("S&P 500 average annual return", 1)
27 | CREDIT_CARD = ("Credit card debt", 1)
28 | RANDOM = ("Random", 2)
29 |
30 |
31 | class ProblemTestCase(TestCase):
32 | def input_tuple(self):
33 | return self.input["amount"], self.input["rate"], self.input["years"]
34 |
35 | def output_tuple(self):
36 | return self.output["new_amount"],
37 |
38 |
39 | def generate_test_case(test_type):
40 | test_case = ProblemTestCase(test_type)
41 |
42 | if test_type is TestCaseType.NO_INTEREST:
43 | amount = uniform(10, 1000)
44 | rate = 0.0
45 | years = randint(1, 10)
46 |
47 | elif test_type is TestCaseType.SAVINGS:
48 | amount = uniform(100, 25000)
49 | rate = 0.005
50 | years = randint(10, 25)
51 |
52 | elif test_type is TestCaseType.SP_500:
53 | amount = uniform(10000, 500000)
54 | rate = 0.1
55 | years = randint(7, 30)
56 |
57 | elif test_type is TestCaseType.CREDIT_CARD:
58 | amount = uniform(250, 10000)
59 | rate = 0.1
60 | years = randint(7, 30)
61 |
62 | elif test_type is TestCaseType.RANDOM:
63 | amount = uniform(0, 100000)
64 | rate = uniform(0, 0.25)
65 | years = randint(0, 30)
66 |
67 | else:
68 | raise ValueError(f"Unrecognized test case: {test_type}")
69 |
70 | test_case.input["amount"] = amount
71 | test_case.input["rate"] = rate
72 | test_case.input["years"] = years
73 | test_case.output["new_amount"] = compound_interest(amount, rate, years)
74 |
75 | return test_case
76 |
--------------------------------------------------------------------------------
/problems/correlation_does_not_imply_causation.py:
--------------------------------------------------------------------------------
1 | import os
2 | import csv
3 | import logging
4 | from typing import Tuple
5 |
6 | from numpy import array
7 | from numpy.random import rand, randint
8 |
9 | from problems.test_case import TestCase, TestCaseTypeEnum
10 | from problems.solutions.correlation_does_not_imply_causation import correlation_coefficient
11 |
12 | logger = logging.getLogger(__name__)
13 |
14 | FUNCTION_NAME = "correlation_coefficient"
15 | INPUT_VARS = ['x', 'y']
16 | OUTPUT_VARS = ['r']
17 |
18 | STATIC_RESOURCES = ["spurious_xy.csv"]
19 |
20 | PHYSICAL_CONSTANTS = {}
21 | ATOL = {
22 | 'r': 0.0001
23 | }
24 | RTOL = {}
25 |
26 |
27 | class TestCaseType(TestCaseTypeEnum):
28 | SPURIOUS_DATASET = ('spurious dataset', 1)
29 | RANDOM_DATASET = ('random dataset', 1)
30 | UNKNOWN = ('unknown case', 0)
31 |
32 |
33 | class ProblemTestCase(TestCase):
34 | def input_tuple(self) -> tuple:
35 | return self.input['x'], self.input['y']
36 |
37 | def output_tuple(self) -> tuple:
38 | return self.output['r']
39 |
40 |
41 | def write_random_dataset_csv(x, y):
42 | cwd = os.path.dirname(os.path.abspath(__file__))
43 | csv_filename = os.path.join(cwd, "..", "resources", "correlation_does_not_imply_causation", "random_xy.csv")
44 | with open(csv_filename, 'w') as outfile:
45 | xy_writer = csv.writer(outfile, delimiter=',')
46 | for i in range(len(x)):
47 | xy_writer.writerow((x[i], y[i]))
48 |
49 |
50 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
51 | test_case = ProblemTestCase(test_type)
52 |
53 | if test_type is TestCaseType.SPURIOUS_DATASET:
54 | csv_filepath = "../resources/correlation_does_not_imply_causation/spurious_xy.csv"
55 | x = []
56 | y = []
57 |
58 | with open(csv_filepath) as csvfile:
59 | xy_reader = csv.reader(csvfile, delimiter=',')
60 | for row in xy_reader:
61 | x.append(float(row[0]))
62 | y.append(float(row[1]))
63 |
64 | dataset_filename = "spurious_xy.csv"
65 |
66 | elif test_type is TestCaseType.RANDOM_DATASET:
67 | N = randint(10, 100)
68 | x = rand(N).tolist()
69 | y = rand(N).tolist()
70 | write_random_dataset_csv(x, y)
71 | dataset_filename = "random_xy.csv"
72 | test_case.input['DYNAMIC_RESOURCES'] = [dataset_filename]
73 |
74 | else:
75 | raise ValueError(f"Unrecognized test case: {test_type}")
76 |
77 | test_case.input['x'] = x
78 | test_case.input['y'] = y
79 | test_case.input['dataset_filename'] = dataset_filename
80 | test_case.output['r'] = correlation_coefficient(x, y)
81 |
82 | return test_case
83 |
--------------------------------------------------------------------------------
/problems/definite_integrals.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 | from numpy import pi, exp, sin, linspace, ndarray
4 | from numpy.random import randint, uniform
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.definite_integrals import area_of_rectangles
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "area_of_rectangles"
12 | INPUT_VARS = ['rectangle_heights', 'rectangle_width']
13 | OUTPUT_VARS = ['area']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {}
18 | ATOL = {
19 | 'area': 1e-6
20 | }
21 | RTOL = {}
22 |
23 |
24 | class TestCaseType(TestCaseTypeEnum):
25 | ZERO = ("Zero function (rectangles with no height)", 1)
26 | CONSTANT = ("Constant function (all rectangles have the same height)", 1)
27 | LINEAR = ("Linear function", 1)
28 | QUADRATIC = ("Quadratic polynomial", 1)
29 | EXPONENTIAL = ("Exponential function", 1)
30 | SINE = ("Sine function", 1)
31 | RANDOM = ("Random function", 1)
32 |
33 |
34 | class ProblemTestCase(TestCase):
35 | def input_tuple(self) -> tuple:
36 | return self.input['rectangle_heights'], self.input['rectangle_width']
37 |
38 | def output_tuple(self) -> tuple:
39 | return self.output['area'],
40 |
41 |
42 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
43 | test_case = ProblemTestCase(test_type)
44 |
45 | if test_type is TestCaseType.ZERO:
46 | N = randint(5, 10)
47 | f = [0.0 for _ in range(N)]
48 | dx = 1
49 |
50 | elif test_type is TestCaseType.CONSTANT:
51 | N = randint(5, 10)
52 | c = uniform(-5, 5)
53 | f = [c for _ in range(N)]
54 | dx = 1
55 |
56 | elif test_type is TestCaseType.LINEAR:
57 | N = randint(10, 20)
58 | x1, x2 = uniform(0, 2), uniform(3, 5)
59 | dx = (x2 - x1) / N
60 |
61 | a, b = uniform(-5, 5), uniform(-5, 5)
62 | f = [a*x + b for x in linspace(x1, x2, N)]
63 |
64 | elif test_type is TestCaseType.QUADRATIC:
65 | N = randint(15, 30)
66 | x1, x2 = uniform(-5, -1), uniform(2, 5)
67 | dx = (x2 - x1) / N
68 |
69 | a, b, c = uniform(-1, 1), uniform(-1, 1), uniform(-1, 1)
70 | f = [a*x**2 + b*x + c for x in linspace(x1, x2, N)]
71 |
72 | elif test_type is TestCaseType.EXPONENTIAL:
73 | N = randint(20, 40)
74 | x1, x2 = 0, uniform(3, 5)
75 | dx = (x2 - x1) / N
76 | f = [exp(x) for x in linspace(x1, x2, N)]
77 |
78 | elif test_type is TestCaseType.SINE:
79 | N = randint(50, 75)
80 | x1, x2 = 0, 2*pi
81 | dx = (x2 - x1) / N
82 | f = [sin(x) for x in linspace(x1, x2, N)]
83 |
84 | elif test_type is TestCaseType.RANDOM:
85 | N = randint(40, 60)
86 | x1, x2 = 0, 1
87 | dx = (x2 - x1) / N
88 | f = uniform(-1, 1, size=N).tolist()
89 |
90 | else:
91 | raise ValueError(f"Unrecognized test case: {test_type}")
92 |
93 | test_case.input = {
94 | "rectangle_heights": f,
95 | "rectangle_width": float(dx)
96 | }
97 |
98 | test_case.output['area'] = float(area_of_rectangles(f, dx))
99 |
100 | return test_case
101 |
--------------------------------------------------------------------------------
/problems/dna_transcription.py:
--------------------------------------------------------------------------------
1 | import random
2 | import logging
3 | from typing import Tuple
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.dna_transcription import rna
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "rna"
11 | INPUT_VARS = ['dna_str']
12 | OUTPUT_VARS = ['rna_str']
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {}
19 |
20 |
21 | class TestCaseType(TestCaseTypeEnum):
22 | SHORT_TRNA = ('tRNA-SeC-TCA-2-1', 1)
23 | INSULIN = ('Insulin [Homo sapiens (human)]', 1)
24 | RANDOM = ('Randomly generated DNA sequence', 1)
25 |
26 |
27 | class ProblemTestCase(TestCase):
28 | def input_tuple(self) -> tuple:
29 | return self.input['dna_str'],
30 |
31 | def output_tuple(self) -> tuple:
32 | return self.output['rna_str'],
33 |
34 |
35 | def generate_dna_sequence(length):
36 | return "".join(random.choice("ATGC") for _ in range(length))
37 |
38 |
39 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
40 | test_case = ProblemTestCase(test_type)
41 |
42 | if test_type == TestCaseType.RANDOM:
43 | dna_length = random.randint(5, 100)
44 | dna_str = generate_dna_sequence(dna_length)
45 |
46 | elif test_type == TestCaseType.SHORT_TRNA:
47 | dna_str = "CTCGGATGATCCTCAGTGGTCTGGGGTGCAGGCTTCAAACCTGTAGCTGTCTAGTGACAGAGTGGTTCAATTCCACCTTTGTAGG"
48 |
49 | elif test_type == TestCaseType.INSULIN:
50 | dna_str = "GAGAGCCACTGCATGCTGGGCCTGGCCGGCGTTGGCACCTGTGGGCACCCAGAGAGCGTGGAGAGAGCTGGGAGGGGCTCACAACAGTGCCGGGAAGTGGGGCTTGGCCCAGGGCCCCCAAGACACACAGACGGCACAGCAGGGCTGGTTCAAGGGCTTTATTCCATCTCTCTCGGTGCAGGAGGCGGCGGGTGTGGGGCTGCCTGCGGGCTGCGTCTAGTTGCAGTAGTTCTCCAGCTGGTAGAGGGAGCAGATGCTGGTACAGCATTGTTCCACAATGCCACGCTTCTGCAGGGACCCCTCCAGGGCCAAGGGCTGCAGGCTGCCTGCACCAGGGCCCCCGCCCAGCTCCACCTGCCCCACTGCCAGGACGTGCCGCGCAGAGCAGGTTCCGGAACAGCGGCGAGGCAGAGGGACACAGGAGGACACAGTCAGGGAGACACAGTGCCCGCCTGCCCGCCAGCCCTAGGTCGCACTCCCACCCATCTCCAGCCGGGCTGGACCCAGGTTAGAGGGAGGGTCACCCACACTGGGTGTGGACCTACAGGCCCCAACGCCCACATGTCCCACCTCCTTCCCCCGCCCCGGGGCAGCGTCACAGTGGGAGCCTGAACAGGTGATCCCAGTACTTCTCCCCAGGGCCTGTCCCCAGCATCTTCCCCATCTCCTGACTATGGAGCTGCCGTGAGGCCTGGCGACAGGGGTCTGGCCCACTCAGGCAGGCAGCCACGCCCTCCTCCGGGCGTGATGGGGTGTTCGCCCAGAGGCAGGCAGCGTGGGGCACCCTGTGACCCCAGGTCACCCAGGACTTTACTTAACAAAACACTTGAATCTGCGGTCATCAAATGAGGGTGGAGAAATGGGCTGCGGGGCATTTGTTTGAGGGGCGAGTGGAGGGAGGAGCGTGCCCACCCTCTGATGTATCTCGGGGCTGCCGAAGCCAACACCGTCCTCAGGCTGAGATTCTGACTGGGCCACAGGGAGCTGGTCACTTTTAGGACGTGACCAAGAGAACTTCTTTTTAAAAAAGTGCACCTGACCCCCTGCTGGGTGGCAGCCTCCTGCCCCCTTCTGCCCATGCTGGGTGGGAGCGCCAGGAGCAGGGGGTGGCTGGGGGCGGCCAGGGGCAGCAATGGGCAGTTGGCTCACCCTGCAGGTCCTCTGCCTCCCGGCGGGTCTTGGGTGTGTAGAAGAAGCCTCGTTCCCCGCACACTAGGTAGAGAGCTTCCACCAGGTGTGAGCCGCACAGGTGTTGGTTCACAAAGGCTGCGGCTGGGTCAGGTCCCCAGAGGGCCAGCAGCGCCAGCAGGGGCAGGAGGCGCATCCACAGGGCCATGGCAGAAGGACAGTGATCTGGGAGACAGGCAGGGCTGAGGCAGGCTGAAGGCCAGGTGCCCTGCCTTGGGGCCCCTGGGCTCACCCCCACATGCTTCACGAGCCCAGCCACGTCCTCCCTGCTGCAGAGCTGGGGCCTGGGGTCCAGCCACCCTGGAATCCTGAGCCCACCTGACGCAAAGGCCCTTGGAACAGACCTGCTTGATGGCCTCTTCTGATGCAGCCTGTCCTGGAGGGCTGAGGGCTGCTGGGCCCCCGCTGGCTTTATAGTCTCAGAGCCCATCTCCCCTACCTCTCAACCCCTGCCGCCTGGCCCATTAGGGCCTGGGGTGGGGGGGTCGGCAGATGGCTGGGGGCTGAGGCTGCAATTTCCGGACCATTT"
51 |
52 | else:
53 | raise ValueError(f"Unrecognized test case: {test_type}")
54 |
55 | test_case.input['dna_str'] = dna_str
56 | test_case.output['rna_str'] = rna(dna_str)
57 |
58 | return test_case
59 |
--------------------------------------------------------------------------------
/problems/earthquake_epicenters.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy import array, pi, sin, cos
5 | from numpy.linalg import norm
6 | from numpy.random import uniform, choice
7 |
8 | from problems.test_case import TestCase, TestCaseTypeEnum
9 | from problems.solutions.earthquake_epicenters import earthquake_epicenter
10 |
11 | logger = logging.getLogger(__name__)
12 |
13 | FUNCTION_NAME = "earthquake_epicenter"
14 | INPUT_VARS = ['x1', 'y1', 't1', 'x2', 'y2', 't2', 'x3', 'y3', 't3']
15 | OUTPUT_VARS = ['x', 'y']
16 |
17 | STATIC_RESOURCES = []
18 |
19 | PHYSICAL_CONSTANTS = {
20 | 'v': 6.0 # velocity of seismic waves [km/s]
21 | }
22 |
23 | ATOL = {
24 | 'x': 0.0001, # [km]
25 | 'y': 0.0001 # [km]
26 | }
27 | RTOL = {}
28 |
29 |
30 | class TestCaseType(TestCaseTypeEnum):
31 | GENERAL = ("General case", 3)
32 | ZERO_CASE = ("Zero case", 1)
33 | EQUIDISTANT = ("Equidistant case", 1)
34 |
35 |
36 | class ProblemTestCase(TestCase):
37 | def input_tuple(self) -> tuple:
38 | return (self.input['x1'], self.input['y1'], self.input['t1'],
39 | self.input['x2'], self.input['y2'], self.input['t2'],
40 | self.input['x3'], self.input['y3'], self.input['t3'])
41 |
42 | def output_tuple(self) -> tuple:
43 | return self.output['x'], self.output['y']
44 |
45 |
46 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
47 | test_case = ProblemTestCase(test_type)
48 |
49 | if test_type is TestCaseType.GENERAL:
50 | r0 = uniform(-100, 100, 2)
51 | r1 = uniform(-100, 100, 2)
52 | r2 = uniform(-100, 100, 2)
53 | r3 = uniform(-100, 100, 2)
54 |
55 | elif test_type is TestCaseType.ZERO_CASE:
56 | r0 = uniform(-100, 100, 2)
57 | zero_station = choice([1, 2, 3])
58 | if zero_station == 1:
59 | r1 = r0
60 | r2 = uniform(-100, 100, 2)
61 | r3 = uniform(-100, 100, 2)
62 | elif zero_station == 2:
63 | r1 = uniform(-100, 100, 2)
64 | r2 = r0
65 | r3 = uniform(-100, 100, 2)
66 | else:
67 | r1 = uniform(-100, 100, 2)
68 | r2 = uniform(-100, 100, 2)
69 | r3 = r0
70 |
71 | elif test_type is TestCaseType.EQUIDISTANT:
72 | r0 = uniform(-10, 10, 2) # Place the earthquake near the origin.
73 | d = uniform(10, 90) # Distance to all the stations. Max=90 ensures we stay inside the box.
74 | theta = uniform(0, 2*pi, 3) # Choose three angles to place the stations at a distance d away.
75 |
76 | r1 = r0 + d * array([cos(theta[0]), sin(theta[0])])
77 | r2 = r0 + d * array([cos(theta[1]), sin(theta[1])])
78 | r3 = r0 + d * array([cos(theta[2]), sin(theta[2])])
79 |
80 | else:
81 | raise ValueError(f"Unrecognized test case: {test_type}")
82 |
83 | v = PHYSICAL_CONSTANTS['v']
84 |
85 | t1 = norm(r1-r0) / v
86 | t2 = norm(r2-r0) / v
87 | t3 = norm(r3-r0) / v
88 |
89 | # Convert to float so the user gets Python floats and not numpy floats.
90 | test_case.input = {
91 | 'x1': float(r1[0]),
92 | 'y1': float(r1[1]),
93 | 't1': float(t1),
94 | 'x2': float(r2[0]),
95 | 'y2': float(r2[1]),
96 | 't2': float(t2),
97 | 'x3': float(r3[0]),
98 | 'y3': float(r3[1]),
99 | 't3': float(t3)
100 | }
101 |
102 | # We already know the solution by constructions.
103 | test_case.output['x'] = r0[0]
104 | test_case.output['y'] = r0[1]
105 |
106 | return test_case
107 |
--------------------------------------------------------------------------------
/problems/el_nino_intensities.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import randint
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "enso_classification"
11 | INPUT_VARS = ['year']
12 | OUTPUT_VARS = ['enso_classification', 'enso_intensity']
13 |
14 | STATIC_RESOURCES = ["mei.ext_index.txt"]
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {}
19 |
20 |
21 | class TestCaseType(TestCaseTypeEnum):
22 | QUIET_YEAR = ("Quiet year", 1)
23 | WEAK_EL_NINO = ("Weak El Niño", 1)
24 | WEAK_LA_NINA = ("Weak La Niña", 1)
25 | MODERATE_EL_NINO = ("Moderate El Niño", 1)
26 | MODERATE_LA_NINA = ("Moderate La Niña", 1)
27 | STRONG_EL_NINO = ("Strong El Niño", 1)
28 | STRONG_LA_NINA = ("Strong La Niña", 1)
29 | VERY_STRONG_EL_NINO = ("Very strong El Niño", 1)
30 | VERY_STRONG_LA_NINA = ("Very strong La Niña", 1)
31 | RANDOM_YEAR = ("Random year", 5)
32 |
33 |
34 | class ProblemTestCase(TestCase):
35 | def input_tuple(self) -> tuple:
36 | return self.input["year"],
37 |
38 | def output_tuple(self) -> tuple:
39 | return self.output['enso_classification'], self.output['enso_intensity']
40 |
41 |
42 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
43 | test_case = ProblemTestCase(test_type)
44 |
45 | if test_type is TestCaseType.QUIET_YEAR:
46 | year = 1996
47 |
48 | elif test_type is TestCaseType.WEAK_EL_NINO:
49 | year = 1948
50 |
51 | elif test_type is TestCaseType.WEAK_LA_NINA:
52 | year = 1871
53 |
54 | elif test_type is TestCaseType.MODERATE_EL_NINO:
55 | year = 1914
56 |
57 | elif test_type is TestCaseType.MODERATE_LA_NINA:
58 | year = 2000
59 |
60 | elif test_type is TestCaseType.STRONG_EL_NINO:
61 | year = 1993
62 |
63 | elif test_type is TestCaseType.STRONG_LA_NINA:
64 | year = 1890
65 |
66 | elif test_type is TestCaseType.VERY_STRONG_EL_NINO:
67 | year = 2016
68 |
69 | elif test_type is TestCaseType.VERY_STRONG_LA_NINA:
70 | year = 1955
71 |
72 | elif test_type is TestCaseType.RANDOM_YEAR:
73 | year = randint(1871, 2016)
74 |
75 | else:
76 | raise ValueError(f"Unrecognized test case: {test_type}")
77 |
78 | test_case.input['year'] = year
79 |
80 | # Delay solution import until tests can handle static resources:
81 | # https://github.com/project-lovelace/lovelace-problems/issues/60
82 | from problems.solutions.el_nino_intensities import enso_classification
83 | classification, intensity = enso_classification(year)
84 |
85 | test_case.output['enso_classification'] = classification
86 | test_case.output['enso_intensity'] = intensity
87 |
88 | return test_case
89 |
--------------------------------------------------------------------------------
/problems/exponential_growth.py:
--------------------------------------------------------------------------------
1 | import random
2 | import logging
3 |
4 | from problems.test_case import TestCase, TestCaseTypeEnum
5 | from problems.solutions.exponential_growth import exponential_growth
6 |
7 | logger = logging.getLogger(__name__)
8 |
9 | FUNCTION_NAME = "exponential_growth"
10 |
11 | INPUT_VARS = ["x0", "k", "dt", "N"]
12 | OUTPUT_VARS = ["x"]
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {
19 | "x": 1e-8
20 | }
21 |
22 | class ProblemTestCase(TestCase):
23 | def input_tuple(self):
24 | return (self.input["x0"], self.input["k"], self.input["dt"], self.input["N"])
25 |
26 | def output_tuple(self):
27 | return self.output["x"],
28 |
29 | class TestCaseType(TestCaseTypeEnum):
30 | NO_GROWTH_OR_DECAY = ("No growth or decay", 1)
31 | SLOW_GROWTH = ("Slow growth (small k)", 1)
32 | FAST_GROWTH = ("Fast growth (large k)", 1)
33 | DECAY = ("Exponential decay (k < 0)", 1)
34 | SMALL_TIME_STEPS = ("Small time steps (more accurate solution)", 1)
35 | LARGE_TIME_STEPS = ("Large time steps (less accurate solution)", 1)
36 | LONG_INTEGRATION = ("Long integration time", 1)
37 | NUMERICAL_NOISE = ("Numerical noise", 1)
38 | NUMERICAL_OSCILLATIONS = ("Numerical oscillations", 1)
39 | NUMERICAL_INSTABILITY = ("Numerical instability", 1)
40 |
41 | def generate_test_case(test_type):
42 | test_case = ProblemTestCase(test_type)
43 |
44 | if test_type is TestCaseType.NO_GROWTH_OR_DECAY:
45 | x0 = random.uniform(-10, 10)
46 | k = 0
47 | dt = random.uniform(0.1, 1)
48 | N = random.randint(10, 15)
49 |
50 | elif test_type is TestCaseType.SLOW_GROWTH:
51 | x0 = random.uniform(-10, 10)
52 | k = random.uniform(0.01, 0.1)
53 | dt = random.uniform(0.1, 0.2)
54 | N = random.randint(20, 50)
55 |
56 | elif test_type is TestCaseType.FAST_GROWTH:
57 | x0 = random.uniform(-10, 10)
58 | k = random.uniform(4, 6)
59 | dt = random.uniform(0.1, 0.2)
60 | N = random.randint(20, 50)
61 |
62 | elif test_type is TestCaseType.DECAY:
63 | x0 = random.uniform(-10, 10)
64 | k = - random.uniform(1, 2)
65 | dt = random.uniform(0.1, 0.2)
66 | N = random.randint(20, 50)
67 |
68 | elif test_type is TestCaseType.SMALL_TIME_STEPS:
69 | x0 = random.uniform(1, 10)
70 | k = random.uniform(1, 1.2)
71 | dt = random.uniform(0.01, 0.02)
72 | N = random.randint(10, 15)
73 |
74 | elif test_type is TestCaseType.LARGE_TIME_STEPS:
75 | x0 = random.uniform(1, 10)
76 | k = random.uniform(1, 1.2)
77 | dt = random.uniform(0.4, 0.5)
78 | N = random.randint(10, 15)
79 |
80 | elif test_type is TestCaseType.LONG_INTEGRATION:
81 | x0 = random.uniform(-10, 10)
82 | k = random.uniform(0.2, 0.5)
83 | dt = random.uniform(0.2, 0.3)
84 | N = random.randint(200, 300)
85 |
86 | elif test_type is TestCaseType.NUMERICAL_NOISE:
87 | x0 = random.uniform(1, 10)
88 | k = -2.5
89 | dt = 0.7
90 | N = random.randint(20, 30)
91 |
92 | elif test_type is TestCaseType.NUMERICAL_OSCILLATIONS:
93 | x0 = random.uniform(1, 10)
94 | k = -2.5
95 | dt = 0.8
96 | N = random.randint(20, 30)
97 |
98 | elif test_type is TestCaseType.NUMERICAL_INSTABILITY:
99 | x0 = random.uniform(1, 10)
100 | k = -2.5
101 | dt = 0.9
102 | N = random.randint(30, 40)
103 |
104 | else:
105 | raise ValueError(f"Unrecognized test case: {test_type}")
106 |
107 | test_case.input["x0"] = x0
108 | test_case.input["k"] = k
109 | test_case.input["dt"] = dt
110 | test_case.input["N"] = N
111 | test_case.output["x"] = exponential_growth(x0, k, dt, N)
112 |
113 | return test_case
114 |
--------------------------------------------------------------------------------
/problems/flight_paths.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import uniform
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.flight_paths import haversine_distance
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "haversine_distance"
12 | INPUT_VARS = ['lat1', 'lon1', 'lat2', 'lon2']
13 | OUTPUT_VARS = ['distance']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {
18 | 'R': 6372.1, # Radius of the Earth [km]
19 |
20 | 'New_York_lat': 40.7128,
21 | 'New_York_lon': -74.0060,
22 | 'Madrid_lat': 40.4168,
23 | 'Madrid_lon': -3.7038,
24 | 'Vancouver_lat': 49.2827,
25 | 'Vancouver_lon': -123.1207,
26 | 'St_Johns_lat': 47.5615,
27 | 'St_Johns_lon': -52.7126,
28 | 'Ushuaia_lat': -54.8019,
29 | 'Ushuaia_lon': -68.3030,
30 | 'Alert_lat': 82.5018,
31 | 'Alert_lon': -62.3481,
32 | 'Dakar_lat': 14.7167,
33 | 'Dakar_lon': -17.4677,
34 | 'Antananarivo_lat': -18.8792,
35 | 'Antananarivo_lon': 47.5079,
36 | 'Rome_lat': 41.9028,
37 | 'Rome_lon': 12.4964,
38 | 'Istanbul_lat': 41.0082,
39 | 'Istanbul_lon': 28.9784,
40 | 'Bengaluru_lat': 12.9716,
41 | 'Bengaluru_lon': 77.5946,
42 | 'Lhasa_lat': 29.6548,
43 | 'Lhasa_lon': 91.1406,
44 | 'Manaus_lat': -3.1190,
45 | 'Manaus_lon': -60.0217,
46 | 'Bandung_lat': -6.9175,
47 | 'Bandung_lon': 107.6191,
48 |
49 | # Eurasian Pole of Inaccessibility or EPIA
50 | 'EPIA_lat': 46.283,
51 | 'EPIA_lon': 86.667,
52 |
53 | # Pacific Pole of Inaccessibility or PPIA
54 | 'PPIA_lat': -48.877,
55 | 'PPIA_lon': -123.393
56 | }
57 | ATOL = {}
58 | RTOL = {
59 | 'distance': 1e-3
60 | }
61 |
62 |
63 | class TestCaseType(TestCaseTypeEnum):
64 | SAME_POINT = ("Same point", 1)
65 | EQUATORIAL = ("Equatorial", 1)
66 | POLE_TO_POLE = ("Pole to pole", 1)
67 | NEW_YORK_TO_MADRID = ("New York to Madrid", 1)
68 | VANCOUVER_TO_ST_JOHNS = ("Vancouver to St. Johns", 1)
69 | DAKAR_TO_ANTANANARIVO = ("Dakar to Antananarivo", 1)
70 | ROME_TO_ISTANBUL = ("Rome to Istanbul", 1)
71 | BENGALURU_TO_LHASA = ("Bengaluru to Lhasa", 1)
72 | MANAUS_TO_BANDUNG = ("Manaus to Bandung", 1)
73 | USHUAIA_TO_ALERT = ("Ushuaia to Alert", 1)
74 | EPIA_TO_PPIA = ("Eurasian Pole of Inaccessibility to Pacific Pole of Inaccessibility", 1)
75 | RANDOM_POINTS = ("Two random points", 1)
76 |
77 |
78 | class ProblemTestCase(TestCase):
79 | def input_tuple(self) -> tuple:
80 | return self.input['lat1'], self.input['lon1'], self.input['lat2'], self.input['lon2']
81 |
82 | def output_tuple(self) -> tuple:
83 | return self.output['distance'],
84 |
85 |
86 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
87 | test_case = ProblemTestCase(test_type)
88 |
89 | if test_type is TestCaseType.SAME_POINT:
90 | lat1 = uniform(-90, 90)
91 | lon1 = uniform(-180, 180)
92 | lat2 = lat1
93 | lon2 = lon1
94 |
95 | elif test_type is TestCaseType.EQUATORIAL:
96 | lat1 = 0.0
97 | lon1 = uniform(-180, 180)
98 | lat2 = 0.0
99 | lon2 = uniform(-180, 180)
100 |
101 | elif test_type is TestCaseType.POLE_TO_POLE:
102 | lat1 = 90.0
103 | lon1 = uniform(-180, 180)
104 | lat2 = -90.0
105 | lon2 = uniform(-180, 180)
106 |
107 | elif test_type is TestCaseType.NEW_YORK_TO_MADRID:
108 | lat1 = PHYSICAL_CONSTANTS['New_York_lat']
109 | lon1 = PHYSICAL_CONSTANTS['New_York_lon']
110 | lat2 = PHYSICAL_CONSTANTS['Madrid_lat']
111 | lon2 = PHYSICAL_CONSTANTS['Madrid_lon']
112 |
113 | elif test_type is TestCaseType.VANCOUVER_TO_ST_JOHNS:
114 | lat1 = PHYSICAL_CONSTANTS['Vancouver_lat']
115 | lon1 = PHYSICAL_CONSTANTS['Vancouver_lon']
116 | lat2 = PHYSICAL_CONSTANTS['St_Johns_lat']
117 | lon2 = PHYSICAL_CONSTANTS['St_Johns_lon']
118 |
119 | elif test_type is TestCaseType.DAKAR_TO_ANTANANARIVO:
120 | lat1 = PHYSICAL_CONSTANTS['Dakar_lat']
121 | lon1 = PHYSICAL_CONSTANTS['Dakar_lon']
122 | lat2 = PHYSICAL_CONSTANTS['Antananarivo_lat']
123 | lon2 = PHYSICAL_CONSTANTS['Antananarivo_lon']
124 |
125 | elif test_type is TestCaseType.ROME_TO_ISTANBUL:
126 | lat1 = PHYSICAL_CONSTANTS['Rome_lat']
127 | lon1 = PHYSICAL_CONSTANTS['Rome_lon']
128 | lat2 = PHYSICAL_CONSTANTS['Istanbul_lat']
129 | lon2 = PHYSICAL_CONSTANTS['Istanbul_lon']
130 |
131 | elif test_type is TestCaseType.BENGALURU_TO_LHASA:
132 | lat1 = PHYSICAL_CONSTANTS['Bengaluru_lat']
133 | lon1 = PHYSICAL_CONSTANTS['Bengaluru_lon']
134 | lat2 = PHYSICAL_CONSTANTS['Lhasa_lat']
135 | lon2 = PHYSICAL_CONSTANTS['Lhasa_lon']
136 |
137 | elif test_type is TestCaseType.MANAUS_TO_BANDUNG:
138 | lat1 = PHYSICAL_CONSTANTS['Manaus_lat']
139 | lon1 = PHYSICAL_CONSTANTS['Manaus_lon']
140 | lat2 = PHYSICAL_CONSTANTS['Bandung_lat']
141 | lon2 = PHYSICAL_CONSTANTS['Bandung_lon']
142 |
143 | elif test_type is TestCaseType.USHUAIA_TO_ALERT:
144 | lat1 = PHYSICAL_CONSTANTS['Ushuaia_lat']
145 | lon1 = PHYSICAL_CONSTANTS['Ushuaia_lon']
146 | lat2 = PHYSICAL_CONSTANTS['Alert_lat']
147 | lon2 = PHYSICAL_CONSTANTS['Alert_lon']
148 |
149 | elif test_type is TestCaseType.EPIA_TO_PPIA:
150 | lat1 = PHYSICAL_CONSTANTS['EPIA_lat']
151 | lon1 = PHYSICAL_CONSTANTS['EPIA_lon']
152 | lat2 = PHYSICAL_CONSTANTS['PPIA_lat']
153 | lon2 = PHYSICAL_CONSTANTS['PPIA_lon']
154 |
155 | elif test_type is TestCaseType.RANDOM_POINTS:
156 | lat1 = uniform(-90, 90)
157 | lon1 = uniform(-180, 180)
158 | lat2 = uniform(-90, 90)
159 | lon2 = uniform(-180, 180)
160 |
161 | else:
162 | raise ValueError(f"Unrecognized test case: {test_type}")
163 |
164 | test_case.input = {
165 | "lat1": lat1,
166 | "lon1": lon1,
167 | "lat2": lat2,
168 | "lon2": lon2
169 | }
170 |
171 | test_case.output['distance'] = haversine_distance(lat1, lon1, lat2, lon2)
172 |
173 | return test_case
174 |
--------------------------------------------------------------------------------
/problems/game_of_life.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy import array, zeros, reshape
5 | from numpy.random import randint, choice
6 |
7 | from problems.test_case import TestCase, TestCaseTypeEnum
8 | from problems.solutions.game_of_life import game_of_life
9 |
10 | logger = logging.getLogger(__name__)
11 |
12 | FUNCTION_NAME = "game_of_life"
13 | INPUT_VARS = ['board', 'steps']
14 | OUTPUT_VARS = ['board']
15 |
16 | STATIC_RESOURCES = ["still_life.txt", "oscillators.txt", "spaceships.txt"]
17 |
18 | PHYSICAL_CONSTANTS = {}
19 | ATOL = {}
20 | RTOL = {}
21 |
22 | ALIVE = 1
23 | DEAD = 0
24 |
25 | # Still life patterns
26 | BLOCK = reshape(array(list("1111"), dtype=int), (2, 2))
27 | BEEHIVE = reshape(array(list("011010010110"), dtype=int), (3, 4))
28 | LOAF = reshape(array(list("0110100101010010"), dtype=int), (4, 4))
29 | BOAT = reshape(array(list("110101010"), dtype=int), (3, 3))
30 | TUB = reshape(array(list("010101010"), dtype=int), (3, 3))
31 |
32 | # Oscillator patterns
33 | BLINKER = reshape(array(list("111"), dtype=int), (1, 3))
34 | TOAD = reshape(array(list("01111110"), dtype=int), (2, 4))
35 | BEACON = reshape(array(list("1100110000110011"), dtype=int), (4, 4))
36 |
37 | PULSAR_STR = "0011100011100" + 13*"0" + 3*"1000010100001" + "0011100011100" + 13*"0" + "0011100011100" + 3*"1000010100001" + 13*"0" + "0011100011100"
38 | PULSAR = reshape(array(list(PULSAR_STR), dtype=int), (13, 13))
39 |
40 | # Infinite growth patterns
41 | GLIDER_GUN_STR = 24*"0" + "1" + 11*"0" + \
42 | 22*"0" + "101" + 11*"0" + \
43 | 12*"0" + "11" + 6*"0" + "11" + 12*"0" + "11" + \
44 | 11*"0" + "10001000011" + 12*"0" + "11" + \
45 | "11" + 8*"0" + "100000100011" + 14*"0" + \
46 | "11" + 8*"0" + "100010110000101" + 11*"0" + \
47 | 10*"0" + "100000100000001" + 11*"0" + \
48 | 11*"0" + "10001" + 20*"0" + \
49 | 12*"0" + "11" + 22*"0"
50 | GLIDER_GUN = reshape(array(list(GLIDER_GUN_STR), dtype=int), (9, 36))
51 |
52 |
53 | class TestCaseType(TestCaseTypeEnum):
54 | STILL_LIFE = ("still life", 1)
55 | OSCILLATORS = ("oscillators", 1)
56 | GLIDERS = ("gliders", 0)
57 | GLIDER_GUN = ("glider gun", 1)
58 | RANDOM_SMALL = ("small random", 1)
59 | RANDOM_LARGE = ("large random", 0)
60 |
61 |
62 | class ProblemTestCase(TestCase):
63 | def input_tuple(self) -> tuple:
64 | return self.input['board'], self.input['steps']
65 |
66 | def output_tuple(self) -> tuple:
67 | return self.output['board'],
68 |
69 |
70 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
71 | test_case = ProblemTestCase(test_type)
72 |
73 | if test_type is TestCaseType.STILL_LIFE:
74 | grid = zeros((15, 15), dtype=int)
75 | steps = 5
76 | grid[1:3, 1:3] = BLOCK
77 | grid[5:8, 2:6] = BEEHIVE
78 | grid[1:5, 7:11] = LOAF
79 | grid[6:9, 10:13] = TUB
80 |
81 | elif test_type is TestCaseType.OSCILLATORS:
82 | grid = zeros((17, 36), dtype=int)
83 | steps = 31
84 | grid[3, 16:19] = BLINKER
85 | grid[8:10, 15:19] = TOAD
86 | grid[10:14, 1:5] = BEACON
87 | grid[2:15, 21:34] = PULSAR
88 |
89 | elif test_type is TestCaseType.GLIDERS:
90 | raise NotImplementedError
91 |
92 | elif test_type is TestCaseType.GLIDER_GUN:
93 | N = randint(15, 50)
94 | M = randint(50, 100)
95 | steps = 10
96 | grid = zeros((N, M), dtype=int)
97 | grid[2:11, 2:38] = GLIDER_GUN
98 |
99 | elif test_type is TestCaseType.RANDOM_SMALL:
100 | N = randint(5, 50)
101 | M = randint(5, 50)
102 | steps = randint(3, 10)
103 | grid = choice([ALIVE, DEAD], N*M, p=[0.5, 0.5]).reshape(N, M)
104 |
105 | elif test_type is TestCaseType.RANDOM_LARGE:
106 | N = randint(200, 250)
107 | M = randint(200, 250)
108 | steps = randint(3, 10)
109 | grid = choice([ALIVE, DEAD], N*M, p=[0.5, 0.5]).reshape(N, M)
110 |
111 | else:
112 | raise ValueError(f"Unrecognized test case: {test_type}")
113 |
114 | test_case.input['board'] = grid.tolist()
115 | test_case.input['steps'] = steps
116 |
117 | test_case.output['board'] = game_of_life(test_case.input['board'], steps)
118 |
119 | return test_case
120 |
--------------------------------------------------------------------------------
/problems/habitable_exoplanets.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import uniform
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.habitable_exoplanets import habitable_exoplanet
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "habitable_exoplanet"
12 | INPUT_VARS = ['L_star', 'r']
13 | OUTPUT_VARS = ['habitability']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {
18 | # For the sun L=1 and for the Earth r=1 by definition.
19 | 'L_sun': 1.00, # [L☉]
20 | 'r_Earth': 1.00, # [AU]
21 |
22 | # Source: https://en.wikipedia.org/wiki/Proxima_Centauri_b
23 | 'L_Proxima_Centauri': 0.0015, # [L☉]
24 | 'r_Proxima_Centauri_b': 0.05, # [AU]
25 |
26 | # Sources:
27 | # https://en.wikipedia.org/wiki/Kepler-440b (for semi-major axis of 0.242 AU)
28 | # https://exoplanetarchive.ipac.caltech.edu/cgi-bin/DisplayOverview/nph-DisplayOverview?objname=Kepler-440+b
29 | # &type=CONFIRMED_PLANET (for log10(L☉) of -1.102 +0.111 -0.142 => L☉ = 0.079)
30 | # https://iopscience.iop.org/article/10.1088/0004-637X/800/2/99 (Table 6 for L☉ of 0.079 +0.023 −0.022)
31 | 'L_Kepler_440': 0.079,
32 | 'r_Kepler_440b': 0.242
33 | }
34 |
35 | ATOL = {}
36 | RTOL = {}
37 |
38 |
39 | class TestCaseType(TestCaseTypeEnum):
40 | EARTH = ("Earth", 1)
41 | PROXIMA_CENTAURI_B = ("Proxima Centauri b", 1)
42 | KEPLER_440B = ("Kepler 440b", 1)
43 | RANDOM = ("Randomly generated exoplanet", 1)
44 |
45 |
46 | class ProblemTestCase(TestCase):
47 | def input_tuple(self) -> tuple:
48 | return self.input['L_star'], self.input['r']
49 |
50 | def output_tuple(self) -> tuple:
51 | return self.output['habitability'],
52 |
53 |
54 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
55 | test_case = ProblemTestCase(test_type)
56 |
57 | if test_type is TestCaseType.EARTH:
58 | L_star = PHYSICAL_CONSTANTS['L_sun']
59 | r = PHYSICAL_CONSTANTS['r_Earth']
60 |
61 | elif test_type is TestCaseType.PROXIMA_CENTAURI_B:
62 | L_star = PHYSICAL_CONSTANTS['L_Proxima_Centauri']
63 | r = PHYSICAL_CONSTANTS['r_Proxima_Centauri_b']
64 |
65 | elif test_type is TestCaseType.KEPLER_440B:
66 | L_star = PHYSICAL_CONSTANTS['L_Kepler_440']
67 | r = PHYSICAL_CONSTANTS['r_Kepler_440b']
68 |
69 | elif test_type is TestCaseType.RANDOM:
70 | L_star = float(uniform(0.1, 5.0, 1)[0])
71 | r = float(uniform(0.1, 5.0, 1)[0])
72 |
73 | else:
74 | raise ValueError(f"Unrecognized test case: {test_type}")
75 |
76 | test_case.input['L_star'] = L_star
77 | test_case.input['r'] = r
78 | test_case.output['habitability'] = habitable_exoplanet(L_star, r)
79 |
80 | return test_case
81 |
--------------------------------------------------------------------------------
/problems/molecular_mass_calculator.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import choice
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "molecular_mass"
11 | INPUT_VARS = ['chemical_formula']
12 | OUTPUT_VARS = ['mass']
13 |
14 | STATIC_RESOURCES = ["periodic_table.csv"]
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {
19 | 'mass': 0.1
20 | }
21 |
22 |
23 | class TestCaseType(TestCaseTypeEnum):
24 | RUST = ("Rust (ferric oxide)", 1)
25 | PLUTONIUM = ("Plutonium", 1)
26 | LSD = ("LSD", 1)
27 | HIGH_T_SUPERCONDUCTOR = ("BSCCO (high temperature superconductor)", 1)
28 | RANDOM_CHEMICAL = ("random chemical", 2)
29 |
30 |
31 | class ProblemTestCase(TestCase):
32 | def input_tuple(self) -> tuple:
33 | return self.input['chemical_formula'],
34 |
35 | def output_tuple(self) -> tuple:
36 | return self.output['mass'],
37 |
38 |
39 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
40 | test_case = ProblemTestCase(test_type)
41 |
42 | if test_type is TestCaseType.RUST:
43 | chemical_formula = "Fe2O3"
44 |
45 | elif test_type is TestCaseType.PLUTONIUM:
46 | chemical_formula = "Pu"
47 |
48 | elif test_type is TestCaseType.HIGH_T_SUPERCONDUCTOR:
49 | chemical_formula = "Bi2Sr2Ca2Cu3O10"
50 |
51 | elif test_type is TestCaseType.LSD:
52 | chemical_formula = "C20H25N3O"
53 |
54 | elif test_type is TestCaseType.RANDOM_CHEMICAL:
55 | chemical_formula = choice(["CO2", "CH4", "C6H12O6", "PuCoGa5", "CH3NH2", "W", "C2H5OH"], 1)[0]
56 |
57 | else:
58 | raise ValueError(f"Unrecognized test case: {test_type}")
59 |
60 |
61 | # Delay solution import until tests can handle static resources:
62 | # https://github.com/project-lovelace/lovelace-problems/issues/60
63 | from problems.solutions.molecular_mass_calculator import molecular_mass
64 |
65 | test_case.input['chemical_formula'] = chemical_formula
66 | test_case.output['mass'] = molecular_mass(chemical_formula)
67 |
68 | return test_case
69 |
--------------------------------------------------------------------------------
/problems/nand_gate.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from problems.test_case import TestCase, TestCaseTypeEnum
5 | from problems.solutions.nand_gate import NAND
6 |
7 | logger = logging.getLogger(__name__)
8 |
9 | FUNCTION_NAME = "NAND"
10 | INPUT_VARS = ['p', 'q']
11 | OUTPUT_VARS = ['nand']
12 |
13 | STATIC_RESOURCES = []
14 |
15 | PHYSICAL_CONSTANTS = {}
16 | ATOL = {}
17 | RTOL = {}
18 |
19 |
20 | class TestCaseType(TestCaseTypeEnum):
21 | ZERO_ZERO = ("00", 1)
22 | ZERO_ONE = ("01", 1)
23 | ONE_ZERO = ("10", 1)
24 | ONE_ONE = ("11", 1)
25 |
26 |
27 | class ProblemTestCase(TestCase):
28 | def input_tuple(self) -> tuple:
29 | return self.input['p'], self.input['q']
30 |
31 | def output_tuple(self) -> tuple:
32 | return self.output['nand'],
33 |
34 |
35 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
36 | test_case = ProblemTestCase(test_type)
37 |
38 | if test_type is TestCaseType.ZERO_ZERO:
39 | p, q = 0, 0
40 |
41 | elif test_type is TestCaseType.ZERO_ONE:
42 | p, q = 0, 1
43 |
44 | elif test_type is TestCaseType.ONE_ZERO:
45 | p, q = 1, 0
46 |
47 | elif test_type is TestCaseType.ONE_ONE:
48 | p, q = 1, 1
49 |
50 | else:
51 | raise ValueError(f"Unrecognized test case: {test_type}")
52 |
53 | test_case.input["p"], test_case.input["q"] = p, q
54 | test_case.output["nand"] = NAND(p, q)
55 |
56 | return test_case
57 |
--------------------------------------------------------------------------------
/problems/numerical_diff.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from math import exp, sin
3 | from random import uniform
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.numerical_diff import numerical_diff
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "numerical_diff"
11 | INPUT_VARS = ['f', 'x']
12 | OUTPUT_VARS = ['df']
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {
19 | 'df': 0.005, # 0.5% relative tolerance
20 | }
21 |
22 |
23 | class TestCaseType(TestCaseTypeEnum):
24 | ZERO = ("Zero function", 1)
25 | CONSTANT = ("Constant function", 1)
26 | LINEAR = ("Linear polynomial", 1)
27 | QUADRATIC = ("Quadratic polynomial", 1)
28 | CUBIC = ("Cubic polynomial", 1)
29 | SINE = ("Sine function", 1)
30 | EXPONENTIAL = ("Exponential function", 1)
31 |
32 |
33 | class ProblemTestCase(TestCase):
34 | def input_tuple(self) -> tuple:
35 | return (self.input['f'], self.input['x'])
36 |
37 | def output_tuple(self) -> tuple:
38 | return self.output['df'],
39 |
40 |
41 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
42 | test_case = ProblemTestCase(test_type)
43 |
44 | x = uniform(-10, 10)
45 |
46 | if test_type is TestCaseType.ZERO:
47 | f = lambda x: 0
48 |
49 | elif test_type is TestCaseType.CONSTANT:
50 | f = lambda x: 2021
51 |
52 | elif test_type is TestCaseType.LINEAR:
53 | f = lambda x: 2021*x + 3
54 |
55 | elif test_type is TestCaseType.QUADRATIC:
56 | f = lambda x: x**2 + 2*x
57 |
58 | elif test_type is TestCaseType.CUBIC:
59 | f = lambda x: 3*x**3
60 |
61 | elif test_type is TestCaseType.SINE:
62 | f = sin
63 |
64 | elif test_type is TestCaseType.EXPONENTIAL:
65 | f = exp
66 |
67 | else:
68 | raise ValueError(f"Unrecognized test case: {test_type}")
69 |
70 | test_case.input = {
71 | 'f': f,
72 | 'x': x,
73 | }
74 |
75 | test_case.output['df'] = numerical_diff(f, x)
76 |
77 | return test_case
78 |
--------------------------------------------------------------------------------
/problems/plump_moose.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from numpy.random import uniform
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.plump_moose import moose_body_mass
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "moose_body_mass"
11 | INPUT_VARS = ["latitude"]
12 | OUTPUT_VARS = ["mass"]
13 |
14 | STATIC_RESOURCES = []
15 | PHYSICAL_CONSTANTS = {}
16 |
17 | ATOL = {}
18 | RTOL = {
19 | "mass": 1e-6
20 | }
21 |
22 |
23 | class TestCaseType(TestCaseTypeEnum):
24 | MALMO = ("Moose from Malmö", 1)
25 | STOCKHOLM = ("Moose from Stockholm", 1)
26 | KIRUNA = ("Moose from Kiruna", 1)
27 | SOUTHERN = ("Southern moose", 1)
28 | NORTHERN = ("Northern moose", 1)
29 | RANDOM = ("Random", 2)
30 |
31 |
32 | class ProblemTestCase(TestCase):
33 | def input_tuple(self):
34 | return self.input["latitude"],
35 |
36 | def output_tuple(self):
37 | return self.output["mass"],
38 |
39 |
40 | def generate_test_case(test_type):
41 | test_case = ProblemTestCase(test_type)
42 |
43 | if test_type is TestCaseType.MALMO:
44 | latitude = 55.60587
45 |
46 | elif test_type is TestCaseType.STOCKHOLM:
47 | latitude = 59.33258
48 |
49 | elif test_type is TestCaseType.KIRUNA:
50 | latitude = 67.85507
51 |
52 | elif test_type is TestCaseType.SOUTHERN:
53 | latitude = uniform(58, 62)
54 |
55 | elif test_type is TestCaseType.NORTHERN:
56 | latitude = uniform(62, 66)
57 |
58 | elif test_type is TestCaseType.RANDOM:
59 | latitude = uniform(57, 67)
60 |
61 | else:
62 | raise ValueError(f"Unrecognized test case: {test_type}")
63 |
64 | test_case.input["latitude"] = latitude
65 | test_case.output["mass"] = moose_body_mass(latitude)
66 |
67 | return test_case
68 |
--------------------------------------------------------------------------------
/problems/rna_translation.py:
--------------------------------------------------------------------------------
1 | import random
2 | import logging
3 |
4 | from problems.test_case import TestCase, TestCaseTypeEnum
5 | from problems.solutions.rna_translation import amino_acid_sequence, rna_codon_table
6 |
7 | logger = logging.getLogger(__name__)
8 |
9 | FUNCTION_NAME = "amino_acid_sequence"
10 |
11 | INPUT_VARS = ["rna"]
12 | OUTPUT_VARS = ["amino_acid_sequence"]
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {}
19 |
20 | class ProblemTestCase(TestCase):
21 | def input_tuple(self):
22 | return self.input["rna"],
23 |
24 | def output_tuple(self):
25 | return self.output["amino_acid_sequence"],
26 |
27 | class TestCaseType(TestCaseTypeEnum):
28 | NO_CODONS = ("No codons", 1)
29 | ONE_CODON = ("One codon", 5)
30 | FEW_CODONS = ("Few codons", 2)
31 | MANY_CODONS = ("Many codons", 2)
32 | ALL_CODONS = ("All codons", 1)
33 | END_CODON = ("End codon", 2)
34 | STOP_IN_MIDDLE = ("Stop codon in the middle", 1)
35 |
36 | start_codons = ["AUG"]
37 | stop_codons = ["UAA", "UAG", "UGA"]
38 |
39 | intermediate_codon_table = rna_codon_table.copy()
40 | [intermediate_codon_table.pop(codon) for codon in start_codons + stop_codons]
41 |
42 | def random_codons(N):
43 | return "".join(["".join(random.choices("ACGU", k=3)) for _ in range(N)])
44 |
45 | def random_intermediate_codons(N):
46 | return "".join(random.choices(list(intermediate_codon_table.keys()), k=N))
47 |
48 | def generate_test_case(test_type):
49 | test_case = ProblemTestCase(test_type)
50 |
51 | if test_type is TestCaseType.NO_CODONS:
52 | rna = ""
53 |
54 | elif test_type is TestCaseType.ONE_CODON:
55 | rna = random_intermediate_codons(1)
56 |
57 | elif test_type is TestCaseType.FEW_CODONS:
58 | N = random.randint(2, 5)
59 | rna = random_intermediate_codons(N) + random.choice(stop_codons)
60 |
61 | elif test_type is TestCaseType.MANY_CODONS:
62 | N = random.randint(10, 100)
63 | rna = random_intermediate_codons(N)
64 |
65 | elif test_type is TestCaseType.ALL_CODONS:
66 | rna = start_codons[0] + "".join(list(intermediate_codon_table.keys())) + random.choice(stop_codons)
67 |
68 | elif test_type is TestCaseType.END_CODON:
69 | rna = random.choice(stop_codons)
70 |
71 | elif test_type is TestCaseType.STOP_IN_MIDDLE:
72 | N1 = random.randint(10, 20)
73 | N2 = random.randint(5, 10)
74 | rna = random_intermediate_codons(N1) + random.choice(stop_codons) + random_intermediate_codons(N2)
75 |
76 | else:
77 | raise ValueError(f"Unrecognized test case: {test_type}")
78 |
79 | test_case.input["rna"] = rna
80 | test_case.output["amino_acid_sequence"] = amino_acid_sequence(rna)
81 |
82 | return test_case
83 |
--------------------------------------------------------------------------------
/problems/rock_star_climate.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import uniform
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.rock_star_climate import rock_temperature
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "rock_temperature"
12 | INPUT_VARS = ['solar_constant', 'albedo', 'emissivity']
13 | OUTPUT_VARS = ['T_rock']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {
18 | # Earth
19 | 'S_Earth': 1361, # Solar constant [W/m^2] from Kopp & Lean (2011).
20 | 'a_Earth': 0.306, # Bond albedo from NASA Earth fact sheet: https://nssdc.gsfc.nasa.gov/planetary/factsheet/earthfact.html
21 | 'ε_Earth': 0.612, # Effective emissivity.
22 |
23 | # Mars
24 | 'S_Mars': 586, # Assuming S falls off as 1/r^2 from Kopp & Lean (2011) and r = 1.524 AU.
25 | 'a_Mars': 0.24, # Bond albedo from NASA Mars fact sheet: https://nssdc.gsfc.nasa.gov/planetary/factsheet/marsfact.html
26 | 'ε_Mars': 0.9, # Can't find anything so picking a 0.9 which is close to limestone and brick.
27 |
28 | # Pluto
29 | 'S_Pluto': 0.87, # Assuming S falls off as 1/r^2 from Kopp & Lean (2011) and r = 39.48 AU (semi-major axis).
30 | 'a_Pluto': 0.72, # Bond albedo from NASA Pluto fact sheet: https://nssdc.gsfc.nasa.gov/planetary/factsheet/plutofact.html
31 | 'ε_Pluto': 0.9
32 | }
33 |
34 | ATOL = {}
35 | RTOL = {
36 | 'T_rock': 1e-6
37 | }
38 |
39 |
40 | class TestCaseType(TestCaseTypeEnum):
41 | EARTH = ("Earth", 1)
42 | BLACKBODY_EARTH = ("Blackbody Earth", 1)
43 | REFLECTIVE_EARTH = ("Reflective Earth", 1)
44 | MARS = ("Mars", 1)
45 | PLUTO = ("Pluto", 1)
46 | RANDOM = ("Random", 1)
47 |
48 |
49 | class ProblemTestCase(TestCase):
50 | def input_tuple(self) -> tuple:
51 | return self.input['solar_constant'], self.input['albedo'], self.input['emissivity'],
52 |
53 | def output_tuple(self) -> tuple:
54 | return self.output['T_rock'],
55 |
56 |
57 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
58 | test_case = ProblemTestCase(test_type)
59 |
60 | if test_type is TestCaseType.EARTH:
61 | S = PHYSICAL_CONSTANTS['S_Earth']
62 | a = PHYSICAL_CONSTANTS['a_Earth']
63 | ε = PHYSICAL_CONSTANTS['ε_Earth']
64 |
65 | elif test_type is TestCaseType.BLACKBODY_EARTH:
66 | S = PHYSICAL_CONSTANTS['S_Earth']
67 | a = PHYSICAL_CONSTANTS['a_Earth']
68 | ε = 1.0
69 |
70 | elif test_type is TestCaseType.REFLECTIVE_EARTH:
71 | S = PHYSICAL_CONSTANTS['S_Earth']
72 | a = 1
73 | ε = PHYSICAL_CONSTANTS['ε_Earth']
74 |
75 | elif test_type is TestCaseType.MARS:
76 | S = PHYSICAL_CONSTANTS['S_Mars']
77 | a = PHYSICAL_CONSTANTS['a_Mars']
78 | ε = PHYSICAL_CONSTANTS['ε_Mars']
79 |
80 | elif test_type is TestCaseType.PLUTO:
81 | S = PHYSICAL_CONSTANTS['S_Pluto']
82 | a = PHYSICAL_CONSTANTS['a_Pluto']
83 | ε = PHYSICAL_CONSTANTS['ε_Pluto']
84 |
85 | elif test_type is TestCaseType.RANDOM:
86 | S = uniform(1000, 10000)
87 | a = uniform(0, 1)
88 | ε = uniform(0, 1)
89 |
90 | else:
91 | raise ValueError(f"Unrecognized test case: {test_type}")
92 |
93 | test_case.input = {'solar_constant': S, 'albedo': a, 'emissivity': ε}
94 | test_case.output['T_rock'] = rock_temperature(S, a, ε)
95 |
96 | return test_case
97 |
--------------------------------------------------------------------------------
/problems/rocket_science.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import uniform
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.rocket_science import rocket_fuel
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "rocket_fuel"
12 | INPUT_VARS = ['v']
13 | OUTPUT_VARS = ['m_fuel']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {
18 | 'v_e': 2550.0, # [m/s]
19 | 'M': 250000.0, # [kg]
20 |
21 | # All escape velocities in [m/s].
22 | # Source: https://en.wikipedia.org/wiki/Escape_velocity#List_of_escape_velocities
23 | 'v_Earth': 11186.0,
24 | 'v_Moon': 2380.0,
25 | 'v_Jupiter': 60200.0,
26 | 'v_Pluto': 1230.0,
27 | 'v_Phobos': 1.139
28 | }
29 |
30 | ATOL = {}
31 | RTOL = {
32 | 'm_fuel': 1e-6
33 | }
34 |
35 |
36 | class TestCaseType(TestCaseTypeEnum):
37 | EARTH = ('Earth', 1)
38 | MOON = ('Moon', 1)
39 | JUPITER = ('Jupiter', 1)
40 | PLUTO = ('Pluto', 1)
41 | PHOBOS = ('Phobos', 1)
42 | RANDOM = ('Random', 1)
43 |
44 |
45 | class ProblemTestCase(TestCase):
46 | def input_tuple(self) -> tuple:
47 | return self.input['v'],
48 |
49 | def output_tuple(self) -> tuple:
50 | return self.output['m_fuel'],
51 |
52 |
53 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
54 | test_case = ProblemTestCase(test_type)
55 |
56 | if test_type is TestCaseType.EARTH:
57 | v = PHYSICAL_CONSTANTS['v_Earth']
58 |
59 | elif test_type is TestCaseType.MOON:
60 | v = PHYSICAL_CONSTANTS['v_Moon']
61 |
62 | elif test_type is TestCaseType.JUPITER:
63 | v = PHYSICAL_CONSTANTS['v_Jupiter']
64 |
65 | elif test_type is TestCaseType.PLUTO:
66 | v = PHYSICAL_CONSTANTS['v_Pluto']
67 |
68 | elif test_type is TestCaseType.PHOBOS:
69 | v = PHYSICAL_CONSTANTS['v_Phobos']
70 |
71 | elif test_type is TestCaseType.RANDOM:
72 | v = float(uniform(1.0, 100.0, 1)[0])
73 |
74 | else:
75 | raise ValueError(f"Unrecognized test case: {test_type}")
76 |
77 | test_case.input['v'] = v
78 | test_case.output['m_fuel'] = rocket_fuel(v)
79 |
80 | return test_case
81 |
--------------------------------------------------------------------------------
/problems/scientific_temperatures.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 | from numpy.random import uniform
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.scientific_temperatures import fahrenheit_to_celsius
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "fahrenheit_to_celsius"
11 | INPUT_VARS = ['F']
12 | OUTPUT_VARS = ['C']
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {
19 | 'C': 1e-5
20 | }
21 |
22 |
23 | class TestCaseType(TestCaseTypeEnum):
24 | WARM_DAY = ("Warm day", 1)
25 | COLD_DAY = ("Cold day", 1)
26 | WATER_FREEZING_POINT = ("Freezing point of water", 1)
27 | WATER_BOILING_POINT = ("Boiling point of water", 1)
28 | MINUS_40 = ("-40°C", 1)
29 | ABSOLUTE_ZERO = ("Absolute zero", 1)
30 | SUN_SURFACE = ("Surface of the sun", 1)
31 |
32 |
33 | class ProblemTestCase(TestCase):
34 | def input_tuple(self) -> tuple:
35 | return self.input['F'],
36 |
37 | def output_tuple(self) -> tuple:
38 | return self.output['C'],
39 |
40 |
41 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
42 | test_case = ProblemTestCase(test_type)
43 |
44 | if test_type is TestCaseType.WARM_DAY:
45 | F = uniform(80, 110)
46 |
47 | elif test_type is TestCaseType.COLD_DAY:
48 | F = uniform(-20, 30)
49 |
50 | elif test_type is TestCaseType.WATER_FREEZING_POINT:
51 | F = 32.0
52 |
53 | elif test_type is TestCaseType.WATER_BOILING_POINT:
54 | F = 212.0
55 |
56 | elif test_type is TestCaseType.MINUS_40:
57 | F = -40.0
58 |
59 | elif test_type is TestCaseType.ABSOLUTE_ZERO:
60 | F = -459.67
61 |
62 | elif test_type is TestCaseType.SUN_SURFACE:
63 | F = 9940.73
64 |
65 | else:
66 | raise ValueError(f"Unrecognized test case: {test_type}")
67 |
68 | test_case.input['F'] = F
69 | test_case.output['C'] = fahrenheit_to_celsius(F)
70 | return test_case
71 |
--------------------------------------------------------------------------------
/problems/sha_256.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 |
4 | from numpy.random import uniform
5 |
6 | from problems.test_case import TestCase, TestCaseTypeEnum
7 | from problems.solutions.sha_256 import SHA256
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 | FUNCTION_NAME = "SHA256"
12 | INPUT_VARS = ['message']
13 | OUTPUT_VARS = ['digest']
14 |
15 | STATIC_RESOURCES = []
16 |
17 | PHYSICAL_CONSTANTS = {}
18 | ATOL = {}
19 | RTOL = {}
20 |
21 |
22 | class TestCaseType(TestCaseTypeEnum):
23 | EMPTY_STRING = ("Empty string", 1)
24 | ABC = ("ABC", 1)
25 | QUICK_BROWN_FOX = ("Quick brown fox", 1)
26 |
27 |
28 | class ProblemTestCase(TestCase):
29 | def input_tuple(self) -> tuple:
30 | return self.input['message'],
31 |
32 | def output_tuple(self) -> tuple:
33 | return self.output['digest'],
34 |
35 |
36 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
37 | test_case = ProblemTestCase(test_type)
38 |
39 | if test_type is TestCaseType.EMPTY_STRING:
40 | M = ""
41 |
42 | elif test_type is TestCaseType.ABC:
43 | M = "ABC"
44 |
45 | elif test_type is TestCaseType.QUICK_BROWN_FOX:
46 | M = "The quick brown fox jumps over the lazy dog"
47 |
48 | else:
49 | raise ValueError(f"Unrecognized test case: {test_type}")
50 |
51 | test_case.input['message'] = M
52 | test_case.output['digest'] = SHA256(M)
53 |
54 | return test_case
55 |
--------------------------------------------------------------------------------
/problems/speed_of_light.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 | from numpy.random import uniform
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.speed_of_light import light_time
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "light_time"
11 | INPUT_VARS = ['distance']
12 | OUTPUT_VARS = ['time']
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {
17 | # Source: https://en.wikipedia.org/wiki/Speed_of_light
18 | 'c': 299792458, # speed of light [m/s]
19 |
20 | # All distances in meters.
21 |
22 | # Time-averaged distance between the Earth and lunar surfaces. The average distance between the Earth and Moon is
23 | # 384,400 km but the radius of the Earth is 6,371 km and the radius of the Moon is 1,737 km, so in this case light
24 | # actually travels 384,400 - 6,371, - 1,737 = 376,292 km
25 | # Source: https://en.wikipedia.org/wiki/Lunar_distance_(astronomy)
26 | 'd_Earth_Moon': 376292e3, # 376,292 km
27 |
28 | # 1 Astronomical unit (almost equal to the average of Earth's aphelion and perihelion).
29 | # Source: https://en.wikipedia.org/wiki/Astronomical_unit
30 | 'd_Sun_Earth': 149597870700.0, # ~150 million kilometres
31 |
32 | # Maximum distance between the Earth and Mars.
33 | # Source: https://www.space.com/14729-spacekids-distance-earth-mars.html
34 | 'd_Earth_Mars': 401e9, # ~401 million km
35 |
36 | # Maximum distance between Earth and Jupiter.
37 | # Source: https://www.universetoday.com/14514/how-far-is-jupiter-from-earth/
38 | 'd_Earth_Jupiter': 928e9, # ~928 million km
39 | }
40 |
41 | ATOL = {}
42 | RTOL = {
43 | 'time': 1e-5
44 | }
45 |
46 |
47 | class TestCaseType(TestCaseTypeEnum):
48 | EARTH_TO_MOON = ("Earth to moon", 1)
49 | SUN_TO_EARTH = ("Sun to Earth", 1)
50 | MAX_EARTH_TO_MARS = ("Earth to Mars", 1)
51 | MAX_EARTH_TO_JUPITER = ("Earth to Jupiter", 1)
52 | RANDOM = ("Random", 1)
53 |
54 |
55 | class ProblemTestCase(TestCase):
56 | def input_tuple(self) -> tuple:
57 | return self.input['distance'],
58 |
59 | def output_tuple(self) -> tuple:
60 | return self.output['time'],
61 |
62 |
63 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
64 | test_case = ProblemTestCase(test_type)
65 |
66 | if test_type is TestCaseType.EARTH_TO_MOON:
67 | distance = PHYSICAL_CONSTANTS['d_Earth_Moon']
68 |
69 | elif test_type is TestCaseType.SUN_TO_EARTH:
70 | distance = PHYSICAL_CONSTANTS['d_Sun_Earth']
71 |
72 | elif test_type is TestCaseType.MAX_EARTH_TO_MARS:
73 | distance = PHYSICAL_CONSTANTS['d_Earth_Mars']
74 |
75 | elif test_type is TestCaseType.MAX_EARTH_TO_JUPITER:
76 | distance = PHYSICAL_CONSTANTS['d_Earth_Jupiter']
77 |
78 | elif test_type is TestCaseType.RANDOM:
79 | c = PHYSICAL_CONSTANTS['c']
80 | distance = uniform(c, 60*c)
81 |
82 | else:
83 | raise ValueError(f"Unrecognized test case: {test_type}")
84 |
85 | test_case.input['distance'] = distance
86 | test_case.output['time'] = light_time(distance)
87 |
88 | return test_case
89 |
--------------------------------------------------------------------------------
/problems/temperature_variations.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 | from numpy.random import randint, uniform
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.temperature_variations import temperature_statistics
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "temperature_statistics"
11 | INPUT_VARS = ['T']
12 | OUTPUT_VARS = ['T_avg', 'T_std']
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {
17 | 'T': {
18 | 'Saskatoon, Canada': [-13.9, -11.4, -4.9, 5.2, 11.8, 16.1, 19.0, 18.2, 12.0, 4.4, -5.2, -12.4],
19 | 'Baku, Azerbaijan': [4.4, 4.2, 7.0, 12.9, 18.5, 23.5, 26.4, 26.3, 22.5, 16.6, 11.2, 7.3],
20 | 'Khartoum, Sudan': [23.2, 25.0, 28.7, 31.9, 34.5, 34.3, 32.1, 31.5, 32.5, 32.4, 28.1, 24.5],
21 | 'Singapore': [26.5, 27.1, 27.5, 28.0, 28.3, 28.3, 27.9, 27.9, 27.6, 27.6, 27.0, 26.4],
22 | 'San Juan, Argentina': [27.1, 25.5, 22.8, 17.2, 12.2, 8.3, 7.7, 10.6, 14.4, 19.8, 23.4, 26.3]
23 | }
24 | }
25 |
26 | ATOL = {}
27 | RTOL = {
28 | 'T_avg': 1e-8,
29 | 'T_std': 1e-8
30 | }
31 |
32 |
33 | class TestCaseType(TestCaseTypeEnum):
34 | ONE_VALUE = ("One value", 1)
35 | TWO_VALUES = ("Two values", 1)
36 | BUNCH_OF_VALUES = ("Bunch of values", 1)
37 | SASKATOON = ("Saskatoon, Canada", 1)
38 | BAKU = ("Baku, Azerbaijan", 1)
39 | KHARTOUM = ("Khartoum, Sudan", 1)
40 | SINGAPORE = ("Singapore", 1)
41 | SAN_JUAN = ("San Juan, Argentina", 1)
42 |
43 |
44 | class ProblemTestCase(TestCase):
45 | def input_tuple(self) -> tuple:
46 | return self.input['T'],
47 |
48 | def output_tuple(self) -> tuple:
49 | return self.output['T_avg'], self.output['T_std']
50 |
51 |
52 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
53 | test_case = ProblemTestCase(test_type)
54 |
55 | if test_type is TestCaseType.ONE_VALUE:
56 | T = [uniform(-20, 35)]
57 |
58 | elif test_type is TestCaseType.TWO_VALUES:
59 | T = uniform(-20, 35, size=2).tolist()
60 |
61 | elif test_type is TestCaseType.BUNCH_OF_VALUES:
62 | N = randint(5, 20)
63 | T = uniform(-20, 35, size=N).tolist()
64 |
65 | elif test_type is TestCaseType.SASKATOON:
66 | T = PHYSICAL_CONSTANTS['T']['Saskatoon, Canada']
67 |
68 | elif test_type is TestCaseType.BAKU:
69 | T = PHYSICAL_CONSTANTS['T']['Baku, Azerbaijan']
70 |
71 | elif test_type is TestCaseType.KHARTOUM:
72 | T = PHYSICAL_CONSTANTS['T']['Khartoum, Sudan']
73 |
74 | elif test_type is TestCaseType.SINGAPORE:
75 | T = PHYSICAL_CONSTANTS['T']['Singapore']
76 |
77 | elif test_type is TestCaseType.SAN_JUAN:
78 | T = PHYSICAL_CONSTANTS['T']['San Juan, Argentina']
79 |
80 | else:
81 | raise ValueError(f"Unrecognized test case: {test_type}")
82 |
83 | test_case.input['T'] = T
84 | test_case.output['T_avg'], test_case.output['T_std'] = temperature_statistics(T)
85 |
86 | return test_case
87 |
--------------------------------------------------------------------------------
/problems/test_case.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from enum import Enum
3 | from math import isclose
4 | from typing import Tuple
5 |
6 | from numpy import ndarray, array_equal, allclose
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 |
11 | class TestCaseMismatchError(Exception):
12 | pass
13 |
14 |
15 | class TestCaseTypeEnum(Enum):
16 | def __init__(self, test_name, multiplicity):
17 | self.test_name = test_name
18 | self.multiplicity = multiplicity
19 |
20 |
21 | class TestCase(object):
22 | def __init__(self, test_type=None, input_vars=None, input_tuple=None, output_vars=None, output_tuple=None):
23 | self.test_type = test_type
24 |
25 | self.input = {}
26 | if input_vars is not None and input_tuple is not None:
27 | if len(input_vars) != len(input_tuple):
28 | raise ValueError("input_vars and input_tuple must be of the same length but input_vars={:} (len={:d}), "
29 | "input_tuple={:} (len={:d})".format(input_vars, len(input_vars),
30 | input_tuple, len(input_tuple)))
31 | for var, val in zip(input_vars, input_tuple):
32 | self.input[var] = val
33 |
34 | self.output = {}
35 | if output_vars is not None and output_tuple is not None:
36 | if len(output_vars) != len(output_tuple):
37 | raise ValueError("output_vars and output_tuple must be of the same length but output_vars={:} "
38 | "(len={:d}), output_tuple={:} (len={:d})".format(output_vars, len(output_vars),
39 | output_tuple, len(output_tuple)))
40 | for var, val in zip(output_vars, output_tuple):
41 | self.output[var] = val
42 |
43 | def input_tuple(self) -> tuple:
44 | pass
45 |
46 | def output_tuple(self) -> tuple:
47 | pass
48 |
49 |
50 | def values_match(v1, v2, tt, tol) -> bool:
51 | """
52 | Check if v1 and v2 are equal. For ints and floats, an absolute or relative tolerance can be specified.
53 | Lists and tuples are checked recursively.
54 |
55 | :param v1: One value.
56 | :param v2: Another value.
57 | :param tt: Tolerance type: None, "absolute", or "relative".
58 | :param tol: Tolerance value if using "absolute" or "relative".
59 | :return: True or False
60 | """
61 |
62 | if isinstance(v1, (int, float)):
63 | if tt is None:
64 | return v1 == v2
65 | elif tt == "absolute":
66 | return isclose(v1, v2, abs_tol=tol)
67 | elif tt == "relative":
68 | return isclose(v1, v2, rel_tol=tol)
69 |
70 | if isinstance(v1, (list, ndarray)) and isinstance(v2, (list, ndarray)):
71 | if tt is None:
72 | return array_equal(v1, v2)
73 | elif tt == "absolute":
74 | return allclose(v1, v2, atol=tol)
75 | elif tt == "relative":
76 | return allclose(v1, v2, rtol=tol)
77 |
78 | if isinstance(v1, (list, tuple)) and isinstance(v2, (list, tuple)):
79 | if len(v1) != len(v2):
80 | logger.debug("v1 and v2 lengths do not match: v1_len={:d}, v2_len={:d}".format(len(v1), len(v2)))
81 | return False
82 |
83 | for e1, e2 in zip(v1, v2):
84 | # Recursively check that each element of the two lists or tuples match.
85 | if values_match(e1, e2, tt, tol) is False:
86 | return False
87 |
88 | # We've gone through the entire list/tuple and each pair of elements match, so return True.
89 | return True
90 |
91 | # Values can't match if they're of different types (unless we're comparing ints with floats,
92 | # or lists with numpy arrays, or lists with tuples).
93 | if type(v1) != type(v2):
94 | logger.debug("v1 and v2 types do not match: v1_type={:}, v2_type={:}".format(type(v1), type(v2)))
95 | return False
96 |
97 | return v1 == v2 # Takes care of strings (and hopefully other data types not considered above).
98 |
99 |
100 | def test_case_solution_correct(correct_test_case: TestCase, user_test_case: TestCase, atol: dict, rtol: dict) -> Tuple[bool, TestCase]:
101 | """
102 | Check whether user_test_case contains the correct output. Absolute and relative tolerances can be specified via
103 | the atol and rtol dictionaries.
104 |
105 | :param user_test_case: A TestCase object containing input given to the user and the output they produced.
106 | :param atol: A dictionary of absolute tolerances.
107 | :param rtol: A dictionary of relative tolerances.
108 | :param problem_test_case: A function that can be used to construct TestCase objects.
109 | :param solve_test_case: A function that can be used to fill the test case output with the correct output.
110 | :return: True or False, and a TestCase containing the correct output.
111 | """
112 | logger.info("Verifying user solution for test case {:}...".format(correct_test_case.test_type))
113 |
114 | input_vars = list(user_test_case.input.keys())
115 | output_vars = list(user_test_case.output.keys())
116 |
117 | for i, var in enumerate(input_vars):
118 | var_type = type(user_test_case.input[var])
119 | val = user_test_case.input[var]
120 | logger.debug("Test case input {:d}: name={:s}, type={:}, value={:}".format(i, var, var_type, val))
121 |
122 | # Assume user output is false until we can check that all values match.
123 | test_case_passed = False
124 | values_passed = []
125 |
126 | # Verify that every output matches.
127 | for i, var in enumerate(output_vars):
128 | var_type = type(correct_test_case.output[var])
129 | correct_val = correct_test_case.output[var]
130 | user_val = user_test_case.output[var]
131 |
132 | # Raise an error if both an absolute and relative tolerance are defined. Technically non-fatal as we could just
133 | # pick one, but this should encourage less sloppy problem modules.
134 | if var in atol.keys() and var in rtol.keys():
135 | raise TestCaseMismatchError("Both atol={:} and rtol={:} are defined for output var {:s}! "
136 | "Only one must be defined.""".format(atol[var], rtol[var], var))
137 |
138 | if var in atol.keys():
139 | tolerance_type, tolerance = "absolute", atol[var]
140 | elif var in rtol.keys():
141 | tolerance_type, tolerance = "relative", rtol[var]
142 | else:
143 | tolerance_type, tolerance = None, 0
144 |
145 | logger.debug("Test case output {:d}: name={:s}, type={:}, user_value={:}, correct_value={:}, "
146 | "tolerance_type={:}, tolerance={:}".format(i, var, var_type, user_val, correct_val,
147 | tolerance_type, tolerance))
148 |
149 | output_correct = values_match(user_val, correct_val, tolerance_type, tolerance)
150 |
151 | values_passed.append(output_correct)
152 | if output_correct is False:
153 | logger.info("User output for {:s} is wrong.".format(var))
154 | else:
155 | logger.info("User output for {:s} is correct.".format(var))
156 |
157 | if all(values_passed):
158 | test_case_passed = True
159 |
160 | return test_case_passed, correct_test_case
161 |
--------------------------------------------------------------------------------
/problems/wind_chill.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from typing import Tuple
3 | from numpy.random import uniform
4 |
5 | from problems.test_case import TestCase, TestCaseTypeEnum
6 | from problems.solutions.wind_chill import wind_chill
7 |
8 | logger = logging.getLogger(__name__)
9 |
10 | FUNCTION_NAME = "wind_chill"
11 | INPUT_VARS = ['T_a', 'v']
12 | OUTPUT_VARS = ['T_wc']
13 |
14 | STATIC_RESOURCES = []
15 |
16 | PHYSICAL_CONSTANTS = {}
17 | ATOL = {}
18 | RTOL = {
19 | 'T_wc': 1e-8
20 | }
21 |
22 |
23 | class TestCaseType(TestCaseTypeEnum):
24 | ZERO_TEMP = ("Zero celsius", 1)
25 | ZERO_WIND = ("Zero wind", 1)
26 | COLD_CALM = ("Cold calm weather", 1)
27 | COLD_WINDY = ("Cold windy weather", 1)
28 | VERY_COLD_CALM = ("Very cold and calm weather", 1)
29 | VERY_COLD_WINDY = ("Very cold and windy weather", 1)
30 |
31 |
32 | class ProblemTestCase(TestCase):
33 | def input_tuple(self) -> tuple:
34 | return self.input['T_a'], self.input['v']
35 |
36 | def output_tuple(self) -> tuple:
37 | return self.output['T_wc'],
38 |
39 |
40 | def generate_test_case(test_type: TestCaseType) -> ProblemTestCase:
41 | test_case = ProblemTestCase(test_type)
42 |
43 | if test_type is TestCaseType.ZERO_TEMP:
44 | T_a = 0.0
45 | v = uniform(5, 40)
46 |
47 | elif test_type is TestCaseType.ZERO_WIND:
48 | T_a = uniform(-20, 20)
49 | v = 0.0
50 |
51 | elif test_type is TestCaseType.COLD_CALM:
52 | T_a = uniform(-15, 2)
53 | v = uniform(2, 8)
54 |
55 | elif test_type is TestCaseType.COLD_WINDY:
56 | T_a = uniform(-15, 2)
57 | v = uniform(25, 60)
58 |
59 | elif test_type is TestCaseType.VERY_COLD_CALM:
60 | T_a = uniform(-50, -20)
61 | v = uniform(2, 8)
62 |
63 | elif test_type is TestCaseType.VERY_COLD_WINDY:
64 | T_a = uniform(-50, -20)
65 | v = uniform(25, 60)
66 |
67 | else:
68 | raise ValueError(f"Unrecognized test case: {test_type}")
69 |
70 | test_case.input['T_a'], test_case.input['v'] = T_a, v
71 | test_case.output['T_wc'] = wind_chill(T_a, v)
72 |
73 | return test_case
74 |
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | log_cli = 1
3 | log_cli_level = INFO
4 | log_cli_format = %(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)s)
5 | log_cli_date_format=%Y-%m-%d %H:%M:%S
6 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | atomicwrites==1.4.0
2 | attrs==20.3.0
3 | bitstring==3.1.7
4 | black==19.3b0
5 | certifi==2020.12.5
6 | chardet==4.0.0
7 | click==7.1.2
8 | docker==4.4.1
9 | entrypoints==0.3
10 | falcon==2.0.0
11 | filelock==3.0.12
12 | flake8==3.8.4
13 | flake8-bugbear==20.11.1
14 | gunicorn==20.0.4
15 | importlib-metadata==3.3.0
16 | iniconfig==1.1.1
17 | mccabe==0.6.1
18 | more-itertools==8.6.0
19 | numpy==1.19.4
20 | packaging==20.9
21 | pip==21.0.1
22 | pluggy==0.13.1
23 | py==1.10.0
24 | pycodestyle==2.6.0
25 | pyflakes==2.2.0
26 | pyparsing==2.4.7
27 | pytest==6.2.1
28 | scipy==1.5.4
29 | setuptools==52.0.0
30 | six==1.15.0
31 | toml==0.10.2
32 | urllib3==1.26.4
33 | virtualenv==20.4.3
34 | wcwidth==0.2.5
35 | websocket-client==0.57.0
36 | wheel==0.36.2
37 | zipp==3.4.0
38 |
--------------------------------------------------------------------------------
/resources/correlation_does_not_imply_causation/spurious_xy.csv:
--------------------------------------------------------------------------------
1 | 5427,18.079
2 | 5688,18.594
3 | 6198,19.753
4 | 6462,20.734
5 | 6635,20.831
6 | 7336,23.029
7 | 7248,23.597
8 | 7491,23.584
9 | 8161,22.525
10 | 8578,27.731
11 | 9000,29.449
12 |
--------------------------------------------------------------------------------
/resources/el_nino_intensities/mei.ext_index.txt:
--------------------------------------------------------------------------------
1 | YEAR DECJAN JANFEB FEBMAR MARAPR APRMAY MAYJUN JUNJUL JULAUG AUGSEP SEPOCT OCTNOV NOVDEC
2 | 1871 .032 -.055 .005 .42 .195 -.639 -.842 -.474 -.143 .072 .107 -.206
3 | 1872 -.633 -.875 -.864 -.664 -.59 -.618 -.658 -.795 -.8 -.669 -.651 -.847
4 | 1873 -.93 -.991 -1.301 -1.489 -1.066 -.689 -.657 -.646 -.318 -.012 -.355 -.54
5 | 1874 -.611 -.802 -1.036 -1.275 -1.258 -.964 -.902 -1.143 -1.148 -1.003 -.771 -.679
6 | 1875 -.774 -.686 -.67 -.958 -1.315 -1.812 -1.964 -1.553 -1.295 -.993 -.682 -.688
7 | 1876 -.878 -1.196 -1.504 -1.691 -1.878 -1.845 -1.435 -.867 -.393 -.071 .088 .17
8 | 1877 .193 .334 .373 .353 .441 .69 1.318 1.687 1.788 2.07 2.165 1.763
9 | 1878 1.877 2.361 2.495 1.883 1.006 .58 .54 .235 -.371 -.845 -.911 -.921
10 | 1879 -1.005 -.729 -.445 -.447 -.956 -1.18 -.827 -.607 -.523 -.557 -.72 -1.031
11 | 1880 -1.057 -.83 -.899 -1.069 -.965 -.595 .113 .394 .083 -.228 -.128 .17
12 | 1881 .148 .215 .174 .082 -.105 -.062 -.153 -.559 -.656 -.389 -.499 -.776
13 | 1882 -.598 -.461 -.585 -.799 -1.028 -.97 -.686 -.436 -.267 -.261 -.379 -.404
14 | 1883 -.562 -.628 -.562 -.672 -1.07 -.898 -.284 -.228 -.314 -.01 .004 -.012
15 | 1884 .094 .121 .112 .369 .786 .725 .368 .433 .628 .525 .333 .432
16 | 1885 .639 .599 .262 .247 .588 .414 -.044 .211 .506 .804 1.216 .914
17 | 1886 .384 .112 .016 -.103 -.619 -1.274 -1.466 -1.351 -1.228 -1.233 -1.306 -1.272
18 | 1887 -1.451 -1.573 -1.313 -1.088 -1.257 -.926 -.066 .194 .113 -.154 -.314 -.155
19 | 1888 .036 .389 .792 .948 .874 .921 1.038 1.34 1.732 1.788 1.723 1.851
20 | 1889 1.893 1.865 1.618 1.163 .481 -.068 -.555 -1.192 -1.494 -1.534 -1.532 -1.451
21 | 1890 -1.53 -1.731 -1.584 -1.456 -1.55 -1.564 -1.406 -1.264 -1.186 -1.006 -.876 -.638
22 | 1891 -.557 -.287 .17 .285 .061 .128 .36 .382 .192 .021 -.097 -.188
23 | 1892 -.128 -.184 -.708 -1.486 -1.572 -1.152 -1.027 -1.29 -1.619 -1.748 -1.682 -1.545
24 | 1893 -1.553 -1.625 -1.565 -1.524 -1.941 -2.356 -2.56 -2.524 -2.427 -2.167 -1.832 -1.368
25 | 1894 -1.206 -1.323 -1.448 -1.645 -1.32 -.771 -.92 -1.018 -.856 -.914 -.799 -.665
26 | 1895 -.706 -.775 -.772 -.803 -.83 -.624 -.182 .281 .453 .264 .082 .044
27 | 1896 -.16 -.208 -.065 -.161 .066 .657 .91 1.223 1.484 1.294 1.2 1.418
28 | 1897 1.543 1.351 1.15 1.085 .876 .369 -.031 -.147 -.296 -.449 -.348 -.384
29 | 1898 -.461 -.691 -.999 -.896 -.55 -.425 -.307 -.597 -1.093 -.85 -.312 -.334
30 | 1899 -.608 -.694 -.539 -.245 .03 .182 .482 .883 1.114 .966 .906 1.079
31 | 1900 1.056 1.05 1.187 1.46 1.713 1.653 1.361 .688 .314 .354 .467 .438
32 | 1901 .612 .533 .156 -.09 -.013 -.062 -.112 .074 -.121 -.383 -.576 -.425
33 | 1902 -.328 -.256 .098 .487 .955 1.519 1.917 1.591 1.395 1.706 2.039 1.713
34 | 1903 .925 .853 .978 .606 -.058 -.397 -.46 -.657 -.495 -.504 -.784 -.925
35 | 1904 -1.194 -1.251 -1.09 -.924 -.563 -.208 .3 .657 .707 .812 .743 .736
36 | 1905 .789 .893 1.124 1.261 1.129 1.457 1.954 1.869 1.641 1.482 1.221 1.232
37 | 1906 1.053 .664 .512 .477 .37 .169 -.28 -.492 -.792 -1.184 -.874 -.674
38 | 1907 -.828 -.727 -.669 -.89 -1.016 -.923 -.511 -.008 .143 .024 -.356 -.466
39 | 1908 -.447 -.465 -.644 -.988 -1.306 -1.32 -.873 -.672 -.885 -1.004 -1.064 -1.282
40 | 1909 -1.029 -.613 -.677 -.914 -.975 -1.203 -1.5 -1.673 -1.753 -1.6 -1.532 -1.498
41 | 1910 -1.385 -1.42 -1.623 -1.813 -1.911 -1.985 -2.008 -1.65 -1.488 -1.611 -1.426 -1.348
42 | 1911 -1.196 -.728 -.55 -1.047 -1.55 -1.556 -.919 -.05 .301 .502 .744 .789
43 | 1912 .913 1.004 .798 .619 .36 -.306 -.58 -.569 -.285 .003 .003 -.048
44 | 1913 -.019 .194 .17 -.548 -.944 -.376 .056 .342 .681 .531 .548 .78
45 | 1914 .805 .809 .796 .604 .451 .354 .613 1.219 1.328 .974 1.028 1.257
46 | 1915 1.308 1.173 .885 1.091 1.556 1.552 .983 .523 .21 -.317 -.688 -.589
47 | 1916 -.405 -.675 -.993 -.872 -.851 -1.053 -1.498 -2.099 -2.173 -1.773 -1.678 -1.899
48 | 1917 -1.915 -1.764 -1.606 -1.572 -1.745 -1.713 -1.127 -.967 -1.093 -1.116 -1.127 -1.202
49 | 1918 -1.328 -1.138 -.949 -.787 -.574 -.055 1.046 1.479 1.052 .969 1.051 1.158
50 | 1919 1.527 1.753 1.538 1.083 1.026 1.279 1.224 1.151 .995 .81 .5 .145
51 | 1920 .265 .564 .663 .567 .347 .248 -.197 -.342 -.235 -.3 -.054 .037
52 | 1921 .034 -.052 -.638 -1.124 -.834 -.578 -.605 -.563 -.595 -.617 -.368 -.296
53 | 1922 -.483 -.565 -.365 -.044 .029 -.223 -.492 -.401 -.37 -.506 -.465 -.691
54 | 1923 -.838 -.623 -.517 -.528 -.41 -.035 .494 .788 .895 .92 1.021 .929
55 | 1924 .664 .409 .124 -.216 -.685 -1.093 -1.244 -1.448 -1.458 -.996 -.845 -.956
56 | 1925 -.954 -.79 -.482 -.134 .048 .025 .217 .802 1.286 1.511 1.511 1.588
57 | 1926 1.407 1.35 1.602 1.71 1.33 1.06 1.067 .685 .179 -.03 -.025 -.001
58 | 1927 .014 .158 .193 -.274 -.399 -.239 -.405 -.195 .162 .206 .376 .343
59 | 1928 .312 .476 .346 -.001 -.131 .071 .346 .06 -.471 -.466 -.296 -.161
60 | 1929 -.064 -.054 -.189 -.237 .076 .418 .557 .734 .845 .677 .459 .468
61 | 1930 .506 .552 .767 1.038 1.199 1.054 1.143 1.395 1.507 1.652 1.723 1.925
62 | 1931 1.915 1.855 1.955 1.905 1.496 .877 .425 .262 .172 .179 .248 .197
63 | 1932 .187 .276 .485 .787 .964 .998 1.114 1.025 .483 -.05 -.147 -.023
64 | 1933 -.059 -.126 -.396 -.497 -.388 -.671 -.769 -.827 -1.014 -1.001 -.987 -1.093
65 | 1934 -1.251 -1.214 -1.007 -.651 -.246 -.074 -.133 -.174 .037 .114 -.084 -.141
66 | 1935 -.197 -.331 -.511 -.508 -.11 .096 -.055 .017 .322 .412 .364 .372
67 | 1936 .193 .156 .319 .44 .513 .474 .292 .249 .206 .138 .258 .361
68 | 1937 .34 .206 .218 .082 -.13 -.277 -.292 -.071 .186 .26 .157 -.081
69 | 1938 -.277 -.408 -.477 -.68 -.971 -1.035 -1.25 -1.392 -1.198 -.787 -.595 -.714
70 | 1939 -.786 -.824 -.979 -.93 -.433 .033 .269 .579 .877 .715 .313 .313
71 | 1940 .658 .921 1.163 1.419 1.441 1.419 1.352 1.119 .988 .965 .873 .967
72 | 1941 1.35 1.55 1.793 2.07 2.269 2.001 1.496 1.522 1.514 1.435 1.629 1.605
73 | 1942 1.324 1.029 .87 .798 .41 -.262 -.706 -.879 -.908 -.968 -1.089 -1.165
74 | 1943 -1.257 -1.166 -.846 -.463 -.004 .375 .504 .145 -.179 -.363 -.428 -.347
75 | 1944 -.289 -.035 .22 .258 .239 .161 .108 .029 -.257 -.273 -.256 -.425
76 | 1945 -.395 -.41 -.471 -.425 -.193 .024 -.362 -.645 -.462 -.01 .234 -.018
77 | 1946 -.213 -.247 -.223 -.158 -.214 -.193 -.072 -.043 .141 .36 .33 .212
78 | 1947 .197 .111 -.116 -.301 -.382 -.157 -.177 -.476 -.633 -.67 -.541 -.406
79 | 1948 -.063 .326 .606 .569 .259 .18 .067 -.042 -.027 -.083 -.279 -.175
80 | 1949 .066 -.063 -.252 -.133 .194 .007 -.519 -.698 -.611 -.726 -.957 -.924
81 | 1950 -.941 -1.131 -1.347 -1.377 -1.282 -1.232 -1.538 -1.797 -1.433 -1.03 -1.109 -1.378
82 | 1951 -1.264 -1.18 -1.176 -.795 -.13 .382 .782 1.116 1.233 1.04 .867 .738
83 | 1952 .613 .565 .449 .437 .434 .017 -.303 -.251 .004 .178 -.035 -.119
84 | 1953 .154 .403 .59 .69 .758 .581 .31 .447 .778 .568 .104 .138
85 | 1954 .103 -.137 -.024 .137 -.065 -.419 -.838 -1.041 -.962 -.954 -1.049 -1.155
86 | 1955 -.955 -.858 -1.147 -1.292 -1.497 -1.887 -1.749 -1.543 -1.647 -1.96 -2.081 -1.965
87 | 1956 -1.763 -1.614 -1.532 -1.329 -1.366 -1.538 -1.371 -.898 -.754 -.95 -1.076 -.947
88 | 1957 -.736 -.387 .049 .41 .792 1.371 1.628 1.551 1.436 1.152 1.157 1.312
89 | 1958 1.478 1.549 1.505 1.38 1.5 1.602 1.419 1.02 .604 .424 .373 .534
90 | 1959 .639 .747 .86 .748 .797 .864 .549 .308 .232 .142 .048 -.125
91 | 1960 -.07 .073 .106 .167 .177 .071 -.078 -.303 -.253 -.256 -.286 -.197
92 | 1961 -.011 .016 -.065 .156 .273 .193 .087 .108 .006 -.396 -.508 -.526
93 | 1962 -.894 -.891 -.434 -.436 -.975 -.894 -.408 -.408 -.534 -.59 -.616 -.3
94 | 1963 -.371 -.579 -.461 -.233 .037 .232 .508 .91 1.059 1.11 1.07 .974
95 | 1964 .933 .73 .25 -.349 -.827 -1.093 -1.277 -1.289 -1.191 -1.171 -1.059 -.895
96 | 1965 -.66 -.333 -.132 .076 .519 1.025 1.363 1.731 1.73 1.42 1.523 1.726
97 | 1966 1.682 1.526 1.213 .889 .823 .648 .357 .264 .076 -.05 .07 .023
98 | 1967 -.208 -.501 -.753 -.727 -.55 -.582 -.741 -.828 -.837 -.785 -.591 -.372
99 | 1968 -.456 -.62 -.658 -.759 -.733 -.525 -.544 -.338 .204 .619 .726 .655
100 | 1969 .755 1.04 1.027 .941 1.092 1.219 1.077 .906 .857 .831 .76 .744
101 | 1970 .675 .575 .439 .309 .09 -.501 -1.153 -1.347 -1.228 -1.1 -1.06 -1.128
102 | 1971 -1.281 -1.563 -1.873 -1.979 -1.764 -1.534 -1.304 -1.041 -1.296 -1.398 -1.102 -.986
103 | 1972 -.651 -.258 -.146 .08 .751 1.546 2.087 2.209 2.068 1.88 1.844 2.008
104 | 1973 2.023 1.756 1.303 .743 .154 -.52 -1.077 -1.363 -1.664 -1.742 -1.591 -1.768
105 | 1974 -2.024 -2.188 -2.088 -1.802 -1.241 -.821 -.976 -.858 -.543 -.815 -1.094 -1.033
106 | 1975 -.703 -.479 -.625 -.953 -1.083 -1.168 -1.408 -1.744 -2.055 -2.27 -2.19 -1.909
107 | 1976 -1.764 -1.626 -1.459 -1.289 -.777 -.007 .674 .771 .948 1.272 .982 .731
108 | 1977 .71 .651 .548 .558 .511 .421 .641 .749 .715 .831 1.01 1.112
109 | 1978 .939 .846 .895 .57 .023 -.235 -.137 .19 -.056 -.142 .39 .626
110 | 1979 .577 .402 .132 .164 .623 .637 .346 .584 .876 .818 .831 1.012
111 | 1980 1.014 .813 .832 1.167 1.249 .919 .783 .689 .411 .336 .226 .189
112 | 1981 -.087 -.337 -.128 .362 .29 .013 -.096 -.076 .082 .201 .177 .064
113 | 1982 .077 .019 .107 .44 .949 1.48 1.768 1.841 1.871 2.254 2.593 2.777
114 | 1983 3.08 3.31 3.349 3.084 2.665 2.337 1.922 1.155 .462 .105 -.099 .054
115 | 1984 .054 -.166 -.192 .096 -.052 -.402 -.416 -.365 -.214 -.102 -.359 -.66
116 | 1985 -.744 -.777 -.863 -.772 -.791 -.533 -.102 -.365 -.601 -.257 .015 -.151
117 | 1986 -.278 -.228 -.087 -.004 .141 .319 .377 .736 1.147 1.198 1.237 1.342
118 | 1987 1.38 1.529 1.883 2.171 2.162 2.069 2.2 2.343 2.054 1.824 1.575 1.49
119 | 1988 1.387 1.02 .703 .429 -.047 -.735 -1.416 -1.704 -1.783 -1.76 -1.778 -1.67
120 | 1989 -1.468 -1.33 -1.198 -1.143 -1.124 -1.027 -.951 -.637 -.35 -.389 -.33 .001
121 | 1990 .225 .462 .742 .723 .531 .422 .352 .147 .257 .454 .382 .298
122 | 1991 .407 .413 .493 .763 1.014 1.184 1.102 .553 .296 .8 1.257 1.45
123 | 1992 1.857 2.039 2.052 2.289 2.404 2.293 1.742 .723 .464 .751 .765 .808
124 | 1993 .925 1.049 1.177 1.44 1.86 1.835 1.228 .724 .795 1.186 1.158 .784
125 | 1994 .644 .422 .278 .646 1.011 .992 .885 .731 .796 1.355 1.447 1.319
126 | 1995 1.337 1.196 .935 .83 .94 .813 .483 .258 -.081 -.274 -.407 -.432
127 | 1996 -.437 -.421 -.348 -.361 -.435 -.272 -.214 -.218 -.22 -.327 -.213 -.203
128 | 1997 -.453 -.577 -.323 .529 1.61 2.602 3.12 3.181 3.168 3.214 3.201 3.022
129 | 1998 2.822 2.823 2.944 2.821 2.336 1.355 .18 -.731 -.863 -.759 -.857 -.973
130 | 1999 -1.108 -1.231 -1.121 -1.098 -1.187 -.975 -.826 -.801 -.727 -.847 -1.045 -1.182
131 | 2000 -1.255 -1.295 -1.319 -1.168 -.96 -.711 -.479 -.298 -.113 -.109 -.397 -.642
132 | 2001 -.706 -.796 -.71 -.398 -.322 -.349 -.225 -.041 -.098 -.256 -.29 -.199
133 | 2002 -.064 -.091 .045 .359 .669 .912 .667 .636 .903 1.09 1.207 1.223
134 | 2003 1.252 1.084 .953 .816 .262 -.094 .004 .083 .36 .533 .594 .646
135 | 2004 .526 .331 .118 .125 .299 .214 .271 .582 .803 .68 .69 .74
136 | 2005 .432 .28 .631 .773 .588 .737 .8 .492 .08 -.661 -.998 -.954
137 | 2006 -.438 -.424 -.527 -.575 .043 .546 .718 .77 .84 .955 1.286 .951
138 | 2007 .985 .528 .12 .02 .354 -.136 -.247 -.427 -1.175 -1.217 -1.165 -1.193
139 | 2008 -1.02 -1.388 -1.579 -.879 -.349 .164 .089 -.252 -.545 -.692 -.597 -.663
140 | 2009 -.726 -.707 -.723 -.105 .328 .779 1.06 1.073 .745 .909 1.121 1.045
141 | 2010 1.067 1.52 1.469 .99 .668 -.211 -1.099 -1.662 -1.86 -1.899 -1.49 -1.577
142 | 2011 -1.739 -1.563 -1.575 -1.399 -.202 .016 -.191 -.502 -.751 -.933 -.949 -.957
143 | 2012 -.993 -.695 -.398 .112 .769 .866 1.128 .628 .351 .081 .125 .094
144 | 2013 .096 -.08 -.037 .095 .203 -.078 -.311 -.466 -.125 .13 -.053 -.248
145 | 2014 -.275 -.266 .027 .312 1.01 1.057 .921 .961 .593 .438 .763 .558
146 | 2015 .42 .459 .631 .943 1.592 2.101 1.987 2.365 2.532 2.241 2.297 2.112
147 | 2016 2.227 2.169 1.984 2.124 1.77 1.069 .354 .186 -.091 -.379 -.212 -.121
--------------------------------------------------------------------------------
/resources/game_of_life/oscillators.txt:
--------------------------------------------------------------------------------
1 | .............................
2 | .............................
3 | ........................ooo..
4 | .............................
5 | .............................
6 | .............................
7 | .............................
8 | .............................
--------------------------------------------------------------------------------
/resources/game_of_life/spaceships.txt:
--------------------------------------------------------------------------------
1 | .............................
2 | ....o........................
3 | ..o.o........................
4 | ...oo........................
5 | .............................
6 | .............................
7 | .............................
8 | .............................
--------------------------------------------------------------------------------
/resources/game_of_life/still_life.txt:
--------------------------------------------------------------------------------
1 | .....................o.......
2 | ....................o.o......
3 | .....................o.......
4 | .............................
5 | .............................
6 | .............................
7 | .............................
8 | .............................
--------------------------------------------------------------------------------
/resources/molecular_mass_calculator/periodic_table.csv:
--------------------------------------------------------------------------------
1 | H,1.008
2 | He,4.0026022
3 | Li,6.94
4 | Be,9.01218315
5 | B,10.81
6 | C,12.011
7 | N,14.007
8 | O,15.999
9 | F,18.99840316
10 | Ne,20.17976
11 | Na,22.98976928
12 | Mg,24.305
13 | Al,26.98153857
14 | Si,28.085
15 | P,30.973762
16 | S,32.06
17 | Cl,35.45
18 | Ar,39.9481
19 | K,39.09831
20 | Ca,40.0784
21 | Sc,44.9559085
22 | Ti,47.8671
23 | V,50.94151
24 | Cr,51.99616
25 | Mn,54.9380443
26 | Fe,55.8452
27 | Co,58.9331944
28 | Ni,58.69344
29 | Cu,63.5463
30 | Zn,65.382
31 | Ga,69.7231
32 | Ge,72.6308
33 | As,74.9215956
34 | Se,78.9718
35 | Br,79.904
36 | Kr,83.7982
37 | Rb,85.46783
38 | Sr,87.621
39 | Y,88.905842
40 | Zr,91.2242
41 | Nb,92.906372
42 | Mo,95.951
43 | Tc,98
44 | Ru,101.072
45 | Rh,102.905502
46 | Pd,106.421
47 | Ag,107.86822
48 | Cd,112.4144
49 | In,114.8181
50 | Sn,118.7107
51 | Sb,121.7601
52 | Te,127.603
53 | I,126.904473
54 | Xe,131.2936
55 | Cs,132.905452
56 | Ba,137.3277
57 | La,138.905477
58 | Ce,140.1161
59 | Pr,140.907662
60 | Nd,144.2423
61 | Pm,145
62 | Sm,150.362
63 | Eu,151.9641
64 | Gd,157.253
65 | Tb,158.925352
66 | Dy,162.5001
67 | Ho,164.930332
68 | Er,167.2593
69 | Tm,168.934222
70 | Yb,173.0451
71 | Lu,174.96681
72 | Hf,178.492
73 | Ta,180.947882
74 | W,183.841
75 | Re,186.2071
76 | Os,190.233
77 | Ir,192.2173
78 | Pt,195.0849
79 | Au,196.9665695
80 | Hg,200.5923
81 | Tl,204.38
82 | Pb,207.21
83 | Bi,208.980401
84 | Po,209
85 | At,210
86 | Rn,222
87 | Fr,223
88 | Ra,226
89 | Ac,227
90 | Th,232.03774
91 | Pa,231.035882
92 | U,238.028913
93 | Np,237
94 | Pu,244
95 | Am,243
96 | Cm,247
97 | Bk,247
98 | Cf,251
99 | Es,252
100 | Fm,257
101 | Md,258
102 | No,259
103 | Lr,266
104 | Db,268
105 | Sg,269
106 | Bh,270
107 | Hs,269
108 | Mt,278
109 | Ds,281
110 | Rg,282
111 | Cn,285
112 | Nh,286
113 | Fl,289
114 | Mc,290
115 | Lv,293
116 | Ts,294
117 | Og,294
118 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | setup(name="lovelace_problems", packages=find_packages())
4 |
--------------------------------------------------------------------------------
/tests/test_problems.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pkgutil
3 | import importlib
4 | import pytest
5 | import problems
6 |
7 | from types import ModuleType
8 |
9 | log = logging.getLogger(__name__)
10 |
11 | problem_modules = pkgutil.iter_modules(problems.__path__)
12 | problem_names = [m.name for m in problem_modules]
13 |
14 | # test_case.py is not a problem module
15 | problem_names = list(filter(lambda s: "test_case" not in s, problem_names))
16 |
17 | @pytest.mark.parametrize("problem_name", problem_names)
18 | def test_importing_problem_module(problem_name):
19 | problem_module = importlib.import_module("problems." + problem_name)
20 | assert isinstance(problem_module, ModuleType)
21 |
22 | @pytest.mark.parametrize("problem_name", problem_names)
23 | def test_function_name_exists(problem_name):
24 | problem_module = importlib.import_module("problems." + problem_name)
25 | assert isinstance(problem_module.FUNCTION_NAME, str)
26 |
27 | @pytest.mark.parametrize("problem_name", problem_names)
28 | def test_input_output_vars(problem_name):
29 | problem_module = importlib.import_module("problems." + problem_name)
30 | assert isinstance(problem_module.INPUT_VARS, list)
31 | assert isinstance(problem_module.OUTPUT_VARS, list)
32 |
33 | @pytest.mark.parametrize("problem_name", problem_names)
34 | def test_problem_parameters(problem_name):
35 | problem_module = importlib.import_module("problems." + problem_name)
36 | assert isinstance(problem_module.STATIC_RESOURCES, list)
37 | assert isinstance(problem_module.PHYSICAL_CONSTANTS, dict)
38 | assert isinstance(problem_module.ATOL, dict)
39 | assert isinstance(problem_module.RTOL, dict)
40 |
41 | @pytest.mark.parametrize("problem_name", problem_names)
42 | def test_problem_test_case_types(problem_name):
43 | problem_module = importlib.import_module("problems." + problem_name)
44 | assert len(problem_module.TestCaseType) > 0
45 | for test_case_type in problem_module.TestCaseType:
46 | assert isinstance(test_case_type.test_name, str)
47 | assert isinstance(test_case_type.multiplicity, int)
48 |
49 | #####
50 | ##### Test each test case for each problem
51 | #####
52 |
53 | def problem_test_cases(problem_name):
54 | problem_module = importlib.import_module("problems." + problem_name)
55 | return [_ for _ in problem_module.TestCaseType]
56 |
57 | problem_test_cases_dict = {problem_name: problem_test_cases(problem_name) for problem_name in problem_names}
58 |
59 | problem_test_case_combos = [(name, test_case) for name, test_cases in problem_test_cases_dict.items() for test_case in test_cases]
60 |
61 | @pytest.mark.parametrize("problem_name, test_case_type", problem_test_case_combos)
62 | def test_problem_test_case_generation(problem_name, test_case_type):
63 |
64 | problem_module = importlib.import_module("problems." + problem_name)
65 | assert len(problem_module.TestCaseType) > 0
66 |
67 | if len(problem_module.STATIC_RESOURCES) > 0:
68 | log.info(f"Skipping problem {problem_name} as we can't test problems with static resources yet.")
69 | return True
70 |
71 | if test_case_type.multiplicity == 0:
72 | # These are usually disabled or not yet implemented tests.
73 | log.info(f"Skipping problem {problem_name} test case type {test_case_type} with multiplicity 0.")
74 | return True
75 |
76 | test_case = problem_module.generate_test_case(test_case_type)
77 |
78 | assert test_case.test_type == test_case_type
79 | assert isinstance(test_case.input, dict)
80 | assert isinstance(test_case.output, dict)
81 |
82 | for input_name, input_value in test_case.input.items():
83 | assert isinstance(input_name, str)
84 | assert input_name in problem_module.INPUT_VARS
85 |
86 | for output_name, output_value in test_case.output.items():
87 | assert isinstance(output_name, str)
88 | assert output_name in problem_module.OUTPUT_VARS
89 |
90 | input_tuple = test_case.input_tuple()
91 | assert len(input_tuple) == len(problem_module.INPUT_VARS)
92 | for input_name, input_value in zip(problem_module.INPUT_VARS, input_tuple):
93 | assert test_case.input[input_name] == input_value
94 |
95 | output_tuple = test_case.output_tuple()
96 | assert len(output_tuple) == len(problem_module.OUTPUT_VARS)
97 | for output_name, output_value in zip(problem_module.OUTPUT_VARS, output_tuple):
98 | assert test_case.output[output_name] == output_value
99 |
--------------------------------------------------------------------------------