reply_data = []<EOL>address = (data[<NUM_LIT:0>] & <NUM_LIT>) + (data[<NUM_LIT:1>] << <NUM_LIT:7>)<EOL>register = data[<NUM_LIT:2>] & <NUM_LIT> + data[<NUM_LIT:3>] << <NUM_LIT:7><EOL>reply_data.append(register)<EOL>for i in range(<NUM_LIT:4>, len(data), <NUM_LIT:2>):<EOL><INDENT>data_item = (data[i] & <NUM_LIT>) + (data[i + <NUM_LIT:1>] << <NUM_LIT:7>)<EOL>reply_data.append(data_item)<EOL><DEDENT>if address in self.i2c_map:<EOL><INDENT>i2c_data = self.i2c_map.get(address, None)<EOL>i2c_data[<NUM_LIT:1>] = reply_data<EOL>self.i2c_map[address] = i2c_data<EOL>if i2c_data[<NUM_LIT:0>] is not None:<EOL><INDENT>i2c_data[<NUM_LIT:0>]([self.pymata.I2C, address, reply_data])<EOL><DEDENT><DEDENT>

This method receives replies to i2c_read requests. It stores the data for each i2c device
address in a dictionary called i2c_map. The data is retrieved via a call to i2c_get_read_data()
in pymata.py
It a callback was specified in pymata.i2c_read, the raw data is sent through the callback

:param data: raw data returned from i2c device


self.capability_query_results = data<EOL>

This method handles a capability response message and stores the results to be retrieved
via get_capability_query_results() in pymata.py

:param data: raw capability data


self.last_pin_query_results = data<EOL>

This method handles a pin state response message and stores the results to be retrieved
via get_pin_state_query_results() in pymata.py

:param data:  raw pin state data


self.analog_mapping_query_results = data<EOL>

This method handles an analog mapping query response message and stores the results to be retrieved
via get_analog_mapping_request_results() in pymata.py

:param data: raw analog mapping data


self.stepper_library_version = (data[<NUM_LIT:0>] & <NUM_LIT>) + (data[<NUM_LIT:1>] << <NUM_LIT:7>)<EOL>

This method handles a stepper library version message sent from the Arduino


<EOL>self.command_dispatch.update({self.REPORT_VERSION: [self.report_version, <NUM_LIT:2>]})<EOL>self.command_dispatch.update({self.REPORT_FIRMWARE: [self.report_firmware, <NUM_LIT:1>]})<EOL>self.command_dispatch.update({self.ANALOG_MESSAGE: [self.analog_message, <NUM_LIT:2>]})<EOL>self.command_dispatch.update({self.DIGITAL_MESSAGE: [self.digital_message, <NUM_LIT:2>]})<EOL>self.command_dispatch.update({self.ENCODER_DATA: [self.encoder_data, <NUM_LIT:3>]})<EOL>self.command_dispatch.update({self.SONAR_DATA: [self.sonar_data, <NUM_LIT:3>]})<EOL>self.command_dispatch.update({self.STRING_DATA: [self._string_data, <NUM_LIT:2>]})<EOL>self.command_dispatch.update({self.I2C_REPLY: [self.i2c_reply, <NUM_LIT:2>]})<EOL>self.command_dispatch.update({self.CAPABILITY_RESPONSE: [self.capability_response, <NUM_LIT:2>]})<EOL>self.command_dispatch.update({self.PIN_STATE_RESPONSE: [self.pin_state_response, <NUM_LIT:2>]})<EOL>self.command_dispatch.update({self.ANALOG_MAPPING_RESPONSE: [self.analog_mapping_response, <NUM_LIT:2>]})<EOL>self.command_dispatch.update({self.STEPPER_DATA: [self.stepper_version_response, <NUM_LIT:2>]})<EOL>while not self.is_stopped():<EOL><INDENT>if len(self.pymata.command_deque):<EOL><INDENT>data = self.pymata.command_deque.popleft()<EOL>command_data = []<EOL>if data == self.START_SYSEX:<EOL><INDENT>while len(self.pymata.command_deque) == <NUM_LIT:0>:<EOL><INDENT>pass<EOL><DEDENT>sysex_command = self.pymata.command_deque.popleft()<EOL>dispatch_entry = self.command_dispatch.get(sysex_command)<EOL>method = dispatch_entry[<NUM_LIT:0>]<EOL>end_of_sysex = False<EOL>while not end_of_sysex:<EOL><INDENT>while len(self.pymata.command_deque) == <NUM_LIT:0>:<EOL><INDENT>pass<EOL><DEDENT>data = self.pymata.command_deque.popleft()<EOL>if data != self.END_SYSEX:<EOL><INDENT>command_data.append(data)<EOL><DEDENT>else:<EOL><INDENT>end_of_sysex = True<EOL>method(command_data)<EOL><DEDENT><DEDENT>continue<EOL><DEDENT>elif <NUM_LIT> <= data <= <NUM_LIT>:<EOL><INDENT>if <NUM_LIT> <= data <= <NUM_LIT>:<EOL><INDENT>port = data & <NUM_LIT><EOL>command_data.append(port)<EOL>data = <NUM_LIT><EOL><DEDENT>elif <NUM_LIT> <= data <= <NUM_LIT>:<EOL><INDENT>pin = data & <NUM_LIT><EOL>command_data.append(pin)<EOL>data = <NUM_LIT><EOL><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT>dispatch_entry = self.command_dispatch.get(data)<EOL>method = dispatch_entry[<NUM_LIT:0>]<EOL>num_args = dispatch_entry[<NUM_LIT:1>]<EOL>for i in range(num_args):<EOL><INDENT>while len(self.pymata.command_deque) == <NUM_LIT:0>:<EOL><INDENT>pass<EOL><DEDENT>data = self.pymata.command_deque.popleft()<EOL>command_data.append(data)<EOL><DEDENT>method(command_data)<EOL>continue<EOL><DEDENT><DEDENT>else:<EOL><INDENT>time.sleep(<NUM_LIT>)<EOL><DEDENT><DEDENT>

This method starts the thread that continuously runs to receive and interpret
messages coming from Firmata. This must be the last method in this file
It also checks the deque for messages to be sent to Firmata.


def __init__(self, port_id='<STR_LIT>', bluetooth=True, verbose=True, baud_rate=<NUM_LIT>):

<EOL>self.baud_rate = baud_rate<EOL>try:<EOL><INDENT>self.verbose = verbose<EOL>if self.verbose:<EOL><INDENT>print("<STR_LIT>" % sys.version)<EOL>print('<STR_LIT>')<EOL><DEDENT>self.transport = PyMataSerial(port_id, self.command_deque, self.baud_rate)<EOL>if bluetooth:<EOL><INDENT>time.sleep(<NUM_LIT:5>)<EOL><DEDENT>self.transport.open(self.verbose)<EOL>if bluetooth:<EOL><INDENT>time.sleep(<NUM_LIT:2>)<EOL><DEDENT>else:<EOL><INDENT>time.sleep(<NUM_LIT:1>)<EOL><DEDENT>self.transport.start()<EOL>self._command_handler = PyMataCommandHandler(self)<EOL>self._command_handler.system_reset()<EOL>self.LATCH_IGNORE = self._command_handler.LATCH_IGNORE<EOL>self.LATCH_ARMED = self._command_handler.LATCH_ARMED<EOL>self.LATCH_LATCHED = self._command_handler.LATCH_LATCHED<EOL>self.DIGITAL_LATCH_HIGH = self._command_handler.DIGITAL_LATCH_HIGH<EOL>self.DIGITAL_LATCH_LOW = self._command_handler.DIGITAL_LATCH_LOW<EOL>self.ANALOG_LATCH_GT = self._command_handler.ANALOG_LATCH_GT<EOL>self.ANALOG_LATCH_LT = self._command_handler.ANALOG_LATCH_LT<EOL>self.ANALOG_LATCH_GTE = self._command_handler.ANALOG_LATCH_GTE<EOL>self.ANALOG_LATCH_LTE = self._command_handler.ANALOG_LATCH_LTE<EOL>self.LATCH_PIN = <NUM_LIT:0><EOL>self.LATCH_STATE = <NUM_LIT:1><EOL>self.LATCHED_DATA = <NUM_LIT:2><EOL>self.LATCHED_TIME_STAMP = <NUM_LIT:3><EOL>self._command_handler.start()<EOL>if self.verbose:<EOL><INDENT>print('<STR_LIT>')<EOL><DEDENT>if not self._command_handler.auto_discover_board(self.verbose):<EOL><INDENT>if self.verbose:<EOL><INDENT>print("<STR_LIT>")<EOL><DEDENT>self._command_handler.stop()<EOL>self.transport.stop()<EOL>self._command_handler.join()<EOL>self.transport.join()<EOL>time.sleep(<NUM_LIT:2>)<EOL><DEDENT><DEDENT>except KeyboardInterrupt:<EOL><INDENT>if self.verbose:<EOL><INDENT>print("<STR_LIT>")<EOL><DEDENT>sys.exit()<EOL><DEDENT>

The "constructor" instantiates the entire interface. It starts the operational threads for the serial
interface as well as for the command handler.

:param port_id: Communications port specifier (COM3, /dev/ttyACM0, etc)

:param bluetooth: Sets start up delays for bluetooth connectivity. Set to False for faster start up.

:param verbose: If set to False, the status print statements are suppressed.

:param baud_rate: Set serial baud rate. Must match that of Firmata sketch on Arduino


self._command_handler.send_sysex(self._command_handler.ANALOG_MAPPING_QUERY, None)<EOL>

Send an analog mapping query message via sysex. Client retrieves the results with a
call to get_analog_mapping_request_results()


with self.data_lock:<EOL><INDENT>data = self._command_handler.analog_response_table[pin][self._command_handler.RESPONSE_TABLE_PIN_DATA_VALUE]<EOL><DEDENT>return data<EOL>

Retrieve the last analog data value received for the specified pin.

:param pin: Selected pin

:return: The last value entered into the analog response table.


if self._command_handler.ANALOG_MESSAGE + pin < <NUM_LIT>:<EOL><INDENT>command = [self._command_handler.ANALOG_MESSAGE + pin, value & <NUM_LIT>, (value >> <NUM_LIT:7>) & <NUM_LIT>]<EOL>self._command_handler.send_command(command)<EOL><DEDENT>else:<EOL><INDENT>self.extended_analog(pin, value)<EOL><DEDENT>

Set the specified pin to the specified value.

:param pin: Pin number

:param value: Pin value

:return: No return value


self._command_handler.send_sysex(self._command_handler.CAPABILITY_QUERY, None)<EOL>

Send a Firmata capability query message via sysex. Client retrieves the results with a
call to get_capability_query_results()
The Arduino can be rather slow in responding to this command. For
the Mega 2560 R3 it has taken up to 25 seconds for a response.


This method will close the transport (serial port) and exit
:return: No return value, but sys.exit(0) is called.


with self.data_lock:<EOL><INDENT>data =self._command_handler.digital_response_table[pin][self._command_handler.RESPONSE_TABLE_PIN_DATA_VALUE]<EOL><DEDENT>return data<EOL>

Retrieve the last digital data value received for the specified pin.
NOTE: This command will return values for digital, pwm, etc,  pin types

:param pin: Selected pin

:return: The last value entered into the digital response table.


<EOL>port = pin // <NUM_LIT:8><EOL>calculated_command = self._command_handler.DIGITAL_MESSAGE + port<EOL>mask = <NUM_LIT:1> << (pin % <NUM_LIT:8>)<EOL>if value == <NUM_LIT:1>:<EOL><INDENT>self.digital_output_port_pins[port] |= mask<EOL><DEDENT>else:<EOL><INDENT>self.digital_output_port_pins[port] &= ~mask<EOL><DEDENT>command = (calculated_command, self.digital_output_port_pins[port] & <NUM_LIT>,<EOL>(self.digital_output_port_pins[port] >> <NUM_LIT:7>) & <NUM_LIT>)<EOL>self._command_handler.send_command(command)<EOL>

Set the specified pin to the specified value.

:param pin: pin number

:param value: pin value

:return: No return value


command = [self._command_handler.REPORT_ANALOG + pin, self.REPORTING_DISABLE]<EOL>self._command_handler.send_command(command)<EOL>

Disables analog reporting for a single analog pin.

:param pin: Analog pin number. For example for A0, the number is 0.

:return: No return value


port = pin // <NUM_LIT:8><EOL>command = [self._command_handler.REPORT_DIGITAL + port, self.REPORTING_DISABLE]<EOL>self._command_handler.send_command(command)<EOL>

Disables digital reporting. By turning reporting off for this pin, reporting
is disabled for all 8 bits in the "port" -

:param pin: Pin and all pins for this port

:return: No return value


command = [self._command_handler.REPORT_ANALOG + pin, self.REPORTING_ENABLE]<EOL>self._command_handler.send_command(command)<EOL>

Enables analog reporting. By turning reporting on for a single pin.

:param pin: Analog pin number. For example for A0, the number is 0.

:return: No return value


port = pin // <NUM_LIT:8><EOL>command = [self._command_handler.REPORT_DIGITAL + port, self.REPORTING_ENABLE]<EOL>self._command_handler.send_command(command)<EOL>

Enables digital reporting. By turning reporting on for all 8 bits in the "port" -
this is part of Firmata's protocol specification.

:param pin: Pin and all pins for this port

:return: No return value


def encoder_config(self, pin_a, pin_b, cb=None):

This command enables the rotary encoder (2 pin + ground) and will
enable encoder reporting.

NOTE: This command is not currently part of standard arduino firmata, but is provided for legacy
support of CodeShield on an Arduino UNO.

Encoder data is retrieved by performing a digital_read from pin a (encoder pin 1)

:param pin_a: Encoder pin 1.

:param pin_b: Encoder pin 2.

:param cb: callback function to report encoder changes

:return: No return value


analog_data = [pin, data & <NUM_LIT>, (data >> <NUM_LIT:7>) & <NUM_LIT>, (data >> <NUM_LIT>) & <NUM_LIT>]<EOL>self._command_handler.send_sysex(self._command_handler.EXTENDED_ANALOG, analog_data)<EOL>

