Dynamical array increment at a certain index without knowing the increment value

In this program, I want to update an array of task_table(M) dynamically, i.e. I search the whole min_dist array exhaustively and if min_dist_p(n,k) is the minimum among min_dist(n,k,p), of which I do not know the value, I increment task_table(p) by one. Specifically, because I don’t know the value of a(n,k,p) before optimization, I want to increment it as a cvx object. However, I get this error when I write “task_table(last_node) = task_table(last_node) + a(n,k,p);”:

Incorrect number or types of inputs
or outputs for function sign.

Error in brute_force (line 124)
a(n,k,p) = ~sign(min_dist(n,k,p) - min_dist_p(n,k));

Do you know another way to write this program? Thanks….
cvx_begin

% --- Recursive Helper (Propagation Logic) ---
function path_data = build_propagation_paths(n, dist_matrix, task_table, r_max, L, M, depth, acc_dist, prev_uav, current_seq)
    %if 
    expr = (depth > 1 && task_table(prev_uav) < r_max)
    node.total_dist = acc_dist * ~expr;
    node.last_uav = prev_uav * ~expr;
    node.sequence = current_seq  * ~expr;
    if ~expr
        path_data =  node;
        return;
    end
    %end
    if depth > L
        node.total_dist = acc_dist;
        node.last_uav = prev_uav;
        node.sequence = current_seq;
        path_data = node;
        return;
    end
    path_data = [];
    for uav_idx = 1:M
        if uav_idx == prev_uav, continue; end
        step_dist = dist_matrix(prev_uav, uav_idx);
        child_paths = build_propagation_paths(n, dist_matrix, task_table, r_max, L, M, ...
                                               depth + 1, acc_dist + step_dist, ...
                                               uav_idx, [current_seq, uav_idx]);
        path_data = [path_data, child_paths];
    end
end
    cvx_begin
        variable alpha_switch(M,N,K)
        variable node_val
        variable beta_switch(N,M,M)
        clear rate_raw_first
        variable min_dist_p(N,num_tasks)
        
        variable index_min(N,num_tasks)
        variable task_table(M)
        expression a(N, num_tasks)
        total_sum_time_n = 0.0;
        for n=1:N
            for i = 1:M
                for j = 1:M
                    [trans_time rate]= transfer_time(M, n, i, j, sigma_squ, rho_0, P, H, Q, w, D); 
                    dist_matrix(n,i,j) = trans_time * GREAT_NUMBER * (1 - beta_switch(n,i,j));
                    trans_time_x_x(n,i,j) = trans_time;
                end
            end
        end
        
        for n=1:N
            trans_x_time = squeeze(trans_time_x_x(n,:,:));
            for k = 1:num_tasks
                    % 2. Discovery: Enumerate valid propagation paths
                paths = build_propagation_paths(n, squeeze(dist_matrix(n,:,:)), task_table, r_max, L, M, 1, 0, 1, 1);
                
                total_sum_time = 0.0;
               
                first_uav = 1;
                r_max = 1;
                f=1e+9;
                C = 1e+7;
                num_paths(n,k) = length(paths);
                % clear min_dist
                % variable min_dist(N,num_tasks,num_paths)
                D_travel = [];
                last_node = 0;
                for p = 1:num_paths(n,k)
                    min_dist(n,k,p) = paths(p).total_dist;
                    min_sequence = paths(p).sequence;
                    last_node = min_sequence(end);
                    task_table(last_node) = task_table(last_node) + a(n,k,p);
                    
                end

                total_sum_time = total_sum_time +  min_dist_p(n,k);
                 
            end
            total_sum_time_n = total_sum_time_n + total_sum_time;
        end
        minimize( total_sum_time_n)
        subject to
            for n=1:N
                 for k = 1:num_tasks
                     for p = 1:num_paths
                        min_dist(n,k,p) - min_dist_p(n,k) >= 0 
                        a(n,k,p) = ~sign(min_dist(n,k,p) - min_dist_p(n,k));
                     end
                     
                 end
            end
             for n=1:N
                 %total_beta = 0.0;
                 for i=1:M
                    sum(transpose(squeeze(beta_switch(n,i,:)))) <= 1;
                    %total_beta = total_beta + transpose(squeeze(beta_switch(n,i,:)));
                    for j=1:M
                        0<= beta_switch(n,i,j)<=1;
                    end
                 end
                 %total_beta <= 1;
             end    
    cvx_end
    

