#!/usr/bin/env python # Generate the body of ieee.numeric_std and numeric_bit from a template. # The implementation is based only on the specification and on testing (as # the specifications are often ambiguous). # The algorithms are very simple: carry ripple adder, restoring division. # This file is part of GHDL. # Both this file and the outputs of this file are copyrighted. # Copyright (C) 2015-2021 Tristan Gingold # # GHDL is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free # Software Foundation; either version 2, or (at your option) any later # version. # # GHDL is distributed in the hope that it will be useful, but WITHOUT ANY # WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License # along with GHDL; see the file COPYING.md. If not see # . import sys # My python 'style' and knowledge is basic... Do not hesitate to comment. binary_funcs = ["and", "nand", "or", "nor", "xor"] compare_funcs = ["=", "/=", ">", ">=", "<", "<="] vec_types = ["UNSIGNED", "SIGNED"] logics = ["bit", "std"] logic_types = {"bit": "bit", "std": "sl_x01"} logic_undefs = {"bit": "'0'", "std": "'X'"} logic = "xx" # Current logic, either bit or std v93 = False # Stream to write. out = sys.stdout def w(s): """Write S to the output.""" out.write(s) def logic_type(): return logic_types[logic] def logic_undef(): return logic_undefs[logic] def disp_vec_binary(func, typ): """Generate the body of a vector binary logic function.""" res = ( """ function "{0}" (l, r : {1}) return {1} is subtype res_type is {1} (l'length - 1 downto 0); alias la : res_type is l; alias ra : {1} (r'length - 1 downto 0) is r; variable res : res_type; begin if la'left /= ra'left then assert false report "NUMERIC_STD.""{0}"": arguments are not of the same length" severity failure; res := (others => """ + logic_undef() + """); else for I in res_type'range loop res (I) := la (I) {0} ra (I); end loop; end if; return res; end "{0}";\n""" ) w(res.format(func, typ)) def disp_non_logical_warning(func): return """ assert NO_WARNING report "NUMERIC_STD.""{0}"": non logical value detected" severity warning;""".format( func ) def conv_bit(expr): if logic == "std": return "sl_to_x01 (" + expr + ")" else: return expr def extract_bit(name): res = "{0}b := " + conv_bit("{0}a (i)") + ";" return res.format(name) def init_carry(func): if func == "+": return """ carry := '0';""" else: return """ carry := '1';""" def extract_extend_bit(name, typ): res = """ if i > {0}a'left then {0}b := """ if typ == "UNSIGNED": res += "'0';" else: res += "{0} ({0}'left);" res += ( """ else """ + extract_bit(name) + """ end if;""" ) return res.format(name) def disp_vec_vec_binary(func, typ): """Generate vector binary function body.""" res = ( """ function "{0}" (l, r : {1}) return {1} is constant lft : integer := MAX (l'length, r'length) - 1; subtype res_type is {1} (lft downto 0); alias la : {1} (l'length - 1 downto 0) is l; alias ra : {1} (r'length - 1 downto 0) is r; variable res : res_type; variable lb, rb, carry : """ + logic_type() + """; begin if la'left < 0 or ra'left < 0 then return null_{1}; end if;""" ) res += init_carry(func) res += """ for i in 0 to lft loop""" res += extract_extend_bit("l", typ) res += extract_extend_bit("r", typ) if logic == "std": res += ( """ if lb = 'X' or rb = 'X' then""" + disp_non_logical_warning(func) + """ res := (others => 'X'); exit; end if;""" ) if func == "-": res += """ rb := not rb;""" res += """ res (i) := compute_sum (carry, rb, lb); carry := compute_carry (carry, rb, lb); end loop; return res; end "{0}"; """ w(res.format(func, typ)) def declare_int_var(name, typ): res = """ variable {0}1, {0}2 : {1}; variable {0}d : nat1;""" if typ == "INTEGER": res += """ constant {0}msb : nat1 := boolean'pos({0} < 0);""" return res.format(name, typ) def init_int_var(name, typ): return """ {0}1 := {0};""".format( name ) def extract_int_lsb(name, typ): res = """ {0}2 := {0}1 / 2;""" if typ == "INTEGER": res += """ if {0}1 < 0 then {0}d := 2 * {0}2 - {0}1; {0}1 := {0}2 - {0}d; else {0}d := {0}1 - 2 * {0}2; {0}1 := {0}2; end if;""" else: res += """ {0}d := {0}1 - 2 * {0}2; {0}1 := {0}2;""" res += """ {0}b := nat1_to_01 ({0}d);""" return res.format(name, typ) def check_int_truncated(func, name, typ): if typ == "INTEGER": v = "-{0}msb".format(name) else: v = "0" return """ if {1}1 /= {2} then assert NO_WARNING report "NUMERIC_STD.""{0}"": vector is truncated" severity warning; end if;""".format( func, name, v ) def create_vec_int_dict(func, left, right): if left in vec_types: dic = {"vtype": left, "itype": right, "vparam": "l", "iparam": "r"} else: dic = {"vtype": right, "itype": left, "vparam": "r", "iparam": "l"} dic.update({"ltype": left, "rtype": right, "func": func, "logic": logic_type()}) return dic def disp_vec_int_binary(func, left, right): """Generate vector binary function body.""" dic = create_vec_int_dict(func, left, right) res = ( """ function "{func}" (l : {ltype}; r : {rtype}) return {vtype} is subtype res_type is {vtype} ({vparam}'length - 1 downto 0); alias {vparam}a : res_type is {vparam};""" + declare_int_var(dic["ip
/*
    tests/test_numpy_vectorize.cpp -- auto-vectorize functions over NumPy array
    arguments

    Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>

    All rights reserved. Use of this source code is governed by a
    BSD-style license that can be found in the LICENSE file.
*/