This method will send an extended data analog output command to the selected pin

:param pin: 0 - 127

:param data: 0 - 0xfffff


return self._command_handler.get_analog_latch_data(pin)<EOL>

A list is returned containing the latch state for the pin, the latched value, and the time stamp
[pin_num, latch_state, latched_value, time_stamp]
If the the latch state is LATCH_LATCHED, the table is reset (data and timestamp set to zero)

:param pin: Pin number.

:return: [pin, latch_state, latch_data_value, time_stamp]


return self._command_handler.analog_mapping_query_results<EOL>

Call this method after calling analog_mapping_query() to retrieve its results
:return: raw data returned by firmata


return self._command_handler.get_analog_response_table()<EOL>

This method returns a list of lists representing the current pin mode and
associated data values for all analog pins.
All configured pin types, both input and output will be listed. Output pin data will contain zero.
:return: The last update of the digital response table


return self._command_handler.capability_query_results<EOL>

Retrieve the data returned by a previous call to capability_query()
:return: Raw capability data returned by firmata


return self._command_handler.get_digital_latch_data(pin)<EOL>


return self._command_handler.get_digital_response_table()<EOL>

This method returns a list of lists representing the current pin mode
and associated data for all digital pins.
All pin types, both input and output will be listed. Output pin data will contain zero.
:return: The last update of the digital response table


return self._command_handler.firmata_version<EOL>

Retrieve the firmata version information returned by a previous call to refresh_report_version()
:return: Firmata_version list [major, minor] or None


return self._command_handler.firmata_firmware<EOL>

Retrieve the firmware id information returned by a previous call to refresh_report_firmware()
:return: Firmata_firmware  list [major, minor, file_name] or None


r_data = self._command_handler.last_pin_query_results<EOL>self._command_handler.last_pin_query_results = []<EOL>return r_data<EOL>

This method returns the results of a previous call to pin_state_query() and then resets
the pin state query data to None

:return: Raw pin state query data


return ['<STR_LIT:2>', '<STR_LIT>']<EOL>

Returns the PyMata version number in a list: [Major Number, Minor Number]

:return:


return self._command_handler.active_sonar_map<EOL>

Retrieve Ping (HC-SR04 type) data. The data is presented as a dictionary.
The 'key' is the trigger pin specified in sonar_config() and the 'data' is the
current measured distance (in centimeters)
for that pin. If there is no data, the value is set to IGNORE (127).

:return: active_sonar_map


<EOL>start_time = time.time()<EOL>while self._command_handler.stepper_library_version <= <NUM_LIT:0>:<EOL><INDENT>if time.time() - start_time > timeout:<EOL><INDENT>if self.verbose is True:<EOL><INDENT>print("<STR_LIT>"<EOL>"<STR_LIT>")<EOL><DEDENT>return<EOL><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT><DEDENT>return self._command_handler.stepper_library_version<EOL>

Get the stepper library version number.

:param timeout: specify a time to allow arduino to process and return a version

:return: the stepper version number if it was set.


def i2c_config(self, read_delay_time=<NUM_LIT:0>, pin_type=None, clk_pin=<NUM_LIT:0>, data_pin=<NUM_LIT:0>):

data = [read_delay_time & <NUM_LIT>, (read_delay_time >> <NUM_LIT:7>) & <NUM_LIT>]<EOL>self._command_handler.send_sysex(self._command_handler.I2C_CONFIG, data)<EOL>if pin_type:<EOL><INDENT>if pin_type == self.DIGITAL:<EOL><INDENT>self._command_handler.digital_response_table[clk_pin][self._command_handler.RESPONSE_TABLE_MODE]= self.I2C<EOL>self._command_handler.digital_response_table[data_pin][self._command_handler.RESPONSE_TABLE_MODE]= self.I2C<EOL><DEDENT>else:<EOL><INDENT>self._command_handler.analog_response_table[clk_pin][self._command_handler.RESPONSE_TABLE_MODE]= self.I2C<EOL>self._command_handler.analog_response_table[data_pin][self._command_handler.RESPONSE_TABLE_MODE]= self.I2C<EOL><DEDENT><DEDENT>

NOTE: THIS METHOD MUST BE CALLED BEFORE ANY I2C REQUEST IS MADE
This method initializes Firmata for I2c operations.
It allows setting of a read time delay amount, and to optionally track
the pins as I2C in the appropriate response table.
To track pins: Set the pin_type to ANALOG or DIGITAL and provide the pin numbers.
If using ANALOG, pin numbers use the analog number, for example A4: use 4.

:param read_delay_time: an optional parameter, default is 0

:param pin_type: ANALOG or DIGITAL to select response table type to track pin numbers

:param clk_pin: pin number (see comment above).

:param data_pin: pin number (see comment above).

:return: No Return Value


def i2c_read(self, address, register, number_of_bytes, read_type, cb=None):

data = [address, read_type, register & <NUM_LIT>, (register >> <NUM_LIT:7>) & <NUM_LIT>,<EOL>number_of_bytes & <NUM_LIT>, (number_of_bytes >> <NUM_LIT:7>) & <NUM_LIT>]<EOL>self._command_handler.i2c_map[address] = [cb, None]<EOL>self._command_handler.send_sysex(self._command_handler.I2C_REQUEST, data)<EOL>

This method requests the read of an i2c device. Results are retrieved by a call to
i2c_get_read_data().
If a callback method is provided, when data is received from the device it will be sent to the callback method

:param address: i2c device address

:param register: register number (can be set to zero)

:param number_of_bytes: number of bytes expected to be returned

:param read_type: I2C_READ  or I2C_READ_CONTINUOUSLY

:param cb: Optional callback function to report i2c data as result of read command


data = [address, self.I2C_WRITE]<EOL>for item in args:<EOL><INDENT>data.append(item & <NUM_LIT>)<EOL>data.append((item >> <NUM_LIT:7>) & <NUM_LIT>)<EOL><DEDENT>self._command_handler.send_sysex(self._command_handler.I2C_REQUEST, data)<EOL>

Write data to an i2c device.

:param address: i2c device address

:param args: A variable number of bytes to be sent to the device


data = [address, self.I2C_STOP_READING]<EOL>self._command_handler.send_sysex(self._command_handler.I2C_REQUEST, data)<EOL>

This method stops an I2C_READ_CONTINUOUSLY operation for the i2c device address specified.

:param address: address of i2c device


if address in self._command_handler.i2c_map:<EOL><INDENT>map_entry = self._command_handler.i2c_map[address]<EOL>return map_entry[<NUM_LIT:1>]<EOL><DEDENT>

This method retrieves the i2c read data as the result of an i2c_read() command.

:param address: i2c device address

:return: raw data read from device


self._command_handler.send_sysex(self._command_handler.PIN_STATE_QUERY, [pin])<EOL>

This method issues a pin state query command. Data returned is retrieved via
a call to get_pin_state_query_results()
:param pin: pin number


def play_tone(self, pin, tone_command, frequency, duration):

<EOL>if tone_command == self.TONE_TONE:<EOL><INDENT>if duration:<EOL><INDENT>data = [tone_command, pin, frequency & <NUM_LIT>, (frequency >> <NUM_LIT:7>) & <NUM_LIT>, duration & <NUM_LIT>, (duration >> <NUM_LIT:7>) & <NUM_LIT>]<EOL><DEDENT>else:<EOL><INDENT>data = [tone_command, pin, frequency & <NUM_LIT>, (frequency >> <NUM_LIT:7>) & <NUM_LIT>, <NUM_LIT:0>, <NUM_LIT:0>]<EOL><DEDENT>self._command_handler.digital_response_table[pin][self._command_handler.RESPONSE_TABLE_MODE] =self.TONE<EOL><DEDENT>else:<EOL><INDENT>data = [tone_command, pin]<EOL><DEDENT>self._command_handler.send_sysex(self._command_handler.TONE_PLAY, data)<EOL>

This method will call the Tone library for the selected pin.
If the tone command is set to TONE_TONE, then the specified tone will be played.
Else, if the tone command is TONE_NO_TONE, then any currently playing tone will be disabled.
It is intended for a future release of Arduino Firmata

:param pin: Pin number

:param tone_command: Either TONE_TONE, or TONE_NO_TONE

:param frequency: Frequency of tone in hz

:param duration: Duration of tone in milliseconds

:return: No return value


command = [self._command_handler.REPORT_VERSION]<EOL>self._command_handler.send_command(command)<EOL>

This method will query firmata for the report version.
Retrieve the report version via a call to get_firmata_version()


self._command_handler.send_sysex(self._command_handler.REPORT_FIRMWARE, None)<EOL>

This method will query firmata to report firmware. Retrieve the report via a
call to get_firmata_firmware_version()


<EOL>for pin in range(<NUM_LIT:0>, self._command_handler.total_pins_discovered):<EOL><INDENT>if self._command_handler.digital_response_table[self._command_handler.RESPONSE_TABLE_MODE]== self.PWM:<EOL><INDENT>self.analog_write(pin, <NUM_LIT:0>)<EOL><DEDENT>elif self._command_handler.digital_response_table[self._command_handler.RESPONSE_TABLE_MODE]== self.SERVO:<EOL><INDENT>self.analog_write(pin, <NUM_LIT:0>)<EOL><DEDENT>elif self._command_handler.digital_response_table[self._command_handler.RESPONSE_TABLE_MODE]== self.TONE:<EOL><INDENT>data = [self.TONE_NO_TONE, pin]<EOL>self._command_handler.send_sysex(self._command_handler.TONE_PLAY, data)<EOL><DEDENT>else:<EOL><INDENT>self.digital_write(pin, <NUM_LIT:0>)<EOL><DEDENT><DEDENT>self._command_handler.system_reset()<EOL>

This command sends a reset message to the Arduino. The response tables will be reinitialized
:return: No return value.


def set_analog_latch(self, pin, threshold_type, threshold_value, cb=None):

if self.ANALOG_LATCH_GT <= threshold_type <= self.ANALOG_LATCH_LTE:<EOL><INDENT>if <NUM_LIT:0> <= threshold_value <= <NUM_LIT>:<EOL><INDENT>self._command_handler.set_analog_latch(pin, threshold_type, threshold_value, cb)<EOL>return True<EOL><DEDENT><DEDENT>else:<EOL><INDENT>return False<EOL><DEDENT>

This method "arms" an analog pin for its data to be latched and saved in the latching table
If a callback method is provided, when latching criteria is achieved, the callback function is called
with latching data notification. In that case, the latching table is not updated.

