import math import sys from pathlib import Path import pytest # Добавляем путь к родительской директории для корректных импортов sys.path.insert(0, str(Path(__file__).parent.parent)) from gp.chromosome import Chromosome from gp.operation import Operation from gp.ops import ADD, COS, DIV, EXP, MUL, NEG, SIN, SUB from gp.terminal import Terminal class TestChromosomeEval: """Тесты для метода eval класса Chromosome.""" def test_eval_single_terminal(self): """Тест вычисления хромосомы с одним терминалом.""" x = Terminal("x") def init_single_terminal(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node(x, []) chromosome = Chromosome([ADD], [x], init_single_terminal) result = chromosome.eval([5.0]) assert result == 5.0 result = chromosome.eval([-3.5]) assert result == -3.5 def test_eval_addition(self): """Тест вычисления простого сложения: x + y.""" x = Terminal("x") y = Terminal("y") def init_addition(chr: Chromosome) -> Chromosome.Node: # Создаём дерево: x + y return Chromosome.Node( ADD, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ) chromosome = Chromosome([ADD], [x, y], init_addition) result = chromosome.eval([3.0, 4.0]) assert result == 7.0 result = chromosome.eval([10.0, -5.0]) assert result == 5.0 def test_eval_subtraction(self): """Тест вычисления вычитания: x - y.""" x = Terminal("x") y = Terminal("y") def init_subtraction(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node( SUB, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ) chromosome = Chromosome([SUB], [x, y], init_subtraction) result = chromosome.eval([10.0, 3.0]) assert result == 7.0 result = chromosome.eval([5.0, 8.0]) assert result == -3.0 def test_eval_multiplication(self): """Тест вычисления умножения: x * y.""" x = Terminal("x") y = Terminal("y") def init_multiplication(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node( MUL, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ) chromosome = Chromosome([MUL], [x, y], init_multiplication) result = chromosome.eval([3.0, 4.0]) assert result == 12.0 result = chromosome.eval([-2.0, 5.0]) assert result == -10.0 def test_eval_division(self): """Тест вычисления деления: x / y.""" x = Terminal("x") y = Terminal("y") def init_division(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node( DIV, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ) chromosome = Chromosome([DIV], [x, y], init_division) result = chromosome.eval([10.0, 2.0]) assert result == 5.0 result = chromosome.eval([7.0, 2.0]) assert result == 3.5 def test_eval_division_by_zero(self): """Тест деления на ноль (должно вернуть inf).""" x = Terminal("x") y = Terminal("y") def init_division(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node( DIV, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ) chromosome = Chromosome([DIV], [x, y], init_division) result = chromosome.eval([10.0, 0.0]) assert result == float("inf") def test_eval_unary_negation(self): """Тест вычисления унарного минуса: -x.""" x = Terminal("x") def init_negation(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node(NEG, [Chromosome.Node(x, [])]) chromosome = Chromosome([NEG], [x], init_negation) result = chromosome.eval([5.0]) assert result == -5.0 result = chromosome.eval([-3.0]) assert result == 3.0 def test_eval_sin(self): """Тест вычисления синуса: sin(x).""" x = Terminal("x") def init_sin(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node(SIN, [Chromosome.Node(x, [])]) chromosome = Chromosome([SIN], [x], init_sin) result = chromosome.eval([0.0]) assert result == pytest.approx(0.0) result = chromosome.eval([math.pi / 2]) assert result == pytest.approx(1.0) result = chromosome.eval([math.pi]) assert result == pytest.approx(0.0, abs=1e-10) def test_eval_cos(self): """Тест вычисления косинуса: cos(x).""" x = Terminal("x") def init_cos(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node(COS, [Chromosome.Node(x, [])]) chromosome = Chromosome([COS], [x], init_cos) result = chromosome.eval([0.0]) assert result == pytest.approx(1.0) result = chromosome.eval([math.pi / 2]) assert result == pytest.approx(0.0, abs=1e-10) result = chromosome.eval([math.pi]) assert result == pytest.approx(-1.0) def test_eval_complex_expression(self): """Тест вычисления сложного выражения: (x + y) * (x - y).""" x = Terminal("x") y = Terminal("y") def init_complex(chr: Chromosome) -> Chromosome.Node: # (x + y) * (x - y) return Chromosome.Node( MUL, [ Chromosome.Node( ADD, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ), Chromosome.Node( SUB, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ), ], ) chromosome = Chromosome([ADD, SUB, MUL], [x, y], init_complex) # (5 + 3) * (5 - 3) = 8 * 2 = 16 result = chromosome.eval([5.0, 3.0]) assert result == 16.0 # (10 + 2) * (10 - 2) = 12 * 8 = 96 result = chromosome.eval([10.0, 2.0]) assert result == 96.0 def test_eval_nested_expression(self): """Тест вычисления вложенного выражения: sin(x + y).""" x = Terminal("x") y = Terminal("y") def init_nested(chr: Chromosome) -> Chromosome.Node: # sin(x + y) return Chromosome.Node( SIN, [ Chromosome.Node( ADD, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ) ], ) chromosome = Chromosome([ADD, SIN], [x, y], init_nested) result = chromosome.eval([math.pi / 4, math.pi / 4]) # sin(π/4 + π/4) = sin(π/2) = 1 assert result == pytest.approx(1.0) def test_eval_exp(self): """Тест вычисления экспоненты: exp(x).""" x = Terminal("x") def init_exp(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node(EXP, [Chromosome.Node(x, [])]) chromosome = Chromosome([EXP], [x], init_exp) result = chromosome.eval([0.0]) assert result == pytest.approx(1.0) result = chromosome.eval([1.0]) assert result == pytest.approx(math.e) result = chromosome.eval([2.0]) assert result == pytest.approx(math.e**2) def test_eval_multiple_calls(self): """Тест многократного вызова eval с разными значениями.""" x = Terminal("x") y = Terminal("y") def init_mul(chr: Chromosome) -> Chromosome.Node: return Chromosome.Node( MUL, [ Chromosome.Node(x, []), Chromosome.Node(y, []), ], ) chromosome = Chromosome([MUL], [x, y], init_mul) # Проверяем, что терминалы правильно обновляются assert chromosome.eval([2.0, 3.0]) == 6.0 assert chromosome.eval([4.0, 5.0]) == 20.0 assert chromosome.eval([10.0, 0.5]) == 5.0 def test_eval_without_root_raises_error(self): """Тест, что eval вызывает ошибку, если root = None.""" def init_none(chr: Chromosome) -> Chromosome.Node: return None # type: ignore chromosome = Chromosome([ADD], [Terminal("x")], init_none) with pytest.raises(ValueError, match="Chromosome is not initialized"): chromosome.eval([1.0])