aboutsummaryrefslogtreecommitdiffstats
path: root/fpga_interchange/sampler.cc
blob: 0868867e47724651b31a5bdc10721ad086ccee73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
/*
 *  nextpnr -- Next Generation Place and Route
 *
 *  Copyright (C) 2021  Symbiflow Authors
 *
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "sampler.h"
#include <algorithm>
#include <cmath>
#include <stdexcept>

NEXTPNR_NAMESPACE_BEGIN

static size_t partition_x(std::vector<size_t>::iterator begin, std::vector<size_t>::iterator end,
                          const std::vector<std::pair<int32_t, int32_t>> &samples)
{
    if (std::distance(begin, end) == 0) {
        return 0;
    }

    // Find the median x value.
    std::vector<int32_t> xs;
    xs.reserve(std::distance(begin, end));

    for (auto iter = begin; iter != end; ++iter) {
        xs.push_back(samples[*iter].first);
    }

    std::sort(xs.begin(), xs.end());
    xs.erase(std::unique(xs.begin(), xs.end()), xs.end());

    // Partion on the median x value (e.g. 50% of samples on one side and
    // 50% of samples on the other side).
    int32_t x_div = xs[(xs.size() - 1) / 2];

    auto split = std::partition(begin, end,
                                [x_div, &samples](size_t index) -> bool { return samples[index].first <= x_div; });

    return std::distance(begin, split);
}

/* Don't both splitting when the partition has less than kMinSplit. */
static constexpr ptrdiff_t kMinSplit = 20;

static size_t partition_y(std::vector<size_t>::iterator begin, std::vector<size_t>::iterator end,
                          const std::vector<std::pair<int32_t, int32_t>> &samples)
{
    if (std::distance(begin, end) == 0) {
        return 0;
    }

    std::vector<int32_t> ys;
    ys.reserve(std::distance(begin, end));

    for (auto iter = begin; iter != end; ++iter) {
        ys.push_back(samples[*iter].second);
    }

    std::sort(ys.begin(), ys.end());
    ys.erase(std::unique(ys.begin(), ys.end()), ys.end());

    int32_t y_div = ys[(ys.size() - 1) / 2];

    auto split = std::partition(begin, end,
                                [y_div, &samples](size_t index) -> bool { return samples[index].second <= y_div; });

    return std::distance(begin, split);
}

static void add_split(std::vector<size_t> *splits, size_t new_split)
{
    if (splits->back() < new_split) {
        splits->push_back(new_split);
    } else if (splits->back() != new_split) {
        throw std::runtime_error("Split is not consectutive!");
    }
}

void Sampler::divide_samples(size_t target_sample_count, const std::vector<std::pair<int32_t, int32_t>> &samples)
{
    // Initialize indicies lookup and make 1 split with entire sample range.
    indicies.resize(samples.size());
    for (size_t i = 0; i < samples.size(); ++i) {
        indicies[i] = i;
    }

    splits.reserve(2);
    splits.push_back(0);
    splits.push_back(samples.size());

    size_t divisions = std::ceil(std::sqrt(target_sample_count) / 2.);
    if (divisions == 0) {
        throw std::runtime_error("Math failure, unreachable!");
    }

    if (divisions > samples.size()) {
        // Handle cases where there are few samples.
        return;
    }

    // Recursively split samples first 50% / 50% in x direction, and then
    // 50% / 50% in y direction.  Repeat until the bucket is smaller than
    // kMinSplit or the samples have been divided `divisions` times.
    std::vector<size_t> new_splits;
    for (size_t division_count = 0; division_count < divisions; ++division_count) {
        new_splits.clear();
        new_splits.push_back(0);
        for (size_t i = 0; i < splits.size() - 1; ++i) {
            size_t split_begin = splits.at(i);
            size_t split_end = splits.at(i + 1);
            if (split_end > indicies.size()) {
                throw std::runtime_error("split_end is not valid!");
            }
            if (split_begin >= split_end) {
                throw std::runtime_error("Invalid split from earlier pass!");
            }

            std::vector<size_t>::iterator begin = indicies.begin() + split_begin;
            std::vector<size_t>::iterator end = indicies.begin() + split_end;

            if (std::distance(begin, end) < kMinSplit) {
                add_split(&new_splits, split_begin);
                continue;
            }

            // Try to split samples 50/50 in x direction.
            size_t split = partition_x(begin, end, samples);
            // Try to split samples 50/50 in y direction after the x split.
            size_t split_y1 = partition_y(begin, begin + split, samples);
            size_t split_y2 = partition_y(begin + split, end, samples);

            // Because the y2 split starts at split, add it here.
            split_y2 += split;

            add_split(&new_splits, split_begin);
            add_split(&new_splits, split_begin + split_y1);
            add_split(&new_splits, split_begin + split);
            add_split(&new_splits, split_begin + split_y2);
        }

        add_split(&new_splits, samples.size());

        if (new_splits.front() != 0) {
            throw std::runtime_error("Split must start at 0");
        }
        if (new_splits.back() != samples.size()) {
            throw std::runtime_error("Split must end at last element");
        }

        for (size_t i = 0; i < new_splits.size() - 1; ++i) {
            if (new_splits[i] >= new_splits[i + 1]) {
                throw std::runtime_error("Split indicies must be increasing");
            }
        }

        std::swap(splits, new_splits);
    }
}

NEXTPNR_NAMESPACE_END