I don’t understand exactly what you’re trying to d. But if you’re trying to pick out a specific index which is a CVX variable or expression, or branching something based on a CVX variable or expression, you’re probably going to need to use binary or integer variables to do logic programming.

Browsing through Q&A at https://or.stackexchange.com/search?q=logic+ or https://or.stackexchange.com/search?q=index+is+decision+variable+ . might help you more clearly define what you are trying to do, and find an optimization problem formulation involving binary or integer variables which can be implemented following CVX MIDCP rules.

Thank you for your response. I am trying to do the following:

I have two “cvx” objects or a “cvx” and a “constant”. I compare both and try to get the result of comparison, which I don’t know the result yet. If the first argument is greater than the second, then increment the task array by one, which I don’t know yet numerically because I want to do every calculation is in cvx objects and I check every time if the task table overflows, again, I don’t know the value. Everything will have a value after cvx_end. Is there any way to accomplish this? I get always the same error:

Incorrect number or types of
inputs or outputs for function
sparse.

Thanks…

Baris Koc

You’ll need to do logic programming. Look at the links I provided. You may also find Logics and integer-programming representations - YALMIP to be useful; the ideas in it can be used in CVX MIDCP.

Thank you very much…

This time I tried logic programming, but I still have problems:

I have written this piece of code:

task_table(i) >= r_max_table(i) - M_big * lower_than_r_max(i)

task_table(i) <= r_max_table(i) + M_big * (1 - lower_than_r_max(i))

1)Here, “task_table” is a vector of variables and “r_max_table” is vector of constants and I use lower_than_r_max in the program for decision. If I define “variable lower_than_r_max(M) binary“, I get this error:

The following error occurred converting from cvx to
logical:
Conversion to logical from cvx is not possible.

expr = depth > 1 && lower_than_r_max(prev_uav);(I get error from this line)

  1. If I don’t define “variable lower_than_r_max(M) binary“, it cannot recognize the variable, then I initialize “lower_than_r_max”, but this time, CVX fixes the variable and the optimization doesn’t work.

In summary, I want “lower_than_r_max” to be a logical variable but not a CVX object. Is there any way to make this variable a logical variable.

There is no such thing in CVX as a logical variable. You can declare a binary variable. You still need to follow CVX’s rules. Things like expr = depth > 1 && lower_than_r_max(prev_uav) are not allowed if they involve any CVX variables or expressions. For anything which are just “MATLAB” variables, you can do whatever you want within MATLAB”s rules, but when such an expression enters into a CVX expression, it must follow CVX’s rules for input data.

You’re going to have to do whatever you’re trying to do using binary variables with logic programming. If you use YALMIP instead of CVX, you may be able to use implies to do the Big M logic modeling for you. Some solvers, such as Gurobi, allow you to use implies directly in their native modeling language (solver front end).

Perhaps what you would like to do the way you want to do it is now supported by CVXPY.

CVXPY Boolean Logic Operations

Simply put, when you DEFINE the model you cannot depend on the variable values that will only be known after you SOLVE the model.

Everything must be known at the time of defining the model, or it has to be a variable in the model (but not the assumed future value of that variable).

And that is typically done with binary variables and logic modeling as written by @Mark_L_Stone Or, per his other suggestion, you can use a modeling toolbox which will do that modeling step for you and introduce the necessary binary variables behind the scenes. But in either case it has to happen somewhere in the process.

Thank you very much…

This time I tried logic programming, but I still have problems:

I have written this piece of code:

task_table(i) >= r_max_table(i) - M_big * lower_than_r_max(i)

task_table(i) <= r_max_table(i) + M_big * (1 - lower_than_r_max(i))

1)Here, “task_table” is a vector of variables and “r_max_table” is vector of constants and I use lower_than_r_max in the program for decision. If I define “variable lower_than_r_max(M) binary“, I get this error:

