package org.cellocad.MIT.dnacompiler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
/**
* Created by Bryan Der on 12/5/14.
*/
public class GateUtil {
/**
* If the output module is on a different plasmid
* (with a different copy number or different affect on cell growth),
* the RPU units of the circuit might need to be converted.
*
* @param g
*/
public static void outputHistogramUnitConversion(Gate g) {
if(g.Type == Gate.GateType.OUTPUT || g.Type == Gate.GateType.OUTPUT_OR) {
//outer arraylist: rows in truth table
//inner array: fractional counts representing a histogram
ArrayList<double[]> histogram_rpus = g.get_histogram_rpus();
ArrayList<double[]> shifted_histogram_rpus = new ArrayList<double[]>();
for (double[] histogram : histogram_rpus) {
//calculate the new median
double current_median = HistogramUtil.median(histogram, g.get_histogram_bins());
double new_median = current_median * g.get_unit_conversion();
//shift the histogram to the new median
double[] shifted_histogram = HistogramUtil.normalizeHistogramToNewMedian(histogram, new_median, g.get_histogram_bins());
shifted_histogram_rpus.add(shifted_histogram);
}
g.set_histogram_rpus(shifted_histogram_rpus);
}
}
/**
*
* Given gate type and child gate logics, compute gate logic
*
*/
public static ArrayList<Integer> computeGateLogics(Gate g) {
ArrayList<Gate> children = g.getChildren();
ArrayList<Integer> gate_logics = new ArrayList<Integer>();
if(g.Type != Gate.GateType.INPUT) {
for (int i = 0; i < children.get(0).get_logics().size(); ++i) { //rows in truth table
//arraylength = # children
ArrayList<Integer> this_row_child_logics = new ArrayList<Integer>();
for (Gate child : children) {
this_row_child_logics.add(child.get_logics().get(i));
}
//compute Boolean value based on gate type and child logics
Integer logic = BooleanLogic.computeLogic(g.Type, this_row_child_logics);
/*if(Args.dontcare_rows.contains(i)) {
logic = 2; //integer required, 2 used as placeholder for dont-care. will be changed to - when printing
}*/
gate_logics.add(logic);
}
return gate_logics;
}
else {
return g.get_logics();
}
}
/**
*
* Compute depth of gate
*
* recurse until reaching inputs
* set gates connected to inputs == 1
* when recursing back up from inputs, dist += 1 of child
* if multiple paths, choose farthest
*
*/
public static void calculateDistanceToFarthestInput(Gate g){
if (g.is_unvisited()) {
//initialize with 0
g.set_distance_to_input(0);
g.set_unvisited(false);
if (g.Type != Gate.GateType.INPUT) { //inputs are an endpoint (inputs gates are also initialized with unvisited=false;
for (Gate child : g.getChildren()) {
calculateDistanceToFarthestInput(child); //recurse until reaching inputs
//then when recursing up from inputs, dist += 1 of child
g.set_distance_to_input(child.get_distance_to_input() + 1);
//if the distance_to_input exceeds any prior distances, update the farthest_dist2in
if (g.get_farthest_dist2in() < g.get_distance_to_input()) {
g.set_farthest_dist2in(g.get_distance_to_input());
}
else {
g.set_distance_to_input(g.get_farthest_dist2in());
}
}
}
}
}
/**
*
* return min of all ON incoming rpus.
*
* Used to in noise margin analysis. This is different than calculating ONmin or OFFmax for a gate,
* which is based on the output RPU. Noise margin analysis is based in the input RPU value(s).
*
*/
public static HashMap<String, Double> getIncomingONlow(Gate g) {
GateUtil.mapWiresToVariables(g, g.get_variable_names());
HashMap<String, ArrayList<Double>> all_incoming_ON = new HashMap<>();
HashMap<String, Double> lowest_ons = new HashMap<>();
for (String var : g.get_variable_wires().keySet()) {
all_incoming_ON.put(var, new ArrayList<Double>());
}
for(int i=0; i<g.get_logics().size(); ++i) {
for (String var : g.get_variable_wires().keySet()) {
boolean ON = false;
for (Wire w : g.get_variable_wires().get(var)) {
if (w.To.get_logics().get(i) == 1) {
ON = true;
}
}
if(ON) {
Double sum_of_ons = g.get_inrpus().get(var).get(i);
/**
* _inrpus (prompted by tandem promoter eval) replacing this...
*/
/*
Double sum_of_ons = 0.0;
for (Wire w : g.get_variable_wires().get(var)) {
sum_of_ons += w.To.get_outrpus().get(i);
}*/
all_incoming_ON.get(var).add(sum_of_ons);
}
}
}
for(String var: g.get_variable_wires().keySet()) {
Double lowest_on = Collections.min(all_incoming_ON.get(var));
lowest_ons.put(var, lowest_on);
}
return lowest_ons;
}
/**
*
* return max of all OFF incoming rpus
*
* Used to in noise margin analysis. This is different than calculating ONmin or OFFmax for a gate,
* which is based on the output RPU. Noise margin analysis is based in the input RPU value(s).
*
*/
public static HashMap<String, Double> getIncomingOFFhigh(Gate g) {
GateUtil.mapWiresToVariables(g, g.get_variable_names());
HashMap<String, ArrayList<Double>> all_incoming_OFF = new HashMap<>();
HashMap<String, Double> highest_offs = new HashMap<>();
for (String var : g.get_variable_wires().keySet()) {
all_incoming_OFF.put(var, new ArrayList<Double>());
}
for(int i=0; i<g.get_logics().size(); ++i) {
for (String var : g.get_variable_wires().keySet()) {
boolean OFF = true;
for (Wire w : g.get_variable_wires().get(var)) {
if (w.To.get_logics().get(i) == 1) {
OFF = false;
}
}
if(OFF) {
Double sum_of_offs = g.get_inrpus().get(var).get(i);
/**
* _inrpus (prompted by tandem promoter eval) replacing this...
*/
/*
Double sum_of_offs = 0.0;
for (Wire w : g.get_variable_wires().get(var)) {
sum_of_offs += w.To.get_outrpus().get(i);
}*/
all_incoming_OFF.get(var).add(sum_of_offs);
}
}
}
for(String var: g.get_variable_wires().keySet()) {
//Collections.sort(all_incoming_OFF.get(var));
//Collections.reverse(all_incoming_OFF.get(var));
Double highest_off = Collections.max(all_incoming_OFF.get(var));
highest_offs.put(var, highest_off);
}
return highest_offs;
}
/**
*
* Cello assumes tandem promoters have additive activity (RPU)
*
*/
//replaced by gate.get_inrpus()
/*public static ArrayList<Double> getSumOfGateInputRPUs(Gate g) {
ArrayList<Gate> children = g.getChildren();
ArrayList<Double> sum_child_rpus = new ArrayList<Double>();
for(int i = 0; i<children.get(0).get_outrpus().size(); ++i){ //rows in truth table
Double this_row_sum_rpus = 0.0;
for(Gate child: children) {
this_row_sum_rpus += child.get_outrpus().get(i);
}
sum_child_rpus.add(this_row_sum_rpus);
}
return sum_child_rpus;
}*/
//TODO using gird look up for medians of inputs RPU histograms or grid look up for inputs RPU
// should be very close depent on the bins been chosen
// public static ArrayList<Double[]> getSumOfGateInputHistograms_v1(Gate g) {
// ArrayList <Double[]> tt_rpu_medians = new ArrayList<Double>();
//
// return tt_rpu_medians;
// }
public static ArrayList<double[]> getSumOfGateInputHistograms(Gate g, GateLibrary gate_library, Args options) {
ArrayList<Double> tt_rpu_medians = new ArrayList<Double>();
// TODO changed
/**
* New way, does not assume additivity but does not use distribution medians.
*/
if (options.is_tandem_promoter()){
HistogramBins hbins = new HistogramBins();
hbins.init();
String var = "x";
if(g.get_variable_names().size() == 1) {
var = g.get_variable_names().get(0);
}
boolean tp_exists = false;
String tp_name = "";
double[][] grid = new double[hbins.get_NBINS()][hbins.get_NBINS()];
Gate child1 = new Gate();
Gate child2 = new Gate();
ArrayList<String> fanin_gate_names = new ArrayList<>();
if (g.get_variable_wires().get(var).size() == 2) { //hard-coded
child1 = g.getChildren().get(0);
child2 = g.getChildren().get(1);
if (child1.Type == Gate.GateType.INPUT) {
fanin_gate_names.add("input_" + child1.Name);
} else {
fanin_gate_names.add(child1.Name);
}
if (child2.Type == Gate.GateType.INPUT) {
fanin_gate_names.add("input_" + child2.Name);
} else {
fanin_gate_names.add(child2.Name);
}
String tandem_promoter_name_1 = fanin_gate_names.get(0) + "_" + fanin_gate_names.get(1);
String tandem_promoter_name_2 = fanin_gate_names.get(1) + "_" + fanin_gate_names.get(0);
if (gate_library.get_TANDEM_PROMOTERS().containsKey(tandem_promoter_name_1)) {
grid = gate_library.get_TANDEM_PROMOTERS().get(tandem_promoter_name_1);
tp_name = tandem_promoter_name_1;
tp_exists = true;
} else if (gate_library.get_TANDEM_PROMOTERS().containsKey(tandem_promoter_name_2)) {
grid = gate_library.get_TANDEM_PROMOTERS().get(tandem_promoter_name_2);
tp_name = tandem_promoter_name_2;
tp_exists = true;
}
}
if(tp_exists) {
for (int i = 0; i < g.get_logics().size(); ++i) { //rows in truth table
double sum_median = 0.0;
int bin_median1 = 0;
int bin_median2 = 0;
if(child1.Type == Gate.GateType.INPUT) {
if(child1.get_logics().get(i) == 0) {
bin_median1 = 0;
}
else if(child1.get_logics().get(i) == 1) {
bin_median1 = hbins.get_NBINS() - 1;
}
}
else {
bin_median1 = HistogramUtil.bin_median(child1.get_in_histogram_rpus().get(i));
}
if(child2.Type == Gate.GateType.INPUT) {
if(child2.get_logics().get(i) == 0) {
bin_median2 = 0;
}
else if(child2.get_logics().get(i) == 1) {
bin_median2 = hbins.get_NBINS() - 1;
}
}
else {
bin_median2 = HistogramUtil.bin_median(child2.get_in_histogram_rpus().get(i));
}
String gate1_name = fanin_gate_names.get(0);
String gate2_name = fanin_gate_names.get(1);
if (tp_name.startsWith(gate1_name) && tp_name.endsWith(gate2_name)) {
//correct in1 and in2 order
}
else if (tp_name.startsWith(gate2_name) && tp_name.endsWith(gate1_name)) {
int temp = bin_median1;
bin_median1 = bin_median2;
bin_median2 = temp;
}
else {
throw new IllegalStateException("Problem with tandem promoter lookup");
}
sum_median = grid[bin_median1][bin_median2];
double out = Math.pow(10, sum_median);
tt_rpu_medians.add(out);
}
}
else{
for (int i = 0; i < g.get_logics().size(); ++i) { //rows in truth table
double sum_median = 0.0;
for (Gate child : g.getChildren()) {
int bin_median = HistogramUtil.bin_median(child.get_histogram_rpus().get(i));
Double rpu_median = Math.pow(10, g.get_histogram_bins().get_LOG_BIN_CENTERS()[bin_median]);
sum_median += rpu_median;
}
tt_rpu_medians.add(sum_median);
}
}
}
/**
* Old way, assumes additivity but uses distribution medians
*/
else {
for (int i = 0; i < g.get_logics().size(); ++i) { //rows in truth table
double sum_median = 0.0;
//if(!Args.dontcare_rows.contains(i)) {
for (Gate child : g.getChildren()) {
int bin_median = HistogramUtil.bin_median(child.get_histogram_rpus().get(i));
Double rpu_median = Math.pow(10, g.get_histogram_bins().get_LOG_BIN_CENTERS()[bin_median]);
sum_median += rpu_median;
}
//}
tt_rpu_medians.add(sum_median);
}
}
ArrayList<double[]> tt_sum_zeroed = new ArrayList<double[]>();
//initialize
for (int i = 0; i < g.get_logics().size(); ++i) { //rows in truth table
tt_sum_zeroed.add(new double[g.get_histogram_bins().get_NBINS()]);
for (int j = 0; j < g.get_histogram_bins().get_NBINS(); ++j) {
tt_sum_zeroed.get(i)[j] = 0.0;
}
}
for (int i = 0; i < g.get_logics().size(); ++i) { //rows in truth table
for(Gate child: g.getChildren()) {
Double target_median = tt_rpu_medians.get(i);
double[] child_histogram = child.get_histogram_rpus().get(i);
double[] shifted_histogram = HistogramUtil.normalizeHistogramToNewMedian(child_histogram, target_median, g.get_histogram_bins());
//for each child of this gate, sum the zeroed fractional counts.
for(int bin=0; bin<g.get_histogram_bins().get_NBINS(); ++bin) {
tt_sum_zeroed.get(i)[bin] += shifted_histogram[bin];
}
}
}
ArrayList<double[]> tt_normalized_zeroed = new ArrayList<double[]>();
for(double[] sum_zeroed: tt_sum_zeroed) {
tt_normalized_zeroed.add(HistogramUtil.normalize(sum_zeroed));
}
return tt_normalized_zeroed;
}
/***********************************************************************
Synopsis [ ]
***********************************************************************/
public static ArrayList<double[]> getANDOfGateInputHistograms(Gate g) {
ArrayList<double[]> tt_AND = new ArrayList<double[]>();
for (int i = 0; i < g.get_logics().size(); ++i) { //rows in truth table
double min_median = 1000.0;
double[] min_histogram = new double[g.get_histogram_bins().get_NBINS()];
for (Gate child : g.getChildren()) {
if(HistogramUtil.median( child.get_histogram_rpus().get(i), g.get_histogram_bins()) < min_median ) {
min_median = HistogramUtil.median( child.get_histogram_rpus().get(i), g.get_histogram_bins());
min_histogram = child.get_histogram_rpus().get(i);
}
}
tt_AND.add(min_histogram);
}
return tt_AND;
}
public static ArrayList<ArrayList<Double>> getGateInputRPUs(Gate g) {
ArrayList<Gate> children = g.getChildren();
//outer is truth table
//inner is children
ArrayList<ArrayList<Double>> all_child_rpus = new ArrayList<ArrayList<Double>>();
for(int i = 0; i<children.get(0).get_outrpus().size(); ++i) { //rows in truth table
ArrayList<Double> all_child_rpu_for_row = new ArrayList<Double>();
for(Gate child: children) {
all_child_rpu_for_row.add(child.get_outrpus().get(i));
}
all_child_rpus.add(all_child_rpu_for_row);
}
return all_child_rpus;
}
public static void mapWiresToVariables(Gate g, ArrayList<String> variables) {
HashMap<String, ArrayList<Wire>> map = new HashMap<String, ArrayList<Wire>>();
/**
* List of all input wires for gate 'g'
*/
ArrayList<Wire> input_wires = new ArrayList<Wire>();
if (g.Outgoing != null) {
input_wires.add(g.Outgoing);
Wire w = g.Outgoing;
while(w.Next != null) {
input_wires.add(w.Next);
w = w.Next;
}
}
/**
* If response function as 1 independent variable, all wires map to that variable.
*/
if(variables.size() == 1) {
String v = variables.get(0);
map.put(v, input_wires);
}
/**
* Else, for example, a 2-input AND might have 2 independent variables.
* In this case, we have two different ways to map 2 promoters to the 2 independent variables.
* Just choose a default mapping for now, see also: Evaluate.setBestVariableMapping, which establishes the
* best variable name order.
*/
else if(variables.size() == input_wires.size()) {
for(int i=0; i<variables.size(); ++i) {
ArrayList<Wire> input_wire = new ArrayList<Wire>();
input_wire.add(input_wires.get(i));
map.put(variables.get(i), input_wire);
}
}
//
g.set_variable_wires(map);
}
/**
* Sum RPUs for tandem promoters, map the summed value to the name of the independent variable
* from the gate's response function.
*
* @param g
* @param row
* @return
*/
//need GateLibrary (get_TANDEM_PROMOTERS() )
//need options ( is_tandem_promoter() )
public static HashMap<String, Double> getVariableValues(Gate g, int row, GateLibrary gate_library, Args options) {
HashMap<String, Double> variable_values = new HashMap<>();
if(options.is_tandem_promoter()) {
//TODO Shuyi's idea is to calculate the grid here, so that we only calculate those we actually need.
// the trick here is to avoid calculating the grid more than once.
//P3_PhlF_B1_BM3R1
//B1_BM3R1_P3_PhlF
HistogramBins hbins = new HistogramBins();
hbins.init();
/**
* First goal is to figure out if the data exists for this tandem promoter: 'tp_exists'
*/
boolean tp_exists = false;
String tp_name = "";
double[][] grid = new double[hbins.get_NBINS()][hbins.get_NBINS()];
Gate child1 = new Gate();
Gate child2 = new Gate();
ArrayList<String> fanin_gate_names = new ArrayList<>();
String var = "x";
if(g.get_variable_names().size() == 1) {
var = g.get_variable_names().get(0);
}
if (g.get_variable_wires().get(var).size() == 2) { //hard-coded
child1 = g.getChildren().get(0);
child2 = g.getChildren().get(1);
if (child1.Type == Gate.GateType.INPUT) {
fanin_gate_names.add("input_" + child1.Name);
} else {
fanin_gate_names.add(child1.Name);
}
if (child2.Type == Gate.GateType.INPUT) {
fanin_gate_names.add("input_" + child2.Name);
} else {
fanin_gate_names.add(child2.Name);
}
String tandem_promoter_name_1 = fanin_gate_names.get(0) + "_" + fanin_gate_names.get(1);
String tandem_promoter_name_2 = fanin_gate_names.get(1) + "_" + fanin_gate_names.get(0);
if (gate_library.get_TANDEM_PROMOTERS().containsKey(tandem_promoter_name_1)) {
grid = gate_library.get_TANDEM_PROMOTERS().get(tandem_promoter_name_1);
tp_name = tandem_promoter_name_1;
tp_exists = true;
} else if (gate_library.get_TANDEM_PROMOTERS().containsKey(tandem_promoter_name_2)) {
grid = gate_library.get_TANDEM_PROMOTERS().get(tandem_promoter_name_2);
tp_name = tandem_promoter_name_2;
tp_exists = true;
}
}
//get a number for additive assumption, testing
/*boolean DEBUG = false;
if(DEBUG) {
Double additive_val = 0.0;
for (String v : g.get_variable_wires().keySet()) {
for (Wire w : g.get_variable_wires().get(v)) {
//this is the line that adds RPU values for tandem promoters
additive_val += w.To.get_outrpus().get(row);
}
}
}*/
//for example, String v = "x" in the hill equation "ymin+(ymax-ymin)/(1.0+(x/K)^n)"
//"x" is the variable, while ymax, ymin, K, n are parameters
for (String v : g.get_variable_wires().keySet()) {
Double d = 0.0;
Double dtest = 0.0;
for (Wire w : g.get_variable_wires().get(v)) {
//this is the line that adds RPU values for tandem promoters
dtest += w.To.get_outrpus().get(row);
}
/**
* Non-additive tandem promoter data
*/
if (tp_exists) {
String gate1_name = fanin_gate_names.get(0);
String gate2_name = fanin_gate_names.get(1);
Double in1 = 0.0;
Double in2 = 0.0;
if(child1.Type == Gate.GateType.INPUT) {
if(child1.get_logics().get(row) == 0) {
in1 = Math.pow(10, hbins.get_LOGMIN());
}
else if(child1.get_logics().get(row) == 1) {
in1 = Math.pow(10, hbins.get_LOGMAX());
}
}
else {
in1 = child1.get_inrpus().get(v).get(row);
}
if(child2.Type == Gate.GateType.INPUT) {
if(child2.get_logics().get(row) == 0) {
in2 = Math.pow(10, hbins.get_LOGMIN());
}
else if(child2.get_logics().get(row) == 1) {
in2 = Math.pow(10, hbins.get_LOGMAX());
}
}
else {
in2 = child2.get_inrpus().get(v).get(row);
}
if (tp_name.startsWith(gate1_name) && tp_name.endsWith(gate2_name)) {
//correct in1 and in2 order
}
else if (tp_name.startsWith(gate2_name) && tp_name.endsWith(gate1_name)) {
Double temp = new Double(in1);
in1 = in2;
in2 = temp;
}
else {
throw new IllegalStateException("Problem with tandem promoter lookup");
}
Integer bin1 = HistogramUtil.bin_of_logrpu(Math.log10(in1), hbins);
Integer bin2 = HistogramUtil.bin_of_logrpu(Math.log10(in2), hbins);
Double log_out = grid[bin1][bin2];
Double out = Math.pow(10, log_out);
variable_values.put(v, out);
}
/**
* Additive
*/
else {
for (Wire w : g.get_variable_wires().get(v)) {
//this is the line that adds RPU values for tandem promoters
d += w.To.get_outrpus().get(row);
}
variable_values.put(v, d);
}
}
}
else {
for(String v: g.get_variable_wires().keySet()) {
Double d = 0.0;
for (Wire w : g.get_variable_wires().get(v)) {
//this is the line that adds RPU values for tandem promoters
d += w.To.get_outrpus().get(row);
}
variable_values.put(v, d);
}
}
return variable_values;
}
}