<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def macro_tpm_sbs(self, state_by_state_micro_tpm):
 """Create a state-by-state coarse-grained macro TPM. Args: micro_tpm (nd.array):
 The state-by-state TPM of the micro-system. Returns: np.ndarray: The state-by-state TPM of the macro-system. """

        validate.tpm(state_by_state_micro_tpm, check_independence=False)

        mapping = self.make_mapping()

        num_macro_states = 2 ** len(self.macro_indices)
        macro_tpm = np.zeros((num_macro_states, num_macro_states))

        micro_states = range(2 ** len(self.micro_indices))
        micro_state_transitions = itertools.product(micro_states, repeat=2)

        # For every possible micro-state transition, get the corresponding
        # previous and next macro-state using the mapping and add that
        # probability to the state-by-state macro TPM.
        for previous_state, current_state in micro_state_transitions:
            macro_tpm[mapping[previous_state], mapping[current_state]] += (
                state_by_state_micro_tpm[previous_state, current_state])

        # Re-normalize each row because we're going from larger to smaller TPM
        return np.array([distribution.normalize(row) for row in macro_tpm])