The following error occurred converting from cvx to
logical:
Conversion to logical from cvx is not possible.

expr = depth > 1 && lower_than_r_max(prev_uav);(I get error from this line)

  1. If I don’t define “variable lower_than_r_max(M) binary“, it cannot recognize the variable, then I initialize “lower_than_r_max”, but this time, CVX fixes the variable and the optimization doesn’t work.

In summary, I want “lower_than_r_max” to be a logical variable but not a CVX object. Is there any way to make this variable a logical variable.

Hi,
I tried to solve by CVXPY, unfortunately, it didn’t work. The problem is the same; because lower_than_r_max(prev_uav) is not known before doing operations in this stage (such as path determination in the function), it cannot continue. Here, I share the content of the function in CVXPY:
def build_propagation_paths( lower_than_r_max: cp.Variable,

n: int,

dist_matrix: np.ndarray,

r_max_val: int,

L_depth: int,

M_dim: int,

depth: int,

acc_dist: Any,

prev_uav: int,

current_seq: List[int],

) → List[Dict[str, Any]]:

if depth > 1 and lower_than_r_max[prev_uav]:

return [{“total_dist”: acc_dist, “last_uav”: prev_uav, “sequence”: current_seq}]

if depth > L_depth:

return [{“total_dist”: acc_dist, “last_uav”: prev_uav, “sequence”: current_seq}]

path_data = []

for uav_idx in range(M_dim):

if uav_idx == prev_uav:

continue

step_dist = dist_matrix[prev_uav, uav_idx]

if isinstance(acc_dist, (int, float)) and acc_dist == 0:

new_acc = step_dist

else:

new_acc = acc_dist + step_dist

child_paths = build_propagation_paths(

lower_than_r_max,

n,

dist_matrix,

r_max_val,

L_depth,

M_dim,

depth + 1,

new_acc,

uav_idx,

current_seq + [uav_idx],

)

path_data.extend(child_paths)

if not path_data:

return [{“total_dist”: acc_dist, “last_uav”: prev_uav, “sequence”: current_seq}]

return path_data

If you have something which you want to use “logically”, but it is not defined purely in terms of MATLAB variables, i.e. as input data to the CVX program, you will need to do (further) logic (Big M) modeling with it.

For help using CVXPY, use one of its support channels. CVXPY’s Boolean Logic Operation rules don’t have counterparts in CVX’s rule, and hence are outside the area covered by people answering questions here. Nevertheless, CVXPY has built in capabilities which make logic operations easier to deal with than in CVX, so that may be a beneficial route for you to pursue.

What I’m trying do is the following: I have objects for task-offloading and objects connect to each other for task delivery and each has a r_max capacity(beta_switch stands for on/off connection between objects). If the task selects a minimal path for task delivery it increments task_table by one. Also lower_than_r_max represents if the task_table is full or not. After task_table increment, it determines new paths. If I try to express lower_than_r_max(i) in terms of M_Big, it solves(optimally) but cannot show the solved values of task_table array. I have 4 tasks for processing (arrival_times) , task_table is [3,0,0] initially and r_max=7. I expect the program to distribute tasks to 2nd and 3rd objects. Here is my code:
“”"

UAV trajectory and task assignment optimization — CVXPY version.

Converted from MATLAB CVX; all indices use 0-based loops (tested).

“”"

import numpy as np

import cvxpy as cp

import matplotlib.pyplot as plt

from typing import List, Dict, Any, Tuple, Optional, cast

# ---------------------------------------------------------------------------

# Parameters (match MATLAB)

# ---------------------------------------------------------------------------

M = 3

D = 1e8

K = 4

w = np.array([

[305, 5],

[7.5, 315],

[-314, 5],

[5, -313],

])

GREAT_NUMBER = 1e9

P_max = 0.1

rho_0 = 10 ** (-60 / 10)

tau_num = 100

sigma_squ = 10 ** (-110 / 10)

V_max = 50

eps = 1e-4

T = 200

delta_T = 10

N = int(T / delta_T) # 20

S_max = 100

d_min = 30

H = 100

L = 3 # depth

arrival_times = np.array([0.0, 20, 40, 56])

