# Part 3: The Linear Problem and Validation¶

In this exercise you will solve the *linear* forward modeling equations and
verify that the adjoint conditions hold for your code. The definition of the
adjoint of a linear operator states that

where \(\left< \cdot, \cdot \right> _{\mathcal{D}}\) and \(\left< \cdot, \cdot \right> _{\mathcal{M}}\) are inner products in the data and model spaces, respectively. One test to see if your migration operator \(F^*\) is working correctly is to test if this relationship holds to high precision for any pair of \(d\) and \(\delta m\).

## Linear Forward Operator¶

To implement the adjoint test, you will need to solve the linear modeling equations, which are derived in the notes,

where \(u_1\) is the Born scattered field and equivalently \(F\delta m = u_1\).

Problem 3.1

Write a function `linear_sources(dm, u0s, config)`

that takes an
arbitrary model perturbation `dm`

and a time sequence of incident
wavefields `u0s`

and generates the linear source wavefields (the
right-hand-sides of the linear modeling equations). Functions you have
previously written should be useful.

Problem 3.2

Use your `leap_frog`

function to solve for the linear forward wavefield
\(u_1\) due to a perturbation \(\delta m\). Use this code to write
a function `linear_forward_operator(C0, dm, config)`

which returns a
tuple containing the wavefields and the sampled data.

## Adjoint Validation¶

When sampling is accounted for, the adjoint condition requires that,

hold to high precision.

Problem 3.3

Verify that the adjoint condition is satisfied by your implementation of
the linear forward modeling and migration operators. Be careful to take
into account the differences in the model and data inner product. Write a
function `adjoint_condition(C0, config)`

that implements this test. How
accurate is the relationship? It should be accurate to machine precision
for random values of the data \(d\) and the model perturbation
\(\delta m\). Be sure that you define \(\delta m(0) = \delta m(1)
= 0\), as it is nonphysical to have sources on an absorbing boundary.

Consider modifying your `construct_matrices`

function to accept a
`config`

key `'bc'`

, which allows you to toggle between absorbing and
homogeneous Dirichlet boundaries. This result should still hold.

```
print "Absorbing BC"
adjoint_condition(C0, config)
print "Dirichlet BC"
config['bc'] = 'dirichlet'
adjoint_condition(C0, config)
```

## Bonus Problems¶

**Bonus Problem 3.4:** It is compuationally intractible to compute the
linear forward operator \(F\) directly. Why? If you wanted to
explicitely compute this operator, how would you do it?