def test_extra_time_points(self): """Tests the extra time points of the step back method.""" dtype = np.float64 min_x, max_x, size = dtype(0.0), dtype(1.0), 21 np_grid = np.linspace(min_x, max_x, num=size) grid_spec = grids.uniform_grid([min_x], [max_x], [size], dtype=dtype) r = dtype(0.03) def transform_fn(state): return tf.where(state.time <= 1.0, state.value_grid, payoff_fn(state)) def kernel(state): return state.value_grid * tf.exp(-r * state.time_step) def payoff_fn(state): return tf.maximum(state.coordinate_grid.grid, 0.5) bgs = grid_stepper.BackwardGridStepper( 2.0, kernel, grid_spec, time_step=0.3, dtype=dtype) bgs.transform_values(payoff_fn) # At time 2. expected_final_vals = np.maximum(np_grid, 0.5) expected_initial_values = expected_final_vals * np.exp(-r * 1) bgs.step_back_to_time( 0.0, value_transform_fn=transform_fn, extra_time_points=[1.3, 1.5, 1.0, 3.0]) initial_state = self.evaluate(bgs.state()) self.assertArrayNear(expected_initial_values, initial_state.value_grid, 1e-10) self.assertEqual(0.0, initial_state.time)
def test_grid_stepper_basic(self): dtype = np.float64 min_x, max_x, size = dtype(0.0), dtype(1.0), 21 np_grid = np.linspace(min_x, max_x, num=size) grid_spec = grids.uniform_grid([min_x], [max_x], [size], dtype=dtype) r = dtype(0.03) def kernel(state): return state.value_grid * tf.exp(-r * state.time_step) def time_step_fn(state): del state return tf.constant(0.1, dtype=dtype) def payoff_fn(state): return tf.maximum(state.coordinate_grid.grid, 0.5) bgs = grid_stepper.BackwardGridStepper( 2.0, kernel, grid_spec, time_step_fn=time_step_fn, dtype=dtype) bgs.transform_values(payoff_fn) # At time 2. bgs.step_back_to_time(dtype(0.13)) intermediate_state = self.evaluate(bgs.state()) bgs.step_back_to_time(dtype(0.0)) initial_state = self.evaluate(bgs.state()) expected_final_vals = np.maximum(np_grid, 0.5) expected_intermediate_values = expected_final_vals * np.exp(-r * (2 - 0.13)) expected_initial_values = expected_final_vals * np.exp(-r * 2) self.assertArrayNear(expected_intermediate_values, intermediate_state.value_grid, 1e-10) self.assertEqual(0.13, intermediate_state.time) self.assertArrayNear(expected_initial_values, initial_state.value_grid, 1e-10) self.assertEqual(0.0, initial_state.time)
def test_value_transform_fn(self): """Tests the value transform functionality of the step back method.""" dtype = np.float64 min_x, max_x, size = dtype(0.0), dtype(1.0), 21 np_grid = np.linspace(min_x, max_x, num=size) grid_spec = grids.uniform_grid([min_x], [max_x], [size], dtype=dtype) r = dtype(0.03) multipliers = np.linspace(0.1, 1.1, num=size) def transform_fn(state): return state.value_grid * multipliers.reshape([-1, 1]) def kernel(state): return state.value_grid * tf.exp(-r * state.time_step) def payoff_fn(state): return tf.maximum(state.coordinate_grid.grid, 0.5) bgs = grid_stepper.BackwardGridStepper( 2.0, kernel, grid_spec, time_step=0.5, dtype=dtype) bgs.transform_values(payoff_fn) # At time 2. expected_final_vals = np.maximum(np_grid, 0.5) expected_initial_values = ( expected_final_vals * np.exp(-r * 2) * (multipliers**4)) bgs.step_back_to_time(0.0, value_transform_fn=transform_fn) initial_state = self.evaluate(bgs.state()) self.assertArrayNear(expected_initial_values, initial_state.value_grid, 1e-10) self.assertEqual(0.0, initial_state.time)
def test_step_back(self): """Tests the step_back method.""" dtype = np.float64 min_x, max_x, size = dtype(0.0), dtype(1.0), 21 grid_spec = grids.uniform_grid([min_x], [max_x], [size], dtype=dtype) r = dtype(0.03) def kernel(state): return state.value_grid * tf.exp(-r * state.time_step) def time_step_fn(state): """A non-constant time step.""" # Returns the first element of the harmonic sequence which is smaller # than the current time floored by 0.01. If the current time is t, # then returns tf.max(1/tf.ceil(1/t), 0.01). return tf.maximum(dtype(0.01), 1. / tf.ceil(1. / state.time)) def payoff_fn(state): return tf.maximum(state.coordinate_grid.grid, 0.5) bgs = grid_stepper.BackwardGridStepper( 1.9, kernel, grid_spec, time_step_fn=time_step_fn, dtype=dtype) bgs.transform_values(payoff_fn) # At time 1.9. # Calls step back and checks that the time of the grid changes to 0.9. bgs.step_back() state_1 = self.evaluate(bgs.state()) self.assertTrue(state_1.time, 0.9) # Also check that the next time step is correctly populated. It should be # 0.5 now. self.assertTrue(state_1.time_step, 0.5) # Step back again and check that the time step is correctly applied. bgs.step_back() state_2 = self.evaluate(bgs.state()) self.assertTrue(state_2.time, 0.4)
def test_uniform_grid_1d(self): dtype = np.float64 min_x, max_x, size = dtype(0.0), dtype(1.0), 21 np_grid = np.linspace(min_x, max_x, num=size) grid_spec = self.evaluate( grids.uniform_grid([min_x], [max_x], [size], dtype=dtype)) self.assertEqual(grid_spec.dim, 1) grid = grid_spec.grid self.assertEqual(grid.shape, tuple([21, 1])) self.assertArrayNear(np.squeeze(grid), np_grid, 1e-10) self.assertEqual(grid.dtype, dtype)
def test_nonconst_time_step(self): dtype = np.float64 min_x, max_x, size = dtype(0.0), dtype(1.0), 21 np_grid = np.linspace(min_x, max_x, num=size) grid_spec = grids.uniform_grid([min_x], [max_x], [size], dtype=dtype) r = dtype(0.03) def kernel(state): return state.value_grid * tf.exp(-r * state.time_step) def time_step_fn(state): """A non-constant time step.""" # Returns the first element of the harmonic sequence which is smaller # than the current time floored by 0.01. If the current time is t, # then returns tf.max(1/tf.math.ceil(1/t), 0.01). return tf.maximum(dtype(0.01), 1. / tf.math.ceil(1. / state.time)) def payoff_fn(state): return tf.maximum(state.coordinate_grid.grid, 0.5) bgs = pde.BackwardGridStepper(1.9, kernel, grid_spec, time_step_fn=time_step_fn, dtype=dtype) bgs.transform_values(payoff_fn) # At time 1.9. bgs.step_back_to_time(dtype(0.13)) intermediate_state = self.evaluate(bgs.state()) bgs.step_back_to_time(dtype(0.0)) initial_state = self.evaluate(bgs.state()) expected_final_vals = np.maximum(np_grid, 0.5) expected_intermediate_values = expected_final_vals * np.exp( -r * (1.9 - 0.13)) expected_initial_values = expected_final_vals * np.exp(-r * 1.9) self.assertArrayNear(expected_intermediate_values, intermediate_state.value_grid, 1e-10) self.assertEqual(0.13, intermediate_state.time) self.assertArrayNear(expected_initial_values, initial_state.value_grid, 1e-10) self.assertEqual(0.0, initial_state.time)
def test_uniform_grid_2d(self): dtype = np.float64 min_x, max_x, sizes = [0.0, 1.0], [3.0, 5.0], [11, 21] grid_spec = self.evaluate( grids.uniform_grid(min_x, max_x, sizes, dtype=dtype)) self.assertEqual(grid_spec.dim, 2) grid = grid_spec.grid self.assertEqual(grid.shape, tuple([11, 21, 2])) self.assertEqual(grid.dtype, dtype) grid_marks = [] for i in range(2): grid_marks.append(np.linspace(min_x[i], max_x[i], num=sizes[i])) np_grid = np.stack(np.meshgrid(*grid_marks, indexing='ij'), axis=-1) self.assertArrayNear(grid.reshape([-1]), np_grid.reshape([-1]), 1e-10) locs = grid_spec.locations self.assertEqual(len(locs), 2) self.assertArrayNear(locs[0], grid_marks[0], 1e-10) self.assertArrayNear(locs[1], grid_marks[1], 1e-10) dxs = grid_spec.deltas self.assertEqual(len(dxs), 2) self.assertArrayNear(dxs[0].reshape([-1]), [0.3], 1e-10) self.assertArrayNear(dxs[1].reshape([-1]), [0.2], 1e-10)