num_tasks = len(arrival_times)

r_max = 5

B = 1e8

M_big = 1e6

tol = 1e-9

# Path-selection big-M constraints can over-constrain; set True to enable (may be infeasible).

USE_PATH_SELECTION_CONSTRAINTS = False

def transfer_time(

M_dim: int,

n: int,

first_uav: int,

second_uav: int,

sigma_squ: float,

rho_0: float,

P: np.ndarray,

H: float,

Q: np.ndarray,

w: np.ndarray,

D: float,

B: float,

) → Tuple[float, float]:

“”"

Compute transfer time and rate between two UAVs at time slot n.

Returns (trans_time, rate_sum). Uses 0-based UAV indices.

"""

rate_sum = 0.0

q_first = Q[first_uav, n, :]

q_second = Q[second_uav, n, :]

dist_sq = np.sum((q_first - q_second) ** 2)

denom = sigma_squ

for j in range(M_dim):

if j != first_uav:

q_j = Q[j, n, :]

dist_j_sq = np.sum((q_first - q_j) ** 2)

denom += P[j, n] * rho_0 / (H**2 + dist_j_sq)

sinr = P[first_uav, n] * rho_0 / (H**2 + dist_sq) / denom

rate_raw_first = np.log2(1 + sinr)

if first_uav == second_uav:

return 0.0, 0.0

trans_time = 1.0 / (rate_raw_first / D * B)

rate_sum = rate_raw_first

return trans_time, rate_sum

def build_propagation_paths(

lower_than_r_max: np.ndarray,

n: int,

dist_matrix: np.ndarray,

r_max_val: int,

L_depth: int,

M_dim: int,

depth: int,

acc_dist: Any,

prev_uav: int,

current_seq: List[int],

) → List[Dict[str, Any]]:

“”"

Recursive helper: build propagation paths. Returns list of dicts with

'total_dist', 'last_uav', 'sequence'. All indices 0-based.

"""

if depth > 1 and lower_than_r_max[prev_uav]:

return [{“total_dist”: acc_dist, “last_uav”: prev_uav, “sequence”: current_seq}]

if depth > L_depth:

return [{“total_dist”: acc_dist, “last_uav”: prev_uav, “sequence”: current_seq}]

path_data = []

for uav_idx in range(M_dim):

if uav_idx == prev_uav:

continue

step_dist = dist_matrix[prev_uav, uav_idx]

if isinstance(acc_dist, (int, float)) and acc_dist == 0:

new_acc = step_dist

else:

new_acc = acc_dist + step_dist

child_paths = build_propagation_paths(

lower_than_r_max,

n,

dist_matrix,

r_max_val,

L_depth,

M_dim,

depth + 1,

new_acc,

uav_idx,

current_seq + [uav_idx],

)

path_data.extend(child_paths)

if not path_data:

return [{“total_dist”: acc_dist, “last_uav”: prev_uav, “sequence”: current_seq}]

return path_data

def build_initial_Q() → np.ndarray:

“”“Initial waypoints for 3 UAVs, shape (3, 21, 2). 0-based.”“”

Q = np.zeros((3, 21, 2))

Q[0, :, :] = [

[300, 10], [240, 60], [180, 120], [120, 180], [60, 240], [10, 300],

[-60, 240], [-120, 180], [-180, 120], [-240, 60], [-300, 10],

[-240, -60], [-180, -120], [-120, -180], [-60, -240], [10, -300],

[60, -240], [120, -180], [180, -120], [240, -60], [300, 10],

]

Q[1, :, :] = [

[0, 10], [0, 60], [0, 120], [0, 180], [0, 240], [10, 307],

[60, 300], [120, 300], [180, 300], [240, 300], [310, 300],

[300, 240], [300, 180], [300, 120], [300, 60], [300, 10],

[240, 0], [180, 0], [120, 0], [60, 0], [0, 10],

]

Q[2, :, :] = [

[0, -10], [0, -60], [0, -120], [0, -180], [0, -240], [10, -307],

[-60, -300], [-120, -300], [-180, -300], [-240, -300], [-310, -300],

[-300, -240], [-300, -180], [-300, -120], [-300, -60], [-300, -10],

[-240, 0], [-180, 0], [-120, 0], [-60, 0], [0, -10],

]

