An MFM network, as its name suggests, is used to house multiple devices the links connecting them. MFM networks are represented by the network_mfm object.

The network_mfm object simplifies adding devices to the network and provides multiple ways to establish links between the devices.

While a network can be used in various fashions, the most straightforwad is to populate it with source devices and destination devices.

Source devices are device objects with transmit capability that will be transmitting to a destination device object having receive capability.

When multiple source devices are present in the same network, interference manfifests via links connecting source devices to devices other than its intended destination.

For example, there are four devices in the network shown in the figure above. The source device device-1 aims to transmit to the destination device device-2. The source device device-3 aims to transmit to the destination device device-4. Four link objects are produced:

1. two desired links shown in solid black—between device-1 and device-2 and frbetweenom device-3 and device-4
2. two interference links shown in dashed red—between device-1 and device-4 and between device-3 and device-2

Using knowledge of these sources, destinations, and links, the network handles high-level operations like acquiring and distributing channel state information (CSI) and reporting performance metrics like mutual information/spectral efficiency and received symbol error.

### Creating a Network

To create a network in MFM, use

n = network_mfm.create()


where n is an empty network_mfm object.

### Key Properties

A network_mfm object n contains a few important properties.

First, as mentioned, each network contains source-destination pairs—pairs of device objects—that indicate which devices (sources) are transmitting to which devices (destinations). The sources are stored in

n.sources


where n.sources is a cell of device objects having transmit capability.

The number of sources in the network is

n.num_sources


Similarly, destination devices are stored in

n.destinations


which is a cell of device objects having receive capability.

The number of destinations in the network—which should equal the number of sources—is stored in

n.num_destinations


The links in the network are stored in

n.links


which is a cell of link objects.

The number of links in the network is stored in

n.num_links


All (unique) devices in the network are stored in

n.devices


which is a cell of device objects.

In most cases, devices should be added to a network n using

n.add_source_destination(source,destination)


where source and destination are device objects. Note that pairs of devices are added to the network, not individual devices.

This will automatically populate the properties n.sources and n.destinations with the newly added devices.

This will also automatically include the source and destination devices in the property n.devices if they are not already present.

### Removing a Source-Destination Pair

To remove a source-destination pair from a network n, simply use

n.remove_source_destination_pair(source,destination)


where source and destination are device objects to remove. Note that pairs of devices are removed from the network, not individual devices.

To remove all source-destination pairs from a network n, use

n.remove_all_source_destination()


Recall that sources are transmitting devices and destinations are receiving devices.

The easiest and most practical way to establish links between devices in a network n is to use

n.populate_links_from_source_destination()


which will automatically establish links between all sources and destinations. In other words, for a given source, a link will be created between it and each destination in the network.

With $N$ source-destination pairs, populating links between all sources and destinations will yield $N^2$ links.

While a physical link may exist between one source device and another source device, it does not hold much practical value since neither is necessarily receiving. Likewise, a physical link may exist between one destination device and another destination device, but it does not hold much practical value since neither is necessarily transmitting.

Nonetheless, MFM supplies functions to manually create links via

n.add_link(lnk)


where lnk is a link object or via

n.add_links(lnks)


where lnks is a vector of link objects.

### Viewing the Network

To view a network n in 3-D use,

n.show_3d()


which will display a figure similar to

### Passthrough Methods

Configuring each link and each device in a network can be quite cumbersome, especially as the network grows.

To make configuring objects network-wide more straightforward, we have provided the following passthrough methods for network_mfm objects.

Links within a network n can be configured one-by-one if desired by accessing the i-th link directly via n.links{i}.

It is often the case that the same channel model and path loss model will be used across all links in a network. In light of this, MFM supplies the functions

n.set_channel(c)
n.set_path_loss(p)


where c is a channel object and p is a path_loss object. This will set each link object in the network to use the same channel model and path loss model, each with their own unique realizations.

#### Carrier Frequency, Propagation Velocity, and Symbol Bandwidth

To set the carrier frequency, propagation velocity, and symbol bandwidth at all devices and links within a network n, use

n.set_carrier_frequency(fc);
n.set_propagation_velocity(vel);
n.set_symbol_bandwidth(B);


