from __future__ import annotations import random from synaptopus.backprop import BackpropNetwork def test_backprop_supports_multiple_hidden_layers() -> None: network = BackpropNetwork.random( input_size=3, hidden_layers=(4, 3), output_size=2, learning_rate=0.5, momentum=0.1, rng=random.Random(7), ) result = network.predict((0.0, 1.0, 0.5)) assert network.hidden_layers == (4, 3) assert network.output_size == 2 assert len(result.outputs) == 2 assert len(result.layer_states) == 3 def test_backprop_can_learn_xor_with_two_hidden_layers() -> None: network = BackpropNetwork.random( input_size=2, hidden_layers=(4, 4), output_size=1, learning_rate=0.8, momentum=0.2, rng=random.Random(11), ) samples = ( ((0.0, 0.0), (0.0,)), ((0.0, 1.0), (1.0,)), ((1.0, 0.0), (1.0,)), ((1.0, 1.0), (0.0,)), ) for _ in range(6000): for inputs, targets in samples: network.train_step(inputs, targets) predictions = { inputs: network.predict(inputs).outputs[0] for inputs, _ in samples } assert predictions[(0.0, 0.0)] < 0.2 assert predictions[(0.0, 1.0)] > 0.8 assert predictions[(1.0, 0.0)] > 0.8 assert predictions[(1.0, 1.0)] < 0.2 def test_backprop_round_trips_through_dict() -> None: network = BackpropNetwork.random( input_size=2, hidden_layers=(3, 2), output_size=1, rng=random.Random(3), ) restored = BackpropNetwork.from_dict(network.to_dict()) assert restored.layer_sizes == network.layer_sizes assert restored.weights == network.weights assert restored.biases == network.biases