#include "pybind11_tests.h"
#include <pybind11/numpy.h>

double my_func(int x, float y, double z) {
    py::print("my_func(x:int={}, y:float={:.0f}, z:float={:.0f})"_s.format(x, y, z));
    return (float) x*y*z;
}

TEST_SUBMODULE(numpy_vectorize, m) {
    try { py::module_::import("numpy"); }
    catch (...) { return; }

    // test_vectorize, test_docs, test_array_collapse
    // Vectorize all arguments of a function (though non-vector arguments are also allowed)
    m.def("vectorized_func", py::vectorize(my_func));

    // Vectorize a lambda function with a capture object (e.g. to exclude some arguments from the vectorization)
    m.def("vectorized_func2",
        [](py::array_t<int> x, py::array_t<float> y, float z) {
            return py::vectorize([z](int x, float y) { return my_func(x, y, z); })(x, y);
        }
    );

    // Vectorize a complex-valued function
    m.def("vectorized_func3", py::vectorize(
        [](std::complex<double> c) { return c * std::complex<double>(2.f); }
    ));

    // test_type_selection
    // NumPy function which only accepts specific data types
    m.def("selective_func", [](py::array_t<int, py::array::c_style>) { return "Int branch taken."; });
    m.def("selective_func", [](py::array_t<float, py::array::c_style>) { return "Float branch taken."; });
    m.def("selective_func", [](py::array_t<std::complex<float>, py::array::c_style>) { return "Complex float branch taken."; });


    // test_passthrough_arguments
    // Passthrough test: references and non-pod types should be automatically passed through (in the
    // function definition below, only `b`, `d`, and `g` are vectorized):
    struct NonPODClass {
        NonPODClass(int v) : value{v} {}
        int value;
    };
    py::class_<NonPODClass>(m, "NonPODClass")
        .def(py::init<int>())
        .def_readwrite("value", &NonPODClass::value);
    m.def("vec_passthrough", py::vectorize(
        [](double *a, double b, py::array_t<double> c, const int &d, int &e, NonPODClass f, const double g) {
            return *a + b + c.at(0) + d + e + f.value + g;
        }
    ));

    // test_method_vectorization
    struct VectorizeTestClass {
        VectorizeTestClass(int v) : value{v} {};
        float method(int x, float y) { return y + (float) (x + value); }
        int value = 0;
    };
    py::class_<VectorizeTestClass> vtc(m, "VectorizeTestClass");
    vtc .def(py::init<int>())
        .def_readwrite("value", &VectorizeTestClass::value);

    // Automatic vectorizing of methods
    vtc.def("method", py::vectorize(&VectorizeTestClass::method));

    // test_trivial_broadcasting
    // Internal optimization test for whether the input is trivially broadcastable:
    py::enum_<py::detail::broadcast_trivial>(m, "trivial")
        .value("f_trivial", py::detail::broadcast_trivial::f_trivial)
        .value("c_trivial", py::detail::broadcast_trivial::c_trivial)
        .value("non_trivial", py::detail::broadcast_trivial::non_trivial);
    m.def("vectorized_is_trivial", [](
                py::array_t<int, py::array::forcecast> arg1,
                py::array_t<float, py::array::forcecast> arg2,
                py::array_t<double, py::array::forcecast> arg3
                ) {
        py::ssize_t ndim;
        std::vector<py::ssize_t> shape;
        std::array<py::buffer_info, 3> buffers {{ arg1.request(), arg2.request(), arg3.request() }};
        return py::detail::broadcast(buffers, ndim, shape);
    });

    m.def("add_to", py::vectorize([](NonPODClass& x, int a) { x.value += a; }));
}
m") disp_vec_int_udiv("rem") disp_vec_vec_udiv("mod") disp_vec_int_udiv("mod") disp_has_0x("SIGNED") disp_neg("-") disp_neg("abs") disp_vec_vec_sdiv("/") disp_vec_int_sdiv("/") disp_vec_vec_sdiv("rem") disp_vec_int_sdiv("rem") disp_vec_vec_sdiv("mod") disp_vec_int_sdiv("mod") # Patterns to replace pats = { " @LOG\n": disp_all_log_funcs, " @ARITH\n": disp_all_arith_funcs, " @MATCH\n": disp_all_match_funcs, } spec_file = "numeric_std.vhdl" # proto_file='numeric_std-body.proto' def gen_body(proto_file): w("-- This -*- vhdl -*- file was generated from " + proto_file + "\n") for line in open(proto_file): if line in pats: pats[line]() continue w(line) # Copy spec for log in logics: for std in ["87", "93"]: out = open("v" + std + "/numeric_" + log + ".vhdl", "w") for line in open("numeric_" + log + ".proto"): if line == " @COMMON\n": for lcom in open("numeric_common.proto"): if lcom[0:2] == "--": pass elif std == "87" and ( '"xnor"' in lcom or '"sll"' in lcom or '"srl"' in lcom or '"rol"' in lcom or '"ror"' in lcom ): w("--" + lcom[2:]) else: w(lcom) else: w(line) out.close() # Generate bodies v93 = False for l in logics: logic = l out = open("v87/numeric_{0}-body.vhdl".format(l), "w") gen_body("numeric_{0}-body.proto".format(l)) out.close() v93 = True binary_funcs.append("xnor") for l in logics: logic = l out = open("v93/numeric_{0}-body.vhdl".format(l), "w") gen_body("numeric_{0}-body.proto".format(l)) out.close()