where fc is the carrier frequency (in Hz), vel is the propagation velocity (in m/s), and B is the symbol bandwidth (in Hz).

#### Transmit Power

To set the transmit power at all devices to a value of P, use

n.set_transmit_power(P,unit)


where unit is a string specifying the units of P (e.g., 'dBm', 'watts').

#### Noise Power

To set the noise power at all devices to a value of noise_psd, use

n.set_noise_power_per_Hz(noise_psd,unit)


where unit is a string specifying the units of noise_psd (e.g., 'dBm_Hz', 'watts_Hz').

#### Number of Streams

To set the number of streams at all devices to a value of Ns, use

n.set_num_streams(Ns)


#### Transmit Symbol

T set the transmit symbol at all devices to a vector s, use

n.set_transmit_symbol(s)


Note that this is only appropriate if all devices are transmitting with the same number of streams.

### Invoking a Network Realization

To invoke a realization of the channels on all links in a network n, use

n.realization_channel()


To invoke a realization of the path loss on all links in a network n, use

n.realization_path_loss()


To invoke a realization of all channels and path losses in one line, simply use

n.realization()


### Channel State Information

To compute the channel state information for a realization of a network n, use

n.compute_channel_state_information()


To supply each device with this computed channel state information, use

n.supply_channel_state_information()


### Configuring Devices to Transmit and Receive

To configure all transmitters in the network to use a particular transmit strategy, use

n.configure_transmitter(strategy)


where strategy is a string specifying which strategy the transmitters should use. This string must be one of the valid transmit strategies that have been defined for the transmitter objects being used in the network. Please refer to documentation on the transmitter object for more information on this.

To configure all receivers in the network to use a particular receive strategy, use

n.configure_receiver(strategy)


where strategy is a string specifying which strategy the receivers should use. This string must be one of the valid receive strategies that have been defined for the receiver objects being used in the network. Please refer to documentation on the receiver object for more information on this.

### Received Signals at Each Device

With a realized network and configured transmitters and receivers, the received signals at each device can be computed via

n.compute_received_signals()


### Performance Metrics

To evaluate the performance of a network, MFM can compute and report the mutual information and estimation error of the receive symbol versus the transmit symbol for each source-destination pair.

#### Mutual Information

To report the achieved Gaussian mutual information (spectral efficiency) between a source device and destination device, use

mi = n.report_mutual_information(source,destination)


where source is a source device, destination is a destination device, and mi is the mutual information in bps/Hz.

#### Symbol Estimation Error

To report the achieved estimation error between the transmit symbol $\mathbf{s}$ at a source device and the receie symbol $\hat{\mathbf{s}}$ destination device, use

[err,nerr] = n.report_symbol_estimation_error(source,destination)


where source is a source device, destination is a destination device, err is the symbol estimation error defined as

and nerr is the symbol estimation error normalized to the transmit symbol energy defined as

### List of Properties

The network_mfm object contains the following properties:

• network_mfm.name
• network_mfm.devices
• network_mfm.num_devices
• network_mfm.links
• network_mfm.num_links
• network_mfm.sources
• network_mfm.num_sources
• network_mfm.destinations
• network_mfm.num_destinations
• network_mfm.propagation_velocity
• network_mfm.carrier_frequency
• network_mfm.carrier_wavelength
• network_mfm.symbol_bandwidth
• network_mfm.channel_state_information_transmit
• network_mfm.channel_state_information_receive

### List of Methods

The network_mfm object contains the following methods:

#### add_destination(destination)

Adds a destination device to the network.

Usage:
add_destination(destination)
Input Arguments:
destination — a device object

Back to methods

#### add_device(device)

Adds a device to the network.

Usage:
add_device(device)
Input Arguments:
device — a device object

Back to methods

#### add_devices(devices)

Adds a vector of devices to the network.

Usage:
add_devices(devices)
Input Arguments:
devices — a vector (or cell) of device objects

Back to methods

Usage:
add_link(lnk)
Input Arguments:
lnk — a link object

Back to methods

Usage:
add_links(links)
Input Arguments:
links — a vector of link objects

Back to methods

