I'm experimenting with a testing regime for my neovim plugin regexplainer. The idea is to put a JSDoc docblock containing the expected output on top of each query.
/**
* #group CaptureGroups
* **0-9** (_>= 0x_)
* capture group 1:
* **0-9**
*/
/\d*(\d)/;
My test file would then iterate through the file, getting each regex from the javascript tree, along with it's expected output from the comment. I could then run my plugin on the regexp and check the output against the comment.
I wrote this function to query the document for comments with regexp following.
function M.get_regexes_with_descriptions()
local log = require'regexplainer.utils'.debug
local query_js = vim.treesitter.query.parse_query('javascript', [[
(comment) #comment
(expression_statement
(regex
(regex_pattern) #regex))
]])
local query_jsdoc = vim.treesitter.query.parse_query('jsdoc', [[
(tag_name) #tag_name
(description) #description
]])
local parser = vim.treesitter.get_parser(0, 'javascript')
local tree, other = unpack(parser:parse())
for id, node, metadata in query_js:iter_captures(tree:root(), 0) do
local name = query_js.captures[id] -- name of the capture in the query
if node:type() == 'comment' then
for cid, cnode, cmetadata in node:iter_children() do
local cname = query_jsdoc.captures[cid] -- name of the capture in the query
log(get_info_on_capture(cid, cnode, cname, cmetadata))
end
end
end
end
I expect this to log the tag name and description nodes from the 'jsdoc' grammar, but i actually get nothing
So how do I "query down" into the embedded JSDoc part of the tree? I tried this query, but got a query: invalid node type at position 10 error when parsing the query:
(comment (description) #desc) #comment
(expression_statement
(regex
(regex_pattern) #regex))
The TSPlayground output for the file in question looks like this:
comment [0, 0] - [5, 3]
tag [1, 3] - [4, 12]
tag_name [1, 3] - [1, 9]
description [1, 10] - [4, 12]
expression_statement [6, 0] - [6, 10]
regex [6, 0] - [6, 9]
pattern: regex_pattern [6, 1] - [6, 8]
term [6, 1] - [6, 8]
character_class_escape [6, 1] - [6, 3]
zero_or_more [6, 3] - [6, 4]
anonymous_capturing_group [6, 4] - [6, 8]
pattern [6, 5] - [6, 7]
term [6, 5] - [6, 7]
character_class_escape [6, 5] - [6, 7]
EDIT: A Workaround
I developed this workaround but it's a bit unsatisfying, I'd prefer to get the comment's contents along with the regexp in a single query
function M.get_regexes_with_descriptions()
local log = require'regexplainer.utils'.debug
local parsers = require "nvim-treesitter.parsers"
local query_js = vim.treesitter.query.parse_query('javascript', [[
(expression_statement
(regex
(regex_pattern) #regex)) #expr
]])
local query_jsdoc = vim.treesitter.query.parse_query('jsdoc', [[
(tag
(tag_name) #tag_name
((description) #description
;; (#eq? #tag_name "example")
))
]])
local parser = parsers.get_parser(0)
local tree = unpack(parser:parse())
local caps = {}
for id, node in query_js:iter_captures(tree:root(), 0) do
local cap = {}
local name = query_js.captures[id] -- name of the capture in the query
log(id, name, ts_utils.get_node_text(node))
if name == 'expr' then
cap.regex = node:named_child('pattern')
cap.comment = node:prev_sibling()
table.insert(caps, cap)
end
end
local results = {}
for _, cap in ipairs(caps) do
local result = {}
result.text = ts_utils.get_node_text(cap.regex)
local comment_str = table.concat(ts_utils.get_node_text(cap.comment), '\n')
local jsdoc_parser = vim.treesitter.get_string_parser(comment_str, 'jsdoc')
local jsdoc_tree = jsdoc_parser:parse()[1]
for id, ch, metadata in query_jsdoc:iter_captures(jsdoc_tree:root()) do
if query_jsdoc.captures[id] == 'description' then
result.description = table.concat(
vim.tbl_map(function(line)
return line:gsub('^%s+%*', '')
end, ts_utils.get_node_text(ch)), '\n')
end
end
table.insert(results, result)
end
return results
end
Related
I am new to constraint programming and OR-Tools. A brief about the problem. There are 8 positions, for each position I need to decide which move of type A (move_A) and which move of type B (move_B) should be selected such that the value achieved from the combination of the 2 moves (at each position) is maximized. (This is only a part of the bigger problem though). And I want to use AddElement approach to do the sub setting.
Please see the below attempt
from ortools.sat.python import cp_model
model = cp_model.CpModel()
# value achieved from combination of different moves of type A
# (moves_A (rows)) and different moves of type B (moves_B (columns))
# for e.g. 2nd move of type A and 3rd move of type B will give value = 2
value = [
[ -1, 5, 3, 2, 2],
[ 2, 4, 2, -1, 1],
[ 4, 4, 0, -1, 2],
[ 5, 1, -1, 2, 2],
[ 0, 0, 0, 0, 0],
[ 2, 1, 1, 2, 0]
]
# 6 moves of type A
num_moves_A = len(value)
# 5 moves of type B
num_moves_B = len(value[0])
num_positions = 8
type_move_A_position = [model.NewIntVar(0, num_moves_A - 1, f"move_A[{i}]") for i in range(num_positions)]
type_move_B_position = [model.NewIntVar(0, num_moves_B - 1, f"move_B[{i}]") for i in range(num_positions)]
value_position = [model.NewIntVar(0, 10, f"value_position[{i}]") for i in range(num_positions)]
# I am getting an error when I run the below
objective_terms = []
for i in range(num_positions):
model.AddElement(type_move_B_position[i], value[type_move_A_position[i]], value_position[i])
objective_terms.append(value_position[i])
The error is as follows:
Traceback (most recent call last):
File "<ipython-input-65-3696379ce410>", line 3, in <module>
model.AddElement(type_move_B_position[i], value[type_move_A_position[i]], value_position[i])
TypeError: list indices must be integers or slices, not IntVar
In MiniZinc the below code would have worked
var int: obj = sum(i in 1..num_positions ) (value [type_move_A_position[i], type_move_B_position[i]])
I know in OR-Tools we will have to create some intermediary variables to store results first, so the above approach of minizinc will not work. But I am struggling to do so.
I can always create a 2 matrix of binary binary variables one for num_moves_A * num_positions and the other for num_moves_B * num_positions, add re;evant constraints and achieve the purpose
But I want to learn how to do the same thing via AddElement constraint
Any help on how to re-write the AddElement snippet is highly appreciated. Thanks.
AddElement is 1D only.
The way it is translated from minizinc to CP-SAT is to create an intermediate variable p == index1 * max(index2) + index2 and use it in an element constraint with a flattened matrix.
Following Laurent's suggestion (using AddElement constraint):
from ortools.sat.python import cp_model
model = cp_model.CpModel()
# value achieved from combination of different moves of type A
# (moves_A (rows)) and different moves of type B (moves_B (columns))
# for e.g. 2 move of type A and 3 move of type B will give value = 2
value = [
[-1, 5, 3, 2, 2],
[2, 4, 2, -1, 1],
[4, 4, 0, -1, 2],
[5, 1, -1, 2, 2],
[0, 0, 0, 0, 0],
[2, 1, 1, 2, 0],
]
min_value = min([min(i) for i in value])
max_value = max([max(i) for i in value])
# 6 moves of type A
num_moves_A = len(value)
# 5 moves of type B
num_moves_B = len(value[0])
# number of positions
num_positions = 5
# flattened matrix of values
value_flat = [value[i][j] for i in range(num_moves_A) for j in range(num_moves_B)]
# flattened indices
flatten_indices = [
index1 * len(value[0]) + index2
for index1 in range(len(value))
for index2 in range(len(value[0]))
]
type_move_A_position = [
model.NewIntVar(0, num_moves_A - 1, f"move_A[{i}]") for i in range(num_positions)
]
model.AddAllDifferent(type_move_A_position)
type_move_B_position = [
model.NewIntVar(0, num_moves_B - 1, f"move_B[{i}]") for i in range(num_positions)
]
model.AddAllDifferent(type_move_B_position)
# below intermediate decision variable is created which
# will store index corresponding to the selected move of type A and
# move of type B for each position
# this will act as index in the AddElement constraint
flatten_index_num = [
model.NewIntVar(0, len(flatten_indices), f"flatten_index_num[{i}]")
for i in range(num_positions)
]
# another intermediate decision variable is created which
# will store value corresponding to the selected move of type A and
# move of type B for each position
# this will act as the target in the AddElement constraint
value_position_index_num = [
model.NewIntVar(min_value, max_value, f"value_position_index_num[{i}]")
for i in range(num_positions)
]
objective_terms = []
for i in range(num_positions):
model.Add(
flatten_index_num[i]
== (type_move_A_position[i] * len(value[0])) + type_move_B_position[i]
)
model.AddElement(flatten_index_num[i], value_flat, value_position_index_num[i])
objective_terms.append(value_position_index_num[i])
model.Maximize(sum(objective_terms))
# Solve
solver = cp_model.CpSolver()
status = solver.Solve(model)
solver.ObjectiveValue()
for i in range(num_positions):
print(
str(i)
+ "--"
+ str(solver.Value(type_move_A_position[i]))
+ "--"
+ str(solver.Value(type_move_B_position[i]))
+ "--"
+ str(solver.Value(value_position_index_num[i]))
)
The below version uses AddAllowedAssignments constraint to achieve the same purpose (per Laurent's alternate approach) :
from ortools.sat.python import cp_model
model = cp_model.CpModel()
# value achieved from combination of different moves of type A
# (moves_A (rows)) and different moves of type B (moves_B (columns))
# for e.g. 2 move of type A and 3 move of type B will give value = 2
value = [
[-1, 5, 3, 2, 2],
[2, 4, 2, -1, 1],
[4, 4, 0, -1, 2],
[5, 1, -1, 2, 2],
[0, 0, 0, 0, 0],
[2, 1, 1, 2, 0],
]
min_value = min([min(i) for i in value])
max_value = max([max(i) for i in value])
# 6 moves of type A
num_moves_A = len(value)
# 5 moves of type B
num_moves_B = len(value[0])
# number of positions
num_positions = 5
type_move_A_position = [
model.NewIntVar(0, num_moves_A - 1, f"move_A[{i}]") for i in range(num_positions)
]
model.AddAllDifferent(type_move_A_position)
type_move_B_position = [
model.NewIntVar(0, num_moves_B - 1, f"move_B[{i}]") for i in range(num_positions)
]
model.AddAllDifferent(type_move_B_position)
value_position = [
model.NewIntVar(min_value, max_value, f"value_position[{i}]")
for i in range(num_positions)
]
tuples_list = []
for i in range(num_moves_A):
for j in range(num_moves_B):
tuples_list.append((i, j, value[i][j]))
for i in range(num_positions):
model.AddAllowedAssignments(
[type_move_A_position[i], type_move_B_position[i], value_position[i]],
tuples_list,
)
model.Maximize(sum(value_position))
# Solve
solver = cp_model.CpSolver()
status = solver.Solve(model)
solver.ObjectiveValue()
for i in range(num_positions):
print(
str(i)
+ "--"
+ str(solver.Value(type_move_A_position[i]))
+ "--"
+ str(solver.Value(type_move_B_position[i]))
+ "--"
+ str(solver.Value(value_position[i]))
)
Recently, when using pybind11, I encountered how to use python code a[0: 3,0: 3] to achieve this problem. There is currently a slice function, but I did not find the relevant user manual, resulting in the incorrect access.
ps: I have tried this way you said before, but when I printed out the matrix, I found it was not right. I don’t know the reason. Please help me. Thank you very much.
cout result
py::scoped_interpreter guard{};
py::module np = py::module::import("numpy");
py::object random = np.attr("random");
py::module sys = py::module::import("sys");
py::print(sys.attr("path"));
py::module scipy = py::module::import("scipy.ndimage");
// get scipy.optimize.curve_fit
py::function affine_transform = scipy.attr("affine_transform");
py::array_t<float> new_affine = np.attr("eye")(4);
py::array_t<float> new_af = new_affine[py::make_tuple(py::slice(0, 3, 1), py::slice(0, 3, 1))];
std::cout << numpy_to_cv_mat(new_affine) << endl;
std::cout << new_af.size() << endl;
std::cout << numpy_to_cv_mat(new_af) << endl;
cv::Mat numpy_to_cv_mat(py::array_t<float>& input) { py::buffer_info buf = input.request(); cv::Mat mat(buf.shape[0], buf.shape[1], CV_32FC1, (float*)buf.ptr); return mat; }
There are a few details required need here. First, py::slice(start, stop, step) creates a slice object via pybind11, like what would be created by slice(start, stop, step) in Python.
Second, given a py::array object a, the [] operator does work for slicing in C++ (a[py::slice(s0,s1,st)]) but there is a big caveat: a[] allows (and compiles) with multiple arguments, but only one argument is actually used for slicing, so a[slice(...), slice(...)] only applies the slice on the first dimension.
For multi-dimensional slicing, the [] operator must be passed a py::tuple of py::slice objects. For example, a[0:3,0:3] in Python would be translated to the following in C++:
// a is py::array
a[py::make_tuple(py::slice(0,3,1), py::slice(0,3,1))]
Putting this together, here's a full example which creates and slices a 2D array based on start/stop inputs:
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
py::array do_slice(py::array a, py::int_ start, py::int_ stop) {
auto res = a[py::make_tuple(py::slice(start, stop, 1), py::slice(start, stop, 1))];
return res;
}
PYBIND11_MODULE(ex, m) {
m.def("do_slice", &do_slice);
}
Some usage examples after compiling:
>>> import numpy as np
>>> a = np.arange(16).reshape(4,4)
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
>>> import ex
>>> ex.do_slice(a, 0, 1)
array([[0]])
>>> ex.do_slice(a, 0, 2)
array([[0, 1],
[4, 5]])
>>> ex.do_slice(a, 0, 3)
array([[ 0, 1, 2],
[ 4, 5, 6],
[ 8, 9, 10]])
I have a list which is needed to be converted to nested lists in a list
my_list = [2,5,6,7,8,15,34,56,78]
I need list as
final_list = [[2,5,6],[7,8,15],[34,56,78]]
I wrote code using for loop with length command and range command, I know there is error with range function, but I couldn't figure it out.
my_list = [2,5,6,7,8,15,34,56,78]
max_split = 3
final_list = [[len(my_list) for _ in range(max_split)] for _ in range(max_split)]
print(final_list)
But the output I get is [[9,9,9],[9,9,9],[9,9,9]]
You can try following code
my_list = [2,5,6,7,8,15,34,56,78]
max_split = 3
final_list = [my_list[i:i + max_split ] for i in range(0, len(my_list), max_split )]
print(final_list)
Demo.
If you use the indexes returned by the for loops, you can use them to count through the indexes in your list like this:
my_list = [2,5,6,7,8,15,34,56,78]
max_split = 3
final_list = [[my_list[i+3*j] for i in range(max_split)] for j in range(max_split)]
print(final_list)
Output:
[[2, 5, 6], [7, 8, 15], [34, 56, 78]]
I have a string with the following format:
var cadenaCoordenadas = """
1,1
1,3
4,1
5,1
1,5
1,6
2,5
0,0
"""
What I want is that each line is in the following format (in an array) to manipulate it (with Int data types as I will do operations with the new string):
[1,1]
I have the following code:
var arregloEntradas = cadenaCoordenadas.split(separator: "\n")
print("primer Arreglo: ", arregloEntradas)
for i in stride(from: 0, through:arregloEntradas.count - 1, by: 1){
let arregloEntradasFinal = arregloEntradas[i].split(separator: ",")
print(arregloEntradasFinal)
}
and I get the result of this:
this is the result
as you can see, the array elements are of string type, however I require them to be of Int type:
[1,1]
[1,3]
[4,1]
...
I hope you can help me, thank you in advance.
Here's one approach using some splitting and mapping:
var cadenaCoordenadas = """
1,1
1,3
4,1
5,1
1,5
1,6
2,5
0,0
"""
let arregloEntradasFinal = cadenaCoordenadas.split(separator: "\n")
.map { $0.split(separator: ",").compactMap { Int($0) } }
print(arregloEntradasFinal)
Output:
[[1, 1], [1, 3], [4, 1], [5, 1], [1, 5], [1, 6], [2, 5], [0, 0]]
var arregloEntradas = cadenaCoordenadas.split(separator: "\n")
print("primer Arreglo: ", arregloEntradas)
for i in stride(from: 0, through:arregloEntradas.count - 1, by: 1){
let arregloEntradasFinal = arregloEntradas[i].split(separator: ",").map { Int(String($0)) }
print(arregloEntradasFinal)
}
What you're getting in arregloEntradasFinal is correct since you're processing the string array. Later, when you want to use arregloEntradasFinal again, you should again split a string by a comma separator from arregloEntradasFinal and use the individual Int value. For example:
let index = 0 // You can also loop through the array
let values = arregloEntradasFinal[index].split(separator: ",")
let num1 = Int(values.first ?? 0) // If no value then returns 0
let num2 = Int(values.last ?? 0) // If no value then returns 0
Note - this is one of the way without using the map function.
Here's my toy example:
t = table([1,2,3;4,5,6;7,8,9],[10,11,12;13,14,15;16,17,18]);
t.Properties.VariableNames = {'system1', 'system2'};
t.Properties.RowNames = {'obs1', 'obs2', 'obs3'};
I am wondering if it's possible to assign sub titles to the three columns of every variable, such as {'min', 'mean', 'max'}?
You can put those subtitles within the variables using a cell array like this:
t = table({'min', 'mean', 'max'; 1, 2, 3; 4, 5, 6; 7, 8, 9},...
{'min', 'mean', 'max'; 10, 11, 12; 13, 14, 15; 16, 17, 18});
t.Properties.VariableNames = {'system1', 'system2'};
t.Properties.RowNames = {'.','obs1', 'obs2', 'obs3'};
%if you don't like dot (.) as a row name, replace it with char(8203) to have nameless row
which will give:
t =
4×2 table
system1 system2
________________________ ________________________
. 'min' 'mean' 'max' 'min' 'mean' 'max'
obs1 [ 1] [ 2] [ 3] [ 10] [ 11] [ 12]
obs2 [ 4] [ 5] [ 6] [ 13] [ 14] [ 15]
obs3 [ 7] [ 8] [ 9] [ 16] [ 17] [ 18]
If you're looking for functional solution (e.g. t.system1.min) You can nest sub-tables for system1 and system2 with {'min', 'mean', 'max'} as Variable Names. Visually it won't be as useful as other solutions.
dat1 = [1,2,3;4,5,6;7,8,9];
dat2 = [10,11,12;13,14,15;16,17,18];
s1 = table(dat1(:,1),dat1(:,2),dat1(:,3));
s2 = table(dat2(:,1),dat2(:,2),dat2(:,3));
s1.Properties.VariableNames = {'min','mean','max'};
s1.Properties.RowNames = {'obs1', 'obs2', 'obs3'};
s2.Properties.VariableNames = {'min','mean','max'};
s2.Properties.RowNames = {'obs1', 'obs2', 'obs3'};
t = table(s1,s2);
t.Properties.VariableNames = {'system1', 'system2'};
t.Properties.RowNames = {'obs1', 'obs2', 'obs3'};