:param pin: Analog pin number (value following an 'A' designator, i.e. A5 = 5

:param threshold_type: ANALOG_LATCH_GT | ANALOG_LATCH_LT  | ANALOG_LATCH_GTE | ANALOG_LATCH_LTE

:param threshold_value: numerical value - between 0 and 1023

:param cb: callback method

:return: True if successful, False if parameter data is invalid


def set_digital_latch(self, pin, threshold_type, cb=None):

if <NUM_LIT:0> <= threshold_type <= <NUM_LIT:1>:<EOL><INDENT>self._command_handler.set_digital_latch(pin, threshold_type, cb)<EOL>return True<EOL><DEDENT>else:<EOL><INDENT>return False<EOL><DEDENT>

This method "arms" a digital pin for its data to be latched and saved in the latching table
If a callback method is provided, when latching criteria is achieved, the callback function is called
with latching data notification. In that case, the latching table is not updated.

:param pin: Digital pin number

:param threshold_type: DIGITAL_LATCH_HIGH | DIGITAL_LATCH_LOW

:param cb: callback function

:return: True if successful, False if parameter data is invalid


def set_pin_mode(self, pin, mode, pin_type, cb=None):

command = [self._command_handler.SET_PIN_MODE, pin, mode]<EOL>self._command_handler.send_command(command)<EOL>if mode == self.INPUT or mode == self.PULLUP:<EOL><INDENT>if pin_type == self.ANALOG:<EOL><INDENT>self._command_handler.analog_response_table[pin][self._command_handler.RESPONSE_TABLE_MODE] =self.INPUT<EOL>self._command_handler.analog_response_table[pin][self._command_handler.RESPONSE_TABLE_CALLBACK] = cb<EOL>self.enable_analog_reporting(pin)<EOL><DEDENT>else:<EOL><INDENT>self._command_handler.digital_response_table[pin][self._command_handler.RESPONSE_TABLE_MODE] =self.INPUT<EOL>self._command_handler.digital_response_table[pin][self._command_handler.RESPONSE_TABLE_CALLBACK] = cb<EOL>self.enable_digital_reporting(pin)<EOL><DEDENT><DEDENT>else:  <EOL><INDENT>if pin_type == self.ANALOG:<EOL><INDENT>self._command_handler.analog_response_table[pin][self._command_handler.RESPONSE_TABLE_MODE] = mode<EOL><DEDENT>else:<EOL><INDENT>self._command_handler.digital_response_table[pin][self._command_handler.RESPONSE_TABLE_MODE] = mode<EOL><DEDENT><DEDENT>

This method sets a pin to the desired pin mode for the pin_type.
It automatically enables data reporting.
NOTE: DO NOT CALL THIS METHOD FOR I2C. See i2c_config().

:param pin: Pin number (for analog use the analog number, for example A4: use 4)

:param mode: INPUT, OUTPUT, PWM, PULLUP

:param pin_type: ANALOG or DIGITAL

:param cb: This is an optional callback function to report data changes to the user

:return: No return value


data = [interval & <NUM_LIT>, (interval >> <NUM_LIT:7>) & <NUM_LIT>]<EOL>self._command_handler.send_sysex(self._command_handler.SAMPLING_INTERVAL, data)<EOL>

This method sends the desired sampling interval to Firmata.
Note: Standard Firmata  will ignore any interval less than 10 milliseconds

:param interval: Integer value for desired sampling interval in milliseconds

:return: No return value.


def servo_config(self, pin, min_pulse=<NUM_LIT>, max_pulse=<NUM_LIT>):

self.set_pin_mode(pin, self.SERVO, self.OUTPUT)<EOL>command = [pin, min_pulse & <NUM_LIT>, (min_pulse >> <NUM_LIT:7>) & <NUM_LIT>,<EOL>max_pulse & <NUM_LIT>, (max_pulse >> <NUM_LIT:7>) & <NUM_LIT>]<EOL>self._command_handler.send_sysex(self._command_handler.SERVO_CONFIG, command)<EOL>

Configure a pin as a servo pin. Set pulse min, max in ms.

:param pin: Servo Pin.

:param min_pulse: Min pulse width in ms.

:param max_pulse: Max pulse width in ms.

:return: No return value


def sonar_config(self, trigger_pin, echo_pin, cb=None, ping_interval=<NUM_LIT:50>, max_distance=<NUM_LIT:200>):

if max_distance > <NUM_LIT:200>:<EOL><INDENT>max_distance = <NUM_LIT:200><EOL><DEDENT>max_distance_lsb = max_distance & <NUM_LIT><EOL>max_distance_msb = (max_distance >> <NUM_LIT:7>) & <NUM_LIT><EOL>data = [trigger_pin, echo_pin, ping_interval, max_distance_lsb, max_distance_msb]<EOL>self.set_pin_mode(trigger_pin, self.SONAR, self.INPUT)<EOL>self.set_pin_mode(echo_pin, self.SONAR, self.INPUT)<EOL>if len(self._command_handler.active_sonar_map) > <NUM_LIT:6>:<EOL><INDENT>if self.verbose:<EOL><INDENT>print("<STR_LIT>")<EOL><DEDENT>return<EOL><DEDENT>else:<EOL><INDENT>with self.data_lock:<EOL><INDENT>self._command_handler.active_sonar_map[trigger_pin] = [cb, [self.IGNORE]]<EOL><DEDENT><DEDENT>self._command_handler.send_sysex(self._command_handler.SONAR_CONFIG, data)<EOL>

Configure the pins,ping interval and maximum distance for an HC-SR04 type device.
Single pin configuration may be used. To do so, set both the trigger and echo pins to the same value.
Up to a maximum of 6 SONAR devices is supported
If the maximum is exceeded a message is sent to the console and the request is ignored.
NOTE: data is measured in centimeters

:param trigger_pin: The pin number of for the trigger (transmitter).

:param echo_pin: The pin number for the received echo.

:param ping_interval: Minimum interval between pings. Lowest number to use is 33 ms.Max is 127

:param max_distance: Maximum distance in cm. Max is 200.

:param cb: optional callback function to report sonar data changes


def stepper_config(self, steps_per_revolution, stepper_pins):

data = [self.STEPPER_CONFIGURE, steps_per_revolution & <NUM_LIT>, (steps_per_revolution >> <NUM_LIT:7>) & <NUM_LIT>]<EOL>for pin in range(len(stepper_pins)):<EOL><INDENT>data.append(stepper_pins[pin])<EOL><DEDENT>self._command_handler.send_sysex(self._command_handler.STEPPER_DATA, data)<EOL>

Configure stepper motor prior to operation.

:param steps_per_revolution: number of steps per motor revolution

:param stepper_pins: a list of control pin numbers - either 4 or 2


def stepper_step(self, motor_speed, number_of_steps):

if number_of_steps > <NUM_LIT:0>:<EOL><INDENT>direction = <NUM_LIT:1><EOL><DEDENT>else:<EOL><INDENT>direction = <NUM_LIT:0><EOL><DEDENT>abs_number_of_steps = abs(number_of_steps)<EOL>data = [self.STEPPER_STEP, motor_speed & <NUM_LIT>, (motor_speed >> <NUM_LIT:7>) & <NUM_LIT>, (motor_speed >> <NUM_LIT>) & <NUM_LIT>,<EOL>abs_number_of_steps & <NUM_LIT>, (abs_number_of_steps >> <NUM_LIT:7>) & <NUM_LIT>, direction]<EOL>self._command_handler.send_sysex(self._command_handler.STEPPER_DATA, data)<EOL>

Move a stepper motor for the number of steps at the specified speed

:param motor_speed: 21 bits of data to set motor speed

:param number_of_steps: 14 bits for number of steps & direction
                        positive is forward, negative is reverse


data = [self.STEPPER_LIBRARY_VERSION]<EOL>self._command_handler.send_sysex(self._command_handler.STEPPER_DATA, data)<EOL>

Request the stepper library version from the Arduino.
To retrieve the version after this command is called, call
get_stepper_version


def __init__(self, port_id, command_deque, baud_rate):

self.port_id = port_id<EOL>self.command_deque = command_deque<EOL>self.baud_rate = baud_rate<EOL>threading.Thread.__init__(self)<EOL>self.daemon = True<EOL>self.arduino = serial.Serial(self.port_id, self.baud_rate,<EOL>timeout=int(self.timeout), writeTimeout=<NUM_LIT:0>)<EOL>self.stop_event = threading.Event()<EOL>if sys.platform == '<STR_LIT>':<EOL><INDENT>self.arduino.nonblocking()<EOL><DEDENT>

Constructor:

:param command_deque: A reference to the deque shared with the _command_handler

:param baud_rate: must match that of Arduino Sketch


<EOL>if verbose:<EOL><INDENT>print('<STR_LIT>' % self.port_id)<EOL><DEDENT>try:<EOL><INDENT>self.arduino.close()<EOL>time.sleep(<NUM_LIT:1>)<EOL>self.arduino.open()<EOL>time.sleep(<NUM_LIT:1>)<EOL>return self.arduino<EOL><DEDENT>except Exception:<EOL><INDENT>raise<EOL><DEDENT>

open the serial port using the configuration data
returns a reference to this instance


try:<EOL><INDENT>self.arduino.close()<EOL><DEDENT>except OSError:<EOL><INDENT>pass<EOL><DEDENT>


if sys.version_info[<NUM_LIT:0>] < <NUM_LIT:3>:<EOL><INDENT>self.arduino.write(data)<EOL><DEDENT>else:<EOL><INDENT>self.arduino.write(bytes([ord(data)]))<EOL><DEDENT>

write the data to the serial port
return: None


while not self.is_stopped():<EOL><INDENT>try:<EOL><INDENT>if self.arduino.inWaiting():<EOL><INDENT>c = self.arduino.read()<EOL>self.command_deque.append(ord(c))<EOL><DEDENT>else:<EOL><INDENT>time.sleep(<NUM_LIT>)<EOL><DEDENT><DEDENT>except OSError:<EOL><INDENT>pass<EOL><DEDENT>except IOError:<EOL><INDENT>self.stop()<EOL><DEDENT><DEDENT>self.close()<EOL>

This method continually runs. If an incoming character is available on the serial port
it is read and placed on the _command_deque
@return: Never Returns


def __init__(self, address, blink_rate, brightness):<EOL>

<EOL>self.firmata = PyMata("<STR_LIT>")<EOL>self.board_address = address<EOL>self.blink_rate = blink_rate<EOL>self.brightness = brightness<EOL>self.clear_display_buffer()<EOL>self.firmata.i2c_config(<NUM_LIT:0>, self.firmata.ANALOG, <NUM_LIT:4>, <NUM_LIT:5>)<EOL>self.oscillator_set(self.OSCILLATOR_ON)<EOL>self.set_blink_rate(self.blink_rate)<EOL>self.set_brightness(self.brightness)<EOL>

@param address: I2C address of the device
@param blink_rate: desired blink rate
@param brightness: brightness level for the display


if b > <NUM_LIT:3>:<EOL><INDENT>b = <NUM_LIT:0>  <EOL><DEDENT>self.firmata.i2c_write(self.board_address,<EOL>(self.HT16K33_BLINK_CMD | self.HT16K33_BLINK_DISPLAYON | (b << <NUM_LIT:1>)))<EOL>

Set the user's desired blink rate (0 - 3)
@param b: blink rate


self.firmata.i2c_write(self.board_address, osc_mode)<EOL>

Turn oscillator on or off
@param osc_mode: osc mode (OSCILLATOR_ON or OSCILLATOR_OFF)


if brightness > <NUM_LIT:15>:<EOL><INDENT>brightness = <NUM_LIT:15><EOL><DEDENT>brightness |= <NUM_LIT><EOL>self.brightness = brightness<EOL>self.firmata.i2c_write(<NUM_LIT>, brightness)<EOL>

Set the brightness level for the entire display
@param brightness: brightness level (0 -15)


def set_pixel(self, row, column, color, suppress_write):<EOL>

if (row < <NUM_LIT:0>) or (row >= <NUM_LIT:8>):<EOL><INDENT>print("<STR_LIT>")<EOL>return<EOL><DEDENT>if (column < <NUM_LIT:0>) or (column >= <NUM_LIT:8>):<EOL><INDENT>print("<STR_LIT>")<EOL>return<EOL><DEDENT>self.display_buffer[row][column] = color<EOL>green = <NUM_LIT:0><EOL>red = <NUM_LIT:0><EOL>for col in range(<NUM_LIT:0>, <NUM_LIT:8>):<EOL><INDENT>if self.display_buffer[row][col] == self.LED_GREEN:<EOL><INDENT>green |= <NUM_LIT:1> << col<EOL><DEDENT>elif self.display_buffer[row][col] == self.LED_RED:<EOL><INDENT>red |= <NUM_LIT:1> << col<EOL><DEDENT>elif self.display_buffer[row][col] == self.LED_YELLOW:<EOL><INDENT>green |= <NUM_LIT:1> << col<EOL>red |= <NUM_LIT:1> << col<EOL><DEDENT>elif self.display_buffer[row][col] == self.LED_OFF:<EOL><INDENT>green &= ~(<NUM_LIT:1> << col)<EOL>red &= ~(<NUM_LIT:1> << col)<EOL><DEDENT><DEDENT>if not suppress_write:<EOL><INDENT>self.firmata.i2c_write(<NUM_LIT>, row * <NUM_LIT:2>, <NUM_LIT:0>, green)<EOL>self.firmata.i2c_write(<NUM_LIT>, row * <NUM_LIT:2> + <NUM_LIT:1>, <NUM_LIT:0>, red)<EOL><DEDENT>

@param row: pixel row number
@param column: pix column number
@param color: pixel color (yellow is both red and green both on)
@param suppress_write: if true, just sets the internal data structure, else writes out the pixel to the display


for row in range(<NUM_LIT:0>, <NUM_LIT:8>):<EOL><INDENT>data = shape[row]<EOL>bit_mask = <NUM_LIT><EOL>for column in range(<NUM_LIT:0>, <NUM_LIT:8>):<EOL><INDENT>if data & bit_mask:<EOL><INDENT>self.set_pixel(row, column, color, True)<EOL><DEDENT>bit_mask >>= <NUM_LIT:1><EOL><DEDENT><DEDENT>self.output_entire_buffer()<EOL>

Populate the bit map with the supplied "shape" and color
and then write the entire bitmap to the display
@param shape: pattern to display
@param color: color for the pattern


green = <NUM_LIT:0><EOL>red = <NUM_LIT:0><EOL>for row in range(<NUM_LIT:0>, <NUM_LIT:8>):<EOL><INDENT>for col in range(<NUM_LIT:0>, <NUM_LIT:8>):<EOL><INDENT>if self.display_buffer[row][col] == self.LED_GREEN:<EOL><INDENT>green |= <NUM_LIT:1> << col<EOL><DEDENT>elif self.display_buffer[row][col] == self.LED_RED:<EOL><INDENT>red |= <NUM_LIT:1> << col<EOL><DEDENT>elif self.display_buffer[row][col] == self.LED_YELLOW:<EOL><INDENT>green |= <NUM_LIT:1> << col<EOL>red |= <NUM_LIT:1> << col<EOL><DEDENT>elif self.display_buffer[row][col] == self.LED_OFF:<EOL><INDENT>green &= ~(<NUM_LIT:1> << col)<EOL>red &= ~(<NUM_LIT:1> << col)<EOL><DEDENT><DEDENT>self.firmata.i2c_write(<NUM_LIT>, row * <NUM_LIT:2>, <NUM_LIT:0>, green)<EOL>self.firmata.i2c_write(<NUM_LIT>, row * <NUM_LIT:2> + <NUM_LIT:1>, <NUM_LIT:0>, red)<EOL><DEDENT>


for row in range(<NUM_LIT:0>, <NUM_LIT:8>):<EOL><INDENT>self.firmata.i2c_write(<NUM_LIT>, row * <NUM_LIT:2>, <NUM_LIT:0>, <NUM_LIT:0>)<EOL>self.firmata.i2c_write(<NUM_LIT>, (row * <NUM_LIT:2>) + <NUM_LIT:1>, <NUM_LIT:0>, <NUM_LIT:0>)<EOL>for column in range(<NUM_LIT:0>, <NUM_LIT:8>):<EOL><INDENT>self.display_buffer[row][column] = <NUM_LIT:0><EOL><DEDENT><DEDENT>


self.firmata.close()<EOL>


def generate_versionwarning_data_json(app, config=None, **kwargs):

<EOL>config = config or kwargs.pop('<STR_LIT>', None)<EOL>if config is None:<EOL><INDENT>config = app.config<EOL><DEDENT>if config.versionwarning_project_version in config.versionwarning_messages:<EOL><INDENT>custom = True<EOL>message = config.versionwarning_messages.get(config.versionwarning_project_version)<EOL><DEDENT>else:<EOL><INDENT>custom = False<EOL>message = config.versionwarning_default_message<EOL><DEDENT>banner_html = config.versionwarning_banner_html.format(<EOL>id_div=config.versionwarning_banner_id_div,<EOL>banner_title=config.versionwarning_banner_title,<EOL>message=message.format(<EOL>**{config.versionwarning_message_placeholder: '<STR_LIT>'},<EOL>),<EOL>admonition_type=config.versionwarning_admonition_type,<EOL>)<EOL>data = json.dumps({<EOL>'<STR_LIT>': {<EOL>'<STR_LIT>': config.versionwarning_api_url,<EOL>},<EOL>'<STR_LIT>': {<EOL>'<STR_LIT:html>': banner_html,<EOL>'<STR_LIT>': config.versionwarning_banner_id_div,<EOL>'<STR_LIT>': config.versionwarning_body_selector,<EOL>'<STR_LIT>': custom,<EOL>},<EOL>'<STR_LIT>': {<EOL>'<STR_LIT>': config.versionwarning_project_slug,<EOL>},<EOL>'<STR_LIT:version>': {<EOL>'<STR_LIT>': config.versionwarning_project_version,<EOL>},<EOL>}, indent=<NUM_LIT:4>)<EOL>data_path = os.path.join(STATIC_PATH, '<STR_LIT:data>')<EOL>if not os.path.exists(data_path):<EOL><INDENT>os.mkdir(data_path)<EOL><DEDENT>with open(os.path.join(data_path, JSON_DATA_FILENAME), '<STR_LIT:w>') as f:<EOL><INDENT>f.write(data)<EOL><DEDENT>config.html_static_path.append(STATIC_PATH)<EOL>

Generate the ``versionwarning-data.json`` file.

This file is included in the output and read by the AJAX request when
accessing to the documentation and used to compare the live versions with
the curent one.

Besides, this file contains meta data about the project, the API to use and
the banner itself.


del mapped['<STR_LIT>']<EOL>del mapped['<STR_LIT>']<EOL>

Remove definitions of the built functions


graph = BELGraph(<EOL>name='<STR_LIT>',<EOL>version='<STR_LIT>',<EOL>description='<STR_LIT>',<EOL>authors='<STR_LIT>',<EOL>contact='<STR_LIT>',<EOL>)<EOL>graph.add_increases(protein_a, protein_b, n(), n())<EOL>graph.add_decreases(protein_b, gene_c, n(), n())<EOL>graph.add_decreases(rna_d, gene_f, n(), n())<EOL>graph.add_increases(protein_e, gene_f, n(), n())<EOL>graph.add_increases(gene_f, gene_c, n(), n())<EOL>graph.add_association(gene_c, protein_g, n(), n())<EOL>return graph<EOL>

Make an example graph.

    A -> B -| C
    D -| F -> C
    C -| F
    C -- G


graph = BELGraph(<EOL>name='<STR_LIT>',<EOL>version='<STR_LIT>',<EOL>description='<STR_LIT>',<EOL>authors='<STR_LIT>',<EOL>contact='<STR_LIT>',<EOL>)<EOL>graph.add_increases(protein_a, protein_b, n(), n())<EOL>graph.add_decreases(protein_b, gene_c, n(), n())<EOL>graph.add_decreases(protein_b, rna_d, n(), n())<EOL>graph.add_decreases(protein_b, protein_e, n(), n())<EOL>graph.add_decreases(protein_b, gene_f, n(), n())<EOL>graph.add_increases(protein_b, protein_g, n(), n())<EOL>graph.add_decreases(protein_b, protein_h, n(), n())<EOL>graph.add_increases(protein_b, protein_h, n(), n())<EOL>graph.add_increases(protein_b, protein_i, n(), n())<EOL>graph.add_association(protein_b, protein_j, n(), n())<EOL>return graph<EOL>

Make an example graph.

    A -> B
    B -| C
    B -| D
    B -| E
    B -| F
    B -> G

    B -> H
    B -| H

    B -> I
    B -- J


def flatten_list_abundance(node: ListAbundance) -> ListAbundance:

return node.__class__(list(chain.from_iterable(<EOL>(<EOL>flatten_list_abundance(member).members<EOL>if isinstance(member, ListAbundance) else<EOL>[member]<EOL>)<EOL>for member in node.members<EOL>)))<EOL>

Flattens the complex or composite abundance.


def list_abundance_expansion(graph: BELGraph) -> None:

mapping = {<EOL>node: flatten_list_abundance(node)<EOL>for node in graph<EOL>if isinstance(node, ListAbundance)<EOL>}<EOL>relabel_nodes(graph, mapping, copy=False)<EOL>


def list_abundance_cartesian_expansion(graph: BELGraph) -> None:

for u, v, k, d in list(graph.edges(keys=True, data=True)):<EOL><INDENT>if CITATION not in d:<EOL><INDENT>continue<EOL><DEDENT>if isinstance(u, ListAbundance) and isinstance(v, ListAbundance):<EOL><INDENT>for u_member, v_member in itt.product(u.members, v.members):<EOL><INDENT>graph.add_qualified_edge(<EOL>u_member, v_member,<EOL>relation=d[RELATION],<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT><DEDENT>elif isinstance(u, ListAbundance):<EOL><INDENT>for member in u.members:<EOL><INDENT>graph.add_qualified_edge(<EOL>member, v,<EOL>relation=d[RELATION],<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT><DEDENT>elif isinstance(v, ListAbundance):<EOL><INDENT>for member in v.members:<EOL><INDENT>graph.add_qualified_edge(<EOL>u, member,<EOL>relation=d[RELATION],<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT><DEDENT><DEDENT>_remove_list_abundance_nodes(graph)<EOL>

Expand all list abundances to simple subject-predicate-object networks.


def _reaction_cartesion_expansion_unqualified_helper(<EOL>graph: BELGraph,<EOL>u: BaseEntity,<EOL>v: BaseEntity,<EOL>d: dict,<EOL>) -> None:

if isinstance(u, Reaction) and isinstance(v, Reaction):<EOL><INDENT>enzymes = _get_catalysts_in_reaction(u) | _get_catalysts_in_reaction(v)<EOL>for reactant, product in chain(itt.product(u.reactants, u.products),<EOL>itt.product(v.reactants, v.products)):<EOL><INDENT>if reactant in enzymes or product in enzymes:<EOL><INDENT>continue<EOL><DEDENT>graph.add_unqualified_edge(<EOL>reactant, product, INCREASES<EOL>)<EOL><DEDENT>for product, reactant in itt.product(u.products, u.reactants):<EOL><INDENT>if reactant in enzymes or product in enzymes:<EOL><INDENT>continue<EOL><DEDENT>graph.add_unqualified_edge(<EOL>product, reactant, d[RELATION],<EOL>)<EOL><DEDENT><DEDENT>elif isinstance(u, Reaction):<EOL><INDENT>enzymes = _get_catalysts_in_reaction(u)<EOL>for product in u.products:<EOL><INDENT>if product in enzymes:<EOL><INDENT>continue<EOL><DEDENT>if v not in u.products and v not in u.reactants:<EOL><INDENT>graph.add_unqualified_edge(<EOL>product, v, INCREASES<EOL>)<EOL><DEDENT>for reactant in u.reactants:<EOL><INDENT>graph.add_unqualified_edge(<EOL>reactant, product, INCREASES<EOL>)<EOL><DEDENT><DEDENT><DEDENT>elif isinstance(v, Reaction):<EOL><INDENT>enzymes = _get_catalysts_in_reaction(v)<EOL>for reactant in v.reactants:<EOL><INDENT>if reactant in enzymes:<EOL><INDENT>continue<EOL><DEDENT>if u not in v.products and u not in v.reactants:<EOL><INDENT>graph.add_unqualified_edge(<EOL>u, reactant, INCREASES<EOL>)<EOL><DEDENT>for product in v.products:<EOL><INDENT>graph.add_unqualified_edge(<EOL>reactant, product, INCREASES<EOL>)<EOL><DEDENT><DEDENT><DEDENT>

Helper to deal with cartension expansion in unqualified edges.


def _get_catalysts_in_reaction(reaction: Reaction) -> Set[BaseAbundance]:

return {<EOL>reactant<EOL>for reactant in reaction.reactants<EOL>if reactant in reaction.products<EOL>}<EOL>

Return nodes that are both in reactants and reactions in a reaction.


def reaction_cartesian_expansion(graph: BELGraph, accept_unqualified_edges: bool = True) -> None:

for u, v, d in list(graph.edges(data=True)):<EOL><INDENT>if CITATION not in d and accept_unqualified_edges:<EOL><INDENT>_reaction_cartesion_expansion_unqualified_helper(graph, u, v, d)<EOL>continue<EOL><DEDENT>if isinstance(u, Reaction) and isinstance(v, Reaction):<EOL><INDENT>catalysts = _get_catalysts_in_reaction(u) | _get_catalysts_in_reaction(v)<EOL>for reactant, product in chain(itt.product(u.reactants, u.products), itt.product(v.reactants, v.products)):<EOL><INDENT>if reactant in catalysts or product in catalysts:<EOL><INDENT>continue<EOL><DEDENT>graph.add_increases(<EOL>reactant, product,<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT>for product, reactant in itt.product(u.products, u.reactants):<EOL><INDENT>if reactant in catalysts or product in catalysts:<EOL><INDENT>continue<EOL><DEDENT>graph.add_qualified_edge(<EOL>product, reactant,<EOL>relation=d[RELATION],<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT><DEDENT>elif isinstance(u, Reaction):<EOL><INDENT>catalysts = _get_catalysts_in_reaction(u)<EOL>for product in u.products:<EOL><INDENT>if product in catalysts:<EOL><INDENT>continue<EOL><DEDENT>if v not in u.products and v not in u.reactants:<EOL><INDENT>graph.add_increases(<EOL>product, v,<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT>for reactant in u.reactants:<EOL><INDENT>graph.add_increases(<EOL>reactant, product,<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT><DEDENT><DEDENT>elif isinstance(v, Reaction):<EOL><INDENT>for reactant in v.reactants:<EOL><INDENT>catalysts = _get_catalysts_in_reaction(v)<EOL>if reactant in catalysts:<EOL><INDENT>continue<EOL><DEDENT>if u not in v.products and u not in v.reactants:<EOL><INDENT>graph.add_increases(<EOL>u, reactant,<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT>for product in v.products:<EOL><INDENT>graph.add_increases(<EOL>reactant, product,<EOL>citation=d.get(CITATION),<EOL>evidence=d.get(EVIDENCE),<EOL>annotations=d.get(ANNOTATIONS),<EOL>)<EOL><DEDENT><DEDENT><DEDENT><DEDENT>_remove_reaction_nodes(graph)<EOL>

Expand all reactions to simple subject-predicate-object networks.


def remove_reified_nodes(graph: BELGraph) -> None:

_remove_list_abundance_nodes(graph)<EOL>_remove_reaction_nodes(graph)<EOL>


def make_pubmed_abstract_group(pmids: Iterable[Union[str, int]]) -> Iterable[str]:

for pmid in set(pmids):<EOL><INDENT>yield '<STR_LIT>'<EOL>res = requests.get(title_url_fmt.format(pmid))<EOL>title = res.content.decode('<STR_LIT:utf-8>').strip()<EOL>yield '<STR_LIT>'.format(title, pmid)<EOL>res = requests.get(abstract_url_fmt.format(pmid))<EOL>abstract = res.content.decode('<STR_LIT:utf-8>').strip()<EOL>yield '<STR_LIT>'.format(abstract)<EOL>yield '<STR_LIT>'<EOL><DEDENT>

Build a skeleton for the citations' statements.

    :param pmids: A list of PubMed identifiers
    :return: An iterator over the lines of the citation section


def get_entrez_gene_data(entrez_ids: Iterable[Union[str, int]]):

url = PUBMED_GENE_QUERY_URL.format('<STR_LIT:U+002C>'.join(str(x).strip() for x in entrez_ids))<EOL>response = requests.get(url)<EOL>tree = ElementTree.fromstring(response.content)<EOL>return {<EOL>element.attrib['<STR_LIT>']: {<EOL>'<STR_LIT>': _sanitize(element.find('<STR_LIT>').text),<EOL>'<STR_LIT:description>': element.find('<STR_LIT>').text<EOL>}<EOL>for element in tree.findall('<STR_LIT>')<EOL>}<EOL>


def make_pubmed_gene_group(entrez_ids: Iterable[Union[str, int]]) -> Iterable[str]:

url = PUBMED_GENE_QUERY_URL.format('<STR_LIT:U+002C>'.join(str(x).strip() for x in entrez_ids))<EOL>response = requests.get(url)<EOL>tree = ElementTree.fromstring(response.content)<EOL>for x in tree.findall('<STR_LIT>'):<EOL><INDENT>yield '<STR_LIT>'.format(x.find('<STR_LIT>').text)<EOL>yield '<STR_LIT>'.format(x.attrib['<STR_LIT>'])<EOL>yield '<STR_LIT>'.format(x.find('<STR_LIT>').text.strip().replace('<STR_LIT:\n>', '<STR_LIT>'))<EOL>yield '<STR_LIT>'<EOL><DEDENT>

Builds a skeleton for gene summaries

    :param entrez_ids: A list of Entrez Gene identifiers to query the PubMed service
    :return: An iterator over statement lines for NCBI Entrez Gene summaries


def write_boilerplate(name: str,<EOL>version: Optional[str] = None,<EOL>description: Optional[str] = None,<EOL>authors: Optional[str] = None,<EOL>contact: Optional[str] = None,<EOL>copyright: Optional[str] = None,<EOL>licenses: Optional[str] = None,<EOL>disclaimer: Optional[str] = None,<EOL>namespace_url: Optional[Mapping[str, str]] = None,<EOL>namespace_patterns: Optional[Mapping[str, str]] = None,<EOL>annotation_url: Optional[Mapping[str, str]] = None,<EOL>annotation_patterns: Optional[Mapping[str, str]] = None,<EOL>annotation_list: Optional[Mapping[str, Set[str]]] = None,<EOL>pmids: Optional[Iterable[Union[str, int]]] = None,<EOL>entrez_ids: Optional[Iterable[Union[str, int]]] = None,<EOL>file: Optional[TextIO] = None,<EOL>) -> None:

lines = make_knowledge_header(<EOL>name=name,<EOL>version=version or '<STR_LIT>',<EOL>description=description,<EOL>authors=authors,<EOL>contact=contact,<EOL>copyright=copyright,<EOL>licenses=licenses,<EOL>disclaimer=disclaimer,<EOL>namespace_url=namespace_url,<EOL>namespace_patterns=namespace_patterns,<EOL>annotation_url=annotation_url,<EOL>annotation_patterns=annotation_patterns,<EOL>annotation_list=annotation_list,<EOL>)<EOL>for line in lines:<EOL><INDENT>print(line, file=file)<EOL><DEDENT>if pmids is not None:<EOL><INDENT>for line in make_pubmed_abstract_group(pmids):<EOL><INDENT>print(line, file=file)<EOL><DEDENT><DEDENT>if entrez_ids is not None:<EOL><INDENT>for line in make_pubmed_gene_group(entrez_ids):<EOL><INDENT>print(line, file=file)<EOL><DEDENT><DEDENT>

Write a boilerplate BEL document, with standard document metadata, definitions.

    :param name: The unique name for this BEL document
    :param contact: The email address of the maintainer
    :param description: A description of the contents of this document
    :param authors: The authors of this document
    :param version: The version. Defaults to current date in format ``YYYYMMDD``.
    :param copyright: Copyright information about this document
    :param licenses: The license applied to this document
    :param disclaimer: The disclaimer for this document
    :param namespace_url: an optional dictionary of {str name: str URL} of namespaces
    :param namespace_patterns: An optional dictionary of {str name: str regex} namespaces
    :param annotation_url: An optional dictionary of {str name: str URL} of annotations
    :param annotation_patterns: An optional dictionary of {str name: str regex} of regex annotations
    :param annotation_list: An optional dictionary of {str name: set of names} of list annotations
    :param pmids: A list of PubMed identifiers to auto-populate with citation and abstract
    :param entrez_ids: A list of Entrez identifiers to autopopulate the gene summary as evidence
    :param file: A writable file or file-like. If None, defaults to :data:`sys.stdout`


for line in in_file:<EOL><INDENT>print(line.strip(), file=out_file)<EOL><DEDENT>

Helps remove extraneous whitespace from the lines of a file

    :param file in_file: A readable file or file-like
    :param file out_file: A writable file or file-like


for path in os.listdir(source):<EOL><INDENT>if not path.endswith('<STR_LIT>'):<EOL><INDENT>continue<EOL><DEDENT>log.info('<STR_LIT>', path)<EOL>with open(os.path.join(source, path)) as i, open(os.path.join(target, path), '<STR_LIT:w>') as o:<EOL><INDENT>lint_file(i, o)<EOL><DEDENT><DEDENT>

Adds a linted version of each document in the source directory to the target directory

    :param str source: Path to directory to lint
    :param str target: Path to directory to output


def node_is_upstream_leaf(graph: BELGraph, node: BaseEntity) -> bool:

return <NUM_LIT:0> == len(graph.predecessors(node)) and <NUM_LIT:1> == len(graph.successors(node))<EOL>

Return if the node is an upstream leaf.

    An upstream leaf is defined as a node that has no in-edges, and exactly 1 out-edge.


def get_upstream_leaves(graph: BELGraph) -> Iterable[BaseEntity]:

return filter_nodes(graph, node_is_upstream_leaf)<EOL>

Iterate over all of the leaves of the graph (with no incoming edges and only one outgoing edge).

    .. seealso:: :func:`upstream_leaf_predicate`


def get_unweighted_upstream_leaves(graph: BELGraph, key: Optional[str] = None) -> Iterable[BaseEntity]:

if key is None:<EOL><INDENT>key = WEIGHT<EOL><DEDENT>return filter_nodes(graph, [node_is_upstream_leaf, data_missing_key_builder(key)])<EOL>

Get nodes with no incoming edges, one outgoing edge, and without the given key in its data dictionary.

    .. seealso :: :func:`data_does_not_contain_key_builder`

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data. Defaults to
     :data:`pybel_tools.constants.WEIGHT`.
    :return: An iterable over leaves (nodes with an in-degree of 0) that don't have the given annotation


@in_place_transformation<EOL>def remove_unweighted_leaves(graph: BELGraph, key: Optional[str] = None) -> None:

unweighted_leaves = list(get_unweighted_upstream_leaves(graph, key=key))<EOL>graph.remove_nodes_from(unweighted_leaves)<EOL>

Remove nodes that are leaves and that don't have a weight (or other key) attribute set.

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data. Defaults to
     :data:`pybel_tools.constants.WEIGHT`.


def is_unweighted_source(graph: BELGraph, node: BaseEntity, key: str) -> bool:

return graph.in_degree(node) == <NUM_LIT:0> and key not in graph.nodes[node]<EOL>

Check if the node is both a source and also has an annotation.

    :param graph: A BEL graph
    :param node: A BEL node
    :param key: The key in the node data dictionary representing the experimental data


def get_unweighted_sources(graph: BELGraph, key: Optional[str] = None) -> Iterable[BaseEntity]:

if key is None:<EOL><INDENT>key = WEIGHT<EOL><DEDENT>for node in graph:<EOL><INDENT>if is_unweighted_source(graph, node, key):<EOL><INDENT>yield node<EOL><DEDENT><DEDENT>

Get nodes on the periphery of the sub-graph that do not have a annotation for the given key.

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data
    :return: An iterator over BEL nodes that are unannotated and on the periphery of this subgraph


@in_place_transformation<EOL>def remove_unweighted_sources(graph: BELGraph, key: Optional[str] = None) -> None:

nodes = list(get_unweighted_sources(graph, key=key))<EOL>graph.remove_nodes_from(nodes)<EOL>

Prune unannotated nodes on the periphery of the sub-graph.

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data. Defaults to
     :data:`pybel_tools.constants.WEIGHT`.


@in_place_transformation<EOL>def prune_mechanism_by_data(graph, key: Optional[str] = None) -> None:

remove_unweighted_leaves(graph, key=key)<EOL>remove_unweighted_sources(graph, key=key)<EOL>

Remove all leaves and source nodes that don't have weights.

    Is a thin wrapper around  :func:`remove_unweighted_leaves` and :func:`remove_unweighted_sources`

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data. Defaults to
     :data:`pybel_tools.constants.WEIGHT`.

    Equivalent to:

    >>> remove_unweighted_leaves(graph)
    >>> remove_unweighted_sources(graph)


@transformation<EOL>def generate_mechanism(graph: BELGraph, node: BaseEntity, key: Optional[str] = None) -> BELGraph:

subgraph = get_upstream_causal_subgraph(graph, node)<EOL>expand_upstream_causal(graph, subgraph)<EOL>remove_inconsistent_edges(subgraph)<EOL>collapse_consistent_edges(subgraph)<EOL>if key is not None:  <EOL><INDENT>prune_mechanism_by_data(subgraph, key)<EOL><DEDENT>return subgraph<EOL>

Generate a mechanistic sub-graph upstream of the given node.

    :param graph: A BEL graph
    :param node: A BEL node
    :param key: The key in the node data dictionary representing the experimental data.
    :return: A sub-graph grown around the target BEL node


def generate_bioprocess_mechanisms(graph, key: Optional[str] = None) -> Mapping[BiologicalProcess, BELGraph]:

return {<EOL>biological_process: generate_mechanism(graph, biological_process, key=key)<EOL>for biological_process in get_nodes_by_function(graph, BIOPROCESS)<EOL>}<EOL>

Generate a mechanistic sub-graph for each biological process in the graph using :func:`generate_mechanism`.

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data.


context = get_network_summary_dict(graph)<EOL>summary_dict = graph.summary_dict()<EOL>citation_years = context['<STR_LIT>']<EOL>function_count = context['<STR_LIT>']<EOL>relation_count = context['<STR_LIT>']<EOL>error_count = context['<STR_LIT>']<EOL>transformations_count = context['<STR_LIT>']<EOL>hub_data = context['<STR_LIT>']<EOL>disease_data = context['<STR_LIT>']<EOL>authors_count = context['<STR_LIT>']<EOL>variants_count = context['<STR_LIT>']<EOL>namespaces_count = context['<STR_LIT>']<EOL>confidence_count = context['<STR_LIT>']<EOL>confidence_data = [<EOL>(label, confidence_count.get(label, <NUM_LIT:0>))<EOL>for label in ('<STR_LIT:None>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>')<EOL>]<EOL>template = environment.get_template('<STR_LIT>')<EOL>return template.render(<EOL>graph=graph,<EOL>chart_1_data=prepare_c3(function_count, '<STR_LIT>'),<EOL>chart_6_data=prepare_c3(namespaces_count, '<STR_LIT>'),<EOL>chart_5_data=prepare_c3(variants_count, '<STR_LIT>'),<EOL>number_variants=sum(variants_count.values()),<EOL>number_namespaces=len(namespaces_count),<EOL>chart_2_data=prepare_c3(relation_count, '<STR_LIT>'),<EOL>chart_4_data=prepare_c3(transformations_count, '<STR_LIT>') if transformations_count else None,<EOL>number_transformations=sum(transformations_count.values()),<EOL>chart_3_data=prepare_c3(error_count, '<STR_LIT>') if error_count else None,<EOL>chart_7_data=prepare_c3(hub_data, '<STR_LIT>'),<EOL>chart_9_data=prepare_c3(disease_data, '<STR_LIT>') if disease_data else None,<EOL>chart_authors_count=prepare_c3(authors_count, '<STR_LIT>'),<EOL>chart_10_data=prepare_c3_time_series(citation_years, '<STR_LIT>') if citation_years else None,<EOL>chart_confidence_count=prepare_c3(confidence_data, '<STR_LIT>'),<EOL>summary_dict=summary_dict,<EOL>**context<EOL>)<EOL>

Render the graph as an HTML string.

    Common usage may involve writing to a file like:

    >>> from pybel.examples import sialic_acid_graph
    >>> with open('html_output.html', 'w') as file:
    ...     print(to_html(sialic_acid_graph), file=file)


def get_network_summary_dict(graph: BELGraph) -> Mapping:

return dict(<EOL>function_count=count_functions(graph),<EOL>modifications_count=get_modifications_count(graph),<EOL>relation_count=count_relations(graph),<EOL>authors_count=count_authors(graph).most_common(<NUM_LIT:15>),<EOL>variants_count=count_variants(graph),<EOL>namespaces_count=count_namespaces(graph),<EOL>hub_data={<EOL>(<EOL>node.name or node.identifier<EOL>if NAME in node or IDENTIFIER in node else<EOL>str(node)<EOL>): degree<EOL>for node, degree in get_top_hubs(graph, n=<NUM_LIT:15>)<EOL>},<EOL>disease_data={<EOL>(<EOL>node.name or node.identifier<EOL>if NAME in node or IDENTIFIER in node else<EOL>str(node)<EOL>): count<EOL>for node, count in get_top_pathologies(graph, n=<NUM_LIT:15>)<EOL>},<EOL>regulatory_pairs=[<EOL>get_pair_tuple(u, v)<EOL>for u, v in get_regulatory_pairs(graph)<EOL>],<EOL>unstable_pairs=list(itt.chain(<EOL>(get_pair_tuple(u, v) + ('<STR_LIT>',) for u, v, in get_chaotic_pairs(graph)),<EOL>(get_pair_tuple(u, v) + ('<STR_LIT>',) for u, v, in get_dampened_pairs(graph)),<EOL>)),<EOL>contradictory_pairs=[<EOL>get_pair_tuple(u, v) + (relation,)<EOL>for u, v, relation in get_contradiction_summary(graph)<EOL>],<EOL>contradictory_triplets=list(itt.chain(<EOL>(get_triplet_tuple(a, b, c) + ('<STR_LIT>',) for a, b, c in<EOL>get_separate_unstable_correlation_triples(graph)),<EOL>(get_triplet_tuple(a, b, c) + ('<STR_LIT>',) for a, b, c in get_mutually_unstable_correlation_triples(graph)),<EOL>(get_triplet_tuple(a, b, c) + ('<STR_LIT>',) for a, b, c in get_jens_unstable(graph)),<EOL>(get_triplet_tuple(a, b, c) + ('<STR_LIT>',) for a, b, c in get_increase_mismatch_triplets(graph)),<EOL>(get_triplet_tuple(a, b, c) + ('<STR_LIT>',) for a, b, c in get_decrease_mismatch_triplets(graph)),<EOL>)),<EOL>unstable_triplets=list(itt.chain(<EOL>(get_triplet_tuple(a, b, c) + ('<STR_LIT>',) for a, b, c in get_chaotic_triplets(graph)),<EOL>(get_triplet_tuple(a, b, c) + ('<STR_LIT>',) for a, b, c in get_dampened_triplets(graph)),<EOL>)),<EOL>causal_pathologies=sorted({<EOL>get_pair_tuple(u, v) + (graph[u][v][k][RELATION],)<EOL>for u, v, k in filter_edges(graph, has_pathology_causal)<EOL>}),<EOL>undefined_namespaces=get_undefined_namespaces(graph),<EOL>undefined_annotations=get_undefined_annotations(graph),<EOL>namespaces_with_incorrect_names=get_namespaces_with_incorrect_names(graph),<EOL>unused_namespaces=get_unused_namespaces(graph),<EOL>unused_annotations=get_unused_annotations(graph),<EOL>unused_list_annotation_values=get_unused_list_annotation_values(graph),<EOL>naked_names=get_naked_names(graph),<EOL>error_count=count_error_types(graph),<EOL>error_groups=get_most_common_errors(graph),<EOL>syntax_errors=get_syntax_errors(graph),<EOL>citation_years=get_citation_years(graph),<EOL>confidence_count=count_confidences(graph),<EOL>)<EOL>


def get_pair_tuple(a: BaseEntity, b: BaseEntity) -> Tuple[str, str, str, str]:

return a.as_bel(), a.sha512, b.as_bel(), b.sha512<EOL>


def get_triplet_tuple(a: BaseEntity, b: BaseEntity, c: BaseEntity) -> Tuple[str, str, str, str, str, str]:

return a.as_bel(), a.sha512, b.as_bel(), b.sha512, c.as_bel(), c.sha512<EOL>

Get the triple as a tuple of BEL/hashes.


def to_jupyter(graph: BELGraph, chart: Optional[str] = None) -> Javascript:

with open(os.path.join(HERE, '<STR_LIT>'), '<STR_LIT>') as f:<EOL><INDENT>js_template = Template(f.read())<EOL><DEDENT>return Javascript(js_template.render(**_get_context(graph, chart=chart)))<EOL>

Render the graph as JavaScript in a Jupyter Notebook.


def to_html(graph: BELGraph, chart: Optional[str] = None) -> str:

with open(os.path.join(HERE, '<STR_LIT>'), '<STR_LIT>') as f:<EOL><INDENT>html_template = Template(f.read())<EOL><DEDENT>return html_template.render(**_get_context(graph, chart=chart))<EOL>

Render the graph as an HTML string.

    Common usage may involve writing to a file like:

    >>> from pybel.examples import sialic_acid_graph
    >>> with open('ideogram_output.html', 'w') as file:
    ...     print(to_html(sialic_acid_graph), file=file)


return '<STR_LIT>'.join(random.sample('<STR_LIT>', <NUM_LIT:16>))<EOL>


def prerender(graph: BELGraph) -> Mapping[str, Mapping[str, Any]]:

import bio2bel_hgnc<EOL>from bio2bel_hgnc.models import HumanGene<EOL>graph: BELGraph = graph.copy()<EOL>enrich_protein_and_rna_origins(graph)<EOL>collapse_all_variants(graph)<EOL>genes: Set[Gene] = get_nodes_by_function(graph, GENE)<EOL>hgnc_symbols = {<EOL>gene.name<EOL>for gene in genes<EOL>if gene.namespace.lower() == '<STR_LIT>'<EOL>}<EOL>result = {}<EOL>hgnc_manager = bio2bel_hgnc.Manager()<EOL>human_genes = (<EOL>hgnc_manager.session<EOL>.query(HumanGene.symbol, HumanGene.location)<EOL>.filter(HumanGene.symbol.in_(hgnc_symbols))<EOL>.all()<EOL>)<EOL>for human_gene in human_genes:<EOL><INDENT>result[human_gene.symbol] = {<EOL>'<STR_LIT:name>': human_gene.symbol,<EOL>'<STR_LIT>': (<EOL>human_gene.location.split('<STR_LIT:q>')[<NUM_LIT:0>]<EOL>if '<STR_LIT:q>' in human_gene.location else<EOL>human_gene.location.split('<STR_LIT:p>')[<NUM_LIT:0>]<EOL>),<EOL>}<EOL><DEDENT>df = get_df()<EOL>for _, (gene_id, symbol, start, stop) in df[df['<STR_LIT>'].isin(hgnc_symbols)].iterrows():<EOL><INDENT>result[symbol]['<STR_LIT:start>'] = start<EOL>result[symbol]['<STR_LIT>'] = stop<EOL><DEDENT>return result<EOL>

Generate the annotations JSON for Ideogram.


return pd.read_csv(os.path.join(HERE, '<STR_LIT>'))<EOL>


@transformation<EOL>def get_subgraph_by_node_filter(graph: BELGraph, node_predicates: NodePredicates) -> BELGraph:

return get_subgraph_by_induction(graph, filter_nodes(graph, node_predicates))<EOL>

Induce a sub-graph on the nodes that pass the given predicate(s).


@transformation<EOL>def get_causal_subgraph(graph: BELGraph) -> BELGraph:

return get_subgraph_by_edge_filter(graph, is_causal_relation)<EOL>

Build a new sub-graph induced over the causal edges.


@transformation<EOL>def get_subgraph_by_node_search(graph: BELGraph, query: Strings) -> BELGraph:

nodes = search_node_names(graph, query)<EOL>return get_subgraph_by_induction(graph, nodes)<EOL>

Get a sub-graph induced over all nodes matching the query string.

    :param graph: A BEL Graph
    :param query: A query string or iterable of query strings for node names

    Thinly wraps :func:`search_node_names` and :func:`get_subgraph_by_induction`.


@transformation<EOL>def get_largest_component(graph: BELGraph) -> BELGraph:

biggest_component_nodes = max(nx.weakly_connected_components(graph), key=len)<EOL>return subgraph(graph, biggest_component_nodes)<EOL>


return filter_nodes(graph, build_node_name_search(query))<EOL>

Search for nodes containing a given string(s).

    :param pybel.BELGraph graph: A BEL graph
    :param query: The search query
    :type query: str or iter[str]
    :return: An iterator over nodes whose names match the search query
    :rtype: iter

    Example:

    .. code-block:: python

        >>> from pybel.examples import sialic_acid_graph
        >>> from pybel_tools.selection import search_node_names
        >>> list(search_node_names(sialic_acid_graph, 'CD33'))
        [('Protein', 'HGNC', 'CD33'), ('Protein', 'HGNC', 'CD33', ('pmod', ('bel', 'Ph')))]


def search_node_namespace_names(graph, query, namespace):

node_predicates = [<EOL>namespace_inclusion_builder(namespace),<EOL>build_node_name_search(query)<EOL>]<EOL>return filter_nodes(graph, node_predicates)<EOL>

Search for nodes with the given namespace(s) and whose names containing a given string(s).

    :param pybel.BELGraph graph: A BEL graph
    :param query: The search query
    :type query: str or iter[str]
    :param namespace: The namespace(s) to filter
    :type namespace: str or iter[str]
    :return: An iterator over nodes whose names match the search query
    :rtype: iter


return search_node_namespace_names(graph, query, namespace='<STR_LIT>')<EOL>

Search for nodes with the HGNC namespace and whose names containing a given string(s).

    :param pybel.BELGraph graph: A BEL graph
    :param query: The search query
    :type query: str or iter[str]
    :return: An iterator over nodes whose names match the search query
    :rtype: iter


a, b = itt.tee(iterable)<EOL>next(b, None)<EOL>return zip(a, b)<EOL>


def rank_path(graph, path, edge_ranking=None):

edge_ranking = default_edge_ranking if edge_ranking is None else edge_ranking<EOL>return sum(max(edge_ranking[d[RELATION]] for d in graph.edge[u][v].values()) for u, v in pairwise(path))<EOL>

Takes in a path (a list of nodes in the graph) and calculates a score

    :param pybel.BELGraph graph: A BEL graph
    :param list[tuple] path: A list of nodes in the path (includes terminal nodes)
    :param dict edge_ranking: A dictionary of {relationship: score}
    :return: The score for the edge
    :rtype: int


path_graph = graph.subgraph(path_nodes)<EOL>node_in_degree_tuple = sorted([(n, d) for n, d in path_graph.in_degree().items()], key=itemgetter(<NUM_LIT:1>))<EOL>node_out_degree_tuple = sorted([(n, d) for n, d in path_graph.out_degree().items()], key=itemgetter(<NUM_LIT:1>),<EOL>reverse=True)<EOL>tied_root_index = <NUM_LIT:0><EOL>for i in range(<NUM_LIT:0>, (len(node_in_degree_tuple) - <NUM_LIT:1>)):<EOL><INDENT>if node_in_degree_tuple[i][<NUM_LIT:1>] < node_in_degree_tuple[i + <NUM_LIT:1>][<NUM_LIT:1>]:<EOL><INDENT>tied_root_index = i<EOL>break<EOL><DEDENT><DEDENT>if tied_root_index != <NUM_LIT:0>:<EOL><INDENT>root_tuple = max(node_out_degree_tuple[:tied_root_index], key=itemgetter(<NUM_LIT:1>))<EOL><DEDENT>else:<EOL><INDENT>root_tuple = node_in_degree_tuple[<NUM_LIT:0>]<EOL><DEDENT>return path_graph, root_tuple[<NUM_LIT:0>]<EOL>

Find the 'root' of the path -> The node with the lowest out degree, if multiple:
         root is the one with the highest out degree among those with lowest out degree

    :param pybel.BELGraph graph: A BEL Graph
    :param list[tuple] path_nodes: A list of nodes in their order in a path
    :return: A pair of the graph: graph of the path and the root node
    :rtype: tuple[pybel.BELGraph,tuple]


return [<EOL>graph.node[node][FUNCTION]<EOL>for node in nodes<EOL>]<EOL>

Converts a list of nodes to their corresponding functions

    :param list[tuple] nodes: A list of BEL node tuples
    :rtype: list[str]


@lru_cache(maxsize=None)<EOL>def get_walks_exhaustive(graph, node, length):

if <NUM_LIT:0> == length:<EOL><INDENT>return (node,),<EOL><DEDENT>return tuple(<EOL>(node, key) + path<EOL>for neighbor in graph.edge[node]<EOL>for path in get_walks_exhaustive(graph, neighbor, length - <NUM_LIT:1>)<EOL>if node not in path<EOL>for key in graph.edge[node][neighbor]<EOL>)<EOL>

Gets all walks under a given length starting at a given node

    :param networkx.Graph graph: A graph
    :param node: Starting node
    :param int length: The length of walks to get
    :return: A list of paths
    :rtype: list[tuple]


def match_simple_metapath(graph, node, simple_metapath):

if <NUM_LIT:0> == len(simple_metapath):<EOL><INDENT>yield node,<EOL><DEDENT>else:<EOL><INDENT>for neighbor in graph.edges[node]:<EOL><INDENT>if graph.nodes[neighbor][FUNCTION] == simple_metapath[<NUM_LIT:0>]:<EOL><INDENT>for path in match_simple_metapath(graph, neighbor, simple_metapath[<NUM_LIT:1>:]):<EOL><INDENT>if node not in path:<EOL><INDENT>yield (node,) + path<EOL><DEDENT><DEDENT><DEDENT><DEDENT><DEDENT>

Matches a simple metapath starting at the given node

    :param pybel.BELGraph graph: A BEL graph
    :param tuple node: A BEL node
    :param list[str] simple_metapath: A list of BEL Functions
    :return: An iterable over paths from the node matching the metapath
    :rtype: iter[tuple]


return [<EOL>graph.nodes[node][FUNCTION]<EOL>for node in simple_walk<EOL>]<EOL>

Converts a walk into a sequence of BEL functions

    :param pybel.BELGraph graph: A BEL graph
    :param iter[tuple] simple_walk: An iterable of BEL nodes
    :return: A list of BEL functions of the walk
    :rtype: list[str]


def match_complex_metapath(graph, node, complex_metapath):

raise NotImplementedError<EOL>

Matches a complex metapath starting at the given node

    :param pybel.BELGraph graph: A BEL graph
    :param tuple node: A BEL node
    :param list[str] complex_metapath: An iterable of alternating BEL nodes and relations
    :return: An iterable over paths from the node matching the metapath
    :rtype: iter[tuple]


raise NotImplementedError<EOL>

Converts a walk into an alternative sequence of BEL functions and BEL relations, starting and ending
    with a BEL function

    :param pybel.BELGraph graph: A BEL graph
    :param iter[tuple] complex_walk: An iterable of alternating BEL nodes and relations
    :return: An alternating list of BEL functions and relations of the walk
    :rtype: list[str]


def get_leaves_by_type(graph, func=None, prune_threshold=<NUM_LIT:1>):

for node, data in graph.nodes(data=True):<EOL><INDENT>if func and func != data.get(FUNCTION):<EOL><INDENT>continue<EOL><DEDENT>if graph.in_degree(node) + graph.out_degree(node) <= prune_threshold:<EOL><INDENT>yield node<EOL><DEDENT><DEDENT>

Returns an iterable over all nodes in graph (in-place) with only a connection to one node. Useful for gene and
     RNA. Allows for optional filter by function type.

    :param pybel.BELGraph graph: A BEL graph
    :param func: If set, filters by the node's function from :mod:`pybel.constants` like
                    :data:`pybel.constants.GENE`, :data:`pybel.constants.RNA`,  :data:`pybel.constants.PROTEIN`, or
                    :data:`pybel.constants.BIOPROCESS`
    :type func: str
    :param prune_threshold: Removes nodes with less than or equal to this number of connections. Defaults to :code:`1`
    :type prune_threshold: int
    :return: An iterable over nodes with only a connection to one node
    :rtype: iter[tuple]


def group_nodes_by_annotation(graph: BELGraph, annotation: str = '<STR_LIT>') -> Mapping[str, Set[BaseEntity]]:

result = defaultdict(set)<EOL>for u, v, d in graph.edges(data=True):<EOL><INDENT>if not edge_has_annotation(d, annotation):<EOL><INDENT>continue<EOL><DEDENT>result[d[ANNOTATIONS][annotation]].add(u)<EOL>result[d[ANNOTATIONS][annotation]].add(v)<EOL><DEDENT>return dict(result)<EOL>

Group the nodes occurring in edges by the given annotation.


def average_node_annotation(graph: BELGraph,<EOL>key: str,<EOL>annotation: str = '<STR_LIT>',<EOL>aggregator: Optional[Callable[[Iterable[X]], X]] = None,<EOL>) -> Mapping[str, X]:

if aggregator is None:<EOL><INDENT>def aggregator(x):<EOL><INDENT>"""<STR_LIT>"""<EOL>return sum(x) / len(x)<EOL><DEDENT><DEDENT>result = {}<EOL>for subgraph, nodes in group_nodes_by_annotation(graph, annotation).items():<EOL><INDENT>values = [graph.nodes[node][key] for node in nodes if key in graph.nodes[node]]<EOL>result[subgraph] = aggregator(values)<EOL><DEDENT>return result<EOL>

Groups graph into subgraphs and assigns each subgraph a score based on the average of all nodes values
    for the given node key

    :param pybel.BELGraph graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data
    :param annotation: A BEL annotation to use to group nodes
    :param aggregator: A function from list of values -> aggregate value. Defaults to taking the average of a list of
                       floats.
    :type aggregator: lambda


def group_nodes_by_annotation_filtered(graph: BELGraph,<EOL>node_predicates: NodePredicates = None,<EOL>annotation: str = '<STR_LIT>',<EOL>) -> Mapping[str, Set[BaseEntity]]:

node_filter = concatenate_node_predicates(node_predicates)<EOL>return {<EOL>key: {<EOL>node<EOL>for node in nodes<EOL>if node_filter(graph, node)<EOL>}<EOL>for key, nodes in group_nodes_by_annotation(graph, annotation).items()<EOL>}<EOL>

Group the nodes occurring in edges by the given annotation, with a node filter applied.

    :param graph: A BEL graph
    :param node_predicates: A predicate or list of predicates (graph, node) -> bool
    :param annotation: The annotation to use for grouping
    :return: A dictionary of {annotation value: set of nodes}


def get_mapped_nodes(graph: BELGraph, namespace: str, names: Iterable[str]) -> Mapping[BaseEntity, Set[BaseEntity]]:

parent_to_variants = defaultdict(set)<EOL>names = set(names)<EOL>for u, v, d in graph.edges(data=True):<EOL><INDENT>if d[RELATION] in {HAS_MEMBER, HAS_COMPONENT} and v.get(NAMESPACE) == namespace and v.get(NAME) in names:<EOL><INDENT>parent_to_variants[v].add(u)<EOL><DEDENT>elif d[RELATION] == HAS_VARIANT and u.get(NAMESPACE) == namespace and u.get(NAME) in names:<EOL><INDENT>parent_to_variants[u].add(v)<EOL><DEDENT>elif d[RELATION] == ORTHOLOGOUS and u.get(NAMESPACE) == namespace and u.get(NAME) in names:<EOL><INDENT>parent_to_variants[u].add(v)<EOL><DEDENT><DEDENT>return dict(parent_to_variants)<EOL>

Return a dict with keys: nodes that match the namespace and in names and values other nodes (complexes, variants, orthologous...) or this node.

    :param graph: A BEL graph
    :param namespace: The namespace to search
    :param names: List or set of values from which we want to map nodes from
    :return: Main node to variants/groups.


if not graph.has_edge(u, v):<EOL><INDENT>raise ValueError('<STR_LIT>'.format(graph, u, v))<EOL><DEDENT>return <NUM_LIT:0> == len(set(d[RELATION] for d in graph.edge[u][v].values()))<EOL>

Check if all edges between two nodes have the same relation.

    :param pybel.BELGraph graph: A BEL Graph
    :param tuple u: The source BEL node
    :param tuple v: The target BEL node
    :return: If all edges from the source to target node have the same relation
    :rtype: bool


return all(<EOL>is_edge_consistent(graph, u, v)<EOL>for u, v in graph.edges()<EOL>)<EOL>

Return if all edges are consistent in a graph. Wraps :func:`pybel_tools.utils.is_edge_consistent`.

    :param pybel.BELGraph graph: A BEL graph
    :return: Are all edges consistent
    :rtype: bool


if not all_edges_consistent(graph):<EOL><INDENT>raise ValueError('<STR_LIT>'.format(graph))<EOL><DEDENT>result = graph.copy()<EOL>nodes = result.nodes()<EOL>for u, v in result.edges():<EOL><INDENT>if random.random() < rewiring_probability:<EOL><INDENT>continue<EOL><DEDENT>w = random.choice(nodes)<EOL>while w == u or result.has_edge(u, w):<EOL><INDENT>w = random.choice(nodes)<EOL><DEDENT>result.add_edge(w, v)<EOL>result.remove_edge(u, v)<EOL><DEDENT>return result<EOL>

Rewire a graph's edges' target nodes.

    - For BEL graphs, assumes edge consistency (all edges between two given nodes are have the same relation)
    - Doesn't make self-edges

    :param pybel.BELGraph graph: A BEL graph
    :param float rewiring_probability: The probability of rewiring (between 0 and 1)
    :return: A rewired BEL graph


resources = {location: get_bel_resource(location) for location in locations}<EOL>if check_keywords:<EOL><INDENT>resource_keywords = set(config['<STR_LIT>']['<STR_LIT>'] for config in resources.values())<EOL>if <NUM_LIT:1> != len(resource_keywords):<EOL><INDENT>raise ValueError('<STR_LIT>'.format(resource_keywords))<EOL><DEDENT><DEDENT>result = {}<EOL>for resource in resources:<EOL><INDENT>result.update(resource['<STR_LIT>'])<EOL><DEDENT>return result<EOL>

Loads many namespaces and combines their names.

    :param iter[str] locations: An iterable of URLs or file paths pointing to BEL namespaces.
    :param bool check_keywords: Should all the keywords be the same? Defaults to ``True``
    :return: A dictionary of {names: labels}
    :rtype: dict[str, str]

    Example Usage

    >>> from pybel.resources import write_namespace
    >>> from pybel_tools.definition_utils import export_namespace, get_merged_namespace_names
    >>> graph = ...
    >>> original_ns_url = ...
    >>> export_namespace(graph, 'MBS') # Outputs in current directory to MBS.belns
    >>> value_dict = get_merged_namespace_names([original_ns_url, 'MBS.belns'])
    >>> with open('merged_namespace.belns', 'w') as f:
    >>> ...  write_namespace('MyBrokenNamespace', 'MBS', 'Other', 'Charles Hoyt', 'PyBEL Citation', value_dict, file=f)


def merge_namespaces(input_locations, output_path, namespace_name, namespace_keyword, namespace_domain, author_name,<EOL>citation_name, namespace_description=None, namespace_species=None, namespace_version=None,<EOL>namespace_query_url=None, namespace_created=None, author_contact=None, author_copyright=None,<EOL>citation_description=None, citation_url=None, citation_version=None, citation_date=None,<EOL>case_sensitive=True, delimiter='<STR_LIT:|>', cacheable=True, functions=None, value_prefix='<STR_LIT>',<EOL>sort_key=None, check_keywords=True):

Merges namespaces from multiple locations to one.

    :param iter input_locations: An iterable of URLs or file paths pointing to BEL namespaces.
    :param str output_path: The path to the file to write the merged namespace
    :param str namespace_name: The namespace name
    :param str namespace_keyword: Preferred BEL Keyword, maximum length of 8
    :param str namespace_domain: One of: :data:`pybel.constants.NAMESPACE_DOMAIN_BIOPROCESS`,
                            :data:`pybel.constants.NAMESPACE_DOMAIN_CHEMICAL`,
                            :data:`pybel.constants.NAMESPACE_DOMAIN_GENE`, or
                            :data:`pybel.constants.NAMESPACE_DOMAIN_OTHER`
    :param str author_name: The namespace's authors
    :param str citation_name: The name of the citation
    :param str namespace_query_url: HTTP URL to query for details on namespace values (must be valid URL)
    :param str namespace_description: Namespace description
    :param str namespace_species: Comma-separated list of species taxonomy id's
    :param str namespace_version: Namespace version
    :param str namespace_created: Namespace public timestamp, ISO 8601 datetime
    :param str author_contact: Namespace author's contact info/email address
    :param str author_copyright: Namespace's copyright/license information
    :param str citation_description: Citation description
    :param str citation_url: URL to more citation information
    :param str citation_version: Citation version
    :param str citation_date: Citation publish timestamp, ISO 8601 Date
    :param bool case_sensitive: Should this config file be interpreted as case-sensitive?
    :param str delimiter: The delimiter between names and labels in this config file
    :param bool cacheable: Should this config file be cached?
    :param functions: The encoding for the elements in this namespace
    :type functions: iterable of characters
    :param str value_prefix: a prefix for each name
    :param sort_key: A function to sort the values with :func:`sorted`
    :param bool check_keywords: Should all the keywords be the same? Defaults to ``True``


def export_namespace(graph, namespace, directory=None, cacheable=False):

directory = os.getcwd() if directory is None else directory<EOL>path = os.path.join(directory, '<STR_LIT>'.format(namespace))<EOL>with open(path, '<STR_LIT:w>') as file:<EOL><INDENT>log.info('<STR_LIT>', path)<EOL>right_names = get_names_by_namespace(graph, namespace)<EOL>log.info('<STR_LIT>', len(right_names), namespace)<EOL>wrong_names = get_incorrect_names_by_namespace(graph, namespace)<EOL>log.info('<STR_LIT>', len(right_names), namespace)<EOL>undefined_ns_names = get_undefined_namespace_names(graph, namespace)<EOL>log.info('<STR_LIT>', len(right_names), namespace)<EOL>names = (right_names | wrong_names | undefined_ns_names)<EOL>if <NUM_LIT:0> == len(names):<EOL><INDENT>log.warning('<STR_LIT>', namespace)<EOL><DEDENT>write_namespace(<EOL>namespace_name=namespace,<EOL>namespace_keyword=namespace,<EOL>namespace_domain='<STR_LIT>',<EOL>author_name=graph.authors,<EOL>author_contact=graph.contact,<EOL>citation_name=graph.name,<EOL>values=names,<EOL>cacheable=cacheable,<EOL>file=file<EOL>)<EOL><DEDENT>

Exports all names and missing names from the given namespace to its own BEL Namespace files in the given
    directory.

    Could be useful during quick and dirty curation, where planned namespace building is not a priority.

    :param pybel.BELGraph graph: A BEL graph
    :param str namespace: The namespace to process
    :param str directory: The path to the directory where to output the namespace. Defaults to the current working
                      directory returned by :func:`os.getcwd`
    :param bool cacheable: Should the namespace be cacheable? Defaults to ``False`` because, in general, this operation
                        will probably be used for evil, and users won't want to reload their entire cache after each
                        iteration of curation.


def export_namespaces(graph, namespaces, directory=None, cacheable=False):

directory = os.getcwd() if directory is None else directory  <EOL>for namespace in namespaces:<EOL><INDENT>export_namespace(graph, namespace, directory=directory, cacheable=cacheable)<EOL><DEDENT>

Thinly wraps :func:`export_namespace` for an iterable of namespaces.

    :param pybel.BELGraph graph: A BEL graph
    :param iter[str] namespaces: An iterable of strings for the namespaces to process
    :param str directory: The path to the directory where to output the namespaces. Defaults to the current working
                      directory returned by :func:`os.getcwd`
    :param bool cacheable: Should the namespaces be cacheable? Defaults to ``False`` because, in general, this operation
                        will probably be used for evil, and users won't want to reload their entire cache after each
                        iteration of curation.


def get_peripheral_successor_edges(graph: BELGraph, subgraph: BELGraph) -> EdgeIterator:

for u in subgraph:<EOL><INDENT>for _, v, k in graph.out_edges(u, keys=True):<EOL><INDENT>if v not in subgraph:<EOL><INDENT>yield u, v, k<EOL><DEDENT><DEDENT><DEDENT>

Get the set of possible successor edges peripheral to the sub-graph.

    The source nodes in this iterable are all inside the sub-graph, while the targets are outside.


def get_peripheral_predecessor_edges(graph: BELGraph, subgraph: BELGraph) -> EdgeIterator:

for v in subgraph:<EOL><INDENT>for u, _, k in graph.in_edges(v, keys=True):<EOL><INDENT>if u not in subgraph:<EOL><INDENT>yield u, v, k<EOL><DEDENT><DEDENT><DEDENT>

Get the set of possible predecessor edges peripheral to the sub-graph.

    The target nodes in this iterable are all inside the sub-graph, while the sources are outside.


def count_sources(edge_iter: EdgeIterator) -> Counter:

return Counter(u for u, _, _ in edge_iter)<EOL>

Count the source nodes in an edge iterator with keys and data.

    :return: A counter of source nodes in the iterable


def count_targets(edge_iter: EdgeIterator) -> Counter:

return Counter(v for _, v, _ in edge_iter)<EOL>

Count the target nodes in an edge iterator with keys and data.

    :return: A counter of target nodes in the iterable


def count_possible_successors(graph: BELGraph, subgraph: BELGraph) -> Counter:

return count_targets(get_peripheral_successor_edges(graph, subgraph))<EOL>

:param graph: A BEL graph
:param subgraph: An iterator of BEL nodes
:return: A counter of possible successor nodes


def count_possible_predecessors(graph: BELGraph, subgraph: BELGraph) -> Counter:

return count_sources(get_peripheral_predecessor_edges(graph, subgraph))<EOL>

:param graph: A BEL graph
:param subgraph: An iterator of BEL nodes
:return: A counter of possible predecessor nodes


def get_subgraph_edges(graph: BELGraph,<EOL>annotation: str,<EOL>value: str,<EOL>source_filter=None,<EOL>target_filter=None,<EOL>):

if source_filter is None:<EOL><INDENT>source_filter = keep_node_permissive<EOL><DEDENT>if target_filter is None:<EOL><INDENT>target_filter = keep_node_permissive<EOL><DEDENT>for u, v, k, data in graph.edges(keys=True, data=True):<EOL><INDENT>if not edge_has_annotation(data, annotation):<EOL><INDENT>continue<EOL><DEDENT>if data[ANNOTATIONS][annotation] == value and source_filter(graph, u) and target_filter(graph, v):<EOL><INDENT>yield u, v, k, data<EOL><DEDENT><DEDENT>

Gets all edges from a given subgraph whose source and target nodes pass all of the given filters

    :param pybel.BELGraph graph: A BEL graph
    :param str annotation:  The annotation to search
    :param str value: The annotation value to search by
    :param source_filter: Optional filter for source nodes (graph, node) -> bool
    :param target_filter: Optional filter for target nodes (graph, node) -> bool
    :return: An iterable of (source node, target node, key, data) for all edges that match the annotation/value and
             node filters
    :rtype: iter[tuple]


def get_subgraph_peripheral_nodes(graph: BELGraph,<EOL>subgraph: Iterable[BaseEntity],<EOL>node_predicates: NodePredicates = None,<EOL>edge_predicates: EdgePredicates = None,<EOL>):

node_filter = concatenate_node_predicates(node_predicates=node_predicates)<EOL>edge_filter = and_edge_predicates(edge_predicates=edge_predicates)<EOL>result = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))<EOL>for u, v, k, d in get_peripheral_successor_edges(graph, subgraph):<EOL><INDENT>if not node_filter(graph, v) or not node_filter(graph, u) or not edge_filter(graph, u, v, k):<EOL><INDENT>continue<EOL><DEDENT>result[v]['<STR_LIT>'][u].append((k, d))<EOL><DEDENT>for u, v, k, d in get_peripheral_predecessor_edges(graph, subgraph):<EOL><INDENT>if not node_filter(graph, v) or not node_filter(graph, u) or not edge_filter(graph, u, v, k):<EOL><INDENT>continue<EOL><DEDENT>result[u]['<STR_LIT>'][v].append((k, d))<EOL><DEDENT>return result<EOL>

Get a summary dictionary of all peripheral nodes to a given sub-graph.

    :return: A dictionary of {external node: {'successor': {internal node: list of (key, dict)},
                                            'predecessor': {internal node: list of (key, dict)}}}
    :rtype: dict

    For example, it might be useful to quantify the number of predecessors and successors:

    >>> from pybel.struct.filters import exclude_pathology_filter
    >>> value = 'Blood vessel dilation subgraph'
    >>> sg = get_subgraph_by_annotation_value(graph, annotation='Subgraph', value=value)
    >>> p = get_subgraph_peripheral_nodes(graph, sg, node_predicates=exclude_pathology_filter)
    >>> for node in sorted(p, key=lambda n: len(set(p[n]['successor']) | set(p[n]['predecessor'])), reverse=True):
    >>>     if 1 == len(p[value][node]['successor']) or 1 == len(p[value][node]['predecessor']):
    >>>         continue
    >>>     print(node,
    >>>           len(p[node]['successor']),
    >>>           len(p[node]['predecessor']),
    >>>           len(set(p[node]['successor']) | set(p[node]['predecessor'])))


@uni_in_place_transformation<EOL>def expand_periphery(universe: BELGraph,<EOL>graph: BELGraph,<EOL>node_predicates: NodePredicates = None,<EOL>edge_predicates: EdgePredicates = None,<EOL>threshold: int = <NUM_LIT:2>,<EOL>) -> None:

nd = get_subgraph_peripheral_nodes(universe, graph, node_predicates=node_predicates,<EOL>edge_predicates=edge_predicates)<EOL>for node, dd in nd.items():<EOL><INDENT>pred_d = dd['<STR_LIT>']<EOL>succ_d = dd['<STR_LIT>']<EOL>in_subgraph_connections = set(pred_d) | set(succ_d)<EOL>if threshold > len(in_subgraph_connections):<EOL><INDENT>continue<EOL><DEDENT>graph.add_node(node, attr_dict=universe[node])<EOL>for u, edges in pred_d.items():<EOL><INDENT>for k, d in edges:<EOL><INDENT>safe_add_edge(graph, u, node, k, d)<EOL><DEDENT><DEDENT>for v, edges in succ_d.items():<EOL><INDENT>for k, d in edges:<EOL><INDENT>safe_add_edge(graph, node, v, k, d)<EOL><DEDENT><DEDENT><DEDENT>

Iterates over all possible edges, peripheral to a given subgraph, that could be added from the given graph.
    Edges could be added if they go to nodes that are involved in relationships that occur with more than the
    threshold (default 2) number of nodes in the subgraph.

    :param universe: The universe of BEL knowledge
    :param graph: The (sub)graph to expand
    :param threshold: Minimum frequency of betweenness occurrence to add a gap node

    A reasonable edge filter to use is :func:`pybel_tools.filters.keep_causal_edges` because this function can allow
    for huge expansions if there happen to be hub nodes.


@uni_in_place_transformation<EOL>def enrich_complexes(graph: BELGraph) -> None:

nodes = list(get_nodes_by_function(graph, COMPLEX))<EOL>for u in nodes:<EOL><INDENT>for v in u.members:<EOL><INDENT>graph.add_has_component(u, v)<EOL><DEDENT><DEDENT>

Add all of the members of the complex abundances to the graph.


nodes = list(get_nodes_by_function(graph, COMPOSITE))<EOL>for u in nodes:<EOL><INDENT>for v in u.members:<EOL><INDENT>graph.add_has_component(u, v)<EOL><DEDENT><DEDENT>

Adds all of the members of the composite abundances to the graph.


nodes = list(get_nodes_by_function(graph, REACTION))<EOL>for u in nodes:<EOL><INDENT>for v in u.reactants:<EOL><INDENT>graph.add_has_reactant(u, v)<EOL><DEDENT>for v in u.products:<EOL><INDENT>graph.add_has_product(u, v)<EOL><DEDENT><DEDENT>

Adds all of the reactants and products of reactions to the graph.


@uni_in_place_transformation<EOL>def enrich_variants(graph: BELGraph, func: Union[None, str, Iterable[str]] = None):

if func is None:<EOL><INDENT>func = {PROTEIN, RNA, MIRNA, GENE}<EOL><DEDENT>nodes = list(get_nodes_by_function(graph, func))<EOL>for u in nodes:<EOL><INDENT>parent = u.get_parent()<EOL>if parent is None:<EOL><INDENT>continue<EOL><DEDENT>if parent not in graph:<EOL><INDENT>graph.add_has_variant(parent, u)<EOL><DEDENT><DEDENT>

Add the reference nodes for all variants of the given function.

    :param graph: The target BEL graph to enrich
    :param func: The function by which the subject of each triple is filtered. Defaults to the set of protein, rna,
     mirna, and gene.


enrich_complexes(graph)<EOL>enrich_composites(graph)<EOL>enrich_reactions(graph)<EOL>enrich_variants(graph)<EOL>

Enrich the sub-graph with the unqualified edges from the graph.

    The reason you might want to do this is you induce a sub-graph from the original graph based on an annotation
    filter, but the unqualified edges that don't have annotations that most likely connect elements within your graph
    are not included.

    .. seealso::

        This function thinly wraps the successive application of the following functions:

        - :func:`enrich_complexes`
        - :func:`enrich_composites`
        - :func:`enrich_reactions`
        - :func:`enrich_variants`

    Equivalent to:

    >>> enrich_complexes(graph)
    >>> enrich_composites(graph)
    >>> enrich_reactions(graph)
    >>> enrich_variants(graph)


@uni_in_place_transformation<EOL>def expand_internal(universe: BELGraph, graph: BELGraph, edge_predicates: EdgePredicates = None) -> None:

edge_filter = and_edge_predicates(edge_predicates)<EOL>for u, v in itt.product(graph, repeat=<NUM_LIT:2>):<EOL><INDENT>if graph.has_edge(u, v) or not universe.has_edge(u, v):<EOL><INDENT>continue<EOL><DEDENT>rs = defaultdict(list)<EOL>for key, data in universe[u][v].items():<EOL><INDENT>if not edge_filter(universe, u, v, key):<EOL><INDENT>continue<EOL><DEDENT>rs[data[RELATION]].append((key, data))<EOL><DEDENT>if <NUM_LIT:1> == len(rs):<EOL><INDENT>relation = list(rs)[<NUM_LIT:0>]<EOL>for key, data in rs[relation]:<EOL><INDENT>graph.add_edge(u, v, key=key, **data)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>log.debug('<STR_LIT>', u, v)<EOL><DEDENT><DEDENT>

Edges between entities in the sub-graph that pass the given filters.

    :param universe: The full graph
    :param graph: A sub-graph to find the upstream information
    :param edge_predicates: Optional list of edge filter functions (graph, node, node, key, data) -> bool


@uni_in_place_transformation<EOL>def expand_internal_causal(universe: BELGraph, graph: BELGraph) -> None:

expand_internal(universe, graph, edge_predicates=is_causal_relation)<EOL>

Add causal edges between entities in the sub-graph.

    Is an extremely thin wrapper around :func:`expand_internal`.

    :param universe: A BEL graph representing the universe of all knowledge
    :param graph: The target BEL graph to enrich with causal relations between contained nodes

    Equivalent to:

    >>> from pybel_tools.mutation import expand_internal
    >>> from pybel.struct.filters.edge_predicates import is_causal_relation
    >>> expand_internal(universe, graph, edge_predicates=is_causal_relation)


def build_expand_node_neighborhood_by_hash(manager: Manager) -> Callable[[BELGraph, BELGraph, str], None]:

@uni_in_place_transformation<EOL>def expand_node_neighborhood_by_hash(universe: BELGraph, graph: BELGraph, node_hash: str) -> None:<EOL><INDENT>"""<STR_LIT>"""<EOL>node = manager.get_dsl_by_hash(node_hash)<EOL>return expand_node_neighborhood(universe, graph, node)<EOL><DEDENT>return expand_node_neighborhood_by_hash<EOL>

Make an expand function that's bound to the manager.


def build_delete_node_by_hash(manager: Manager) -> Callable[[BELGraph, str], None]:

@in_place_transformation<EOL>def delete_node_by_hash(graph: BELGraph, node_hash: str) -> None:<EOL><INDENT>"""<STR_LIT>"""<EOL>node = manager.get_dsl_by_hash(node_hash)<EOL>graph.remove_node(node)<EOL><DEDENT>return delete_node_by_hash<EOL>

Make a delete function that's bound to the manager.


@in_place_transformation<EOL>def remove_inconsistent_edges(graph: BELGraph) -> None:

for u, v in get_inconsistent_edges(graph):<EOL><INDENT>edges = [(u, v, k) for k in graph[u][v]]<EOL>graph.remove_edges_from(edges)<EOL><DEDENT>

Remove all edges between node pairs with inconsistent edges.

    This is the all-or-nothing approach. It would be better to do more careful investigation of the evidences during
    curation.


def get_inconsistent_edges(graph: BELGraph) -> Iterable[Tuple[BaseEntity]]:

for u, v in graph.edges():<EOL><INDENT>if not pair_is_consistent(graph, u, v):<EOL><INDENT>yield u, v<EOL><DEDENT><DEDENT>

Iterate over pairs of nodes with inconsistent edges.


@in_place_transformation<EOL>def collapse_protein_variants(graph: BELGraph) -> None:

_collapse_variants_by_function(graph, PROTEIN)<EOL>

Collapse all protein's variants' edges to their parents, in-place.


@in_place_transformation<EOL>def collapse_gene_variants(graph: BELGraph) -> None:

_collapse_variants_by_function(graph, GENE)<EOL>

Collapse all gene's variants' edges to their parents, in-place.


def _collapse_variants_by_function(graph: BELGraph, func: str) -> None:

for parent_node, variant_node, data in graph.edges(data=True):<EOL><INDENT>if data[RELATION] == HAS_VARIANT and parent_node.function == func:<EOL><INDENT>collapse_pair(graph, from_node=variant_node, to_node=parent_node)<EOL><DEDENT><DEDENT>

Collapse all of the given functions' variants' edges to their parents, in-place.


@in_place_transformation<EOL>def rewire_variants_to_genes(graph: BELGraph) -> None:

mapping = {}<EOL>for node in graph:<EOL><INDENT>if not isinstance(node, Protein) or not node.variants:<EOL><INDENT>continue<EOL><DEDENT>mapping[node] = Gene(<EOL>name=node.name,<EOL>namespace=node.namespace,<EOL>identifier=node.identifier,<EOL>variants=node.variants,<EOL>)<EOL><DEDENT>nx.relabel_nodes(graph, mapping, copy=False)<EOL>

Find all protein variants that are pointing to a gene and not a protein and fixes them by changing their
    function to be :data:`pybel.constants.GENE`, in place

    A use case is after running :func:`collapse_to_genes`.


def _collapse_edge_passing_predicates(graph: BELGraph, edge_predicates: EdgePredicates = None) -> None:

for u, v, _ in filter_edges(graph, edge_predicates=edge_predicates):<EOL><INDENT>collapse_pair(graph, survivor=u, victim=v)<EOL><DEDENT>

Collapse all edges passing the given edge predicates.


def _collapse_edge_by_namespace(graph: BELGraph,<EOL>victim_namespaces: Strings,<EOL>survivor_namespaces: str,<EOL>relations: Strings) -> None:

relation_filter = build_relation_predicate(relations)<EOL>source_namespace_filter = build_source_namespace_filter(victim_namespaces)<EOL>target_namespace_filter = build_target_namespace_filter(survivor_namespaces)<EOL>edge_predicates = [<EOL>relation_filter,<EOL>source_namespace_filter,<EOL>target_namespace_filter<EOL>]<EOL>_collapse_edge_passing_predicates(graph, edge_predicates=edge_predicates)<EOL>

Collapse pairs of nodes with the given namespaces that have the given relationship.

    :param graph: A BEL Graph
    :param victim_namespaces: The namespace(s) of the node to collapse
    :param survivor_namespaces: The namespace of the node to keep
    :param relations: The relation(s) to search


@in_place_transformation<EOL>def collapse_equivalencies_by_namespace(graph: BELGraph, victim_namespace: Strings, survivor_namespace: str) -> None:

_collapse_edge_by_namespace(graph, victim_namespace, survivor_namespace, EQUIVALENT_TO)<EOL>

Collapse pairs of nodes with the given namespaces that have equivalence relationships.

    :param graph: A BEL graph
    :param victim_namespace: The namespace(s) of the node to collapse
    :param survivor_namespace: The namespace of the node to keep

    To convert all ChEBI names to InChI keys, assuming there are appropriate equivalence relations between nodes with
    those namespaces:

    >>> collapse_equivalencies_by_namespace(graph, 'CHEBI', 'CHEBIID')
    >>> collapse_equivalencies_by_namespace(graph, 'CHEBIID', 'INCHI')


@in_place_transformation<EOL>def collapse_orthologies_by_namespace(graph: BELGraph, victim_namespace: Strings, survivor_namespace: str) -> None:

_collapse_edge_by_namespace(graph, victim_namespace, survivor_namespace, ORTHOLOGOUS)<EOL>

Collapse pairs of nodes with the given namespaces that have orthology relationships.

    :param graph: A BEL Graph
    :param victim_namespace: The namespace(s) of the node to collapse
    :param survivor_namespace: The namespace of the node to keep

    To collapse all MGI nodes to their HGNC orthologs, use:
    >>> collapse_orthologies_by_namespace('MGI', 'HGNC')

    To collapse collapse both MGI and RGD nodes to their HGNC orthologs, use:
    >>> collapse_orthologies_by_namespace(['MGI', 'RGD'], 'HGNC')


collapse_equivalencies_by_namespace(graph, ['<STR_LIT>', '<STR_LIT>', '<STR_LIT>'], '<STR_LIT>')<EOL>


collapse_orthologies_by_namespace(graph, ['<STR_LIT>', '<STR_LIT>'], '<STR_LIT>')<EOL>


collapse_orthologies_by_namespace(graph, '<STR_LIT>', '<STR_LIT>')<EOL>


relation_filter = build_relation_predicate(EQUIVALENT_TO)<EOL>source_namespace_filter = build_source_namespace_filter(['<STR_LIT>', '<STR_LIT>', '<STR_LIT>'])<EOL>edge_predicates = [<EOL>relation_filter,<EOL>source_namespace_filter,<EOL>]<EOL>_collapse_edge_passing_predicates(graph, edge_predicates=edge_predicates)<EOL>

Collapse all equivalence edges away from Entrez. Assumes well formed, 2-way equivalencies.