#### add_source(source)

Adds a source device to the network.

Usage:
add_source(source)
Input Arguments:
source — a device object

Back to methods

#### add_source_destination(source,destination)

Adds a source-destination pair to the network.

Usage:
add_source_destination(source,destination)
Input Arguments:
source — a device object to be the source
destination — a device object to be the destination

Back to methods

#### compute_channel_state_information()

Computes the channel state information for each device in the network. For each device, the CSI is collected across all links in which the device is the transmitter (head on the forward link, tail on the reverse link) or receiver (tail on the forward link, head on the reverse link).

Usage:
compute_channel_state_information()

Back to methods

#### compute_channel_state_information_receive(dev)

Computes the receive channel state information for a device. Specifically, it collects the CSI across all links (forward and reverse) in which the device could be a receiver. This reduces to collecting CSI across forward links where the device is the tail and across reverse links where the device is the head.

Usage:
csi = compute_channel_state_information_receive(dev)
Input Arguments:
dev — a device object
Return Values:
csi — a struct of channel state information values

Back to methods

#### compute_channel_state_information_transmit(dev)

Computes the transmit channel state information for a device. Specifically, it collects the CSI across all links (forward and reverse) in which the device could be a transmitter. This reduces to collecting CSI across forward links where the device is the head and across reverse links where the device is the tail.

Usage:
csi = compute_channel_state_information_transmit(dev)
Input Arguments:
dev — a device object
Return Values:
csi — a struct of channel state information values

Back to methods

#### compute_covariance_desired(source,destination)

Computes the covariance of the received symbols at a destination device from its source. Also returns the covariance of received noise.

Usage:
[Ry,Rn] = compute_covariance_desired(source,destination)
Input Arguments:
source — a source device
destination — a destination device
Return Values:
Ry — the desired signal covariance
Rn — the noise covariance

Back to methods

#### compute_covariance_interference(source,destination)

Computes the covariance of received interference at a destination device (i.e., the contribution of all interference links).

Usage:
R = compute_covariance_interference(source,destination)
Input Arguments:
source — a source device
destination — a destination device
Return Values:
R — the covariance matrix of interference
Notes:
Assumes independence across interferers.

Back to methods

#### compute_received_signal(destination)

Computes the signal impinging the receive array at a destination device. This is comprised of all desired and interfering transmitters as well as additive noise.

Usage:
y = compute_received_signal(destination)
Input Arguments:
destination — a device object
Return Values:
y — the vector impinging the receive array at the destination

Back to methods

#### compute_received_signals()

Computes the received signal at each destination device in the network.

Usage:
compute_received_signals()

Back to methods

#### configure_receiver(strategy)

Configures each destination in the network according to their existing/specified receive strategy.

Usage:
configure_receiver()
configure_receiver(strategy)
Input Arguments:
strategy — (optional) a string specifying the receive strategy to use, overwriting existing ones

Back to methods

#### configure_transmitter(strategy)

Configures each source in the network according to their existing/specified transmit strategy.

Usage:
configure_transmitter()
configure_transmitter(strategy)
Input Arguments:
strategy — (optional) a string specifying the transmit strategy to use, overwriting existing ones

Back to methods

#### create()

Creates a network object.

Usage:
obj = network_mfm.create()
Return Values:
obj — an MFM network object

Back to methods

#### get_device_index(dev)

Returns the index of a device within the devices in the network.

Usage:
idx = get_device_index(dev)
Input Arguments:
dev — a device object or index of a device
Return Values:
idx — the index of the device

Back to methods

Returns the index of a link within the links in the network.

Usage:
idx = get_link_index(lnk)
Input Arguments:
lnk — a link object or index of a link
Return Values:
idx — the index of the link

Back to methods

Returns the indeces of the links in the network that contain a destination device. Also returns whether each of those links is forward or reverse.

Usage:
[idx,fwd_rev] = get_link_index_destination(destination)
Input Arguments:
destination — a destination device
Return Values:
idx — a vector of indeces of links in the network
fwd_rev — a vector of booleans indicating if the forward or reverse link contains the destination device as the receiving side (0 for forward, 1 for reverse)

Back to methods