return Q

def build_initial_P() → np.ndarray:

“”“P shape (M, N) with P_max.”“”

return np.full((M, N), P_max)

def run_one_iteration(Q: np.ndarray, P: np.ndarray) → Tuple[Optional[float], Optional[dict]]:

“”"

Run one CVXPY optimization iteration. Returns (objective_value, solution_dict or None).

solution_dict includes: add_table, beta_switch, max_dist_ind, lower_than_r_max, paths.

"""

alpha_switch = cp.Variable((M, N, K), name=“alpha_switch”)

beta_switch = cp.Variable((N, M, M), name=“beta_switch”, nonneg=True)

add_table = cp.Variable(M, boolean=True, name=“add_table”)

max_dist_ind = cp.Variable((N, num_tasks), name=“max_dist_ind”, nonneg=True)

task_table_arr = cp.Variable((N, num_tasks, M), name=“task_table_arr”)

task_table = cp.Variable((M), integer=True, name=“task_table”)

task_table.value = np.array([3, 0, 0])

trans_time_nij = np.zeros((N, M, M))

add_table_arr = cp.Variable((N,num_tasks,M), boolean=True, name=“add_table_arr”)

#lower_than_r_max = cp.Variable((M), boolean=True, name=“lower_than_r_max”)

lower_than_r_max = np.array([True, True, True])

for n in range(N):

for i in range(M):

for j in range(M):

trans_time_nij[n, i, j], _ = transfer_time(

M, n, i, j, sigma_squ, rho_0, P, H, Q, w, D, B

)

dist_matrix_expr = {}

for n in range(N):

for i in range(M):

for j in range(M):

t = trans_time_nij[n, i, j]

dist_matrix_expr[(n, i, j)] = t * (

1 + GREAT_NUMBER * (1 - beta_switch[n, i, j])

)

r_max_table = np.array([r_max] * M)

pathlist_per_nk: List[List[List[Dict]]] = []

num_paths_nk = np.zeros((N, num_tasks), dtype=int)

max_dist_p_expr: List[List[cp.Expression]] = [

[cp.Constant(0.0) for _ in range(num_tasks)] for _ in range(N)

]

# Symbolic running task_table (expression) and constraints list.

constraints: List[Any] = []

for n in range(N):

pathlist_per_nk.append([])

dist_n = np.zeros((M, M), dtype=object)

for i in range(M):

for j in range(M):

dist_n[i, j] = dist_matrix_expr[(n, i, j)]

for k in range(num_tasks):

paths = []

start_uav = 0

paths_from_start = build_propagation_paths(

lower_than_r_max,

n,

dist_n,

r_max,

L,

M,

depth=1,

acc_dist=0.0,

prev_uav=start_uav,

current_seq=[start_uav],

)

paths.extend(paths_from_start)

pathlist_per_nk[n].append(paths)

num_paths_nk[n, k] = len(paths)

if len(paths) == 0:

max_dist_p_expr[n][k] = cp.Constant(0.0)

else:

path_dists = [p[“total_dist”] for p in paths]

max_dist_p_expr[n][k] = (

path_dists[0] if len(path_dists) == 1 else cp.maximum(*path_dists)

)

task_table = task_table + add_table

#lower_than_r_max = (r_max_table - 1) >= task_table

# Link CVXPY Variable task_table_arr to the symbolic task_table expression

constraints.append(add_table_arr[n, k,:] == add_table[:])

constraints.append(task_table_arr[n, k, :] == task_table[:])

paths_summary: List[List[List[Dict[str, Any]]]] = [

\[{"sequence": p\["sequence"\], "last_uav": p\["last_uav"\]} *for* p *in* paths_k\] *for* paths_k *in* paths_n

for paths_n in pathlist_per_nk

]

total_sum_time_n = 0.0

for n in range(N):

for k in range(num_tasks):

total_sum_time_n += max_dist_ind[n, k]

constraints = []

for n in range(N):

for k in range(num_tasks):

mp = max_dist_p_expr[n][k]

constraints.append(max_dist_ind[n, k] >= mp)

constraints.append(cp.sum(add_table) == 1)

#task_table_final = task_table_init + num_tasks * add_table

for i in range(M):

constraints.append(

task_table[i] <= r_max_table[i]

)

constraints.append(r_max_table[i] >= task_table[i] + 1e-5 - M_big * (1 - lower_than_r_max[i]))

constraints.append(r_max_table[i] <= task_table[i] + M_big * lower_than_r_max[i])

if USE_PATH_SELECTION_CONSTRAINTS:

for n in range(N):

paths_n = pathlist_per_nk[n]

for k in range(num_tasks):

paths = paths_n[k]

if len(paths) == 0:

continue

path_dists = [p[“total_dist”] for p in paths]

for p_idx, path in enumerate(paths):

L_uav = path[“last_uav”]

d_p = path_dists[p_idx]

for q_idx, _ in enumerate(paths):

if p_idx == q_idx:

continue

d_q = path_dists[q_idx]

constraints.append(

d_p - d_q <= M_big * (1 - add_table[L_uav])

)

for n in range(N):

for i in range(M):

constraints.append(cp.sum(beta_switch[n, i, :]) <= 2)

constraints.append(beta_switch[n, i, i] == 0)

for j in range(M):

constraints.append(beta_switch[n, i, j] <= 1)

constraints.append(beta_switch[n, i, j] >= 0)

prob = cp.Problem(cp.Minimize(total_sum_time_n), constraints)

try:

prob.solve(verbose=True)

if prob.status in (“optimal”, “optimal_inaccurate”):

assert prob.value is not None

obj_val = float(cast(float, prob.value))

add_val = np.array(add_table.value).flatten()

ltr_val = np.array(lower_than_r_max).flatten()

task_table_arr_val = np.array(task_table_arr.value)

add_table_arr_val = np.array(add_table_arr.value)

return obj_val, {

“add_table”: add_val,

“beta_switch”: beta_switch.value,

“max_dist_ind”: max_dist_ind.value,

“lower_than_r_max”: ltr_val,

“paths”: paths_summary,

“task_table_arr”: task_table_arr_val,

“add_table_arr”: add_table_arr_val,

        }

except Exception as e:

print(“Solve failed:”, e)

return None, None

def main() → None:

Q = build_initial_Q()

P = build_initial_P()

plt.figure(figsize=(8, 8))

plt.plot(Q[0, :, 0], Q[0, :, 1], “-*”, label=“UAV-1”)

plt.plot(Q[1, :, 0], Q[1, :, 1], “-s”, label=“UAV-2”)

plt.plot(Q[2, :, 0], Q[2, :, 1], “-+”, label=“UAV-3”)

plt.xlabel(“x”)

plt.ylabel(“y”)

plt.title(“Double-UAV Initial Trajectory”)

plt.legend()

plt.grid(True)

plt.gcf().set_size_inches(8, 8)

plt.tight_layout()

plt.savefig(“uav_initial_trajectory.png”, dpi=150)

plt.close()

print(“Saved uav_initial_trajectory.png”)

val, sol = run_one_iteration(Q, P)

if val is not None and sol is not None:

print(“Objective:”, val)

add = np.array(sol[“add_table”]).reshape(-1)

ltr = np.array(sol[“lower_than_r_max”]).reshape(-1)

task_table_arr = np.array(sol[“task_table_arr”])

add_table_arr = np.array(sol[“add_table_arr”])

paths = sol[“paths”]

print(“add_table (solution):”, add)

print(“lower_than_r_max (solution):”, ltr)

print(“Per-task values (k: lower_than_r_max, add_table, task_table):”)

for n in range(N):

for k in range(num_tasks):

print(f" task {k}: task_table={task_table_arr[n,k,:]}")

print(f" task {k}: add_table_arr={add_table_arr[n,k,:]}")

else:

print(“Optimization did not solve to optimal.”)

if _name_ == “_main_”:

main()

As I wrote, this is not the right venue for CVXPY help.

I believe DIscord CVXPY questions is now the best venue to ask technical questions on CVXPY.