Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions keras/src/backend/openvino/excluded_concrete_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@ EmbeddingTest::test_quantize_by_setting_dtype_policy_int4
EmbeddingTest::test_quantize_int_int4
EmbeddingTest::test_quantize_on_subclass_int4
EmbeddingTest::test_quantize_when_already_quantized_int4
ExtractSequencesOpTest::test_extract_sequences_call
FalseNegativesTest::test_unweighted
FalseNegativesTest::test_weighted
FalsePositivesTest::test_unweighted
Expand Down Expand Up @@ -429,8 +428,6 @@ LSTMTest::test_pass_initial_state
LSTMTest::test_statefulness
MathOpsCorrectnessTest::test_erfinv_operation_basic
MathOpsCorrectnessTest::test_erfinv_operation_dtype
MathOpsCorrectnessTest::test_erfinv_operation_edge_cases
MathOpsCorrectnessTest::test_extract_sequences
MathOpsCorrectnessTest::test_logdet
MaxPoolingBasicTest::test_max_pooling1d0
MaxPoolingBasicTest::test_max_pooling1d1
Expand Down
115 changes: 111 additions & 4 deletions keras/src/backend/openvino/math.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,68 @@ def qr(x, mode="reduced"):


def extract_sequences(x, sequence_length, sequence_stride):
raise NotImplementedError(
"`extract_sequences` is not supported with openvino backend"
)
x = get_ov_output(x)
x_shape = x.partial_shape
ndim = len(x_shape)

# Define common constants for reuse
zero_const_1d = ov_opset.constant([0], Type.i32)
shape_tensor = ov_opset.shape_of(x, output_type=Type.i32).output(0)

last_idx = ov_opset.constant([ndim - 1], Type.i32)
axis0 = ov_opset.constant(0, Type.i32)
signal_len_1d = ov_opset.gather(shape_tensor, last_idx, axis0).output(0)
signal_len_scalar = ov_opset.squeeze(signal_len_1d, zero_const_1d).output(0)

minus_one = ov_opset.constant([-1], Type.i32).output(0)
shape_2d = ov_opset.concat([minus_one, signal_len_1d], axis=0).output(0)
x_2d = ov_opset.reshape(x, shape_2d, False).output(0)

seq_len_c = ov_opset.constant(sequence_length, Type.i32).output(0)
stride_c = ov_opset.constant(sequence_stride, Type.i32).output(0)
diff = ov_opset.subtract(signal_len_scalar, seq_len_c).output(0)
num_seq_scalar = ov_opset.add(
ov_opset.divide(diff, stride_c).output(0),
ov_opset.constant(1, Type.i32).output(0),
).output(0)

row_stop = ov_opset.multiply(num_seq_scalar, stride_c).output(0)
row_idx = ov_opset.range(
ov_opset.constant(0, Type.i32).output(0),
row_stop,
stride_c,
output_type=Type.i32,
).output(0)
row_idx_2d = ov_opset.unsqueeze(
row_idx, ov_opset.constant([1], Type.i32)
).output(0)

col_idx = ov_opset.constant(
np.arange(sequence_length, dtype=np.int32)
).output(0)
col_idx_2d = ov_opset.unsqueeze(col_idx, zero_const_1d).output(0)

indices = ov_opset.add(row_idx_2d, col_idx_2d).output(0)

gathered = ov_opset.gather(
x_2d, indices, ov_opset.constant(1, Type.i32)
).output(0)

batch_shape = ov_opset.slice(
shape_tensor,
start=zero_const_1d,
stop=ov_opset.constant([ndim - 1], Type.i32),
step=ov_opset.constant([1], Type.i32),
axes=zero_const_1d,
).output(0)
num_seq_1d = ov_opset.unsqueeze(num_seq_scalar, zero_const_1d).output(0)
seq_len_1d = ov_opset.constant([sequence_length], Type.i32).output(0)
out_shape = ov_opset.concat(
[batch_shape, num_seq_1d, seq_len_1d], axis=0
).output(0)
result = ov_opset.reshape(gathered, out_shape, False).output(0)

return OpenVINOKerasTensor(result)


def _dft(x, axes_offsets, inverse=False):
Expand Down Expand Up @@ -716,4 +775,52 @@ def erf(x):


def erfinv(x):
raise NotImplementedError("`erfinv` is not supported with openvino backend")
# TODO: Float64 infinity values are clamped on CPU backend,
# breaking erfinv(±1) = ±inf
# See https://github.com/openvinotoolkit/openvino/issues/34138
# Tests excluded: test_erfinv_operation_basic, test_erfinv_operation_dtype
x = get_ov_output(x)
dtype = x.get_element_type()

a = 0.147
two_over_pi_a = 2.0 / (np.pi * a)
two_over_sqrt_pi = 2.0 / np.sqrt(np.pi)

one = ov_opset.constant(1.0, dtype).output(0)
half = ov_opset.constant(0.5, dtype).output(0)

x_sq = ov_opset.multiply(x, x).output(0)
log_term = ov_opset.log(ov_opset.subtract(one, x_sq).output(0)).output(0)

k = ov_opset.add(
ov_opset.constant(two_over_pi_a, dtype).output(0),
ov_opset.multiply(half, log_term).output(0),
).output(0)

inner = ov_opset.subtract(
ov_opset.multiply(k, k).output(0),
ov_opset.multiply(
ov_opset.constant(1.0 / a, dtype).output(0), log_term
).output(0),
).output(0)

y0 = ov_opset.multiply(
ov_opset.sign(x).output(0),
ov_opset.sqrt(
ov_opset.subtract(ov_opset.sqrt(inner).output(0), k).output(0)
).output(0),
).output(0)

erf_err = ov_opset.subtract(ov_opset.erf(y0).output(0), x).output(0)

y0_sq = ov_opset.multiply(y0, y0).output(0)
exp_term = ov_opset.exp(ov_opset.negative(y0_sq).output(0)).output(0)
deriv = ov_opset.multiply(
ov_opset.constant(two_over_sqrt_pi, dtype).output(0),
exp_term,
).output(0)
y1 = ov_opset.subtract(
y0, ov_opset.divide(erf_err, deriv).output(0)
).output(0)

return OpenVINOKerasTensor(y1)