Returns the index of the link with a specific source-destination pair and an indicator for if that link is a forward or reverse link.

Usage:
[idx,fwd_rev] = get_link_index_source_destination(source,destination)
Input Arguments:
source — a device object
destination — a device object
Return Values:
idx — the index of the link whose head and tail are the source and destination (or vice versa); returns 0 if not found
fwd_rev — a boolean indicating if the head or tail of the link is the source (0 if head = source, forward; 1 if tail = source, reverse)

Back to methods

Returns the index of the link whose head and tail are specific source and destination devices, respectively (i.e., which link has head = source and tail = destination).

Usage:
idx = get_link_index_source_destination_forward(source,destination)
Input Arguments:
source — a device object
destination — a device object
Return Values:
idx — the index of the link whose head and tail are the source and destination, respectively; returns 0 if not found

Back to methods

Returns the index of the link whose tail and head are specific source and destination devices, respectively (i.e., which link has head = destination and tail = source).

Usage:
idx = get_link_index_source_destination_reverse(source,destination)
Input Arguments:
source — a device object
destination — a device object
Return Values:
idx — the index of the link whose tail and head are the source and destination, respectively; returns 0 if not found

Back to methods

#### initialize()

Executes intialization steps to set up a network.

Usage:
initialize()

Back to methods

#### network_mfm(name)

Creates a network object.

Usage:
obj = NETWORK()
obj = NETWORK(name)
Input Arguments:
name — an optional name for the network
Return Values:
obj — an object representing a network

Back to methods

Populates the devices in the network from the links in the network.

Usage:
populate_devices_from_links()

Back to methods

Populates the links in the network from the devices in the network by linking each pair of devices.

Usage:
populate_links_from_devices()

Back to methods

Populates the links in the network from the sources and destinations in the network by linking each pair of devices. Removes all links before populating links.

Usage:
populate_links_from_source_destination()

Back to methods

#### realization()

Invokes a realization of the network, including all of the links it contains.

Usage:
realization()

Back to methods

#### remove_all_devices()

Removes all devices from the network.

Usage:
remove_all_devices()

Back to methods

Removes all links from the network.

Usage:
remove_all_links()

Back to methods

Removes all links having a particular device as its head or tail.

Usage:
remove_all_links_with_device(dev)
Input Arguments:
dev — a device object

Back to methods

#### remove_all_source_destination()

Removes all source-destination pairs from the network.

Usage:
remove_all_source_destination()

Back to methods

#### remove_all_source_destination_with_device(dev)

Removes all source-destination pairs having a particular device as the source or destination.

Usage:
remove_all_source_destination_with_device(dev)
Input Arguments:
dev — a device object

Back to methods

#### remove_device(idx)

Removes a device from the network.

Usage:
remove_device()
remove_device(idx)
Input Arguments:
idx — optional device index to remove from the network (default is the last device in the network)

Back to methods

Removes a link from the network.

Usage:
remove_link()
remove_link(lnk)
Input Arguments:
lnk — (optional) link index or object to remove from the network; if not passed, the last link in the network is removed

Back to methods

#### remove_source_destination(source,destination)

Removes a source-destination pair from the network.

Usage:
remove_source_destination(source,destination)
Input Arguments:
source — a device object
destination — a device object

Back to methods

#### report_mutual_information(source,destination)

Reports the Gaussian mutual information achieved between a source-destination pair based on the current network realization and transmit-receive configurations. Returns the mutual information in bps/Hz. Discards any residual imaginary component.

Usage:
mi = report_mutual_information(source,destination)
Input Arguments:
source — a source device
destination — a destination device
Return Values:
mi — the mutual information in bps/Hz
Notes:
Will warn the user if a significant imaginary portion exists when computing the mutual information. Ultimately discards it though.

Back to methods

#### report_symbol_estimation_error()

Returns the squared-error in the estimated receive symbol at the destination compared to the transmit symbol at the source.

Usage:
[err,nerr] = report_symbol_estimation_error(source,destination)
Return Values:
err — the squared error of the received symbols versus the transmitted symbols
nerr — err normalized to the transmit symbol power

Back to methods

#### set_arrays(transmit_array,receive_array)

