Skip to main content

TCP Sockets

As discussed previously, one possible way of invoking an auto-generated parametric optimizer is over a TCP/IP socket.

Edge Intelligence Logo

The server

Provided that option .with_tcp_interface_config() has been activated, the code generator creates a

build_directory/
|-- optimizer/
|-- tcp_iface_{optimizer_name}/

We can start the server from Python using the OptimizerTcpManager, but we can also start it by running cargo run from within tcp_iface_{optimizer_name} (where {optimizer_name} is the name of the optimizer). In order to see the log messages of the server, start it with

$ RUST_LOG=tcp_iface=info cargo run

We can then call the server from any programming language. Next, we will give examples using a Linux terminal and the command netcat (or nc).

TCP/IP Socket interface

Ping

In order to tell whether the server is alive and listening, we can "ping" it by sending a simple JSON of the form { "Ping" : N }, where N is any integer.

The server is expected to return { "Pong" : N } with the same number.

Here is an example:

$  echo '{ "Ping" : 1 }' | nc localhost 4598

that will return

{
"Pong" : 1
}

Run

To call the optimizer for a given parameter p we run

echo '{"Run": {"parameter": [1.0, 10.0]} }' | nc localhost 4598

which will return the following JSON document

{
"exit_status": "Converged",
"num_outer_iterations": 9,
"num_inner_iterations": 85,
"last_problem_norm_fpr": 8.879341428457282e-06,
"delta_y_norm_over_c": 7.147511762156759e-06,
"f2_norm": 0.0,
"solve_time_ms": 13.569209,
"penalty": 78125.0,
"solution": [
0.018786377508686856,
0.028186552233630396,
-0.10471801035932687,
0.02921323766336347,
0.0007963509453450717
],
"lagrange_multipliers": [
0.7699528316368849,
14.491152879893193
]
}

On success, the response from the TCP server is a JSON document with the following fields:

Response JSON FieldExplanation
exit_statusExit status; can be (i) Converged or (ii) NotConvergedIterations, if the maximum number of iterations was reached, therefore, the algorithm did not converge up to the specified tolerances, or (iii) NotConvergedOutOfTime, if the solver did not have enough time to converge
num_outer_iterationsNumber of outer iterations
num_inner_iterationsTotal number of inner iterations (for all inner problems)
last_problem_norm_fprNorm of the fixed-point residual of the last inner problem; this is a measure of the solution quality of the inner problem
delta_y_norm_over_cEuclidean norm of $c^{-1}(y^+-y)$, which is equal to the distance between $F_1(u, p)$ and $C$ at the solution
f2_normEuclidean norm of $F_2(u, p)$ at the solution
solve_time_msTotal execution time in milliseconds
penaltyLast value of the penalty parameter
solutionSolution
lagrange_multipliersVector of Lagrange multipliers (if $n_1 > 0$) or an empty vector, otherwise

If we call the solver again, it will use the previous solution as an initial guess. The client may override this behaviour and provide a different initial guess:

$ echo '{ "Run" : {"parameter" : [1.0,10.0], \
"initial_guess" : [0.0, 5.0, ...]}}' \
| nc localhost 4598

Kill

To kill the server, just send the following request

$  echo '{ "Kill" : 1 }' | nc localhost 4598

Error reporting

If a request cannot be processed, for example because the JSON payload is malformed, the request body is not valid UTF-8, or one of the provided vectors has the wrong dimension, the TCP server returns an error report instead of a solution.

The error report is a JSON document with three fields:

  • "type": "Error" so the client can distinguish errors from successful solver responses
  • code, a machine-readable integer code
  • message, a human-readable string with more context

In particular, the message field is now intended to be descriptive. For dimension-related errors it includes the provided and expected lengths, and for solver failures it includes the underlying solver-side reason whenever available.

For example, if the client provides an incompatible number of parameters, that is, if vector parameter has the wrong length, the server returns an error like the following:

{
"type": "Error",
"code": 3003,
"message": "wrong number of parameters: provided 1, expected 2"
}

Likewise, if the solver itself fails, the server returns code 2000 together with the propagated solver reason, for example:

{
"type": "Error",
"code": 2000,
"message": "problem solution failed: non-finite computation: gradient evaluation returned a non-finite value during an FBS step"
}

The following error codes may be returned to the client:

CodeExplanation
1000Invalid request: malformed JSON or invalid UTF-8 payload
1600Initial guess has incompatible dimensions
1700Wrong dimension of Lagrange multipliers
2000Problem solution failed; the message contains the solver-side reason
3003Vector parameter has wrong length

When using the Python TCP client, these responses are surfaced as opengen.tcp.solver_error.SolverError, whose code and message properties mirror the JSON payload returned by the TCP server.