Usage:
set_arrays(transmit_array,receive_array)
Input Arguments:
transmit_array — array object
receive_array — array object

Back to methods

#### set_carrier_frequency(fc)

Sets the carrier frequency on each link.

Usage:
set_carrier_frequency(fc)
Input Arguments:
fc — carrier frequency in Hertz
Notes:

Back to methods

#### set_channel(channel_object)

Sets the channel model used on each link.

Usage:
SET_channel(channel_object)
Input Arguments:
channel_object — a channel object

Back to methods

#### set_name(name)

Sets the name of the network.

Usage:
set_name()
set_name(name)
Input Arguments:
name — (optional) a string; if not passed, a name is created

Back to methods

#### set_noise_power_per_Hz(noise_psd,unit)

Sets the noise power spectral density on each link.

Usage:
set_noise_power_per_Hz(noise_psd)
set_noise_power_per_Hz(noise_psd,unit)
Input Arguments:
noise_psd — noise power spectral density
unit — (optional) a string specifying the unit of noise_psd; if not passed, the default will be used

Back to methods

#### set_num_rf_chains(Lt,Lr)

Usage:
set_num_rf_chains(Lt)
set_num_rf_chains(Lt,Lr)
Input Arguments:
Lt — number of transmit RF chains
Lr — (optional) number of receive RF chains; if not passed, the number of transmit RF chains is used

Back to methods

#### set_num_streams(num_streams)

Sets the number of streams on each link.

Usage:
set_num_streams(num_streams)
Input Arguments:
num_streams — number of data streams

Back to methods

#### set_path_loss(path_loss_object)

Sets the path loss model used on each link.

Usage:
set_path_loss(path_loss_object)
Input Arguments:
path_loss_object — a path loss object

Back to methods

#### set_propagation_velocity(velocity)

Sets the propagation vecocity on each link.

Usage:
set_propagation_velocity(velocity)
Input Arguments:
velocity — propagation velocity in meters per second
Notes:

Back to methods

#### set_snr(snr_forward,snr_reverse,unit)

Sets the large-scale SNR on each of the links.

Usage:
set_snr(snr_forward)
set_snr(snr_forward,[],unit)
set_snr(snr_forward,snr_reverse,unit)
Input Arguments:
snr_forward — the SNR to use on the forward links
snr_reverse — (optional) the SNR to use on the reverse links; if not passed, snr_forward is used;
unit — (optional) a string specifying the units of snr_forward and snr_reverse (e.g., ‘dB’ or ‘linear’)

Back to methods

#### set_symbol_bandwidth(B)

Sets the symbol bandwidth on each link.

Usage:
set_symbol_bandwidth(B)
Input Arguments:
B — symbol bandwidth in Hertz

Back to methods

#### set_transmit_power(P,unit)

Sets the transmit power used on each link.

Usage:
set_transmit_power(P)
set_transmit_power(P,unit)
Input Arguments:
P — transmit power
unit — (optional) a string specifying the unit of P (e.g., ‘dBm’, ‘watts’); if not passed, the default is used

Back to methods

#### set_transmit_symbol(s)

Sets the transmit symbol used on each link.

Usage:
set_transmit_symbol(s)
Input Arguments:
s — a symbol vector

Back to methods

#### set_transmit_symbol_covariance(Rs)

Sets the transmit covariance on each link.

Usage:
set_transmit_symbol_covariance(Rs)
Input Arguments:
Rs — a covariance matrix

Back to methods

#### show_2d(fignum)

Displays the network in 2-D.

Usage:
show_2d()
show_2d(fignum)
Input Arguments:
fignum — (optional) a figure number; if not passed, a new figure will be created
Return Values:
fig — the resulting a figure handle

Back to methods

#### show_3d(fignum)

Displays the network in 3-D.

Usage:
show_3d()
show_3d(fignum)
Input Arguments:
fignum — (optional) a figure number; if not passed, a new figure will be created
Return Values:
fig — the resulting a figure handle

Back to methods

#### supply_channel_state_information()

Supplies channel state information to each of the sources and destinations in the network.

Usage:
supply_channel_state_information()

Back to methods